From 678c6fad94c5a0631b9ecd72f1f963c77096e5f9 Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Sun, 17 Sep 2023 14:53:16 +0200 Subject: [PATCH 001/185] checked_ilog: improve performance --- library/core/src/num/int_macros.rs | 8 ++++---- library/core/src/num/uint_macros.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 1f43520e1b3..aed6a34788d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2477,18 +2477,18 @@ macro_rules! int_impl { None } else { let mut n = 0; - let mut r = self; + let mut r = 1; // Optimization for 128 bit wide integers. if Self::BITS == 128 { let b = Self::ilog2(self) / (Self::ilog2(base) + 1); n += b; - r /= base.pow(b as u32); + r *= base.pow(b); } - while r >= base { - r /= base; + while r <= self / base { n += 1; + r *= base; } Some(n) } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 23ca37817d4..d7f2ad8c702 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -810,18 +810,18 @@ macro_rules! uint_impl { None } else { let mut n = 0; - let mut r = self; + let mut r = 1; // Optimization for 128 bit wide integers. if Self::BITS == 128 { let b = Self::ilog2(self) / (Self::ilog2(base) + 1); n += b; - r /= base.pow(b as u32); + r *= base.pow(b); } - while r >= base { - r /= base; + while r <= self / base { n += 1; + r *= base; } Some(n) } From 91af5f59b4301f455094443050ac640e26b3547e Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Mon, 18 Sep 2023 14:55:37 +0200 Subject: [PATCH 002/185] checked_ilog: set `n` and `r` directly avoiding arithmetic operations --- library/core/src/num/int_macros.rs | 5 ++--- library/core/src/num/uint_macros.rs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index aed6a34788d..968ff8d8332 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2481,9 +2481,8 @@ macro_rules! int_impl { // Optimization for 128 bit wide integers. if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r *= base.pow(b); + n = self.ilog2() / (base.ilog2() + 1); + r = base.pow(n); } while r <= self / base { diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index d7f2ad8c702..ce4a0ee98b1 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -814,9 +814,8 @@ macro_rules! uint_impl { // Optimization for 128 bit wide integers. if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r *= base.pow(b); + n = self.ilog2() / (base.ilog2() + 1); + r = base.pow(n); } while r <= self / base { From 0c80243e149e522f28eb3650f42d82a0c5226491 Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Mon, 18 Sep 2023 14:57:32 +0200 Subject: [PATCH 003/185] checked_ilog: add comments explaining the correctness of the 128 bit optimization --- library/core/src/num/int_macros.rs | 8 ++++++++ library/core/src/num/uint_macros.rs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 968ff8d8332..6deb4acb282 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2481,6 +2481,14 @@ macro_rules! int_impl { // Optimization for 128 bit wide integers. if Self::BITS == 128 { + // The following is a correct lower bound for ⌊log(base,self)⌋ because + // + // log(base,self) = log(2,self) / log(2,base) + // ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) + // + // hence + // + // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ . n = self.ilog2() / (base.ilog2() + 1); r = base.pow(n); } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index ce4a0ee98b1..78f8bb27a5d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -814,6 +814,14 @@ macro_rules! uint_impl { // Optimization for 128 bit wide integers. if Self::BITS == 128 { + // The following is a correct lower bound for ⌊log(base,self)⌋ because + // + // log(base,self) = log(2,self) / log(2,base) + // ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) + // + // hence + // + // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ . n = self.ilog2() / (base.ilog2() + 1); r = base.pow(n); } From 0f9a4d9ab45f3f2054e7909425d288ce3fa43e11 Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Mon, 18 Sep 2023 15:15:52 +0200 Subject: [PATCH 004/185] checked_ilog: add benchmarks --- library/core/benches/num/int_log/mod.rs | 79 +++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs index bb61224b5ba..3807cd5d76c 100644 --- a/library/core/benches/num/int_log/mod.rs +++ b/library/core/benches/num/int_log/mod.rs @@ -1,7 +1,7 @@ use rand::Rng; use test::{black_box, Bencher}; -macro_rules! int_log_bench { +macro_rules! int_log10_bench { ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { #[bench] fn $predictable(bench: &mut Bencher) { @@ -51,8 +51,75 @@ macro_rules! int_log_bench { }; } -int_log_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} -int_log_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} -int_log_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} -int_log_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} -int_log_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} +int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} +int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} +int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} +int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} +int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} + +macro_rules! int_log_bench { + ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => { + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t; + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $geometric(bench: &mut Bencher) { + let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]; + let base_and_numbers: Vec<($t, Vec<$t>)> = bases + .iter() + .map(|&b| { + let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect(); + (b, numbers) + }) + .collect(); + bench.iter(|| { + for (b, numbers) in &base_and_numbers { + for &x in numbers { + black_box(black_box(x).ilog(black_box(*b))); + } + } + }); + } + }; +} + +int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric} +int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric} +int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric} +int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric} +int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric} From 3de51c95a100e4a828dd00b2fa7e9d382050449e Mon Sep 17 00:00:00 2001 From: Federico Stra Date: Fri, 22 Sep 2023 16:02:13 +0200 Subject: [PATCH 005/185] checked_ilog: remove duplication by delegating to unsigned integers --- library/core/src/num/int_macros.rs | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 6deb4acb282..e353552cb63 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2476,28 +2476,9 @@ macro_rules! int_impl { if self <= 0 || base <= 1 { None } else { - let mut n = 0; - let mut r = 1; - - // Optimization for 128 bit wide integers. - if Self::BITS == 128 { - // The following is a correct lower bound for ⌊log(base,self)⌋ because - // - // log(base,self) = log(2,self) / log(2,base) - // ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) - // - // hence - // - // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ . - n = self.ilog2() / (base.ilog2() + 1); - r = base.pow(n); - } - - while r <= self / base { - n += 1; - r *= base; - } - Some(n) + // Delegate to the unsigned implementation. + // The condition makes sure that both casts are exact. + (self as $UnsignedT).checked_ilog(base as $UnsignedT) } } From 3a2ae6410dcca044f8e866c040c0f35592eba538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 4 Jan 2024 18:10:14 +0200 Subject: [PATCH 006/185] Teach cargo about cfg(rust_analyzer) --- crates/proc-macro-srv-cli/build.rs | 5 +++++ crates/proc-macro-srv/build.rs | 2 ++ crates/proc-macro-srv/proc-macro-test/build.rs | 2 +- crates/proc-macro-srv/proc-macro-test/imp/build.rs | 5 +++++ 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 crates/proc-macro-srv-cli/build.rs create mode 100644 crates/proc-macro-srv/proc-macro-test/imp/build.rs diff --git a/crates/proc-macro-srv-cli/build.rs b/crates/proc-macro-srv-cli/build.rs new file mode 100644 index 00000000000..07f914fece0 --- /dev/null +++ b/crates/proc-macro-srv-cli/build.rs @@ -0,0 +1,5 @@ +//! This teaches cargo about our cfg(rust_analyzer) + +fn main() { + println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); +} diff --git a/crates/proc-macro-srv/build.rs b/crates/proc-macro-srv/build.rs index a8c732f3154..874d1c6cd38 100644 --- a/crates/proc-macro-srv/build.rs +++ b/crates/proc-macro-srv/build.rs @@ -4,6 +4,8 @@ use std::{env, fs::File, io::Write, path::PathBuf, process::Command}; fn main() { + println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); + let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); path.push("rustc_version.rs"); let mut f = File::create(&path).unwrap(); diff --git a/crates/proc-macro-srv/proc-macro-test/build.rs b/crates/proc-macro-srv/proc-macro-test/build.rs index 7299147686d..17c2595e8ad 100644 --- a/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/crates/proc-macro-srv/proc-macro-test/build.rs @@ -40,7 +40,7 @@ fn main() { println!("Creating {}", src_dir.display()); std::fs::create_dir_all(src_dir).unwrap(); - for item_els in [&["Cargo.toml"][..], &["src", "lib.rs"]] { + for item_els in [&["Cargo.toml"][..], &["build.rs"][..], &["src", "lib.rs"]] { let mut src = imp_dir.clone(); let mut dst = staging_dir.clone(); for el in item_els { diff --git a/crates/proc-macro-srv/proc-macro-test/imp/build.rs b/crates/proc-macro-srv/proc-macro-test/imp/build.rs new file mode 100644 index 00000000000..07f914fece0 --- /dev/null +++ b/crates/proc-macro-srv/proc-macro-test/imp/build.rs @@ -0,0 +1,5 @@ +//! This teaches cargo about our cfg(rust_analyzer) + +fn main() { + println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); +} From 1141259a23cedb07b07dac3ccf4ff054d92adcf5 Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Sun, 24 Mar 2024 09:51:08 -0400 Subject: [PATCH 007/185] Init Wrap/Unwrap cfg_attr --- .../src/handlers/wrap_unwrap_cfg_attr.rs | 538 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 3 + 2 files changed, 541 insertions(+) create mode 100644 crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs new file mode 100644 index 00000000000..4b8fb1e78a8 --- /dev/null +++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -0,0 +1,538 @@ +use ide_db::source_change::SourceChangeBuilder; +use itertools::Itertools; +use syntax::{ + algo, + ast::{self, make, AstNode}, + ted::{self, Position}, + AstToken, NodeOrToken, SyntaxToken, TextRange, T, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: wrap_unwrap_cfg_attr +// +// Wraps an attribute to a cfg_attr attribute or unwraps a cfg_attr attribute to the inner attributes. +// +// ``` +// #[derive$0(Debug)] +// struct S { +// field: i32 +// } +// ``` +// -> +// ``` +// #[cfg_attr($0, derive(Debug))] +// struct S { +// field: i32 +// } + +enum WrapUnwrapOption { + WrapDerive { derive: TextRange, attr: ast::Attr }, + WrapAttr(ast::Attr), +} + +/// Attempts to get the derive attribute from a derive attribute list +/// +/// This will collect all the tokens in the "path" within the derive attribute list +/// But a derive attribute list doesn't have paths. So we need to collect all the tokens before and after the ident +/// +/// If this functions return None just map to WrapAttr +fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { + let attempt_attr = || { + { + let mut derive = ident.text_range(); + // TokenTree is all the tokens between the `(` and `)`. They do not have paths. So a path `serde::Serialize` would be [Ident Colon Colon Ident] + // So lets say we have derive(Debug, serde::Serialize, Copy) ident would be on Serialize + // We need to grab all previous tokens until we find a `,` or `(` and all following tokens until we find a `,` or `)` + // We also want to consume the following comma if it exists + + let mut prev = algo::skip_trivia_token( + ident.prev_sibling_or_token()?.into_token()?, + syntax::Direction::Prev, + )?; + let mut following = algo::skip_trivia_token( + ident.next_sibling_or_token()?.into_token()?, + syntax::Direction::Next, + )?; + if (prev.kind() == T![,] || prev.kind() == T!['(']) + && (following.kind() == T![,] || following.kind() == T!['(']) + { + // This would be a single ident such as Debug. As no path is present + if following.kind() == T![,] { + derive = derive.cover(following.text_range()); + } + + Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() }) + } else { + // Collect the path + + while let Some(prev_token) = algo::skip_trivia_token(prev, syntax::Direction::Prev) + { + let kind = prev_token.kind(); + if kind == T![,] || kind == T!['('] { + break; + } + derive = derive.cover(prev_token.text_range()); + prev = prev_token.prev_sibling_or_token()?.into_token()?; + } + while let Some(next_token) = + algo::skip_trivia_token(following.clone(), syntax::Direction::Next) + { + let kind = next_token.kind(); + if kind != T![')'] { + // We also want to consume a following comma + derive = derive.cover(next_token.text_range()); + } + following = next_token.next_sibling_or_token()?.into_token()?; + + if kind == T![,] || kind == T![')'] { + break; + } + } + Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() }) + } + } + }; + if ident.parent().and_then(ast::TokenTree::cast).is_none() + || !attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() + { + WrapUnwrapOption::WrapAttr(attr) + } else { + attempt_attr().unwrap_or(WrapUnwrapOption::WrapAttr(attr)) + } +} +pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let option = if ctx.has_empty_selection() { + let ident = ctx.find_token_at_offset::().map(|v| v.syntax().clone()); + let attr = ctx.find_node_at_offset::(); + match (attr, ident) { + (Some(attr), Some(ident)) + if attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() => + { + Some(attempt_get_derive(attr.clone(), ident)) + } + (Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)), + _ => None, + } + } else { + let covering_element = ctx.covering_element(); + match covering_element { + NodeOrToken::Node(node) => ast::Attr::cast(node).map(WrapUnwrapOption::WrapAttr), + NodeOrToken::Token(ident) if ident.kind() == syntax::T![ident] => { + let attr = ident.parent_ancestors().find_map(ast::Attr::cast)?; + Some(attempt_get_derive(attr.clone(), ident)) + } + _ => None, + } + }?; + match option { + WrapUnwrapOption::WrapAttr(attr) if attr.simple_name().as_deref() == Some("cfg_attr") => { + unwrap_cfg_attr(acc, attr) + } + WrapUnwrapOption::WrapAttr(attr) => wrap_cfg_attr(acc, ctx, attr), + WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive), + } +} + +fn wrap_derive( + acc: &mut Assists, + ctx: &AssistContext<'_>, + attr: ast::Attr, + derive_element: TextRange, +) -> Option<()> { + let range = attr.syntax().text_range(); + let token_tree = attr.token_tree()?; + let mut path_text = String::new(); + + let mut cfg_derive_tokens = Vec::new(); + let mut new_derive = Vec::new(); + + for tt in token_tree.token_trees_and_tokens() { + let NodeOrToken::Token(token) = tt else { + continue; + }; + if token.kind() == T!['('] || token.kind() == T![')'] { + continue; + } + + if derive_element.contains_range(token.text_range()) { + if token.kind() != T![,] { + path_text.push_str(token.text()); + cfg_derive_tokens.push(NodeOrToken::Token(token)); + } + } else { + new_derive.push(NodeOrToken::Token(token)); + } + } + let handle_source_change = |edit: &mut SourceChangeBuilder| { + let new_derive = make::attr_outer(make::meta_token_tree( + make::ext::ident_path("derive"), + make::token_tree(T!['('], new_derive), + )) + .clone_for_update(); + let meta = make::meta_token_tree( + make::ext::ident_path("cfg_attr"), + make::token_tree( + T!['('], + vec![ + NodeOrToken::Token(make::token(T![,])), + NodeOrToken::Token(make::tokens::whitespace(" ")), + NodeOrToken::Token(make::tokens::ident("derive")), + NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)), + ], + ), + ); + // Remove the derive attribute + let edit_attr = edit.make_syntax_mut(attr.syntax().clone()); + + ted::replace(edit_attr, new_derive.syntax().clone()); + let cfg_attr = make::attr_outer(meta).clone_for_update(); + + ted::insert_all_raw( + Position::after(new_derive.syntax().clone()), + vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()], + ); + if let Some(snippet_cap) = ctx.config.snippet_cap { + if let Some(first_meta) = + cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) + { + edit.add_tabstop_after_token(snippet_cap, first_meta) + } + } + }; + + acc.add( + AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor), + format!("Wrap #[derive({path_text})] in `cfg_attr`",), + range, + handle_source_change, + ); + Some(()) +} +fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> Option<()> { + let range = attr.syntax().text_range(); + let path = attr.path()?; + let handle_source_change = |edit: &mut SourceChangeBuilder| { + let mut raw_tokens = vec![ + NodeOrToken::Token(make::token(T![,])), + NodeOrToken::Token(make::tokens::whitespace(" ")), + ]; + path.syntax().descendants_with_tokens().for_each(|it| { + if let NodeOrToken::Token(token) = it { + raw_tokens.push(NodeOrToken::Token(token)); + } + }); + if let Some(meta) = attr.meta() { + if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) { + raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" "))); + raw_tokens.push(NodeOrToken::Token(eq.clone())); + raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" "))); + + expr.syntax().descendants_with_tokens().for_each(|it| { + if let NodeOrToken::Token(token) = it { + raw_tokens.push(NodeOrToken::Token(token)); + } + }); + } else if let Some(tt) = meta.token_tree() { + raw_tokens.extend(tt.token_trees_and_tokens()); + } + } + let meta = make::meta_token_tree( + make::ext::ident_path("cfg_attr"), + make::token_tree(T!['('], raw_tokens), + ); + let cfg_attr = if attr.excl_token().is_some() { + make::attr_inner(meta) + } else { + make::attr_outer(meta) + } + .clone_for_update(); + let attr_syntax = edit.make_syntax_mut(attr.syntax().clone()); + ted::replace(attr_syntax, cfg_attr.syntax()); + + if let Some(snippet_cap) = ctx.config.snippet_cap { + if let Some(first_meta) = + cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) + { + edit.add_tabstop_after_token(snippet_cap, first_meta) + } + } + }; + acc.add( + AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor), + "Convert to `cfg_attr`", + range, + handle_source_change, + ); + Some(()) +} +fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> { + let range = attr.syntax().text_range(); + let meta = attr.meta()?; + let meta_tt = meta.token_tree()?; + let mut inner_attrs = Vec::with_capacity(1); + let mut found_comma = false; + let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable(); + while let Some(tt) = iter.next() { + if let NodeOrToken::Token(token) = &tt { + if token.kind() == T![')'] { + break; + } + if token.kind() == T![,] { + found_comma = true; + continue; + } + } + if !found_comma { + continue; + } + let Some(attr_name) = tt.into_token().and_then(|token| { + if token.kind() == T![ident] { + Some(make::ext::ident_path(token.text())) + } else { + None + } + }) else { + continue; + }; + let next_tt = iter.next()?; + let meta = match next_tt { + NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt), + NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => { + make::meta_path(attr_name) + } + NodeOrToken::Token(token) => { + let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?; + if equals.kind() != T![=] { + return None; + } + let expr_token = + algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next) + .and_then(|it| { + if it.kind().is_literal() { + Some(make::expr_literal(it.text())) + } else { + None + } + })?; + make::meta_expr(attr_name, ast::Expr::Literal(expr_token)) + } + }; + if attr.excl_token().is_some() { + inner_attrs.push(make::attr_inner(meta)); + } else { + inner_attrs.push(make::attr_outer(meta)); + } + } + if inner_attrs.is_empty() { + return None; + } + let handle_source_change = |f: &mut SourceChangeBuilder| { + let inner_attrs = inner_attrs.iter().map(|it| it.to_string()).join("\n"); + f.replace(range, inner_attrs); + }; + acc.add( + AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor), + "Extract Inner Attributes from `cfg_attr`", + range, + handle_source_change, + ); + Some(()) +} +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::*; + + #[test] + fn test_basic_to_from_cfg_attr() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive$0(Debug)] + pub struct Test { + test: u32, + } + "#, + r#" + #[cfg_attr($0, derive(Debug))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[cfg_attr(debug_assertions, $0 derive(Debug))] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Debug)] + pub struct Test { + test: u32, + } + "#, + ); + } + #[test] + fn to_from_path_attr() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[foo$0] + test: u32, + } + "#, + r#" + pub struct Test { + #[cfg_attr($0, foo)] + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[cfg_attr(debug_assertions$0, foo)] + test: u32, + } + "#, + r#" + pub struct Test { + #[foo] + test: u32, + } + "#, + ); + } + #[test] + fn to_from_eq_attr() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[foo = "bar"$0] + test: u32, + } + "#, + r#" + pub struct Test { + #[cfg_attr($0, foo = "bar")] + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[cfg_attr(debug_assertions$0, foo = "bar")] + test: u32, + } + "#, + r#" + pub struct Test { + #[foo = "bar"] + test: u32, + } + "#, + ); + } + #[test] + fn inner_attrs() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #![no_std$0] + "#, + r#" + #![cfg_attr($0, no_std)] + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #![cfg_attr(not(feature = "std")$0, no_std)] + "#, + r#" + #![no_std] + "#, + ); + } + #[test] + fn test_derive_wrap() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Debug$0, Clone, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive( Clone, Copy)] + #[cfg_attr($0, derive(Debug))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Clone, Debug$0, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Clone, Copy)] + #[cfg_attr($0, derive(Debug))] + pub struct Test { + test: u32, + } + "#, + ); + } + #[test] + fn test_derive_wrap_with_path() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(std::fmt::Debug$0, Clone, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive( Clone, Copy)] + #[cfg_attr($0, derive(std::fmt::Debug))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Clone, std::fmt::Debug$0, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Clone, Copy)] + #[cfg_attr($0, derive(std::fmt::Debug))] + pub struct Test { + test: u32, + } + "#, + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 8f0b8f861c2..1364120ea23 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -217,6 +217,7 @@ mod handlers { mod unwrap_result_return_type; mod unwrap_tuple; mod wrap_return_type_in_result; + mod wrap_unwrap_cfg_attr; pub(crate) fn all() -> &'static [Handler] { &[ @@ -342,6 +343,8 @@ mod handlers { unwrap_tuple::unwrap_tuple, unqualify_method_call::unqualify_method_call, wrap_return_type_in_result::wrap_return_type_in_result, + wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr, + // These are manually sorted for better priorities. By default, // priority is determined by the size of the target range (smaller // target wins). If the ranges are equal, position in this list is From ecac8e3514b84104dc932cb92524cd3152453218 Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Sun, 24 Mar 2024 10:37:41 -0400 Subject: [PATCH 008/185] Format and codegen for attr --- crates/ide-assists/src/lib.rs | 2 +- crates/ide-assists/src/tests/generated.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 1364120ea23..024448ef1e7 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -344,7 +344,7 @@ mod handlers { unqualify_method_call::unqualify_method_call, wrap_return_type_in_result::wrap_return_type_in_result, wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr, - + // These are manually sorted for better priorities. By default, // priority is determined by the size of the target range (smaller // target wins). If the ranges are equal, position in this list is diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index a66e199a75b..2bf09347672 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -3151,3 +3151,22 @@ fn foo() -> Result { Ok(42i32) } "#####, ) } + +#[test] +fn doctest_wrap_unwrap_cfg_attr() { + check_doc_test( + "wrap_unwrap_cfg_attr", + r#####" +#[derive$0(Debug)] +struct S { + field: i32 +} +"#####, + r#####" +#[cfg_attr($0, derive(Debug))] +struct S { + field: i32 +} +"#####, + ) +} From e3f9a0afe143f4f589c1d57bb1aab6c5770dc14a Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Sun, 24 Mar 2024 10:38:03 -0400 Subject: [PATCH 009/185] Fixed cursor being at end --- .../src/handlers/wrap_unwrap_cfg_attr.rs | 73 +++++++++++++++---- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 4b8fb1e78a8..0fa46ef43a1 100644 --- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -4,7 +4,7 @@ use syntax::{ algo, ast::{self, make, AstNode}, ted::{self, Position}, - AstToken, NodeOrToken, SyntaxToken, TextRange, T, + NodeOrToken, SyntaxToken, TextRange, T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -55,39 +55,46 @@ fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { syntax::Direction::Next, )?; if (prev.kind() == T![,] || prev.kind() == T!['(']) - && (following.kind() == T![,] || following.kind() == T!['(']) + && (following.kind() == T![,] || following.kind() == T![')']) { // This would be a single ident such as Debug. As no path is present if following.kind() == T![,] { derive = derive.cover(following.text_range()); + } else if following.kind() == T![')'] && prev.kind() == T![,] { + derive = derive.cover(prev.text_range()); } Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() }) } else { + let mut consumed_comma = false; // Collect the path - while let Some(prev_token) = algo::skip_trivia_token(prev, syntax::Direction::Prev) { let kind = prev_token.kind(); - if kind == T![,] || kind == T!['('] { + if kind == T![,] { + consumed_comma = true; + derive = derive.cover(prev_token.text_range()); break; + } else if kind == T!['('] { + break; + } else { + derive = derive.cover(prev_token.text_range()); } - derive = derive.cover(prev_token.text_range()); prev = prev_token.prev_sibling_or_token()?.into_token()?; } while let Some(next_token) = algo::skip_trivia_token(following.clone(), syntax::Direction::Next) { let kind = next_token.kind(); - if kind != T![')'] { - // We also want to consume a following comma - derive = derive.cover(next_token.text_range()); + match kind { + T![,] if !consumed_comma => { + derive = derive.cover(next_token.text_range()); + break; + } + T![')'] | T![,] => break, + _ => derive = derive.cover(next_token.text_range()), } following = next_token.next_sibling_or_token()?.into_token()?; - - if kind == T![,] || kind == T![')'] { - break; - } } Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() }) } @@ -103,7 +110,7 @@ fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { } pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let option = if ctx.has_empty_selection() { - let ident = ctx.find_token_at_offset::().map(|v| v.syntax().clone()); + let ident = ctx.find_token_syntax_at_offset(T![ident]); let attr = ctx.find_node_at_offset::(); match (attr, ident) { (Some(attr), Some(ident)) @@ -111,6 +118,7 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) - { Some(attempt_get_derive(attr.clone(), ident)) } + (Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)), _ => None, } @@ -156,7 +164,7 @@ fn wrap_derive( } if derive_element.contains_range(token.text_range()) { - if token.kind() != T![,] { + if token.kind() != T![,] && token.kind() != syntax::SyntaxKind::WHITESPACE { path_text.push_str(token.text()); cfg_derive_tokens.push(NodeOrToken::Token(token)); } @@ -527,7 +535,42 @@ mod tests { } "#, r#" - #[derive(Clone, Copy)] + #[derive(Clone, Copy)] + #[cfg_attr($0, derive(std::fmt::Debug))] + pub struct Test { + test: u32, + } + "#, + ); + } + #[test] + fn test_derive_wrap_at_end() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(std::fmt::Debug, Clone, Cop$0y)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(std::fmt::Debug, Clone)] + #[cfg_attr($0, derive(Copy))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Clone, Copy, std::fmt::D$0ebug)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Clone, Copy)] #[cfg_attr($0, derive(std::fmt::Debug))] pub struct Test { test: u32, From 174af88e76fce93ce69beb1e433177cb95c50fa7 Mon Sep 17 00:00:00 2001 From: "Alexis (Poliorcetics) Bourget" Date: Thu, 21 Mar 2024 18:56:52 +0100 Subject: [PATCH 010/185] feat: Add `rust-analyzer.cargo.allTargets` to configure passing `--all-targets` to cargo invocations --- crates/project-model/src/build_scripts.rs | 4 +++- crates/project-model/src/cargo_workspace.rs | 2 ++ crates/rust-analyzer/src/config.rs | 10 +++++++--- docs/user/generated_config.adoc | 10 ++++++++-- editors/code/package.json | 14 +++++++++++--- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index d40eb26063d..e6379bf902a 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -86,7 +86,9 @@ impl WorkspaceBuildScripts { // --all-targets includes tests, benches and examples in addition to the // default lib and bins. This is an independent concept from the --target // flag below. - cmd.arg("--all-targets"); + if config.all_targets { + cmd.arg("--all-targets"); + } if let Some(target) = &config.target { cmd.args(["--target", target]); diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 51c1b094f7b..82fee7112fb 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -76,6 +76,8 @@ impl Default for CargoFeatures { #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CargoConfig { + /// Whether to pass `--all-targets` to cargo invocations. + pub all_targets: bool, /// List of features to activate. pub features: CargoFeatures, /// rustc target diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a5545e79847..38a6618af44 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -71,6 +71,9 @@ config_data! { /// How many worker threads to handle priming caches. The default `0` means to pick automatically. cachePriming_numThreads: ParallelCachePrimingNumThreads = "0", + /// Pass `--all-targets` to cargo invocation. Overridden by `#rust-analyzer.check.allTargets#` + /// when the latter is set. + cargo_allTargets: bool = "true", /// Automatically refresh project info via `cargo metadata` on /// `Cargo.toml` or `.cargo/config.toml` changes. cargo_autoreload: bool = "true", @@ -163,8 +166,8 @@ config_data! { /// Run the check command for diagnostics on save. checkOnSave | checkOnSave_enable: bool = "true", - /// Check all targets and tests (`--all-targets`). - check_allTargets | checkOnSave_allTargets: bool = "true", + /// Check all targets and tests (`--all-targets`). Overrides `#rust-analyzer.cargo.allTargets#`. + check_allTargets | checkOnSave_allTargets: Option = "null", /// Cargo command to use for `cargo check`. check_command | checkOnSave_command: String = "\"check\"", /// Extra arguments for `cargo check`. @@ -1273,6 +1276,7 @@ impl Config { let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata; CargoConfig { + all_targets: self.data.cargo_allTargets, features: match &self.data.cargo_features { CargoFeaturesDef::All => CargoFeatures::All, CargoFeaturesDef::Selected(features) => CargoFeatures::Selected { @@ -1383,7 +1387,7 @@ impl Config { targets => Some(targets.into()), }) .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()), - all_targets: self.data.check_allTargets, + all_targets: self.data.check_allTargets.unwrap_or(self.data.cargo_allTargets), no_default_features: self .data .check_noDefaultFeatures diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index c4024f6d282..5caefc64221 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -19,6 +19,12 @@ Warm up caches on project load. -- How many worker threads to handle priming caches. The default `0` means to pick automatically. -- +[[rust-analyzer.cargo.allTargets]]rust-analyzer.cargo.allTargets (default: `true`):: ++ +-- +Pass `--all-targets` to cargo invocation. Overridden by `#rust-analyzer.check.allTargets#` +when the latter is set. +-- [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: + -- @@ -164,10 +170,10 @@ Unsets the implicit `#[cfg(test)]` for the specified crates. -- Run the check command for diagnostics on save. -- -[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `true`):: +[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `null`):: + -- -Check all targets and tests (`--all-targets`). +Check all targets and tests (`--all-targets`). Overrides `#rust-analyzer.cargo.allTargets#`. -- [[rust-analyzer.check.command]]rust-analyzer.check.command (default: `"check"`):: + diff --git a/editors/code/package.json b/editors/code/package.json index c3ea1ceeb69..0bfe0cc4e44 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -546,6 +546,11 @@ "minimum": 0, "maximum": 255 }, + "rust-analyzer.cargo.allTargets": { + "markdownDescription": "Pass `--all-targets` to cargo invocation. Overridden by `#rust-analyzer.check.allTargets#`\nwhen the latter is set.", + "default": true, + "type": "boolean" + }, "rust-analyzer.cargo.autoreload": { "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` or `.cargo/config.toml` changes.", "default": true, @@ -707,9 +712,12 @@ "type": "boolean" }, "rust-analyzer.check.allTargets": { - "markdownDescription": "Check all targets and tests (`--all-targets`).", - "default": true, - "type": "boolean" + "markdownDescription": "Check all targets and tests (`--all-targets`). Overrides `#rust-analyzer.cargo.allTargets#`.", + "default": null, + "type": [ + "null", + "boolean" + ] }, "rust-analyzer.check.command": { "markdownDescription": "Cargo command to use for `cargo check`.", From 5e370b1cb842e2e29a3fc3b8a599cee5b6040a02 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 27 Mar 2024 10:33:20 -0700 Subject: [PATCH 011/185] Use crate root to choose relevant workspace for flycheck --- crates/project-model/src/project_json.rs | 2 +- crates/rust-analyzer/src/handlers/notification.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 512588cc8f8..54674a6f0f1 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -74,7 +74,7 @@ pub struct ProjectJson { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Crate { pub(crate) display_name: Option, - pub(crate) root_module: AbsPathBuf, + pub root_module: AbsPathBuf, pub(crate) edition: Edition, pub(crate) version: Option, pub(crate) deps: Vec, diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index b5c4a4f435e..3b232578185 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -296,10 +296,9 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { }) } project_model::ProjectWorkspace::Json { project, .. } => { - if !project - .crates() - .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)) - { + if !project.crates().any(|(_, krate)| { + crate_root_paths.contains(&krate.root_module.as_path()) + }) { return None; } None From 0f72ab1dd34715b36586c89568db80c68d364b01 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 27 Mar 2024 10:40:17 -0700 Subject: [PATCH 012/185] Define a separate type for crate indexes in a rust-project.json --- crates/project-model/src/project_json.rs | 41 +++++++++++------------- crates/project-model/src/workspace.rs | 19 +++++------ 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 54674a6f0f1..1872d27c567 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -49,8 +49,7 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use base_db::{CrateDisplayName, CrateId, CrateName, Dependency}; -use la_arena::RawIdx; +use base_db::{CrateDisplayName, CrateName}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::{de, Deserialize}; @@ -77,7 +76,7 @@ pub struct Crate { pub root_module: AbsPathBuf, pub(crate) edition: Edition, pub(crate) version: Option, - pub(crate) deps: Vec, + pub(crate) deps: Vec, pub(crate) cfg: Vec, pub(crate) target: Option, pub(crate) env: FxHashMap, @@ -128,16 +127,7 @@ impl ProjectJson { root_module, edition: crate_data.edition.into(), version: crate_data.version.as_ref().map(ToString::to_string), - deps: crate_data - .deps - .into_iter() - .map(|dep_data| { - Dependency::new( - dep_data.name, - CrateId::from_raw(RawIdx::from(dep_data.krate as u32)), - ) - }) - .collect::>(), + deps: crate_data.deps, cfg: crate_data.cfg, target: crate_data.target, env: crate_data.env, @@ -161,11 +151,8 @@ impl ProjectJson { } /// Returns an iterator over the crates in the project. - pub fn crates(&self) -> impl Iterator + '_ { - self.crates - .iter() - .enumerate() - .map(|(idx, krate)| (CrateId::from_raw(RawIdx::from(idx as u32)), krate)) + pub fn crates(&self) -> impl Iterator { + self.crates.iter().enumerate().map(|(idx, krate)| (CrateArrayIdx(idx), krate)) } /// Returns the path to the project's root folder. @@ -188,7 +175,7 @@ struct CrateData { edition: EditionData, #[serde(default)] version: Option, - deps: Vec, + deps: Vec, #[serde(default)] cfg: Vec, target: Option, @@ -227,13 +214,21 @@ impl From for Edition { } } -#[derive(Deserialize, Debug, Clone)] -struct DepData { +/// Identifies a crate by position in the crates array. +/// +/// This will differ from `CrateId` when multiple `ProjectJson` +/// workspaces are loaded. +#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[serde(transparent)] +pub struct CrateArrayIdx(pub usize); + +#[derive(Deserialize, Debug, Clone, Eq, PartialEq)] +pub(crate) struct Dep { /// Identifies a crate by position in the crates array. #[serde(rename = "crate")] - krate: usize, + pub(crate) krate: CrateArrayIdx, #[serde(deserialize_with = "deserialize_crate_name")] - name: CrateName, + pub(crate) name: CrateName, } #[derive(Deserialize, Debug, Clone)] diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index b8c5885108d..d752b2f32ee 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -22,7 +22,7 @@ use crate::{ build_scripts::BuildScriptOutput, cargo_workspace::{DepKind, PackageData, RustLibSource}, cfg_flag::CfgFlag, - project_json::Crate, + project_json::{Crate, CrateArrayIdx}, rustc_cfg::{self, RustcCfgConfig}, sysroot::{SysrootCrate, SysrootMode}, target_data_layout::{self, RustcDataLayoutConfig}, @@ -878,12 +878,13 @@ fn project_json_to_crate_graph( let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned()); let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); - let crates: FxHashMap = project + + let idx_to_crate_id: FxHashMap = project .crates() - .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?))) + .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?))) .map( |( - crate_id, + idx, Crate { display_name, edition, @@ -939,13 +940,13 @@ fn project_json_to_crate_graph( proc_macros.insert(crate_graph_crate_id, node); } } - (crate_id, crate_graph_crate_id) + (idx, crate_graph_crate_id) }, ) .collect(); - for (from, krate) in project.crates() { - if let Some(&from) = crates.get(&from) { + for (from_idx, krate) in project.crates() { + if let Some(&from) = idx_to_crate_id.get(&from_idx) { if let Some((public_deps, libproc_macro)) = &sysroot_deps { public_deps.add_to_crate_graph(crate_graph, from); if let Some(proc_macro) = libproc_macro { @@ -954,8 +955,8 @@ fn project_json_to_crate_graph( } for dep in &krate.deps { - if let Some(&to) = crates.get(&dep.crate_id) { - add_dep(crate_graph, from, dep.name.clone(), to) + if let Some(&to) = idx_to_crate_id.get(&dep.krate) { + add_dep(crate_graph, from, dep.name.clone(), to); } } } From e8d6a5ec0b87c11d0e74c3d87f0e38b78b81ab73 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Thu, 28 Mar 2024 15:18:39 -0700 Subject: [PATCH 013/185] Rename RustTargetDefinition to CargoTaskDefinition --- editors/code/src/run.ts | 2 +- editors/code/src/tasks.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 02ccbb6956a..87f7e50b767 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -124,7 +124,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise program = await toolchain.cargoPath(); } - const definition: tasks.RustTargetDefinition = { + const definition: tasks.CargoTaskDefinition = { type: tasks.TASK_TYPE, program, args, diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 89abb37b0eb..00b644d5cc3 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -8,7 +8,7 @@ import { log } from "./util"; export const TASK_TYPE = "cargo"; export const TASK_SOURCE = "rust"; -export interface RustTargetDefinition extends vscode.TaskDefinition { +export interface CargoTaskDefinition extends vscode.TaskDefinition { program: string; args: string[]; cwd?: string; @@ -62,7 +62,7 @@ class RustTaskProvider implements vscode.TaskProvider { // we need to inform VSCode how to execute that command by creating // a ShellExecution for it. - const definition = task.definition as RustTargetDefinition; + const definition = task.definition as CargoTaskDefinition; if (definition.type === TASK_TYPE) { return await buildRustTask( @@ -80,7 +80,7 @@ class RustTaskProvider implements vscode.TaskProvider { export async function buildRustTask( scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined, - definition: RustTargetDefinition, + definition: CargoTaskDefinition, name: string, problemMatcher: string[], customRunner?: string, From a758e349bca3eb3ab8b775c51bd8c797869395b9 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Thu, 28 Mar 2024 15:25:05 -0700 Subject: [PATCH 014/185] Document CargoTaskDefinition and factor out converting TaskDefinition to Execution --- editors/code/src/run.ts | 17 ++----- editors/code/src/tasks.ts | 95 ++++++++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 49 deletions(-) diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 87f7e50b767..4470689cd8c 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -2,7 +2,6 @@ import * as vscode from "vscode"; import type * as lc from "vscode-languageclient"; import * as ra from "./lsp_ext"; import * as tasks from "./tasks"; -import * as toolchain from "./toolchain"; import type { CtxInit } from "./ctx"; import { makeDebugConfig } from "./debug"; @@ -112,22 +111,12 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise throw `Unexpected runnable kind: ${runnable.kind}`; } - let program: string; - let args = createArgs(runnable); - if (runnable.args.overrideCargo) { - // Split on spaces to allow overrides like "wrapper cargo". - const cargoParts = runnable.args.overrideCargo.split(" "); - - program = unwrapUndefinable(cargoParts[0]); - args = [...cargoParts.slice(1), ...args]; - } else { - program = await toolchain.cargoPath(); - } + const args = createArgs(runnable); const definition: tasks.CargoTaskDefinition = { type: tasks.TASK_TYPE, - program, - args, + command: unwrapUndefinable(args[0]), // run, test, etc... + args: args.slice(1), cwd: runnable.args.workspaceRoot || ".", env: prepareEnv(runnable, config.runnablesExtraEnv), overrideCargo: runnable.args.overrideCargo, diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 00b644d5cc3..2b3abc5d65f 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -2,17 +2,26 @@ import * as vscode from "vscode"; import * as toolchain from "./toolchain"; import type { Config } from "./config"; import { log } from "./util"; +import { unwrapUndefinable } from "./undefinable"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. export const TASK_TYPE = "cargo"; + export const TASK_SOURCE = "rust"; export interface CargoTaskDefinition extends vscode.TaskDefinition { - program: string; - args: string[]; + // The cargo command, such as "run" or "check". + command: string; + // Additional arguments passed to the cargo command. + args?: string[]; + // The working directory to run the cargo command in. cwd?: string; + // The shell environment. env?: { [key: string]: string }; + // Override the cargo executable name, such as + // "my_custom_cargo_bin". + overrideCargo?: string; } class RustTaskProvider implements vscode.TaskProvider { @@ -37,14 +46,12 @@ class RustTaskProvider implements vscode.TaskProvider { { command: "run", group: undefined }, ]; - const cargoPath = await toolchain.cargoPath(); - const tasks: vscode.Task[] = []; for (const workspaceTarget of vscode.workspace.workspaceFolders || []) { for (const def of defs) { const vscodeTask = await buildRustTask( workspaceTarget, - { type: TASK_TYPE, program: cargoPath, args: [def.command] }, + { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, this.config.problemMatcher, this.config.cargoRunner, @@ -86,36 +93,7 @@ export async function buildRustTask( customRunner?: string, throwOnError: boolean = false, ): Promise { - let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined; - - if (customRunner) { - const runnerCommand = `${customRunner}.buildShellExecution`; - try { - const runnerArgs = { - kind: TASK_TYPE, - args: definition.args, - cwd: definition.cwd, - env: definition.env, - }; - const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs); - if (customExec) { - if (customExec instanceof vscode.ShellExecution) { - exec = customExec; - } else { - log.debug("Invalid cargo ShellExecution", customExec); - throw "Invalid cargo ShellExecution."; - } - } - // fallback to default processing - } catch (e) { - if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`; - // fallback to default processing - } - } - - if (!exec) { - exec = new vscode.ProcessExecution(definition.program, definition.args, definition); - } + const exec = await cargoToExecution(definition, customRunner, throwOnError); return new vscode.Task( definition, @@ -129,6 +107,53 @@ export async function buildRustTask( ); } +async function cargoToExecution( + definition: CargoTaskDefinition, + customRunner: string | undefined, + throwOnError: boolean, +): Promise { + if (customRunner) { + const runnerCommand = `${customRunner}.buildShellExecution`; + + try { + const runnerArgs = { + kind: TASK_TYPE, + args: definition.args, + cwd: definition.cwd, + env: definition.env, + }; + const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs); + if (customExec) { + if (customExec instanceof vscode.ShellExecution) { + return customExec; + } else { + log.debug("Invalid cargo ShellExecution", customExec); + throw "Invalid cargo ShellExecution."; + } + } + // fallback to default processing + } catch (e) { + if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`; + // fallback to default processing + } + } + + // Check whether we must use a user-defined substitute for cargo. + // Split on spaces to allow overrides like "wrapper cargo". + const cargoPath = await toolchain.cargoPath(); + const cargoCommand = definition.overrideCargo?.split(" ") ?? [cargoPath]; + + const args = [definition.command].concat(definition.args ?? []); + const fullCommand = [...cargoCommand, ...args]; + + const processName = unwrapUndefinable(fullCommand[0]); + + return new vscode.ProcessExecution(processName, fullCommand.slice(1), { + cwd: definition.cwd, + env: definition.env, + }); +} + export function activateTaskProvider(config: Config): vscode.Disposable { const provider = new RustTaskProvider(config); return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); From d3fb9f798ad655427326478062cfae0bb71ac649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 31 Mar 2024 09:57:00 +0300 Subject: [PATCH 015/185] Merge commit 'f5a9250147f6569d8d89334dc9cca79c0322729f' into sync-from-ra --- .github/workflows/ci.yaml | 4 +- .github/workflows/publish-libs.yaml | 1 + .github/workflows/release.yaml | 17 +- Cargo.lock | 43 +- Cargo.toml | 11 +- crates/base-db/src/input.rs | 84 +- crates/base-db/src/lib.rs | 6 +- crates/flycheck/src/lib.rs | 8 +- crates/flycheck/src/test_runner.rs | 5 +- crates/hir-def/src/attr.rs | 8 +- crates/hir-def/src/data.rs | 9 +- crates/hir-def/src/generics.rs | 80 +- crates/hir-def/src/item_tree/pretty.rs | 2 +- crates/hir-def/src/lang_item.rs | 2 +- crates/hir-def/src/lib.rs | 4 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 85 + crates/hir-def/src/nameres.rs | 6 +- crates/hir-def/src/nameres/attr_resolution.rs | 2 + crates/hir-def/src/nameres/collector.rs | 42 +- crates/hir-def/src/nameres/path_resolution.rs | 2 +- crates/hir-def/src/nameres/proc_macro.rs | 14 +- crates/hir-def/src/resolver.rs | 20 + crates/hir-expand/src/attrs.rs | 159 +- crates/hir-expand/src/builtin_fn_macro.rs | 4 +- crates/hir-expand/src/cfg_process.rs | 12 +- crates/hir-expand/src/db.rs | 65 +- crates/hir-expand/src/declarative.rs | 38 +- crates/hir-expand/src/lib.rs | 21 +- crates/hir-expand/src/mod_path.rs | 42 +- crates/hir-expand/src/proc_macro.rs | 2 +- crates/hir-ty/Cargo.toml | 3 +- crates/hir-ty/src/builder.rs | 34 +- crates/hir-ty/src/chalk_db.rs | 13 + crates/hir-ty/src/chalk_ext.rs | 14 + crates/hir-ty/src/consteval/tests.rs | 27 + crates/hir-ty/src/db.rs | 16 +- crates/hir-ty/src/diagnostics/expr.rs | 17 +- .../diagnostics/match_check/pat_analysis.rs | 84 +- crates/hir-ty/src/display.rs | 82 +- crates/hir-ty/src/infer.rs | 159 +- crates/hir-ty/src/infer/coerce.rs | 17 + crates/hir-ty/src/infer/expr.rs | 39 +- crates/hir-ty/src/infer/path.rs | 9 +- crates/hir-ty/src/infer/unify.rs | 28 +- crates/hir-ty/src/layout.rs | 3 + crates/hir-ty/src/lib.rs | 95 +- crates/hir-ty/src/lower.rs | 239 +- crates/hir-ty/src/mapping.rs | 8 + crates/hir-ty/src/method_resolution.rs | 15 +- crates/hir-ty/src/mir/eval.rs | 56 +- crates/hir-ty/src/mir/eval/shim.rs | 65 +- crates/hir-ty/src/mir/monomorphization.rs | 15 +- crates/hir-ty/src/tests.rs | 2 +- .../hir-ty/src/tests/display_source_code.rs | 2 +- crates/hir-ty/src/tests/patterns.rs | 2 +- crates/hir-ty/src/tests/regression.rs | 4 +- crates/hir-ty/src/tests/simple.rs | 6 +- crates/hir-ty/src/tests/traits.rs | 109 + crates/hir-ty/src/utils.rs | 166 +- crates/hir/src/display.rs | 33 +- crates/hir/src/lib.rs | 60 +- crates/hir/src/term_search/tactics.rs | 4 +- .../src/handlers/extract_function.rs | 2 +- .../src/handlers/generate_delegate_methods.rs | 2 +- crates/ide-completion/src/completions/dot.rs | 4 +- .../src/completions/item_list/trait_impl.rs | 8 +- .../ide-completion/src/completions/keyword.rs | 6 +- crates/ide-completion/src/context.rs | 2 +- crates/ide-completion/src/item.rs | 3 +- crates/ide-completion/src/render.rs | 49 +- crates/ide-completion/src/render/function.rs | 8 +- crates/ide-completion/src/tests/expression.rs | 2 +- crates/ide-completion/src/tests/predicate.rs | 4 +- crates/ide-completion/src/tests/special.rs | 47 +- crates/ide-completion/src/tests/type_pos.rs | 8 +- crates/ide-db/src/lib.rs | 1 + crates/ide-diagnostics/Cargo.toml | 1 + .../src/handlers/missing_match_arms.rs | 27 + .../src/handlers/mutability_errors.rs | 50 +- .../src/handlers/remove_trailing_return.rs | 19 +- .../src/handlers/remove_unnecessary_else.rs | 23 +- .../src/handlers/unlinked_file.rs | 7 +- .../src/handlers/unused_variables.rs | 28 +- crates/ide-diagnostics/src/lib.rs | 32 +- crates/ide-diagnostics/src/tests.rs | 4 + crates/ide/Cargo.toml | 1 + crates/ide/src/doc_links.rs | 16 +- crates/ide/src/doc_links/tests.rs | 30 +- crates/ide/src/file_structure.rs | 13 +- crates/ide/src/goto_implementation.rs | 71 + crates/ide/src/hover.rs | 1 + crates/ide/src/hover/render.rs | 3 + crates/ide/src/hover/tests.rs | 175 +- crates/ide/src/inlay_hints.rs | 96 +- crates/ide/src/inlay_hints/adjustment.rs | 1 - crates/ide/src/inlay_hints/bind_pat.rs | 9 +- crates/ide/src/inlay_hints/binding_mode.rs | 2 - crates/ide/src/inlay_hints/chaining.rs | 1 - crates/ide/src/inlay_hints/closing_brace.rs | 1 - .../ide/src/inlay_hints/closure_captures.rs | 5 - crates/ide/src/inlay_hints/closure_ret.rs | 1 - crates/ide/src/inlay_hints/discriminant.rs | 1 - crates/ide/src/inlay_hints/fn_lifetime_fn.rs | 3 - crates/ide/src/inlay_hints/implicit_drop.rs | 1 - crates/ide/src/inlay_hints/implicit_static.rs | 1 - crates/ide/src/inlay_hints/param_name.rs | 1 - crates/ide/src/inlay_hints/range_exclusive.rs | 1 - crates/ide/src/lib.rs | 28 +- crates/ide/src/static_index.rs | 1 + crates/ide/src/status.rs | 10 +- .../ide/src/syntax_highlighting/highlight.rs | 48 +- crates/ide/src/syntax_highlighting/html.rs | 1 + crates/ide/src/syntax_highlighting/tags.rs | 9 +- .../test_data/highlight_assoc_functions.html | 7 +- .../test_data/highlight_attributes.html | 8 +- .../test_data/highlight_block_mod_items.html | 3 +- .../test_data/highlight_const.html | 83 + .../test_data/highlight_crate_root.html | 9 +- .../test_data/highlight_default_library.html | 3 +- .../test_data/highlight_doctest.html | 15 +- .../test_data/highlight_extern_crate.html | 1 + .../test_data/highlight_general.html | 57 +- .../test_data/highlight_injection.html | 1 + .../test_data/highlight_keywords.html | 1 + .../test_data/highlight_lifetimes.html | 1 + .../test_data/highlight_macros.html | 1 + .../highlight_module_docs_inline.html | 1 + .../highlight_module_docs_outline.html | 1 + .../test_data/highlight_operators.html | 1 + .../test_data/highlight_rainbow.html | 1 + .../test_data/highlight_strings.html | 5 +- .../test_data/highlight_unsafe.html | 11 +- crates/ide/src/syntax_highlighting/tests.rs | 76 +- crates/ide/src/test_explorer.rs | 68 +- crates/ide/src/typing.rs | 33 +- crates/load-cargo/Cargo.toml | 1 + crates/load-cargo/src/lib.rs | 4 +- crates/mbe/src/benchmark.rs | 20 +- crates/mbe/src/expander.rs | 45 +- crates/mbe/src/expander/matcher.rs | 160 +- crates/mbe/src/expander/transcriber.rs | 99 +- crates/mbe/src/lib.rs | 64 +- crates/mbe/src/parser.rs | 100 +- crates/mbe/src/syntax_bridge.rs | 56 +- crates/mbe/src/to_parser_input.rs | 6 +- crates/mbe/src/tt_iter.rs | 14 +- crates/parser/src/grammar/expressions/atom.rs | 56 +- .../inline/err/0034_match_arms_recovery.rast | 113 + .../inline/err/0034_match_arms_recovery.rs | 11 + crates/paths/Cargo.toml | 6 +- crates/paths/src/lib.rs | 248 +- crates/proc-macro-api/Cargo.toml | 2 +- crates/proc-macro-api/src/lib.rs | 5 +- crates/proc-macro-api/src/msg.rs | 12 +- crates/proc-macro-api/src/msg/flat.rs | 2 - crates/proc-macro-api/src/process.rs | 4 +- crates/proc-macro-srv/src/dylib.rs | 47 +- crates/proc-macro-srv/src/lib.rs | 26 +- crates/proc-macro-srv/src/proc_macros.rs | 2 +- .../proc-macro-srv/src/server/token_stream.rs | 4 +- crates/proc-macro-srv/src/tests/mod.rs | 16 +- crates/project-model/Cargo.toml | 3 +- crates/project-model/src/build_scripts.rs | 19 +- crates/project-model/src/cargo_workspace.rs | 21 +- crates/project-model/src/lib.rs | 4 +- crates/project-model/src/project_json.rs | 20 +- crates/project-model/src/sysroot.rs | 6 +- crates/project-model/src/tests.rs | 20 +- crates/project-model/src/workspace.rs | 86 +- crates/rust-analyzer/Cargo.toml | 3 +- crates/rust-analyzer/src/bin/main.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 5 +- crates/rust-analyzer/src/cli/diagnostics.rs | 2 +- crates/rust-analyzer/src/cli/flags.rs | 1 + crates/rust-analyzer/src/cli/lsif.rs | 2 +- crates/rust-analyzer/src/cli/rustc_tests.rs | 2 +- crates/rust-analyzer/src/cli/scip.rs | 12 +- crates/rust-analyzer/src/config.rs | 49 +- .../rust-analyzer/src/diagnostics/to_proto.rs | 5 +- crates/rust-analyzer/src/global_state.rs | 20 +- .../src/handlers/notification.rs | 2 +- crates/rust-analyzer/src/handlers/request.rs | 31 +- .../src/integrated_benchmarks.rs | 6 +- crates/rust-analyzer/src/lsp/ext.rs | 4 +- .../rust-analyzer/src/lsp/semantic_tokens.rs | 3 +- crates/rust-analyzer/src/lsp/to_proto.rs | 105 +- crates/rust-analyzer/src/main_loop.rs | 36 +- crates/rust-analyzer/src/reload.rs | 92 +- crates/rust-analyzer/tests/crate_graph.rs | 2 +- crates/rust-analyzer/tests/slow-tests/main.rs | 13 +- .../rust-analyzer/tests/slow-tests/support.rs | 6 +- .../rust-analyzer/tests/slow-tests/testdir.rs | 22 +- crates/span/src/lib.rs | 50 + crates/syntax/Cargo.toml | 4 - crates/syntax/rust.ungram | 2 +- crates/syntax/src/ast/generated/nodes.rs | 7738 ++++++++--------- crates/syntax/src/ast/generated/tokens.rs | 184 +- crates/syntax/src/tests.rs | 25 +- crates/test-fixture/src/lib.rs | 25 +- crates/toolchain/Cargo.toml | 3 +- crates/toolchain/src/lib.rs | 43 +- crates/tt/Cargo.toml | 5 +- crates/tt/src/lib.rs | 11 +- crates/vfs-notify/src/lib.rs | 6 +- crates/vfs/src/lib.rs | 57 +- crates/vfs/src/loader.rs | 2 +- crates/vfs/src/vfs_path.rs | 2 +- docs/dev/lsp-extensions.md | 14 +- docs/user/generated_config.adoc | 5 + editors/code/package.json | 9 + editors/code/src/config.ts | 25 +- editors/code/src/debug.ts | 6 +- editors/code/src/lsp_ext.ts | 6 +- editors/code/src/run.ts | 28 +- editors/code/src/tasks.ts | 50 +- editors/code/src/test_explorer.ts | 65 +- xtask/Cargo.toml | 4 + xtask/src/codegen.rs | 2 + .../src/codegen/grammar.rs | 74 +- .../src/codegen/grammar}/ast_src.rs | 0 xtask/src/dist.rs | 21 +- xtask/src/flags.rs | 141 +- xtask/src/install.rs | 16 +- 223 files changed, 7964 insertions(+), 5865 deletions(-) create mode 100644 crates/ide/src/syntax_highlighting/test_data/highlight_const.html create mode 100644 crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast create mode 100644 crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs rename crates/syntax/src/tests/sourcegen_ast.rs => xtask/src/codegen/grammar.rs (93%) rename {crates/syntax/src/tests => xtask/src/codegen/grammar}/ast_src.rs (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2d8946520d5..08ad10c2971 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,7 +71,7 @@ jobs: run: echo "::add-matcher::.github/rust.json" - name: Cache Dependencies - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 with: key: ${{ env.RUST_CHANNEL }} @@ -140,7 +140,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + uses: Swatinem/rust-cache@640a22190e7a783d4c409684cea558f081f92012 - name: Check run: | diff --git a/.github/workflows/publish-libs.yaml b/.github/workflows/publish-libs.yaml index 862373ec1cc..34ca53e2e53 100644 --- a/.github/workflows/publish-libs.yaml +++ b/.github/workflows/publish-libs.yaml @@ -32,4 +32,5 @@ jobs: git config --global user.name "GitHub Action" # Remove r-a crates from the workspaces so we don't auto-publish them as well sed -i 's/ "crates\/\*"//' ./Cargo.toml + sed -i 's/ "xtask\/"//' ./Cargo.toml cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index dc0a6c2d91f..11014338d72 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -36,6 +36,7 @@ jobs: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu code-target: linux-x64 + container: rockylinux:8 - os: ubuntu-20.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 @@ -58,10 +59,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: ${{ env.FETCH_DEPTH }} + - name: Install toolchain dependencies + if: matrix.container == 'rockylinux:8' + shell: bash + run: | + dnf install -y gcc + curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --profile minimal --default-toolchain none -y + echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH + - name: Install Rust toolchain run: | rustup update --no-self-update stable @@ -69,9 +78,9 @@ jobs: rustup component add rust-src - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 18 - name: Update apt repositories if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' @@ -181,7 +190,7 @@ jobs: - name: Install Nodejs uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV if: github.ref == 'refs/heads/release' diff --git a/Cargo.lock b/Cargo.lock index 68ed32391b7..c7cf4479b33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -594,6 +594,7 @@ dependencies = [ "rustc-hash", "scoped-tls", "smallvec", + "span", "stdx", "syntax", "test-fixture", @@ -637,6 +638,7 @@ dependencies = [ "pulldown-cmark", "pulldown-cmark-to-cmark", "smallvec", + "span", "stdx", "syntax", "test-fixture", @@ -732,6 +734,7 @@ dependencies = [ "ide-db", "itertools", "once_cell", + "paths", "serde_json", "stdx", "syntax", @@ -931,6 +934,7 @@ dependencies = [ "hir-expand", "ide-db", "itertools", + "paths", "proc-macro-api", "project-model", "span", @@ -1225,6 +1229,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "paths" version = "0.0.0" +dependencies = [ + "camino", +] [[package]] name = "percent-encoding" @@ -1375,6 +1382,7 @@ dependencies = [ "semver", "serde", "serde_json", + "span", "stdx", "toolchain", "tracing", @@ -1432,9 +1440,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ae52e2d5b08762c9464b541345f519b8719d57b643b73632bade43ecece9dc" +checksum = "b8709df2a746f055316bc0c62bd30948695a25e734863bf6e1f9755403e010ab" dependencies = [ "bitflags 2.4.2", "ra-ap-rustc_index", @@ -1443,9 +1451,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd7e10c7853fe79443d46e1d2d8ab09fe99926118e59653fb8b480d5045f126" +checksum = "9ad68bacffb87dcdbb23a3ce11261375078aaa06b85d348c49f39ffd5510dc20" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1454,9 +1462,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47f1d1c589be6c9a9e852fadee0e60329c0f862e87442ac2fe5adae30663cc76" +checksum = "8782aaf3a113837c533dfb1c45df91cd17e1fdd1d2f9a20c2e0d1976025c4f1f" dependencies = [ "proc-macro2", "quote", @@ -1466,9 +1474,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa852373a757b4c723bbdc96ced7f575cad68a1e266e45fee12bc4c69a482d80" +checksum = "aab683fc8579d09eb72033bd5dc9ba6d701aa9645b5fed087ef19af71184dff3" dependencies = [ "unicode-properties", "unicode-xid", @@ -1476,9 +1484,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2afe3c49accd95a53ac4d72ae13bafc7d115bdd80c8cd56ab09e6fc68f482210" +checksum = "0bcf9ff5edbf784b67b8ad5e03a068f1300fcc24062c0d476b3018965135d933" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1486,9 +1494,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1253da23515d80c377a3998731e0ec3794997b62b989fd47db73efbde6a0bd7c" +checksum = "d63d1e1d5b2a13273cee1a10011147418f40e12b70f70578ce1dee0f1cafc334" dependencies = [ "ra-ap-rustc_index", "rustc-hash", @@ -1598,6 +1606,7 @@ dependencies = [ "oorandom", "parking_lot", "parser", + "paths", "proc-macro-api", "profile", "project-model", @@ -1869,20 +1878,16 @@ dependencies = [ "itertools", "once_cell", "parser", - "proc-macro2", - "quote", "ra-ap-rustc_lexer", "rayon", "rowan", "rustc-hash", "smol_str", - "sourcegen", "stdx", "test-utils", "text-edit", "tracing", "triomphe", - "ungrammar", ] [[package]] @@ -2024,6 +2029,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" name = "toolchain" version = "0.0.0" dependencies = [ + "camino", "home", ] @@ -2109,7 +2115,6 @@ name = "tt" version = "0.0.0" dependencies = [ "smol_str", - "span", "stdx", "text-size", ] @@ -2438,8 +2443,12 @@ version = "0.1.0" dependencies = [ "anyhow", "flate2", + "itertools", + "proc-macro2", + "quote", "stdx", "time", + "ungrammar", "write-json", "xflags", "xshell", diff --git a/Cargo.toml b/Cargo.toml index 0679522efd6..d9343d2b963 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,11 +84,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.42.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.42.0", default-features = false } -ra-ap-rustc_index = { version = "0.42.0", default-features = false } -ra-ap-rustc_abi = { version = "0.42.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.42.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.44.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.44.0", default-features = false } +ra-ap-rustc_index = { version = "0.44.0", default-features = false } +ra-ap-rustc_abi = { version = "0.44.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.44.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. sourcegen = { path = "./crates/sourcegen" } @@ -105,6 +105,7 @@ anyhow = "1.0.75" arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" +camino = "1.1.6" chalk-solve = { version = "0.96.0", default-features = false } chalk-ir = "0.96.0" chalk-recursive = { version = "0.96.0", default-features = false } diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index b243b37b77b..27eb05cd4dc 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -6,11 +6,12 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, mem, ops, str::FromStr}; +use std::{fmt, mem, ops}; use cfg::CfgOptions; use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxHashMap, FxHashSet}; +use span::Edition; use syntax::SmolStr; use triomphe::Arc; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; @@ -293,42 +294,11 @@ pub struct CrateData { pub is_proc_macro: bool, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Edition { - Edition2015, - Edition2018, - Edition2021, - Edition2024, -} - -impl Edition { - pub const CURRENT: Edition = Edition::Edition2021; - pub const DEFAULT: Edition = Edition::Edition2015; -} - #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Env { entries: FxHashMap, } -impl Env { - pub fn new_for_test_fixture() -> Self { - Env { - entries: FxHashMap::from_iter([( - String::from("__ra_is_test_fixture"), - String::from("__ra_is_test_fixture"), - )]), - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum DependencyKind { - Normal, - Dev, - Build, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, @@ -530,13 +500,6 @@ impl CrateGraph { } } - // FIXME: this only finds one crate with the given root; we could have multiple - pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option { - let (crate_id, _) = - self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; - Some(crate_id) - } - pub fn sort_deps(&mut self) { self.arena .iter_mut() @@ -653,6 +616,10 @@ impl CrateGraph { } id_map } + + pub fn shrink_to_fit(&mut self) { + self.arena.shrink_to_fit(); + } } impl ops::Index for CrateGraph { @@ -670,32 +637,6 @@ impl CrateData { } } -impl FromStr for Edition { - type Err = ParseEditionError; - - fn from_str(s: &str) -> Result { - let res = match s { - "2015" => Edition::Edition2015, - "2018" => Edition::Edition2018, - "2021" => Edition::Edition2021, - "2024" => Edition::Edition2024, - _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), - }; - Ok(res) - } -} - -impl fmt::Display for Edition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Edition::Edition2015 => "2015", - Edition::Edition2018 => "2018", - Edition::Edition2021 => "2021", - Edition::Edition2024 => "2024", - }) - } -} - impl Extend<(String, String)> for Env { fn extend>(&mut self, iter: T) { self.entries.extend(iter); @@ -722,19 +663,6 @@ impl Env { } } -#[derive(Debug)] -pub struct ParseEditionError { - invalid_input: String, -} - -impl fmt::Display for ParseEditionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid edition: {:?}", self.invalid_input) - } -} - -impl std::error::Error for ParseEditionError {} - #[derive(Debug)] pub struct CyclicDependenciesError { path: Vec<(CrateId, Option)>, diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 5dcb580723f..785ff9ceffa 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -14,9 +14,9 @@ use triomphe::Arc; pub use crate::{ change::FileChange, input::{ - CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, - DependencyKind, Edition, Env, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, - SourceRootId, TargetLayoutLoadResult, + CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, + LangCrateOrigin, ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, + TargetLayoutLoadResult, }, }; pub use salsa::{self, Cancelled}; diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index f8efb520222..4ee86954acd 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -8,10 +8,10 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -use std::{fmt, io, path::PathBuf, process::Command, time::Duration}; +use std::{fmt, io, process::Command, time::Duration}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize; @@ -53,7 +53,7 @@ pub enum FlycheckConfig { extra_args: Vec, extra_env: FxHashMap, ansi_color_output: bool, - target_dir: Option, + target_dir: Option, }, CustomCommand { command: String, @@ -363,7 +363,7 @@ impl FlycheckActor { }); cmd.arg("--manifest-path"); - cmd.arg(self.root.join("Cargo.toml").as_os_str()); + cmd.arg(self.root.join("Cargo.toml")); for target in target_triples { cmd.args(["--target", target.as_str()]); diff --git a/crates/flycheck/src/test_runner.rs b/crates/flycheck/src/test_runner.rs index 31378716b3e..9f761c9ead1 100644 --- a/crates/flycheck/src/test_runner.rs +++ b/crates/flycheck/src/test_runner.rs @@ -55,13 +55,16 @@ pub struct CargoTestHandle { } // Example of a cargo test command: -// cargo test -- module::func -Z unstable-options --format=json +// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json impl CargoTestHandle { pub fn new(path: Option<&str>) -> std::io::Result { let mut cmd = Command::new(Tool::Cargo.path()); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); + cmd.arg("--workspace"); + // --no-fail-fast is needed to ensure that all requested tests will run + cmd.arg("--no-fail-fast"); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 21536098b82..fa7730f302e 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -148,12 +148,12 @@ impl Attrs { } } - pub fn lang(&self) -> Option<&SmolStr> { + pub fn lang(&self) -> Option<&str> { self.by_key("lang").string_value() } pub fn lang_item(&self) -> Option { - self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + self.by_key("lang").string_value().and_then(LangItem::from_str) } pub fn has_doc_hidden(&self) -> bool { @@ -178,7 +178,7 @@ impl Attrs { self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) } - pub fn export_name(&self) -> Option<&SmolStr> { + pub fn export_name(&self) -> Option<&str> { self.by_key("export_name").string_value() } @@ -565,7 +565,7 @@ impl<'attr> AttrQuery<'attr> { self.attrs().filter_map(|attr| attr.token_tree_value()) } - pub fn string_value(self) -> Option<&'attr SmolStr> { + pub fn string_value(self) -> Option<&'attr str> { self.attrs().find_map(|attr| attr.string_value()) } diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index b815c9b73ef..da790f11516 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -453,8 +453,8 @@ impl ProcMacroData { ( def.name, match def.kind { - ProcMacroKind::CustomDerive { helpers } => Some(helpers), - ProcMacroKind::FnLike | ProcMacroKind::Attr => None, + ProcMacroKind::Derive { helpers } => Some(helpers), + ProcMacroKind::Bang | ProcMacroKind::Attr => None, }, ) } else { @@ -484,10 +484,11 @@ impl ExternCrateDeclData { let extern_crate = &item_tree[loc.id.value]; let name = extern_crate.name.clone(); + let krate = loc.container.krate(); let crate_id = if name == hir_expand::name![self] { - Some(loc.container.krate()) + Some(krate) } else { - db.crate_def_map(loc.container.krate()) + db.crate_def_map(krate) .extern_prelude() .find(|&(prelude_name, ..)| *prelude_name == name) .map(|(_, (root, _))| root.krate()) diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index 1d2c7c3a55f..4638b377197 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -22,8 +22,8 @@ use crate::{ lower::LowerCtx, nameres::{DefMap, MacroSubNs}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, - AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LocalTypeOrConstParamId, Lookup, - TypeOrConstParamId, TypeParamId, + AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId, + LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, }; /// Data about a generic type parameter (to a function, struct, impl, ...). @@ -102,6 +102,52 @@ impl TypeOrConstParamData { impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData); +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum GenericParamData { + TypeParamData(TypeParamData), + ConstParamData(ConstParamData), + LifetimeParamData(LifetimeParamData), +} + +impl GenericParamData { + pub fn name(&self) -> Option<&Name> { + match self { + GenericParamData::TypeParamData(it) => it.name.as_ref(), + GenericParamData::ConstParamData(it) => Some(&it.name), + GenericParamData::LifetimeParamData(it) => Some(&it.name), + } + } + + pub fn type_param(&self) -> Option<&TypeParamData> { + match self { + GenericParamData::TypeParamData(it) => Some(it), + _ => None, + } + } + + pub fn const_param(&self) -> Option<&ConstParamData> { + match self { + GenericParamData::ConstParamData(it) => Some(it), + _ => None, + } + } + + pub fn lifetime_param(&self) -> Option<&LifetimeParamData> { + match self { + GenericParamData::LifetimeParamData(it) => Some(it), + _ => None, + } + } +} + +impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData); + +pub enum GenericParamDataRef<'a> { + TypeParamData(&'a TypeParamData), + ConstParamData(&'a ConstParamData), + LifetimeParamData(&'a LifetimeParamData), +} + /// Data about the generic parameters of a function, struct, impl, etc. #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct GenericParams { @@ -358,6 +404,15 @@ impl GenericParamsCollector { } impl GenericParams { + /// Number of Generic parameters (type_or_consts + lifetimes) + pub fn len(&self) -> usize { + self.type_or_consts.len() + self.lifetimes.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + /// Iterator of type_or_consts field pub fn iter( &self, @@ -365,6 +420,13 @@ impl GenericParams { self.type_or_consts.iter() } + /// Iterator of lifetimes field + pub fn iter_lt( + &self, + ) -> impl DoubleEndedIterator, &LifetimeParamData)> { + self.lifetimes.iter() + } + pub(crate) fn generic_params_query( db: &dyn DefDatabase, def: GenericDefId, @@ -507,4 +569,18 @@ impl GenericParams { .then(|| id) }) } + + pub fn find_lifetime_by_name( + &self, + name: &Name, + parent: GenericDefId, + ) -> Option { + self.lifetimes.iter().find_map(|(id, p)| { + if &p.name == name { + Some(LifetimeParamId { local_id: id, parent }) + } else { + None + } + }) + } } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index 953bf6b85d6..0c84057950b 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -526,7 +526,7 @@ impl Printer<'_> { } fn print_generic_params(&mut self, params: &GenericParams) { - if params.type_or_consts.is_empty() && params.lifetimes.is_empty() { + if params.is_empty() { return; } diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 7d98f6cfe88..3a07c678428 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -192,7 +192,7 @@ impl LangItems { pub(crate) fn lang_attr(db: &dyn DefDatabase, item: AttrDefId) -> Option { let attrs = db.attrs(item); - attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + attrs.by_key("lang").string_value().and_then(LangItem::from_str) } pub(crate) fn notable_traits_in_deps( diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 828842de7e8..46898ce542d 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -73,7 +73,7 @@ use std::{ use base_db::{ impl_intern_key, salsa::{self, impl_intern_value_trivial}, - CrateId, Edition, + CrateId, }; use hir_expand::{ builtin_attr_macro::BuiltinAttrExpander, @@ -90,7 +90,7 @@ use hir_expand::{ use item_tree::ExternBlock; use la_arena::Idx; use nameres::DefMap; -use span::{AstIdNode, FileAstId, FileId, SyntaxContextId}; +use span::{AstIdNode, Edition, FileAstId, FileId, SyntaxContextId}; use stdx::impl_from; use syntax::{ast, AstNode}; diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 965f329acb9..c5c26e26bc0 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1449,6 +1449,7 @@ ok!(); #[test] fn test_new_std_matches() { check( + //- edition:2021 r#" macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { @@ -1480,6 +1481,90 @@ fn main() { ); } +#[test] +fn test_hygienic_pat() { + check( + r#" +//- /new.rs crate:new deps:old edition:2015 +old::make!(); +fn main() { + matches!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2021 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + match 0 { + 0|1 if true =>true , _=>false + }; +} +"#]], + ); + check( + r#" +//- /new.rs crate:new deps:old edition:2021 +old::make!(); +fn main() { + matches/*+errors*/!(0, 0 | 1 if true); +} +//- /old.rs crate:old edition:2015 +#[macro_export] +macro_rules! make { + () => { + macro_rules! matches { + ($expression:expr, $pattern:pat if $guard:expr ) => { + match $expression { + $pattern if $guard => true, + _ => false + } + }; + } + } +} + "#, + expect![[r#" +macro_rules !matches { + ($expression: expr, $pattern: pat if $guard: expr) = > { + match $expression { + $pattern if $guard = > true , _ = > false + } + } + ; +} +fn main() { + /* error: unexpected token in input *//* parse error: expected expression */ +/* parse error: expected FAT_ARROW */ +/* parse error: expected `,` */ +/* parse error: expected pattern */ +match 0 { + 0 if $guard=>true , _=>false + }; +} +"#]], + ); +} + #[test] fn test_dollar_crate_lhs_is_not_meta() { check( diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index b56dee3efb5..a528c4cc697 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -59,14 +59,14 @@ mod tests; use std::ops::Deref; -use base_db::{CrateId, Edition, FileId}; +use base_db::{CrateId, FileId}; use hir_expand::{ name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId, }; use itertools::Itertools; use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{FileAstId, ROOT_ERASED_FILE_AST_ID}; +use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; use syntax::{ast, SmolStr}; use triomphe::Arc; @@ -737,7 +737,7 @@ impl MacroSubNs { MacroId::ProcMacroId(it) => { return match it.lookup(db).kind { ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr, - ProcMacroKind::FuncLike => Self::Bang, + ProcMacroKind::Bang => Self::Bang, }; } }; diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index 662c80edf32..eb7f4c05ae2 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs @@ -136,6 +136,7 @@ pub(super) fn derive_macro_as_call_id( call_site: SyntaxContextId, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, + derive_macro_id: MacroCallId, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { let (macro_id, def_id) = resolver(item_attr.path.clone()) .filter(|(_, def_id)| def_id.is_derive()) @@ -147,6 +148,7 @@ pub(super) fn derive_macro_as_call_id( ast_id: item_attr.ast_id, derive_index: derive_pos, derive_attr_index, + derive_macro_id, }, call_site, ); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 3d026447fb7..ae8f028e488 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{cmp::Ordering, iter, mem, ops::Not}; -use base_db::{CrateId, Dependency, Edition, FileId}; +use base_db::{CrateId, Dependency, FileId}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -22,9 +22,9 @@ use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{ErasedFileAstId, FileAstId, Span, SyntaxContextId}; +use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContextId}; use stdx::always; -use syntax::{ast, SmolStr}; +use syntax::ast; use triomphe::Arc; use crate::{ @@ -237,6 +237,8 @@ enum MacroDirectiveKind { derive_attr: AttrId, derive_pos: usize, ctxt: SyntaxContextId, + /// The "parent" macro it is resolved to. + derive_macro_id: MacroCallId, }, Attr { ast_id: AstIdWithPath, @@ -312,7 +314,7 @@ impl DefCollector<'_> { } } () if *attr_name == hir_expand::name![crate_type] => { - if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) { + if let Some("proc-macro") = attr.string_value() { self.is_proc_macro = true; } } @@ -602,7 +604,7 @@ impl DefCollector<'_> { .intern(self.db); self.define_proc_macro(def.name.clone(), proc_macro_id); let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); - if let ProcMacroKind::CustomDerive { helpers } = def.kind { + if let ProcMacroKind::Derive { helpers } = def.kind { crate_data.exported_derives.insert(self.db.macro_def(proc_macro_id.into()), helpers); } crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); @@ -1146,7 +1148,13 @@ impl DefCollector<'_> { return Resolved::Yes; } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => { + MacroDirectiveKind::Derive { + ast_id, + derive_attr, + derive_pos, + ctxt: call_site, + derive_macro_id, + } => { let id = derive_macro_as_call_id( self.db, ast_id, @@ -1155,6 +1163,7 @@ impl DefCollector<'_> { *call_site, self.def_map.krate, resolver, + *derive_macro_id, ); if let Ok((macro_id, def_id, call_id)) = id { @@ -1224,6 +1233,8 @@ impl DefCollector<'_> { _ => return Resolved::No, }; + let call_id = + attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); if let MacroDefId { kind: MacroDefKind::BuiltInAttr( @@ -1252,6 +1263,7 @@ impl DefCollector<'_> { return recollect_without(self); } }; + let ast_id = ast_id.with_value(ast_adt_id); match attr.parse_path_comma_token_tree(self.db.upcast()) { @@ -1267,6 +1279,7 @@ impl DefCollector<'_> { derive_attr: attr.id, derive_pos: idx, ctxt: call_site.ctx, + derive_macro_id: call_id, }, container: directive.container, }); @@ -1301,10 +1314,6 @@ impl DefCollector<'_> { return recollect_without(self); } - // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute. - let call_id = - attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); - // Skip #[test]/#[bench] expansion, which would merely result in more memory usage // due to duplicating functions into macro expansions if matches!( @@ -1460,13 +1469,20 @@ impl DefCollector<'_> { )); } } - MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => { + MacroDirectiveKind::Derive { + ast_id, + derive_attr, + derive_pos, + derive_macro_id, + .. + } => { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( directive.module_id, MacroCallKind::Derive { ast_id: ast_id.ast_id, derive_attr_index: *derive_attr, derive_index: *derive_pos as u32, + derive_macro_id: *derive_macro_id, }, ast_id.path.clone(), )); @@ -1902,7 +1918,7 @@ impl ModCollector<'_, '_> { } fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { - let path_attr = attrs.by_key("path").string_value().map(SmolStr::as_str); + let path_attr = attrs.by_key("path").string_value(); let is_macro_use = attrs.by_key("macro_use").exists(); let module = &self.item_tree[module_id]; match &module.kind { @@ -2146,7 +2162,7 @@ impl ModCollector<'_, '_> { Some(it) => { // FIXME: a hacky way to create a Name from string. name = tt::Ident { - text: it.clone(), + text: it.into(), span: Span { range: syntax::TextRange::empty(syntax::TextSize::new(0)), anchor: span::SpanAnchor { diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 9e53b037283..ee29b89f3d3 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -10,8 +10,8 @@ //! //! `ReachedFixedPoint` signals about this. -use base_db::Edition; use hir_expand::{name::Name, Lookup}; +use span::Edition; use triomphe::Arc; use crate::{ diff --git a/crates/hir-def/src/nameres/proc_macro.rs b/crates/hir-def/src/nameres/proc_macro.rs index c126fdac1c6..5052708dc93 100644 --- a/crates/hir-def/src/nameres/proc_macro.rs +++ b/crates/hir-def/src/nameres/proc_macro.rs @@ -13,18 +13,16 @@ pub struct ProcMacroDef { #[derive(Debug, PartialEq, Eq)] pub enum ProcMacroKind { - CustomDerive { helpers: Box<[Name]> }, - FnLike, + Derive { helpers: Box<[Name]> }, + Bang, Attr, } impl ProcMacroKind { pub(super) fn to_basedb_kind(&self) -> hir_expand::proc_macro::ProcMacroKind { match self { - ProcMacroKind::CustomDerive { .. } => { - hir_expand::proc_macro::ProcMacroKind::CustomDerive - } - ProcMacroKind::FnLike => hir_expand::proc_macro::ProcMacroKind::FuncLike, + ProcMacroKind::Derive { .. } => hir_expand::proc_macro::ProcMacroKind::CustomDerive, + ProcMacroKind::Bang => hir_expand::proc_macro::ProcMacroKind::Bang, ProcMacroKind::Attr => hir_expand::proc_macro::ProcMacroKind::Attr, } } @@ -34,13 +32,13 @@ impl Attrs { #[rustfmt::skip] pub fn parse_proc_macro_decl(&self, func_name: &Name) -> Option { if self.is_proc_macro() { - Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::FnLike }) + Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Bang }) } else if self.is_proc_macro_attribute() { Some(ProcMacroDef { name: func_name.clone(), kind: ProcMacroKind::Attr }) } else if self.by_key("proc_macro_derive").exists() { let derive = self.by_key("proc_macro_derive").tt_values().next()?; let def = parse_macro_name_and_helper_attrs(&derive.token_trees) - .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::CustomDerive { helpers } }); + .map(|(name, helpers)| ProcMacroDef { name, kind: ProcMacroKind::Derive { helpers } }); if def.is_none() { tracing::trace!("malformed `#[proc_macro_derive]`: {}", derive); diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index 226d6f513f5..fadab858aa1 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -24,6 +24,7 @@ use crate::{ nameres::{DefMap, MacroSubNs}, path::{ModPath, Path, PathKind}, per_ns::PerNs, + type_ref::LifetimeRef, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, @@ -120,6 +121,12 @@ pub enum ValueNs { GenericParam(ConstParamId), } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum LifetimeNs { + Static, + LifetimeParam(LifetimeParamId), +} + impl Resolver { /// Resolve known trait from std, like `std::futures::Future` pub fn resolve_known_trait(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { @@ -418,6 +425,19 @@ impl Resolver { self.resolve_path_as_macro(db, path, expected_macro_kind).map(|(it, _)| db.macro_def(it)) } + pub fn resolve_lifetime(&self, lifetime: &LifetimeRef) -> Option { + if lifetime.name == name::known::STATIC_LIFETIME { + return Some(LifetimeNs::Static); + } + + self.scopes().find_map(|scope| match scope { + Scope::GenericParams { def, params } => { + params.find_lifetime_by_name(&lifetime.name, *def).map(LifetimeNs::LifetimeParam) + } + _ => None, + }) + } + /// Returns a set of names available in the current scope. /// /// Note that this is a somewhat fuzzy concept -- internally, the compiler diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs index af3ecdcd5e3..f1540498f26 100644 --- a/crates/hir-expand/src/attrs.rs +++ b/crates/hir-expand/src/attrs.rs @@ -8,8 +8,8 @@ use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; -use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; -use triomphe::Arc; +use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; +use triomphe::ThinArc; use crate::{ db::ExpandDatabase, @@ -22,8 +22,7 @@ use crate::{ /// Syntactical attributes, without filtering of `cfg_attr`s. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct RawAttrs { - // FIXME: Make this a ThinArc - entries: Option>, + entries: Option>, } impl ops::Deref for RawAttrs { @@ -31,7 +30,7 @@ impl ops::Deref for RawAttrs { fn deref(&self) -> &[Attr] { match &self.entries { - Some(it) => it, + Some(it) => &it.slice, None => &[], } } @@ -45,20 +44,34 @@ impl RawAttrs { owner: &dyn ast::HasAttrs, span_map: SpanMapRef<'_>, ) -> Self { - let entries = collect_attrs(owner).filter_map(|(id, attr)| match attr { - Either::Left(attr) => { - attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) - } - Either::Right(comment) => comment.doc_comment().map(|doc| Attr { - id, - input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))), - path: Interned::new(ModPath::from(crate::name!(doc))), - ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx, - }), - }); - let entries: Arc<[Attr]> = Arc::from_iter(entries); + let entries: Vec<_> = collect_attrs(owner) + .filter_map(|(id, attr)| match attr { + Either::Left(attr) => { + attr.meta().and_then(|meta| Attr::from_src(db, meta, span_map, id)) + } + Either::Right(comment) => comment.doc_comment().map(|doc| { + let span = span_map.span_for_range(comment.syntax().text_range()); + Attr { + id, + input: Some(Interned::new(AttrInput::Literal(tt::Literal { + // FIXME: Escape quotes from comment content + text: SmolStr::new(format_smolstr!("\"{doc}\"",)), + span, + }))), + path: Interned::new(ModPath::from(crate::name!(doc))), + ctxt: span.ctx, + } + }), + }) + .collect(); - Self { entries: if entries.is_empty() { None } else { Some(entries) } } + let entries = if entries.is_empty() { + None + } else { + Some(ThinArc::from_header_and_iter((), entries.into_iter())) + }; + + RawAttrs { entries } } pub fn from_attrs_owner( @@ -75,16 +88,20 @@ impl RawAttrs { (None, entries @ Some(_)) => Self { entries }, (Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(a), Some(b)) => { - let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32; - Self { - entries: Some(Arc::from_iter(a.iter().cloned().chain(b.iter().map(|it| { + let last_ast_index = a.slice.last().map_or(0, |it| it.id.ast_index() + 1) as u32; + let items = a + .slice + .iter() + .cloned() + .chain(b.slice.iter().map(|it| { let mut it = it.clone(); it.id.id = (it.id.ast_index() as u32 + last_ast_index) | (it.id.cfg_attr_index().unwrap_or(0) as u32) << AttrId::AST_INDEX_BITS; it - })))), - } + })) + .collect::>(); + Self { entries: Some(ThinArc::from_header_and_iter((), items.into_iter())) } } } } @@ -100,41 +117,47 @@ impl RawAttrs { } let crate_graph = db.crate_graph(); - let new_attrs = Arc::from_iter(self.iter().flat_map(|attr| -> SmallVec<[_; 1]> { - let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); - if !is_cfg_attr { - return smallvec![attr.clone()]; - } + let new_attrs = + self.iter() + .flat_map(|attr| -> SmallVec<[_; 1]> { + let is_cfg_attr = + attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); + if !is_cfg_attr { + return smallvec![attr.clone()]; + } - let subtree = match attr.token_tree_value() { - Some(it) => it, - _ => return smallvec![attr.clone()], - }; + let subtree = match attr.token_tree_value() { + Some(it) => it, + _ => return smallvec![attr.clone()], + }; - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![attr.clone()], - }; - let index = attr.id; - let attrs = parts - .enumerate() - .take(1 << AttrId::CFG_ATTR_BITS) - .filter_map(|(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx))); + let (cfg, parts) = match parse_cfg_attr_input(subtree) { + Some(it) => it, + None => return smallvec![attr.clone()], + }; + let index = attr.id; + let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( + |(idx, attr)| Attr::from_tt(db, attr, index.with_cfg_attr(idx)), + ); - let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); + let cfg_options = &crate_graph[krate].cfg_options; + let cfg = Subtree { delimiter: subtree.delimiter, token_trees: Box::from(cfg) }; + let cfg = CfgExpr::parse(&cfg); + if cfg_options.check(&cfg) == Some(false) { + smallvec![] + } else { + cov_mark::hit!(cfg_attr_active); - attrs.collect() - } - })); - - RawAttrs { entries: Some(new_attrs) } + attrs.collect() + } + }) + .collect::>(); + let entries = if new_attrs.is_empty() { + None + } else { + Some(ThinArc::from_header_and_iter((), new_attrs.into_iter())) + }; + RawAttrs { entries } } } @@ -179,8 +202,7 @@ pub struct Attr { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AttrInput { /// `#[attr = "string"]` - // FIXME: This is losing span - Literal(SmolStr), + Literal(tt::Literal), /// `#[attr(subtree)]` TokenTree(Box), } @@ -188,7 +210,7 @@ pub enum AttrInput { impl fmt::Display for AttrInput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()), + AttrInput::Literal(lit) => write!(f, " = {lit}"), AttrInput::TokenTree(tt) => tt.fmt(f), } } @@ -208,11 +230,10 @@ impl Attr { })?); let span = span_map.span_for_range(range); let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { - let value = match lit.kind() { - ast::LiteralKind::String(string) => string.value()?.into(), - _ => lit.syntax().first_token()?.text().trim_matches('"').into(), - }; - Some(Interned::new(AttrInput::Literal(value))) + Some(Interned::new(AttrInput::Literal(tt::Literal { + text: lit.token().text().into(), + span, + }))) } else if let Some(tt) = ast.token_tree() { let tree = syntax_node_to_token_tree(tt.syntax(), span_map, span); Some(Interned::new(AttrInput::TokenTree(Box::new(tree)))) @@ -245,9 +266,8 @@ impl Attr { } Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. }))) => { let input = match input.get(1) { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text, .. }))) => { - //FIXME the trimming here isn't quite right, raw strings are not handled - Some(Interned::new(AttrInput::Literal(text.trim_matches('"').into()))) + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + Some(Interned::new(AttrInput::Literal(lit.clone()))) } _ => None, }; @@ -265,9 +285,14 @@ impl Attr { impl Attr { /// #[path = "string"] - pub fn string_value(&self) -> Option<&SmolStr> { + pub fn string_value(&self) -> Option<&str> { match self.input.as_deref()? { - AttrInput::Literal(it) => Some(it), + AttrInput::Literal(it) => match it.text.strip_prefix('r') { + Some(it) => it.trim_matches('#'), + None => it.text.as_str(), + } + .strip_prefix('"')? + .strip_suffix('"'), _ => None, } } diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 9fb6a0b2346..fd3e4e7a4db 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,11 +1,11 @@ //! Builtin macro -use base_db::{AnchoredPath, Edition, FileId}; +use base_db::{AnchoredPath, FileId}; use cfg::CfgExpr; use either::Either; use itertools::Itertools; use mbe::{parse_exprs_with_sep, parse_to_token_tree}; -use span::{Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; +use span::{Edition, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID}; use syntax::ast::{self, AstToken}; use crate::{ diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs index c74c13a6fd3..db3558a84e9 100644 --- a/crates/hir-expand/src/cfg_process.rs +++ b/crates/hir-expand/src/cfg_process.rs @@ -10,7 +10,7 @@ use syntax::{ use tracing::{debug, warn}; use tt::SmolStr; -use crate::{db::ExpandDatabase, MacroCallKind, MacroCallLoc}; +use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind}; fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { if !attr.simple_name().as_deref().map(|v| v == "cfg")? { @@ -139,7 +139,7 @@ fn process_enum( 'variant: for variant in variants.variants() { for attr in variant.attrs() { if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { - // Rustc does not strip the attribute if it is enabled. So we will will leave it + // Rustc does not strip the attribute if it is enabled. So we will leave it debug!("censoring type {:?}", variant.syntax()); remove.insert(variant.syntax().clone().into()); // We need to remove the , as well @@ -180,7 +180,13 @@ pub(crate) fn process_cfg_attrs( db: &dyn ExpandDatabase, ) -> Option> { // FIXME: #[cfg_eval] is not implemented. But it is not stable yet - if !matches!(loc.kind, MacroCallKind::Derive { .. }) { + let is_derive = match loc.def.kind { + MacroDefKind::BuiltInDerive(..) + | MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _) => true, + MacroDefKind::BuiltInAttr(expander, _) => expander.is_derive(), + _ => false, + }; + if !is_derive { return None; } let mut remove = FxHashSet::default(); diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index ec68f2f96e5..5461c1c49a3 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -24,7 +24,8 @@ use crate::{ HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFileId, }; - +/// This is just to ensure the types of smart_macro_arg and macro_arg are the same +type MacroArgResult = (Arc, SyntaxFixupUndoInfo, Span); /// Total limit on the number of tokens produced by any macro invocation. /// /// If an invocation produces more tokens than this limit, it will not be stored in the database and @@ -98,7 +99,13 @@ pub trait ExpandDatabase: SourceDatabase { /// Lowers syntactic macro call to a token tree representation. That's a firewall /// query, only typing in the macro call itself changes the returned /// subtree. - fn macro_arg(&self, id: MacroCallId) -> (Arc, SyntaxFixupUndoInfo, Span); + fn macro_arg(&self, id: MacroCallId) -> MacroArgResult; + #[salsa::transparent] + fn macro_arg_considering_derives( + &self, + id: MacroCallId, + kind: &MacroCallKind, + ) -> MacroArgResult; /// Fetches the expander for this macro. #[salsa::transparent] #[salsa::invoke(TokenExpander::macro_expander)] @@ -144,7 +151,7 @@ pub fn expand_speculative( let span_map = RealSpanMap::absolute(FileId::BOGUS); let span_map = SpanMapRef::RealSpanMap(&span_map); - let (_, _, span) = db.macro_arg(actual_macro_call); + let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind); // Build the subtree and token mapping for the speculative args let (mut tt, undo_info) = match loc.kind { @@ -339,12 +346,24 @@ pub(crate) fn parse_with_map( } } -// FIXME: for derive attributes, this will return separate copies of the same structures! Though -// they may differ in spans due to differing call sites... -fn macro_arg( +/// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. +/// Other wise return the [macro_arg] for the macro_call_id. +/// +/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +fn macro_arg_considering_derives( db: &dyn ExpandDatabase, id: MacroCallId, -) -> (Arc, SyntaxFixupUndoInfo, Span) { + kind: &MacroCallKind, +) -> MacroArgResult { + match kind { + // Get the macro arg for the derive macro + MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id), + // Normal macro arg + _ => db.macro_arg(id), + } +} + +fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let loc = db.lookup_intern_macro_call(id); if let MacroCallLoc { @@ -414,29 +433,30 @@ fn macro_arg( } return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span); } - MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { - let node = ast_id.to_ptr(db).to_node(&root); - let censor_derive_input = censor_derive_input(derive_attr_index, &node); - let item_node = node.into(); - let attr_source = attr_source(derive_attr_index, &item_node); - // FIXME: This is wrong, this should point to the path of the derive attribute` - let span = - map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else( - || item_node.syntax().text_range(), - |it| it.syntax().text_range(), - )); - (censor_derive_input, item_node, span) + // MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro + MacroCallKind::Derive { .. } => { + unreachable!("`ExpandDatabase::macro_arg` called with `MacroCallKind::Derive`") } MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { let node = ast_id.to_ptr(db).to_node(&root); let attr_source = attr_source(invoc_attr_index, &node); + let span = map.span_for_range( attr_source .as_ref() .and_then(|it| it.path()) .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()), ); - (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + // If derive attribute we need to censor the derive input + if matches!(loc.def.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive()) + && ast::Adt::can_cast(node.syntax().kind()) + { + let adt = ast::Adt::cast(node.syntax().clone()).unwrap(); + let censor_derive_input = censor_derive_input(invoc_attr_index, &adt); + (censor_derive_input, node, span) + } else { + (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span) + } } }; @@ -526,7 +546,8 @@ fn macro_expand( let (ExpandResult { value: tt, err }, span) = match loc.def.kind { MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), _ => { - let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id); + let (macro_arg, undo_info, span) = + db.macro_arg_considering_derives(macro_call_id, &loc.kind); let arg = &*macro_arg; let res = @@ -603,7 +624,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let loc = db.lookup_intern_macro_call(id); - let (macro_arg, undo_info, span) = db.macro_arg(id); + let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind); let (expander, ast) = match loc.def.kind { MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast), diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index 33643c02724..9a0b218e6d1 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/crates/hir-expand/src/declarative.rs @@ -1,8 +1,8 @@ //! Compiled declarative macro expanders (`macro_rules!`` and `macro`) use std::sync::OnceLock; -use base_db::{CrateId, Edition, VersionReq}; -use span::{MacroCallId, Span}; +use base_db::{CrateId, VersionReq}; +use span::{MacroCallId, Span, SyntaxContextId}; use syntax::{ast, AstNode}; use triomphe::Arc; @@ -10,13 +10,13 @@ use crate::{ attrs::RawAttrs, db::ExpandDatabase, hygiene::{apply_mark, Transparency}, - tt, AstId, ExpandError, ExpandResult, + tt, AstId, ExpandError, ExpandResult, Lookup, }; /// Old-style `macro_rules` or the new macros 2.0 #[derive(Debug, Clone, Eq, PartialEq)] pub struct DeclarativeMacroExpander { - pub mac: mbe::DeclarativeMacro, + pub mac: mbe::DeclarativeMacro, pub transparency: Transparency, } @@ -94,8 +94,6 @@ impl DeclarativeMacroExpander { def_crate: CrateId, id: AstId, ) -> Arc { - let crate_data = &db.crate_graph()[def_crate]; - let is_2021 = crate_data.edition >= Edition::Edition2021; let (root, map) = crate::db::parse_with_map(db, id.file_id); let root = root.syntax_node(); @@ -133,6 +131,16 @@ impl DeclarativeMacroExpander { ) }); + let edition = |ctx: SyntaxContextId| { + let crate_graph = db.crate_graph(); + if ctx.is_root() { + crate_graph[def_crate].edition + } else { + let data = db.lookup_intern_syntax_context(ctx); + // UNWRAP-SAFETY: Only the root context has no outer expansion + crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition + } + }; let (mac, transparency) = match id.to_ptr(db).to_node(&root) { ast::Macro::MacroRules(macro_rules) => ( match macro_rules.token_tree() { @@ -145,12 +153,11 @@ impl DeclarativeMacroExpander { ), ); - mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_rules).unwrap_or(Transparency::SemiTransparent), ), @@ -163,12 +170,11 @@ impl DeclarativeMacroExpander { map.span_for_range(macro_def.macro_token().unwrap().text_range()), ); - mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars) + mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars) } - None => mbe::DeclarativeMacro::from_err( - mbe::ParseError::Expected("expected a token tree".into()), - is_2021, - ), + None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected( + "expected a token tree".into(), + )), }, transparency(¯o_def).unwrap_or(Transparency::Opaque), ), diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 5d4f7dc1462..db8bbeccef8 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -30,10 +30,11 @@ use triomphe::Arc; use std::{fmt, hash::Hash}; -use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId}; +use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId}; use either::Either; use span::{ - ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId, + Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, + SyntaxContextId, }; use syntax::{ ast::{self, AstNode}, @@ -53,11 +54,9 @@ use crate::{ pub use crate::files::{AstId, ErasedAstId, InFile, InMacroFile, InRealFile}; -pub use mbe::ValueResult; +pub use mbe::{DeclarativeMacro, ValueResult}; pub use span::{HirFileId, MacroCallId, MacroFileId}; -pub type DeclarativeMacro = ::mbe::DeclarativeMacro; - pub mod tt { pub use span::Span; pub use tt::{DelimiterKind, Spacing}; @@ -201,7 +200,7 @@ pub struct EagerCallInfo { /// Call id of the eager macro's input file (this is the macro file for its fully expanded input). arg_id: MacroCallId, error: Option, - /// TODO: Doc + /// The call site span of the eager macro span: Span, } @@ -212,7 +211,7 @@ pub enum MacroCallKind { expand_to: ExpandTo, /// Some if this is a macro call for an eager macro. Note that this is `None` /// for the eager input macro file. - // FIXME: This is being interned, subtrees can vary quickly differ just slightly causing + // FIXME: This is being interned, subtrees can vary quickly differing just slightly causing // leakage problems here eager: Option>, }, @@ -225,6 +224,9 @@ pub enum MacroCallKind { derive_attr_index: AttrId, /// Index of the derive macro in the derive attribute derive_index: u32, + /// The "parent" macro call. + /// We will resolve the same token tree for all derive macros in the same derive attribute. + derive_macro_id: MacroCallId, }, Attr { ast_id: AstId, @@ -484,7 +486,7 @@ impl MacroDefId { matches!( self.kind, MacroDefKind::BuiltIn(..) - | MacroDefKind::ProcMacro(_, ProcMacroKind::FuncLike, _) + | MacroDefKind::ProcMacro(_, ProcMacroKind::Bang, _) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) ) @@ -806,7 +808,8 @@ impl ExpansionInfo { let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() }; - let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id); + let (macro_arg, _, _) = + db.macro_arg_considering_derives(macro_file.macro_call_id, &loc.kind); let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index fc186d2c26d..46f8c2b9d8c 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -225,6 +225,26 @@ fn convert_path( let mut segments = path.segments(); let segment = &segments.next()?; + let handle_super_kw = &mut |init_deg| { + let mut deg = init_deg; + let mut next_segment = None; + for segment in segments.by_ref() { + match segment.kind()? { + ast::PathSegmentKind::SuperKw => deg += 1, + ast::PathSegmentKind::Name(name) => { + next_segment = Some(name.as_name()); + break; + } + ast::PathSegmentKind::Type { .. } + | ast::PathSegmentKind::SelfTypeKw + | ast::PathSegmentKind::SelfKw + | ast::PathSegmentKind::CrateKw => return None, + } + } + + Some(ModPath::from_segments(PathKind::Super(deg), next_segment)) + }; + let mut mod_path = match segment.kind()? { ast::PathSegmentKind::Name(name_ref) => { if name_ref.text() == "$crate" { @@ -245,26 +265,8 @@ fn convert_path( ModPath::from_segments(PathKind::Plain, Some(known::SELF_TYPE)) } ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()), - ast::PathSegmentKind::SelfKw => ModPath::from_segments(PathKind::Super(0), iter::empty()), - ast::PathSegmentKind::SuperKw => { - let mut deg = 1; - let mut next_segment = None; - for segment in segments.by_ref() { - match segment.kind()? { - ast::PathSegmentKind::SuperKw => deg += 1, - ast::PathSegmentKind::Name(name) => { - next_segment = Some(name.as_name()); - break; - } - ast::PathSegmentKind::Type { .. } - | ast::PathSegmentKind::SelfTypeKw - | ast::PathSegmentKind::SelfKw - | ast::PathSegmentKind::CrateKw => return None, - } - } - - ModPath::from_segments(PathKind::Super(deg), next_segment) - } + ast::PathSegmentKind::SelfKw => handle_super_kw(0)?, + ast::PathSegmentKind::SuperKw => handle_super_kw(1)?, ast::PathSegmentKind::Type { .. } => { // not allowed in imports return None; diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index ca6fc0afe2d..abed16fecde 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/crates/hir-expand/src/proc_macro.rs @@ -23,7 +23,7 @@ impl ProcMacroId { #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] pub enum ProcMacroKind { CustomDerive, - FuncLike, + Bang, Attr, } diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 3cfedcdcb4d..bf473740166 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -47,13 +47,14 @@ hir-expand.workspace = true base-db.workspace = true syntax.workspace = true limit.workspace = true +span.workspace = true [dev-dependencies] expect-test = "1.4.0" tracing.workspace = true tracing-subscriber.workspace = true tracing-tree.workspace = true -project-model = { path = "../project-model" } +project-model.workspace = true # local deps test-utils.workspace = true diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs index c485c9b2e80..cb118a36848 100644 --- a/crates/hir-ty/src/builder.rs +++ b/crates/hir-ty/src/builder.rs @@ -9,21 +9,21 @@ use chalk_ir::{ AdtId, DebruijnIndex, Scalar, }; use hir_def::{ - builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId, - GenericDefId, TraitId, TypeAliasId, + builtin_type::BuiltinType, DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId, }; use smallvec::SmallVec; use crate::{ - consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, - to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, - GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, - TyKind, + consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime, + infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, + Binders, BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, + Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, }; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ParamKind { Type, + Lifetime, Const(Ty), } @@ -107,6 +107,9 @@ impl TyBuilder { ParamKind::Const(ty) => { BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner) } + ParamKind::Lifetime => { + BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) + } }); this.vec.extend(filler.take(this.remaining()).casted(Interner)); assert_eq!(this.remaining(), 0); @@ -119,6 +122,7 @@ impl TyBuilder { let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x { ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }); this.vec.extend(filler.casted(Interner)); assert_eq!(this.remaining(), 0); @@ -130,6 +134,7 @@ impl TyBuilder { self.fill(|x| match x { ParamKind::Type => table.new_type_var().cast(Interner), ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), + ParamKind::Lifetime => table.new_lifetime_var().cast(Interner), }) } @@ -142,7 +147,8 @@ impl TyBuilder { fn assert_match_kind(&self, a: &chalk_ir::GenericArg, e: &ParamKind) { match (a.data(Interner), e) { (GenericArgData::Ty(_), ParamKind::Type) - | (GenericArgData::Const(_), ParamKind::Const(_)) => (), + | (GenericArgData::Const(_), ParamKind::Const(_)) + | (GenericArgData::Lifetime(_), ParamKind::Lifetime) => (), _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), } } @@ -201,10 +207,11 @@ impl TyBuilder<()> { Substitution::from_iter( Interner, params.iter_id().map(|id| match id { - either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - either::Either::Right(id) => { + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(id) => { unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) } + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }), ) } @@ -219,11 +226,10 @@ impl TyBuilder<()> { assert!(generics.parent_generics().is_some() == parent_subst.is_some()); let params = generics .iter_self() - .map(|(id, data)| match data { - TypeOrConstParamData::TypeParamData(_) => ParamKind::Type, - TypeOrConstParamData::ConstParamData(_) => { - ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id))) - } + .map(|(id, _data)| match id { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)), + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, }) .collect(); TyBuilder::new((), params, parent_subst) diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index e678a2fee13..46612242b09 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { }; chalk_ir::Binders::new(binders, bound) } + crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = self + .db + .type_alias_impl_traits(alias) + .expect("impl trait id without impl traits"); + let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); + let data = &datas.impl_traits[idx]; + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + chalk_ir::Binders::new(binders, bound) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { if let Some((future_trait, future_output)) = self .db diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 795a5996912..d1aebeff261 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -268,6 +268,13 @@ impl TyExt for Ty { data.substitute(Interner, &subst).into_value_and_skipped_binders().0 }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &subst).into_value_and_skipped_binders().0 + }) + } } } TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { @@ -280,6 +287,13 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &opaque_ty.substitution) + }) + } // It always has an parameter for Future::Output type. ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), }; diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 98384c47490..d1ffd5046c3 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -2825,3 +2825,30 @@ fn unsized_local() { |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), ); } + +#[test] +fn recursive_adt() { + check_fail( + r#" + //- minicore: coerce_unsized, index, slice + pub enum TagTree { + Leaf, + Choice(&'static [TagTree]), + } + const GOAL: TagTree = { + const TAG_TREE: TagTree = TagTree::Choice(&[ + { + const VARIANT_TAG_TREE: TagTree = TagTree::Choice( + &[ + TagTree::Leaf, + ], + ); + VARIANT_TAG_TREE + }, + ]); + TAG_TREE + }; + "#, + |e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)), + ); +} diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 28c497989fe..90bf46b5056 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -23,9 +23,9 @@ use crate::{ layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, - TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, + Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits, + InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment, + TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; #[salsa::invoke(crate::lower::return_type_impl_traits)] - fn return_type_impl_traits( - &self, - def: FunctionId, - ) -> Option>>; + fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; + + #[salsa::invoke(crate::lower::type_alias_impl_traits)] + fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 67cfbc294df..20b0da441da 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -11,7 +11,6 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_pattern_analysis::usefulness::{compute_match_usefulness, ValidityConstraint}; use syntax::{ast, AstNode}; use tracing::debug; use triomphe::Arc; @@ -234,13 +233,7 @@ impl ExprValidator { return; } - let report = match compute_match_usefulness( - &cx, - m_arms.as_slice(), - scrut_ty.clone(), - ValidityConstraint::ValidOnly, - None, - ) { + let report = match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty.clone()) { Ok(report) => report, Err(()) => return, }; @@ -282,13 +275,7 @@ impl ExprValidator { continue; } - let report = match compute_match_usefulness( - &cx, - &[match_arm], - ty.clone(), - ValidityConstraint::ValidOnly, - None, - ) { + let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { Ok(v) => v, Err(e) => { debug!(?e, "match usefulness error"); diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index ca058428796..f45beb4c92b 100644 --- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -8,7 +8,8 @@ use rustc_hash::FxHashMap; use rustc_pattern_analysis::{ constructor::{Constructor, ConstructorSet, VariantVisibility}, index::IdxContainer, - Captures, PrivateUninhabitedField, TypeCx, + usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport}, + Captures, PatCx, PrivateUninhabitedField, }; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -59,6 +60,18 @@ impl<'p> MatchCheckCtx<'p> { Self { module, body, db, exhaustive_patterns, min_exhaustive_patterns } } + pub(crate) fn compute_match_usefulness( + &self, + arms: &[MatchArm<'p>], + scrut_ty: Ty, + ) -> Result, ()> { + // FIXME: Determine place validity correctly. For now, err on the safe side. + let place_validity = PlaceValidity::MaybeInvalid; + // Measured to take ~100ms on modern hardware. + let complexity_limit = Some(500000); + compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) + } + fn is_uninhabited(&self, ty: &Ty) -> bool { is_ty_uninhabited_from(ty, self.module, self.db) } @@ -107,15 +120,17 @@ impl<'p> MatchCheckCtx<'p> { } pub(crate) fn lower_pat(&self, pat: &Pat) -> DeconstructedPat<'p> { - let singleton = |pat| vec![pat]; + let singleton = |pat: DeconstructedPat<'p>| vec![pat.at_index(0)]; let ctor; - let fields: Vec<_>; + let mut fields: Vec<_>; + let arity; match pat.kind.as_ref() { PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = Vec::new(); + arity = 0; } PatKind::Deref { subpattern } => { ctor = match pat.ty.kind(Interner) { @@ -128,23 +143,22 @@ impl<'p> MatchCheckCtx<'p> { } }; fields = singleton(self.lower_pat(subpattern)); + arity = 1; } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + fields = subpatterns + .iter() + .map(|pat| { + let idx: u32 = pat.field.into_raw().into(); + self.lower_pat(&pat.pattern).at_index(idx as usize) + }) + .collect(); match pat.ty.kind(Interner) { TyKind::Tuple(_, substs) => { ctor = Struct; - let mut wilds: Vec<_> = substs - .iter(Interner) - .map(|arg| arg.assert_ty_ref(Interner).clone()) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let idx: u32 = pat.field.into_raw().into(); - wilds[idx as usize] = self.lower_pat(&pat.pattern); - } - fields = wilds + arity = substs.len(Interner); } - TyKind::Adt(adt, substs) if is_box(self.db, adt.0) => { + TyKind::Adt(adt, _) if is_box(self.db, adt.0) => { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -157,16 +171,9 @@ impl<'p> MatchCheckCtx<'p> { // normally or through box-patterns. We'll have to figure out a proper // solution when we introduce generalized deref patterns. Also need to // prevent mixing of those two options. - let pat = - subpatterns.iter().find(|pat| pat.field.into_raw() == 0u32.into()); - let field = if let Some(pat) = pat { - self.lower_pat(&pat.pattern) - } else { - let ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); - DeconstructedPat::wildcard(ty) - }; + fields.retain(|ipat| ipat.idx == 0); ctor = Struct; - fields = singleton(field); + arity = 1; } &TyKind::Adt(adt, _) => { ctor = match pat.kind.as_ref() { @@ -181,37 +188,33 @@ impl<'p> MatchCheckCtx<'p> { } }; let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap(); - // Fill a vec with wildcards, then place the fields we have at the right - // index. - let mut wilds: Vec<_> = self - .list_variant_fields(&pat.ty, variant) - .map(|(_, ty)| ty) - .map(DeconstructedPat::wildcard) - .collect(); - for pat in subpatterns { - let field_id: u32 = pat.field.into_raw().into(); - wilds[field_id as usize] = self.lower_pat(&pat.pattern); - } - fields = wilds; + arity = variant.variant_data(self.db.upcast()).fields().len(); } _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); ctor = Wildcard; - fields = Vec::new(); + fields.clear(); + arity = 0; } } } &PatKind::LiteralBool { value } => { ctor = Bool(value); fields = Vec::new(); + arity = 0; } PatKind::Or { pats } => { ctor = Or; - fields = pats.iter().map(|pat| self.lower_pat(pat)).collect(); + fields = pats + .iter() + .enumerate() + .map(|(i, pat)| self.lower_pat(pat).at_index(i)) + .collect(); + arity = pats.len(); } } let data = PatData { db: self.db }; - DeconstructedPat::new(ctor, fields, pat.ty.clone(), data) + DeconstructedPat::new(ctor, fields, arity, pat.ty.clone(), data) } pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'p>) -> Pat { @@ -271,7 +274,7 @@ impl<'p> MatchCheckCtx<'p> { } } -impl<'p> TypeCx for MatchCheckCtx<'p> { +impl<'p> PatCx for MatchCheckCtx<'p> { type Error = (); type Ty = Ty; type VariantIdx = EnumVariantId; @@ -453,7 +456,7 @@ impl<'p> TypeCx for MatchCheckCtx<'p> { let variant = pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt)); - let db = pat.data().unwrap().db; + let db = pat.data().db; if let Some(variant) = variant { match variant { VariantId::EnumVariantId(v) => { @@ -475,7 +478,6 @@ impl<'p> TypeCx for MatchCheckCtx<'p> { } fn complexity_exceeded(&self) -> Result<(), Self::Error> { - // FIXME(Nadrieril): make use of the complexity counter. Err(()) } } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 20964f5acbd..8740ae6797c 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -938,18 +938,32 @@ impl HirDisplay for Ty { f.end_location_link(); if parameters.len(Interner) > 0 { let generics = generics(db.upcast(), def.into()); - let (parent_params, self_param, type_params, const_params, _impl_trait_params) = - generics.provenance_split(); - let total_len = parent_params + self_param + type_params + const_params; + let ( + parent_params, + self_param, + type_params, + const_params, + _impl_trait_params, + lifetime_params, + ) = generics.provenance_split(); + let total_len = + parent_params + self_param + type_params + const_params + lifetime_params; // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? if total_len > 0 { - // `parameters` are in the order of fn's params (including impl traits), + // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes // parent's params (those from enclosing impl or trait, if any). let parameters = parameters.as_slice(Interner); let fn_params_len = self_param + type_params + const_params; + // This will give slice till last type or const let fn_params = parameters.get(..fn_params_len); + let fn_lt_params = + parameters.get(fn_params_len..(fn_params_len + lifetime_params)); let parent_params = parameters.get(parameters.len() - parent_params..); - let params = parent_params.into_iter().chain(fn_params).flatten(); + let params = parent_params + .into_iter() + .chain(fn_lt_params) + .chain(fn_params) + .flatten(); write!(f, "<")?; f.write_joined(params, ", ")?; write!(f, ">")?; @@ -1063,6 +1077,20 @@ impl HirDisplay for Ty { )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, ¶meters); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => { let future_trait = db .lang_item(body.module(db.upcast()).krate(), LangItem::Future) @@ -1228,6 +1256,20 @@ impl HirDisplay for Ty { SizedByDefault::Sized { anchor: krate }, )?; } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, &opaque_ty.substitution); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; } @@ -1280,8 +1322,17 @@ fn hir_fmt_generics( generic_def: Option, ) -> Result<(), HirDisplayError> { let db = f.db; - let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len()); - if parameters.len(Interner) + lifetime_args_count > 0 { + if parameters.len(Interner) > 0 { + use std::cmp::Ordering; + let param_compare = + |a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) { + (crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => { + Ordering::Equal + } + (crate::GenericArgData::Lifetime(_), _) => Ordering::Less, + (_, crate::GenericArgData::Lifetime(_)) => Ordering::Less, + (_, _) => Ordering::Equal, + }; let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() { match generic_def .map(|generic_def_id| db.generic_defaults(generic_def_id)) @@ -1307,6 +1358,11 @@ fn hir_fmt_generics( return true; } } + if parameter.lifetime(Interner).map(|it| it.data(Interner)) + == Some(&crate::LifetimeData::Static) + { + return true; + } let default_parameter = match default_parameters.get(i) { Some(it) => it, None => return true, @@ -1327,16 +1383,12 @@ fn hir_fmt_generics( } else { parameters.as_slice(Interner) }; - if !parameters_to_write.is_empty() || lifetime_args_count != 0 { + //FIXME: Should handle the ordering of lifetimes when creating substitutions + let mut parameters_to_write = parameters_to_write.to_vec(); + parameters_to_write.sort_by(param_compare); + if !parameters_to_write.is_empty() { write!(f, "<")?; let mut first = true; - for _ in 0..lifetime_args_count { - if !first { - write!(f, ", ")?; - } - first = false; - write!(f, "'_")?; - } for generic_arg in parameters_to_write { if !first { write!(f, ", ")?; diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 34ba17f145e..be3b50e1411 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -25,8 +25,11 @@ pub(crate) mod unify; use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, - Scalar, TyKind, TypeFlags, Variance, + cast::Cast, + fold::TypeFoldable, + interner::HasInterner, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance, }; use either::Either; use hir_def::{ @@ -39,7 +42,7 @@ use hir_def::{ layout::Integer, path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, - type_ref::TypeRef, + type_ref::{LifetimeRef, TypeRef}, AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, }; @@ -53,14 +56,14 @@ use triomphe::Arc; use crate::{ db::HirDatabase, fold_tys, - infer::coerce::CoerceMany, + infer::{coerce::CoerceMany, unify::InferenceTable}, lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -422,7 +425,7 @@ pub struct InferenceResult { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap, pub type_of_binding: ArenaMap, - pub type_of_rpit: ArenaMap, + pub type_of_rpit: ArenaMap, /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. pub type_of_for_iterator: FxHashMap, type_mismatches: FxHashMap, @@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> { } fn collect_const(&mut self, data: &ConstData) { - self.return_ty = self.make_ty(&data.type_ref); + let return_ty = self.make_ty(&data.type_ref); + + // Constants might be associated items that define ATPITs. + self.insert_atpit_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; } fn collect_static(&mut self, data: &StaticData) { @@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> { self.write_binding_ty(self_param, ty); } } + let mut params_and_ret_tys = Vec::new(); for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); self.infer_top_pat(*pat, &ty); + params_and_ret_tys.push(ty); } let return_ty = &*data.ret_type; @@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - let result = - self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); + let result = self.insert_inference_vars_for_impl_trait( + return_ty, + rpits.clone(), + fn_placeholders, + ); let rpits = rpits.skip_binders(); for (id, _) in rpits.impl_traits.iter() { if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { @@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.normalize_associated_types_in(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + + // Functions might be associated items that define ATPITs. + // To define an ATPITs, that ATPIT must appear in the function's signatures. + // So, it suffices to check for params and return types. + params_and_ret_tys.push(self.return_ty.clone()); + self.insert_atpit_coercion_table(params_and_ret_tys.iter()); } - fn insert_inference_vars_for_rpit( + fn insert_inference_vars_for_impl_trait( &mut self, t: T, - rpits: Arc>, - fn_placeholders: Substitution, + rpits: Arc>, + placeholders: Substitution, ) -> T where T: crate::HasInterner + crate::TypeFoldable, @@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> { }; let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, + ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx, _ => unreachable!(), }; let bounds = @@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> { let var = self.table.new_type_var(); let var_subst = Substitution::from1(Interner, var.clone()); for bound in bounds { - let predicate = - bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); + let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders); let (var_predicate, binders) = predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - let var_predicate = self.insert_inference_vars_for_rpit( + let var_predicate = self.insert_inference_vars_for_impl_trait( var_predicate, rpits.clone(), - fn_placeholders.clone(), + placeholders.clone(), ); self.push_obligation(var_predicate.cast(Interner)); } @@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> { ) } + /// The coercion of a non-inference var into an opaque type should fail, + /// but not in the defining sites of the ATPITs. + /// In such cases, we insert an proxy inference var for each ATPIT, + /// and coerce into it instead of ATPIT itself. + /// + /// The inference var stretagy is effective because; + /// + /// - It can still unify types that coerced into ATPIT + /// - We are pushing `impl Trait` bounds into it + /// + /// This function inserts a map that maps the opaque type to that proxy inference var. + fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator) { + struct OpaqueTyCollector<'a, 'b> { + table: &'b mut InferenceTable<'a>, + opaque_tys: FxHashMap, + } + + impl<'a, 'b> TypeVisitor for OpaqueTyCollector<'a, 'b> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty( + &mut self, + ty: &chalk_ir::Ty, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + let ty = self.table.resolve_ty_shallow(ty); + + if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { + self.opaque_tys.insert(*id, ty.clone()); + } + + ty.super_visit_with(self, outer_binder) + } + } + + // Early return if this is not happening inside the impl block + let impl_id = if let Some(impl_id) = self.resolver.impl_def() { + impl_id + } else { + return; + }; + + let assoc_tys: FxHashSet<_> = self + .db + .impl_data(impl_id) + .items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(alias) => Some(*alias), + _ => None, + }) + .collect(); + if assoc_tys.is_empty() { + return; + } + + let mut collector = + OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() }; + for ty in tys { + ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + } + let atpit_coercion_table: FxHashMap<_, _> = collector + .opaque_tys + .into_iter() + .filter_map(|(opaque_ty_id, ty)| { + if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + { + if assoc_tys.contains(&alias_id) { + let atpits = self + .db + .type_alias_impl_traits(alias_id) + .expect("Marked as ATPIT but no impl traits!"); + let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id); + let ty = self.insert_inference_vars_for_impl_trait( + ty, + atpits, + alias_placeholders, + ); + return Some((opaque_ty_id, ty)); + } + } + + None + }) + .collect(); + + if !atpit_coercion_table.is_empty() { + self.table.atpit_coercion_table = Some(atpit_coercion_table); + } + } + fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), @@ -918,6 +1037,12 @@ impl<'a> InferenceContext<'a> { self.result.standard_types.unknown.clone() } + fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime { + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); + let lt = ctx.lower_lifetime(lifetime_ref); + self.insert_type_vars(lt) + } + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { self.table.insert_type_vars_shallow(ty) diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index ff6de61ba64..cfbbc9dd6c0 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -276,6 +276,23 @@ impl InferenceTable<'_> { return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } + // If we are coercing into an ATPIT, coerce into its proxy inference var, instead. + let mut to_ty = to_ty; + let _to; + if let Some(atpit_table) = &self.atpit_coercion_table { + if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { + if !matches!( + from_ty.kind(Interner), + TyKind::InferenceVar(..) | TyKind::OpaqueType(..) + ) { + if let Some(ty) = atpit_table.get(opaque_ty_id) { + _to = ty.clone(); + to_ty = &_to; + } + } + } + } + // Consider coercing the subtype to a DST if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { return Ok(ret); diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index a3dab1fd9d5..35d59679355 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -8,13 +8,12 @@ use std::{ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind}; use either::Either; use hir_def::{ - generics::TypeOrConstParamData, hir::{ ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, - path::{GenericArg, GenericArgs, Path}, - BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, TupleFieldId, TupleId, + path::{GenericArgs, Path}, + BlockId, FieldId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, }; use hir_expand::name::{name, Name}; use stdx::always; @@ -1816,10 +1815,17 @@ impl InferenceContext<'_> { def_generics: Generics, generic_args: Option<&GenericArgs>, ) -> Substitution { - let (parent_params, self_params, type_params, const_params, impl_trait_params) = - def_generics.provenance_split(); + let ( + parent_params, + self_params, + type_params, + const_params, + impl_trait_params, + lifetime_params, + ) = def_generics.provenance_split(); assert_eq!(self_params, 0); // method shouldn't have another Self param - let total_len = parent_params + type_params + const_params + impl_trait_params; + let total_len = + parent_params + type_params + const_params + impl_trait_params + lifetime_params; let mut substs = Vec::with_capacity(total_len); // handle provided arguments @@ -1828,8 +1834,7 @@ impl InferenceContext<'_> { for (arg, kind_id) in generic_args .args .iter() - .filter(|arg| !matches!(arg, GenericArg::Lifetime(_))) - .take(type_params + const_params) + .take(type_params + const_params + lifetime_params) .zip(def_generics.iter_id()) { if let Some(g) = generic_arg_to_chalk( @@ -1850,6 +1855,7 @@ impl InferenceContext<'_> { DebruijnIndex::INNERMOST, ) }, + |this, lt_ref| this.make_lifetime(lt_ref), ) { substs.push(g); } @@ -1858,16 +1864,17 @@ impl InferenceContext<'_> { // Handle everything else as unknown. This also handles generic arguments for the method's // parent (impl or trait), which should come after those for the method. - for (id, data) in def_generics.iter().skip(substs.len()) { - match data { - TypeOrConstParamData::TypeParamData(_) => { + for (id, _data) in def_generics.iter().skip(substs.len()) { + match id { + GenericParamId::TypeParamId(_) => { substs.push(self.table.new_type_var().cast(Interner)) } - TypeOrConstParamData::ConstParamData(_) => substs.push( - self.table - .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id))) - .cast(Interner), - ), + GenericParamId::ConstParamId(id) => { + substs.push(self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)) + } + GenericParamId::LifetimeParamId(_) => { + substs.push(self.table.new_lifetime_var().cast(Interner)) + } } } assert_eq!(substs.len(), total_len); diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 8f537bb448b..9a1835b625b 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -11,15 +11,15 @@ use stdx::never; use crate::{ builder::ParamKind, - consteval, + consteval, error_lifetime, method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, utils::generics, - InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - ValueTyDefId, + InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + TyKind, ValueTyDefId, }; -use super::{ExprOrPatId, InferenceContext, TraitRef}; +use super::{ExprOrPatId, InferenceContext}; impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { @@ -111,6 +111,7 @@ impl InferenceContext<'_> { it.next().unwrap_or_else(|| match x { ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) }) .build(); diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index be7547f9bae..afb89fe1e5b 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -10,16 +10,18 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; +use rustc_hash::FxHashMap; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, - to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, - DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, + static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, + Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, + Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, + ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + TyKind, VariableKind, WhereClause, }; impl InferenceContext<'_> { @@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, + pub(crate) atpit_coercion_table: Option>, var_unification_table: ChalkInferenceTable, type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, @@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> { InferenceTable { db, trait_env, + atpit_coercion_table: None, var_unification_table: ChalkInferenceTable::new(), type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), @@ -803,6 +807,7 @@ impl<'a> InferenceTable<'a> { .fill(|it| { let arg = match it { ParamKind::Type => self.new_type_var(), + ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"), ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; arg_tys.push(arg.clone()); @@ -857,11 +862,16 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - fold_tys_and_consts( + fold_generic_args( ty, - |it, _| match it { - Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), - Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), + |arg, _| match arg { + GenericArgData::Ty(ty) => GenericArgData::Ty(self.insert_type_vars_shallow(ty)), + // FIXME: insert lifetime vars once LifetimeData::InferenceVar + // and specific error variant for lifetimes start being constructed + GenericArgData::Lifetime(lt) => GenericArgData::Lifetime(lt), + GenericArgData::Const(c) => { + GenericArgData::Const(self.insert_const_vars_shallow(c)) + } }, DebruijnIndex::INNERMOST, ) diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 9655981cc9c..dd949e26c2a 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -389,6 +389,9 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index ec97bdc2c43..ba64f5c8d7e 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -15,7 +15,8 @@ extern crate rustc_abi; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_abi as rustc_abi; -// No need to use the in-tree one. +// Use the crates.io version unconditionally until the API settles enough that we can switch to +// using the in-tree one. extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; mod builder; @@ -89,8 +90,8 @@ pub use lower::{ }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, - lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, - to_placeholder_idx, + lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, + to_foreign_def_id, to_placeholder_idx, }; pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; @@ -334,11 +335,23 @@ pub(crate) fn make_binders_with_count>( generics: &Generics, value: T, ) -> Binders { - let it = generics.iter_id().take(count).map(|id| match id { - Either::Left(_) => None, - Either::Right(id) => Some(db.const_param_ty(id)), - }); - crate::make_type_and_const_binders(it, value) + let it = generics.iter_id().take(count); + + Binders::new( + VariableKinds::from_iter( + Interner, + it.map(|x| match x { + hir_def::GenericParamId::ConstParamId(id) => { + chalk_ir::VariableKind::Const(db.const_param_ty(id)) + } + hir_def::GenericParamId::TypeParamId(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime, + }), + ), + value, + ) } pub(crate) fn make_binders>( @@ -584,29 +597,34 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { - ReturnTypeImplTrait(hir_def::FunctionId, RpitId), + ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), + AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } impl_intern_value_trivial!(ImplTraitId); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Arena, +pub struct ImplTraits { + pub(crate) impl_traits: Arena, } -has_interner!(ReturnTypeImplTraits); +has_interner!(ImplTraits); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTrait { +pub struct ImplTrait { pub(crate) bounds: Binders>, } -pub type RpitId = Idx; +pub type ImplTraitIdx = Idx; pub fn static_lifetime() -> Lifetime { LifetimeData::Static.intern(Interner) } +pub fn error_lifetime() -> Lifetime { + static_lifetime() +} + pub(crate) fn fold_free_vars + TypeFoldable>( t: T, for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty, @@ -696,6 +714,55 @@ pub(crate) fn fold_tys_and_consts + TypeFold t.fold_with(&mut TyFolder(f), binders) } +pub(crate) fn fold_generic_args + TypeFoldable>( + t: T, + f: impl FnMut(GenericArgData, DebruijnIndex) -> GenericArgData, + binders: DebruijnIndex, +) -> T { + use chalk_ir::fold::{TypeFolder, TypeSuperFoldable}; + #[derive(chalk_derive::FallibleTypeFolder)] + #[has_interner(Interner)] + struct TyFolder GenericArgData>(F); + impl GenericArgData> TypeFolder + for TyFolder + { + fn as_dyn(&mut self) -> &mut dyn TypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty { + let ty = ty.super_fold_with(self.as_dyn(), outer_binder); + self.0(GenericArgData::Ty(ty), outer_binder) + .intern(Interner) + .ty(Interner) + .unwrap() + .clone() + } + + fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const { + self.0(GenericArgData::Const(c), outer_binder) + .intern(Interner) + .constant(Interner) + .unwrap() + .clone() + } + + fn fold_lifetime(&mut self, lt: Lifetime, outer_binder: DebruijnIndex) -> Lifetime { + let lt = lt.super_fold_with(self.as_dyn(), outer_binder); + self.0(GenericArgData::Lifetime(lt), outer_binder) + .intern(Interner) + .lifetime(Interner) + .unwrap() + .clone() + } + } + t.fold_with(&mut TyFolder(f), binders) +} + /// 'Canonicalizes' the `t` by replacing any errors with new variables. Also /// ensures there are no unbound variables or inference variables anywhere in /// the `t`. diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index dac20f22597..25ccc84c13c 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -24,17 +24,20 @@ use hir_def::{ data::adt::StructKind, expander::Expander, generics::{ - TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, + GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, + WherePredicateTypeTarget, }, lang_item::LangItem, nameres::MacroSubNs, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, - resolver::{HasResolver, Resolver, TypeNs}, - type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + type_ref::{ + ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, + }, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, - GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup, - ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, - TypeParamId, UnionId, VariantId, + GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, + Lookup, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, + UnionId, VariantId, }; use hir_expand::{name::Name, ExpandResult}; use intern::Interned; @@ -52,18 +55,18 @@ use crate::{ unknown_const_as_generic, }, db::HirDatabase, - make_binders, - mapping::{from_chalk_trait_id, ToChalk}, + error_lifetime, make_binders, + mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, - utils::Generics, utils::{ - all_super_trait_refs, associated_type_by_name_including_super_traits, generics, + all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, - QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, + LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, + TyKind, WhereClause, }; #[derive(Debug)] @@ -76,7 +79,7 @@ enum ImplTraitLoweringState { /// we're grouping the mutable data (the counter and this field) together /// with the immutable context (the references to the DB and resolver). /// Splitting this up would be a possible fix. - Opaque(RefCell>), + Opaque(RefCell>), Param(Cell), Variable(Cell), Disallowed, @@ -275,9 +278,11 @@ impl<'a> TyLoweringContext<'a> { let inner_ty = self.lower_ty(inner); TyKind::Slice(inner_ty).intern(Interner) } - TypeRef::Reference(inner, _, mutability) => { + TypeRef::Reference(inner, lifetime, mutability) => { let inner_ty = self.lower_ty(inner); - let lifetime = static_lifetime(); + // FIXME: It should infer the eldided lifetimes instead of stubbing with static + let lifetime = + lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr)); TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty) .intern(Interner) } @@ -301,15 +306,18 @@ impl<'a> TyLoweringContext<'a> { TypeRef::ImplTrait(bounds) => { match &self.impl_trait_mode { ImplTraitLoweringState::Opaque(opaque_type_data) => { - let func = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(f)) => f, - _ => panic!("opaque impl trait lowering in non-function"), + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), }; // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait { + let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { bounds: crate::make_single_type_binders(Vec::new()), }); // We don't want to lower the bounds inside the binders @@ -323,13 +331,17 @@ impl<'a> TyLoweringContext<'a> { // away instead of two. let actual_opaque_type_data = self .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds, func) + ctx.lower_impl_trait(bounds, self.resolver.krate()) }); opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data; - let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx); + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, idx), + |a| ImplTraitId::AssociatedTypeImplTrait(a, idx), + ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = generics(self.db.upcast(), func.into()); + let generics = + generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into())); let parameters = generics.bound_vars_subst(self.db, self.in_binders); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } @@ -344,13 +356,18 @@ impl<'a> TyLoweringContext<'a> { .filter(|(_, data)| { matches!( data, - TypeOrConstParamData::TypeParamData(data) + GenericParamDataRef::TypeParamData(data) if data.provenance == TypeParamProvenance::ArgumentImplTrait ) }) .nth(idx as usize) .map_or(TyKind::Error, |(id, _)| { - TyKind::Placeholder(to_placeholder_idx(self.db, id)) + if let GenericParamId::TypeParamId(id) = id { + TyKind::Placeholder(to_placeholder_idx(self.db, id.into())) + } else { + // we just filtered them out + unreachable!("Unexpected lifetime or const argument"); + } }); param.intern(Interner) } else { @@ -367,11 +384,12 @@ impl<'a> TyLoweringContext<'a> { list_params, const_params, _impl_trait_params, + _lifetime_params, ) = if let Some(def) = self.resolver.generic_def() { let generics = generics(self.db.upcast(), def); generics.provenance_split() } else { - (0, 0, 0, 0, 0) + (0, 0, 0, 0, 0, 0) }; TyKind::BoundVar(BoundVar::new( self.in_binders, @@ -808,9 +826,16 @@ impl<'a> TyLoweringContext<'a> { return Substitution::empty(Interner); }; let def_generics = generics(self.db.upcast(), def); - let (parent_params, self_params, type_params, const_params, impl_trait_params) = - def_generics.provenance_split(); - let item_len = self_params + type_params + const_params + impl_trait_params; + let ( + parent_params, + self_params, + type_params, + const_params, + impl_trait_params, + lifetime_params, + ) = def_generics.provenance_split(); + let item_len = + self_params + type_params + const_params + impl_trait_params + lifetime_params; let total_len = parent_params + item_len; let ty_error = TyKind::Error.intern(Interner).cast(Interner); @@ -825,7 +850,10 @@ impl<'a> TyLoweringContext<'a> { .take(self_params) { if let Some(id) = def_generic_iter.next() { - assert!(id.is_left()); + assert!(matches!( + id, + GenericParamId::TypeParamId(_) | GenericParamId::LifetimeParamId(_) + )); substs.push(x); } } @@ -858,6 +886,7 @@ impl<'a> TyLoweringContext<'a> { &mut (), |_, type_ref| self.lower_ty(type_ref), |_, const_ref, ty| self.lower_const(const_ref, ty), + |_, lifetime_ref| self.lower_lifetime(lifetime_ref), ) { had_explicit_args = true; substs.push(x); @@ -867,15 +896,45 @@ impl<'a> TyLoweringContext<'a> { } } } + + for arg in generic_args + .args + .iter() + .filter(|arg| matches!(arg, GenericArg::Lifetime(_))) + .take(lifetime_params) + { + // Taking into the fact that def_generic_iter will always have lifetimes at the end + // Should have some test cases tho to test this behaviour more properly + if let Some(id) = def_generic_iter.next() { + if let Some(x) = generic_arg_to_chalk( + self.db, + id, + arg, + &mut (), + |_, type_ref| self.lower_ty(type_ref), + |_, const_ref, ty| self.lower_const(const_ref, ty), + |_, lifetime_ref| self.lower_lifetime(lifetime_ref), + ) { + had_explicit_args = true; + substs.push(x); + } else { + // Never return a None explicitly + never!("Unexpected None by generic_arg_to_chalk"); + } + } + } } else { fill_self_params(); } // These params include those of parent. let remaining_params: SmallVec<[_; 2]> = def_generic_iter - .map(|eid| match eid { - Either::Left(_) => ty_error.clone(), - Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)), + .map(|id| match id { + GenericParamId::ConstParamId(x) => { + unknown_const_as_generic(self.db.const_param_ty(x)) + } + GenericParamId::TypeParamId(_) => ty_error.clone(), + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }) .collect(); assert_eq!(remaining_params.len() + substs.len(), total_len); @@ -1107,8 +1166,12 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = - (type_ref, &self.impl_trait_mode) + if let ( + TypeRef::ImplTrait(bounds), + ImplTraitLoweringState::Param(_) + | ImplTraitLoweringState::Variable(_) + | ImplTraitLoweringState::Disallowed, + ) = (type_ref, &self.impl_trait_mode) { for bound in bounds { predicates.extend( @@ -1270,11 +1333,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn lower_impl_trait( - &self, - bounds: &[Interned], - func: FunctionId, - ) -> ReturnTypeImplTrait { + fn lower_impl_trait(&self, bounds: &[Interned], krate: CrateId) -> ImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { @@ -1284,7 +1343,6 @@ impl<'a> TyLoweringContext<'a> { .collect(); if !ctx.unsized_types.borrow().contains(&self_ty) { - let krate = func.krate(ctx.db.upcast()); let sized_trait = ctx .db .lang_item(krate, LangItem::Sized) @@ -1301,7 +1359,34 @@ impl<'a> TyLoweringContext<'a> { } predicates }); - ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) } + ImplTrait { bounds: crate::make_single_type_binders(predicates) } + } + + pub fn lower_lifetime(&self, lifetime: &LifetimeRef) -> Lifetime { + match self.resolver.resolve_lifetime(lifetime) { + Some(resolution) => match resolution { + LifetimeNs::Static => static_lifetime(), + LifetimeNs::LifetimeParam(id) => match self.type_param_mode { + ParamLoweringMode::Placeholder => { + LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) + } + ParamLoweringMode::Variable => { + let generics = generics( + self.db.upcast(), + self.resolver.generic_def().expect("generics in scope"), + ); + let idx = match generics.lifetime_idx(id) { + None => return error_lifetime(), + Some(idx) => idx, + }; + + LifetimeData::BoundVar(BoundVar::new(self.in_binders, idx)) + } + } + .intern(Interner), + }, + None => error_lifetime(), + } } } @@ -1685,7 +1770,7 @@ pub(crate) fn generic_defaults_query( let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| { match p { - TypeOrConstParamData::TypeParamData(p) => { + GenericParamDataRef::TypeParamData(p) => { let mut ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); // Each default can only refer to previous parameters. @@ -1694,13 +1779,13 @@ pub(crate) fn generic_defaults_query( ty = fallback_bound_vars(ty, idx, parent_start_idx); crate::make_binders(db, &generic_params, ty.cast(Interner)) } - TypeOrConstParamData::ConstParamData(p) => { + GenericParamDataRef::ConstParamData(p) => { + let GenericParamId::ConstParamId(id) = id else { + unreachable!("Unexpected lifetime or type argument") + }; + let mut val = p.default.as_ref().map_or_else( - || { - unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), - ) - }, + || unknown_const_as_generic(db.const_param_ty(id)), |c| { let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); c.cast(Interner) @@ -1710,6 +1795,10 @@ pub(crate) fn generic_defaults_query( val = fallback_bound_vars(val, idx, parent_start_idx); make_binders(db, &generic_params, val) } + GenericParamDataRef::LifetimeParamData(_) => { + // using static because it requires defaults + make_binders(db, &generic_params, static_lifetime().cast(Interner)) + } } })); @@ -1726,8 +1815,9 @@ pub(crate) fn generic_defaults_recover( // we still need one default per parameter let defaults = Arc::from_iter(generic_params.iter_id().map(|id| { let val = match id { - Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)), + GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner), }; crate::make_binders(db, &generic_params, val) })); @@ -1869,6 +1959,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, t.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let type_alias_data = db.type_alias_data(t); if type_alias_data.is_extern { @@ -2029,7 +2120,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, def: hir_def::FunctionId, -) -> Option>> { +) -> Option>> { // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_data(def); let resolver = def.resolver(db.upcast()); @@ -2038,7 +2129,7 @@ pub(crate) fn return_type_impl_traits( .with_type_param_mode(ParamLoweringMode::Variable); let _ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let return_type_impl_traits = ReturnTypeImplTraits { + let return_type_impl_traits = ImplTraits { impl_traits: match ctx_ret.impl_trait_mode { ImplTraitLoweringState::Opaque(x) => x.into_inner(), _ => unreachable!(), @@ -2051,6 +2142,32 @@ pub(crate) fn return_type_impl_traits( } } +pub(crate) fn type_alias_impl_traits( + db: &dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>> { + let data = db.type_alias_data(def); + let resolver = def.resolver(db.upcast()); + let ctx = TyLoweringContext::new(db, &resolver, def.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); + if let Some(type_ref) = &data.type_ref { + let _ty = ctx.lower_ty(type_ref); + } + let generics = generics(db.upcast(), def.into()); + let type_alias_impl_traits = ImplTraits { + impl_traits: match ctx.impl_trait_mode { + ImplTraitLoweringState::Opaque(x) => x.into_inner(), + _ => unreachable!(), + }, + }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) + } +} + pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability { match m { hir_def::type_ref::Mutability::Shared => Mutability::Not, @@ -2064,23 +2181,29 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut /// Returns `Some` of the lowered generic arg. `None` if the provided arg is a lifetime. pub(crate) fn generic_arg_to_chalk<'a, T>( db: &dyn HirDatabase, - kind_id: Either, + kind_id: GenericParamId, arg: &'a GenericArg, this: &mut T, for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a, for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a, + for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a, ) -> Option { let kind = match kind_id { - Either::Left(_) => ParamKind::Type, - Either::Right(id) => { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => { let ty = db.const_param_ty(id); ParamKind::Const(ty) } + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, }; Some(match (arg, kind) { (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner), (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner), + (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { + for_lifetime(this, lifetime_ref).cast(Interner) + } (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), + (GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner), (GenericArg::Type(t), ParamKind::Const(c_ty)) => { // We want to recover simple idents, which parser detects them // as types. Maybe here is not the best place to do it, but @@ -2096,7 +2219,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( } unknown_const_as_generic(c_ty) } - (GenericArg::Lifetime(_), _) => return None, + (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty), + (GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner), + (GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner), }) } diff --git a/crates/hir-ty/src/mapping.rs b/crates/hir-ty/src/mapping.rs index fba760974f2..c61d8277142 100644 --- a/crates/hir-ty/src/mapping.rs +++ b/crates/hir-ty/src/mapping.rs @@ -151,6 +151,14 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L db.lookup_intern_lifetime_param_id(interned_id) } +pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex { + let interned_id = db.intern_lifetime_param_id(id); + PlaceholderIndex { + ui: chalk_ir::UniverseIndex::ROOT, + idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), + } +} + pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) } diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index a679a114b4b..73b07df56f7 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -4,7 +4,7 @@ //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::ops::ControlFlow; -use base_db::{CrateId, Edition}; +use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ data::{adt::StructFlags, ImplData}, @@ -15,6 +15,7 @@ use hir_def::{ use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; +use span::Edition; use stdx::never; use triomphe::Arc; @@ -643,7 +644,7 @@ pub fn is_dyn_method( let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { return None; }; - let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let trait_params = db.generic_params(trait_id.into()).len(); let fn_params = fn_subst.len(Interner) - trait_params; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), @@ -685,7 +686,7 @@ pub(crate) fn lookup_impl_method_query( let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { return (func, fn_subst); }; - let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let trait_params = db.generic_params(trait_id.into()).len(); let fn_params = fn_subst.len(Interner) - trait_params; let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), @@ -966,7 +967,7 @@ pub fn iterate_method_candidates_dyn( // the methods by autoderef order of *receiver types*, not *self // types*. - let mut table = InferenceTable::new(db, env.clone()); + let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty.clone()); let deref_chain = autoderef_method_receiver(&mut table, ty); @@ -1044,7 +1045,7 @@ fn iterate_method_candidates_with_autoref( let ref_muted = Canonical { value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) .intern(Interner), - binders: receiver_ty.binders.clone(), + binders: receiver_ty.binders, }; iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut)) @@ -1060,7 +1061,7 @@ fn iterate_method_candidates_by_receiver( name: Option<&Name>, mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { - let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); + let receiver_ty = table.instantiate_canonical(receiver_ty); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1456,7 +1457,7 @@ fn is_valid_trait_method_candidate( if let Some(receiver_ty) = receiver_ty { check_that!(data.has_self_param()); - let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) + let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst)) .fill_with_inference_vars(table) .build(); diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index fd98141af63..045ffb418c8 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1931,7 +1931,11 @@ impl Evaluator<'_> { ty: &Ty, locals: &Locals, mm: &mut ComplexMemoryMap, + stack_depth_limit: usize, ) -> Result<()> { + if stack_depth_limit.checked_sub(1).is_none() { + return Err(MirEvalError::StackOverflow); + } match ty.kind(Interner) { TyKind::Ref(_, _, t) => { let size = this.size_align_of(t, locals)?; @@ -1970,7 +1974,14 @@ impl Evaluator<'_> { if let Some(ty) = check_inner { for i in 0..count { let offset = element_size * i; - rec(this, &b[offset..offset + element_size], ty, locals, mm)?; + rec( + this, + &b[offset..offset + element_size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } } @@ -1984,7 +1995,14 @@ impl Evaluator<'_> { let size = this.size_of_sized(inner, locals, "inner of array")?; for i in 0..len { let offset = i * size; - rec(this, &bytes[offset..offset + size], inner, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + inner, + locals, + mm, + stack_depth_limit - 1, + )?; } } chalk_ir::TyKind::Tuple(_, subst) => { @@ -1993,7 +2011,14 @@ impl Evaluator<'_> { let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { @@ -2008,7 +2033,14 @@ impl Evaluator<'_> { .bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } AdtId::EnumId(e) => { @@ -2027,7 +2059,14 @@ impl Evaluator<'_> { l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); let size = this.layout(ty)?.size.bytes_usize(); - rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + rec( + this, + &bytes[offset..offset + size], + ty, + locals, + mm, + stack_depth_limit - 1, + )?; } } } @@ -2038,7 +2077,7 @@ impl Evaluator<'_> { Ok(()) } let mut mm = ComplexMemoryMap::default(); - rec(self, bytes, ty, locals, &mut mm)?; + rec(self, bytes, ty, locals, &mut mm, self.stack_depth_limit - 1)?; Ok(mm) } @@ -2317,7 +2356,7 @@ impl Evaluator<'_> { fn exec_fn_with_args( &mut self, - def: FunctionId, + mut def: FunctionId, args: &[IntervalAndTy], generic_args: Substitution, locals: &Locals, @@ -2335,6 +2374,9 @@ impl Evaluator<'_> { )? { return Ok(None); } + if let Some(redirect_def) = self.detect_and_redirect_special_function(def)? { + def = redirect_def; + } let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)); match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { MirOrDynIndex::Dyn(self_ty_idx) => { diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 628a1fe2d28..d4d669182f2 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -13,7 +13,7 @@ use crate::mir::eval::{ name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, - ModPath, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, + Mutability, Result, Substitution, Ty, TyBuilder, TyExt, }; mod simd; @@ -158,6 +158,25 @@ impl Evaluator<'_> { Ok(false) } + pub(super) fn detect_and_redirect_special_function( + &mut self, + def: FunctionId, + ) -> Result> { + // `PanicFmt` is redirected to `ConstPanicFmt` + if let Some(LangItem::PanicFmt) = self.db.lang_attr(def.into()) { + let resolver = + self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast()); + + let Some(hir_def::lang_item::LangItemTarget::Function(const_panic_fmt)) = + self.db.lang_item(resolver.krate(), LangItem::ConstPanicFmt) + else { + not_supported!("const_panic_fmt lang item not found or not a function"); + }; + return Ok(Some(const_panic_fmt)); + } + Ok(None) + } + /// Clone has special impls for tuples and function pointers fn exec_clone( &mut self, @@ -291,9 +310,14 @@ impl Evaluator<'_> { use LangItem::*; let candidate = self.db.lang_attr(def.into())?; // We want to execute these functions with special logic - if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { + // `PanicFmt` is not detected here as it's redirected later. + if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } + if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() { + // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. + return Some(LangItem::BeginPanic); + } None } @@ -309,43 +333,6 @@ impl Evaluator<'_> { let mut args = args.iter(); match it { BeginPanic => Err(MirEvalError::Panic("".to_owned())), - PanicFmt => { - let message = (|| { - let resolver = self - .db - .crate_def_map(self.crate_id) - .crate_root() - .resolver(self.db.upcast()); - let Some(format_fn) = resolver.resolve_path_in_value_ns_fully( - self.db.upcast(), - &hir_def::path::Path::from_known_path_with_no_generic( - ModPath::from_segments( - hir_expand::mod_path::PathKind::Abs, - [name![std], name![fmt], name![format]], - ), - ), - ) else { - not_supported!("std::fmt::format not found"); - }; - let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { - not_supported!("std::fmt::format is not a function") - }; - let interval = self.interpret_mir( - self.db - .mir_body(format_fn.into()) - .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, - args.map(|x| IntervalOrOwned::Owned(x.clone())), - )?; - let message_string = interval.get(self)?; - let addr = - Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?; - let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]); - Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?) - .into_owned()) - })() - .unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}")); - Err(MirEvalError::Panic(message)) - } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of <[T]>::len() is not provided".into(), diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index d2e413f0a33..d6557c3a816 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -82,6 +82,9 @@ impl FallibleTypeFolder for Filler<'_> { }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + not_supported!("associated type impl trait"); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); } @@ -181,8 +184,16 @@ impl Filler<'_> { self.generics .as_ref() .and_then(|it| it.iter().nth(b.index)) - .unwrap() - .0, + .and_then(|(id, _)| match id { + hir_def::GenericParamId::ConstParamId(id) => { + Some(hir_def::TypeOrConstParamId::from(id)) + } + hir_def::GenericParamId::TypeParamId(id) => { + Some(hir_def::TypeOrConstParamId::from(id)) + } + _ => None, + }) + .unwrap(), self.subst.clone(), ) })? diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs index d699067b5a6..2a46becbfda 100644 --- a/crates/hir-ty/src/tests.rs +++ b/crates/hir-ty/src/tests.rs @@ -298,7 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { if let Some(syntax_ptr) = body_source_map.self_param_syntax() { let root = db.parse_or_expand(syntax_ptr.file_id); let node = syntax_ptr.map(|ptr| ptr.to_node(&root).syntax().clone()); - types.push((node.clone(), ty)); + types.push((node, ty)); } } diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs index e75b037e38d..50692674996 100644 --- a/crates/hir-ty/src/tests/display_source_code.rs +++ b/crates/hir-ty/src/tests/display_source_code.rs @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo + // ^^^ &dyn Foo<'static> "#, ); } diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 963b4a2aba0..80d5a0ae001 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl<'_> + //^^^ VaListImpl<'static> "#, ); } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 9a8ebd07d01..8565b60210b 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -896,13 +896,13 @@ fn flush(&self) { "#, expect![[r#" 123..127 'self': &Mutex - 150..152 '{}': MutexGuard<'_, T> + 150..152 '{}': MutexGuard<'static, T> 234..238 'self': &{unknown} 240..290 '{ ...()); }': () 250..251 'w': &Mutex 276..287 '*(w.lock())': BufWriter 278..279 'w': &Mutex - 278..286 'w.lock()': MutexGuard<'_, BufWriter> + 278..286 'w.lock()': MutexGuard<'static, BufWriter> "#]], ); } diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 917e9f44085..f39404593e5 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -3092,7 +3092,7 @@ fn main() { 389..394 'boxed': Box> 389..406 'boxed....nner()': &i32 416..421 'good1': &i32 - 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 + 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 424..446 'Foo::g...boxed)': &i32 439..445 '&boxed': &Box> 440..445 'boxed': Box> @@ -3100,7 +3100,7 @@ fn main() { 464..469 'boxed': Box> 464..480 'boxed....self()': &Foo 490..495 'good2': &Foo - 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo + 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo 498..519 'Foo::g...boxed)': &Foo 512..518 '&boxed': &Box> 513..518 'boxed': Box> @@ -3659,7 +3659,7 @@ fn main() { let are = "are"; let count = 10; builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_> + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static> } "#, ); diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index b80cfe18e4c..759af18c98b 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1278,6 +1278,40 @@ fn bar() { ); } +#[test] +fn argument_assoc_impl_trait() { + check_infer( + r#" +trait Outer { + type Item; +} + +trait Inner { } + +fn foo>(baz: T) { +} + +impl Outer for usize { + type Item = usize; +} + +impl Inner for usize {} + +fn main() { + foo(2); +} +"#, + expect![[r#" + 85..88 'baz': T + 93..96 '{ }': () + 182..197 '{ foo(2); }': () + 188..191 'foo': fn foo(usize) + 188..194 'foo(2)': () + 192..193 '2': usize + "#]], + ); +} + #[test] fn simple_return_pos_impl_trait() { cov_mark::check!(lower_rpit); @@ -4655,3 +4689,78 @@ fn f() { "#, ); } + +#[test] +fn associated_type_impl_trait() { + check_types( + r#" +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Self::Item; +} +struct S2; +impl Bar for S2 { + type Item = impl Foo; + fn bar(&self) -> Self::Item { + S1 + } +} + +fn test() { + let x = S2.bar(); + //^ impl Foo + ?Sized +} + "#, + ); +} + +#[test] +fn associated_type_impl_traits_complex() { + check_types( + r#" +struct Unary(T); +struct Binary(T, U); + +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Unary; +} +struct S2; +impl Bar for S2 { + type Item = Unary; + fn bar(&self) -> Unary<::Item> { + Unary(Unary(S1)) + } +} + +trait Baz { + type Target1; + type Target2; + fn baz(&self) -> Binary; +} +struct S3; +impl Baz for S3 { + type Target1 = impl Foo; + type Target2 = Unary; + fn baz(&self) -> Binary { + Binary(S1, Unary(S2)) + } +} + +fn test() { + let x = S3.baz(); + //^ Binary> + let y = x.1.0.bar(); + //^ Unary> +} + "#, + ); +} diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs index 8bd57820d2c..afd4d1f271d 100644 --- a/crates/hir-ty/src/utils.rs +++ b/crates/hir-ty/src/utils.rs @@ -9,18 +9,18 @@ use chalk_ir::{ fold::{FallibleTypeFolder, Shift}, BoundVar, DebruijnIndex, }; -use either::Either; use hir_def::{ db::DefDatabase, generics::{ - GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate, - WherePredicateTypeTarget, + GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, Lookup, - OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ItemContainerId, + LifetimeParamId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, + TypeParamId, }; use hir_expand::name::Name; use intern::Interned; @@ -270,64 +270,130 @@ pub(crate) struct Generics { } impl Generics { - pub(crate) fn iter_id(&self) -> impl Iterator> + '_ { - self.iter().map(|(id, data)| match data { - TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)), - TypeOrConstParamData::ConstParamData(_) => { - Either::Right(ConstParamId::from_unchecked(id)) - } - }) + pub(crate) fn iter_id(&self) -> impl Iterator + '_ { + self.iter().map(|(id, _)| id) } /// Iterator over types and const params of self, then parent. pub(crate) fn iter<'a>( &'a self, - ) -> impl DoubleEndedIterator + 'a { - let to_toc_id = |it: &'a Generics| { - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) + ) -> impl DoubleEndedIterator)> + 'a { + let from_toc_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + } }; - self.params.iter().map(to_toc_id(self)).chain(self.iter_parent()) + + let from_lt_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + } + }; + + let lt_iter = self.params.iter_lt().map(from_lt_id(self)); + self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent()) } /// Iterate over types and const params without parent params. pub(crate) fn iter_self<'a>( &'a self, - ) -> impl DoubleEndedIterator + 'a { - let to_toc_id = |it: &'a Generics| { - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) + ) -> impl DoubleEndedIterator)> + 'a { + let from_toc_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + } }; - self.params.iter().map(to_toc_id(self)) + + let from_lt_id = |it: &'a Generics| { + move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + } + }; + + self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self))) } /// Iterator over types and const params of parent. - pub(crate) fn iter_parent( - &self, - ) -> impl DoubleEndedIterator { + #[allow(clippy::needless_lifetimes)] + pub(crate) fn iter_parent<'a>( + &'a self, + ) -> impl DoubleEndedIterator)> + 'a { self.parent_generics().into_iter().flat_map(|it| { - let to_toc_id = - move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); - it.params.iter().map(to_toc_id) + let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let id = TypeOrConstParamId { parent: it.def, local_id }; + match p { + TypeOrConstParamData::TypeParamData(p) => ( + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)), + GenericParamDataRef::TypeParamData(p), + ), + TypeOrConstParamData::ConstParamData(p) => ( + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)), + GenericParamDataRef::ConstParamData(p), + ), + } + }; + + let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| { + ( + GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), + GenericParamDataRef::LifetimeParamData(p), + ) + }; + let lt_iter = it.params.iter_lt().map(from_lt_id); + it.params.iter().map(from_toc_id).chain(lt_iter) }) } /// Returns total number of generic parameters in scope, including those from parent. pub(crate) fn len(&self) -> usize { let parent = self.parent_generics().map_or(0, Generics::len); - let child = self.params.type_or_consts.len(); + let child = self.params.len(); parent + child } - /// Returns numbers of generic parameters excluding those from parent. + /// Returns numbers of generic parameters and lifetimes excluding those from parent. pub(crate) fn len_self(&self) -> usize { + self.params.len() + } + + /// Returns number of generic parameter excluding those from parent + fn len_params(&self) -> usize { self.params.type_or_consts.len() } - /// (parent total, self param, type param list, const param list, impl trait) - pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) { + /// (parent total, self param, type params, const params, impl trait list, lifetimes) + pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize, usize) { let mut self_params = 0; let mut type_params = 0; let mut impl_trait_params = 0; let mut const_params = 0; + let mut lifetime_params = 0; self.params.iter().for_each(|(_, data)| match data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList => type_params += 1, @@ -337,8 +403,10 @@ impl Generics { TypeOrConstParamData::ConstParamData(_) => const_params += 1, }); + self.params.iter_lt().for_each(|(_, _)| lifetime_params += 1); + let parent_len = self.parent_generics().map_or(0, Generics::len); - (parent_len, self_params, type_params, const_params, impl_trait_params) + (parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params) } pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option { @@ -358,6 +426,26 @@ impl Generics { } } + pub(crate) fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option { + Some(self.find_lifetime(lifetime)?.0) + } + + fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> { + if lifetime.parent == self.def { + let (idx, (_local_id, data)) = self + .params + .iter_lt() + .enumerate() + .find(|(_, (idx, _))| *idx == lifetime.local_id)?; + + Some((self.len_params() + idx, data)) + } else { + self.parent_generics() + .and_then(|g| g.find_lifetime(lifetime)) + .map(|(idx, data)| (self.len_self() + idx, data)) + } + } + pub(crate) fn parent_generics(&self) -> Option<&Generics> { self.parent_generics.as_deref() } @@ -371,10 +459,15 @@ impl Generics { Substitution::from_iter( Interner, self.iter_id().enumerate().map(|(idx, id)| match id { - Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner), - Either::Right(id) => BoundVar::new(debruijn, idx) + GenericParamId::ConstParamId(id) => BoundVar::new(debruijn, idx) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), + GenericParamId::TypeParamId(_) => { + BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner) + } + GenericParamId::LifetimeParamId(_) => { + BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(Interner) + } }), ) } @@ -384,12 +477,15 @@ impl Generics { Substitution::from_iter( Interner, self.iter_id().map(|id| match id { - Either::Left(id) => { + GenericParamId::TypeParamId(id) => { crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner) } - Either::Right(id) => crate::to_placeholder_idx(db, id.into()) + GenericParamId::ConstParamId(id) => crate::to_placeholder_idx(db, id.into()) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), + GenericParamId::LifetimeParamId(id) => { + crate::lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner) + } }), ) } diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index c5d44c11f2c..23c6b078b96 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -186,18 +186,29 @@ impl HirDisplay for Struct { } StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; - let fields = self.fields(f.db); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - if fields.is_empty() { - f.write_str("{}")?; - } else { - f.write_str("{\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; + if let Some(limit) = f.entity_limit { + let fields = self.fields(f.db); + let count = fields.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if fields.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str(" {\n")?; + for field in &fields[..count] { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + + if fields.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; } - f.write_str("}")?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index b922aa8e46d..106056c2fc3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -38,7 +38,7 @@ mod display; use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; -use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; +use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId}; use either::Either; use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, @@ -65,7 +65,7 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, db::InternedClosure, diagnostics::BodyValidationDiagnostic, - known_const_to_ast, + error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{interpret_mir, MutBorrowKind}, @@ -79,6 +79,7 @@ use hir_ty::{ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; +use span::Edition; use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasName}, @@ -971,7 +972,7 @@ fn precise_macro_call_location( MacroKind::ProcMacro, ) } - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let node = ast_id.to_node(db.upcast()); // Compute the precise location of the macro name's token in the derive // list. @@ -1099,13 +1100,14 @@ impl Field { VariantDef::Union(it) => it.id.into(), VariantDef::Variant(it) => it.parent_enum(db).id.into(), }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let substs = TyBuilder::subst_for_def(db, def_id, None) .fill(|x| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) .build(); let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); @@ -1416,7 +1418,7 @@ impl Adt { } pub fn layout(self, db: &dyn HirDatabase) -> Result { - if db.generic_params(self.into()).iter().count() != 0 { + if !db.generic_params(self.into()).is_empty() { return Err(LayoutError::HasPlaceholder); } let krate = self.krate(db).id; @@ -1440,13 +1442,14 @@ impl Adt { /// the greatest API, FIXME find a better one. pub fn ty_with_args(self, db: &dyn HirDatabase, args: impl Iterator) -> Type { let id = AdtId::from(self); - let mut it = args.map(|t| t.ty.clone()); + let mut it = args.map(|t| t.ty); let ty = TyBuilder::def_ty(db, id.into(), None) .fill(|x| { let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); match x { ParamKind::Type => r.cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); @@ -1859,12 +1862,13 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let mut filler = |x: &_| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }; let parent_substs = @@ -1954,7 +1958,7 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let parent_substs = parent_id.map(|id| { TyBuilder::subst_for_def(db, id, None) .fill(|x| match x { @@ -1963,6 +1967,7 @@ impl Function { .unwrap_or_else(|| TyKind::Error.intern(Interner)) .cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }) .build() }); @@ -2007,8 +2012,7 @@ impl Function { } let data = db.function_data(self.id); - data.name.to_smol_str() == "main" - || data.attrs.export_name().map(core::ops::Deref::deref) == Some("main") + data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main") } /// Does this function have the ignore attribute? @@ -2215,12 +2219,13 @@ impl SelfParam { } }; - let mut generics = generics.map(|it| it.ty.clone()); + let mut generics = generics.map(|it| it.ty); let mut filler = |x: &_| match x { ParamKind::Type => { generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)).cast(Interner) } ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), }; let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); @@ -2592,7 +2597,7 @@ impl Macro { }, MacroId::ProcMacroId(it) => match it.lookup(db.upcast()).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, - ProcMacroKind::FuncLike => MacroKind::ProcMacro, + ProcMacroKind::Bang => MacroKind::ProcMacro, ProcMacroKind::Attr => MacroKind::Attr, }, } @@ -3628,16 +3633,41 @@ impl Impl { .filter(filter), ); } + + if let Some(block) = + ty.adt_id(Interner).and_then(|def| def.0.module(db.upcast()).containing_block()) + { + if let Some(inherent_impls) = db.inherent_impls_in_block(block) { + all.extend( + inherent_impls.for_self_ty(&ty).iter().cloned().map(Self::from).filter(filter), + ); + } + if let Some(trait_impls) = db.trait_impls_in_block(block) { + all.extend( + trait_impls + .for_self_ty_without_blanket_impls(fp) + .map(Self::from) + .filter(filter), + ); + } + } + all } pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec { - let krate = trait_.module(db).krate(); + let module = trait_.module(db); + let krate = module.krate(); let mut all = Vec::new(); for Crate { id } in krate.transitive_reverse_dependencies(db) { let impls = db.trait_impls_in_crate(id); all.extend(impls.for_trait(trait_.id).map(Self::from)) } + if let Some(block) = module.id.containing_block() { + if let Some(trait_impls) = db.trait_impls_in_block(block) { + all.extend(trait_impls.for_trait(trait_.id).map(Self::from)); + } + } all } @@ -3683,7 +3713,7 @@ impl Impl { let macro_file = src.file_id.macro_file()?; let loc = macro_file.macro_call_id.lookup(db.upcast()); let (derive_attr, derive_index) = match loc.kind { - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => { + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { let module_id = self.id.lookup(db.upcast()).container; ( db.crate_def_map(module_id.krate())[module_id.local_id] @@ -4114,6 +4144,7 @@ impl Type { // FIXME: this code is not covered in tests. unknown_const_as_generic(ty.clone()) } + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); @@ -4144,6 +4175,7 @@ impl Type { match it { ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner), ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + ParamKind::Lifetime => error_lifetime().cast(Interner), } }) .build(); diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index 102e0ca4c3d..63b2a2506f8 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -177,7 +177,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( // Note that we need special case for 0 param constructors because of multi cartesian // product let variant_exprs: Vec = if param_exprs.is_empty() { - vec![Expr::Variant { variant, generics: generics.clone(), params: Vec::new() }] + vec![Expr::Variant { variant, generics, params: Vec::new() }] } else { param_exprs .into_iter() @@ -462,7 +462,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( /// # Impl method tactic /// -/// Attempts to to call methods on types from lookup table. +/// Attempts to call methods on types from lookup table. /// This includes both functions from direct impl blocks as well as functions from traits. /// Methods defined in impl blocks that are generic and methods that are themselves have /// generics are ignored for performance reasons. diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index d111005c2ec..65ce3e822c5 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -5617,7 +5617,7 @@ fn func(i: Struct<'_, T>) { fun_name(i); } -fn $0fun_name(i: Struct<'_, T>) { +fn $0fun_name(i: Struct<'static, T>) { foo(i); } "#, diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 38f40b8d58b..2150003bc14 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -614,7 +614,7 @@ struct Foo<'a, T> { } impl<'a, T> Foo<'a, T> { - $0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> { + $0fn bar(self, mut b: Vec<&'a Bar<'a, T>>) -> &'a Bar<'a, T> { self.field.bar(b) } } diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 24a1f9492e2..a4f092cc498 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -961,11 +961,11 @@ struct Foo { field: i32 } impl Foo { fn foo(&self) { $0 } }"#, expect![[r#" fd self.field i32 + me self.foo() fn(&self) lc self &Foo sp Self Foo st Foo Foo bt u32 u32 - me self.foo() fn(&self) "#]], ); check( @@ -975,11 +975,11 @@ struct Foo(i32); impl Foo { fn foo(&mut self) { $0 } }"#, expect![[r#" fd self.0 i32 + me self.foo() fn(&mut self) lc self &mut Foo sp Self Foo st Foo Foo bt u32 u32 - me self.foo() fn(&mut self) "#]], ); } diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 79467841502..c48672e80ac 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -186,11 +186,11 @@ fn add_function_impl( if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); - let completion_kind = if func.has_self_param(ctx.db) { - CompletionItemKind::Method + let completion_kind = CompletionItemKind::SymbolKind(if func.has_self_param(ctx.db) { + SymbolKind::Method } else { - CompletionItemKind::SymbolKind(SymbolKind::Function) - }; + SymbolKind::Function + }); let mut item = CompletionItem::new(completion_kind, replacement_range, label); item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index ed32a5db23e..1322c05e30e 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -75,8 +75,8 @@ impl Future for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) @@ -102,8 +102,8 @@ fn foo() { } "#, expect![[r#" - kw await expr.await me into_future() (use core::future::IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) @@ -131,8 +131,8 @@ impl IntoFuture for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + kw await expr.await sn box Box::new(expr) sn call function(expr) sn dbg dbg!(expr) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index aa22155feff..995e3f48253 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -540,7 +540,7 @@ impl CompletionContext<'_> { /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { - Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()), + Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang), None => false, } } diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 357060817c7..b9a2c383bdd 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -342,7 +342,6 @@ pub enum CompletionItemKind { BuiltinType, InferredType, Keyword, - Method, Snippet, UnresolvedReference, Expression, @@ -369,6 +368,7 @@ impl CompletionItemKind { SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", SymbolKind::Macro => "ma", + SymbolKind::Method => "me", SymbolKind::ProcMacro => "pm", SymbolKind::Module => "md", SymbolKind::SelfParam => "sp", @@ -388,7 +388,6 @@ impl CompletionItemKind { CompletionItemKind::BuiltinType => "bt", CompletionItemKind::InferredType => "it", CompletionItemKind::Keyword => "kw", - CompletionItemKind::Method => "me", CompletionItemKind::Snippet => "sn", CompletionItemKind::UnresolvedReference => "??", CompletionItemKind::Expression => "ex", diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 6d1a5a0bc52..ca0424809ed 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -312,7 +312,7 @@ pub(crate) fn render_expr( None => ctx.source_range(), }; - let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label.clone()); + let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label); let snippet = format!( "{}$0", @@ -677,10 +677,11 @@ mod tests { #[track_caller] fn check_function_relevance(ra_fixture: &str, expect: Expect) { - let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::Method) - .into_iter() - .map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) - .collect(); + let actual: Vec<_> = + do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method)) + .into_iter() + .map(|item| (item.detail.unwrap_or_default(), item.relevance.function)) + .collect(); expect.assert_debug_eq(&actual); } @@ -1392,7 +1393,10 @@ impl S { /// Method docs fn bar(self) { self.$0 } }"#, - &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], + &[ + CompletionItemKind::SymbolKind(SymbolKind::Method), + CompletionItemKind::SymbolKind(SymbolKind::Field), + ], expect![[r#" [ CompletionItem { @@ -1400,7 +1404,9 @@ impl S { source_range: 94..94, delete: 94..94, insert: "bar()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "bar", detail: "fn(self)", documentation: Documentation( @@ -1520,7 +1526,7 @@ impl S { } fn foo(s: S) { s.$0 } "#, - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), expect![[r#" [ CompletionItem { @@ -1528,7 +1534,9 @@ fn foo(s: S) { s.$0 } source_range: 81..81, delete: 81..81, insert: "the_method()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "the_method", detail: "fn(&self)", relevance: CompletionRelevance { @@ -2408,7 +2416,10 @@ impl Foo { fn baz(&self) -> u32 { 0 } } fn foo(f: Foo) { let _: &u32 = f.b$0 } "#, - &[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)], + &[ + CompletionItemKind::SymbolKind(SymbolKind::Method), + CompletionItemKind::SymbolKind(SymbolKind::Field), + ], expect![[r#" [ CompletionItem { @@ -2416,7 +2427,9 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } source_range: 109..110, delete: 109..110, insert: "baz()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "baz", detail: "fn(&self) -> u32", relevance: CompletionRelevance { @@ -2631,7 +2644,7 @@ fn main() { let _: bool = (9 > 2).not$0; } "#, - &[CompletionItemKind::Snippet, CompletionItemKind::Method], + &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not [snippet] me not() (use ops::Not) [type_could_unify+requires_import] @@ -2664,7 +2677,7 @@ fn main() { S.$0 } "#, - &[CompletionItemKind::Snippet, CompletionItemKind::Method], + &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" me f() [] sn ref [] @@ -2907,7 +2920,7 @@ fn main() { } "#, &[ - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), CompletionItemKind::SymbolKind(SymbolKind::Field), CompletionItemKind::SymbolKind(SymbolKind::Function), ], @@ -2918,7 +2931,9 @@ fn main() { source_range: 193..193, delete: 193..193, insert: "flush()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "flush", detail: "fn(&self)", relevance: CompletionRelevance { @@ -2941,7 +2956,9 @@ fn main() { source_range: 193..193, delete: 193..193, insert: "write()$0", - kind: Method, + kind: SymbolKind( + Method, + ), lookup: "write", detail: "fn(&self)", relevance: CompletionRelevance { diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index cf9fe1ab307..1634b0a9206 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -68,11 +68,11 @@ fn render( }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( - if has_self_param { - CompletionItemKind::Method + CompletionItemKind::SymbolKind(if has_self_param { + SymbolKind::Method } else { - CompletionItemKind::SymbolKind(SymbolKind::Function) - }, + SymbolKind::Function + }), ctx.source_range(), call.clone(), ); diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 7749fac40b9..a653314233d 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -127,6 +127,7 @@ impl Unit { en Enum Enum fn function() fn() fn local_func() fn() + me self.foo() fn(self) lc self Unit ma makro!(…) macro_rules! makro md module @@ -166,7 +167,6 @@ impl Unit { kw use kw while kw while let - me self.foo() fn(self) sn macro_rules sn pd sn ppd diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs index 46a3e97d3e9..3718dff56e8 100644 --- a/crates/ide-completion/src/tests/predicate.rs +++ b/crates/ide-completion/src/tests/predicate.rs @@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index ff32eccfbff..69d8fe91040 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -1,6 +1,7 @@ //! Tests that don't fit into a specific category. use expect_test::{expect, Expect}; +use ide_db::SymbolKind; use crate::{ tests::{ @@ -316,15 +317,15 @@ trait Sub: Super { fn foo() { T::$0 } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + "#]], ); } @@ -356,15 +357,15 @@ impl Sub for Wrap { } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - "#]], + ct C2 (as Sub) const C2: () + ct CONST (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty + "#]], ); } @@ -555,10 +556,10 @@ impl Foo { } "#, expect![[r#" - ev Bar Bar - ev Baz Baz - me foo(…) fn(self) - "#]], + me foo(…) fn(self) + ev Bar Bar + ev Baz Baz + "#]], ); } @@ -1399,7 +1400,7 @@ fn main() { bar.b$0 } "#, - CompletionItemKind::Method, + CompletionItemKind::SymbolKind(SymbolKind::Method), expect!("const fn(&'foo mut self, &Foo) -> !"), expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), ); diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index db4ac9381ce..97709728656 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'static, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'static, {unknown}, _> + st Foo<…> Foo<'static, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 0d5a93f7b8e..357209ceb0b 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -346,6 +346,7 @@ pub enum SymbolKind { Enum, Field, Function, + Method, Impl, Label, LifetimeParam, diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index 8ccea99e9e1..edd05009332 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -26,6 +26,7 @@ text-edit.workspace = true cfg.workspace = true hir.workspace = true ide-db.workspace = true +paths.workspace = true [dev-dependencies] expect-test = "1.4.0" diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 67daa172b27..045154614f8 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -23,6 +23,7 @@ mod tests { }, DiagnosticsConfig, }; + use test_utils::skip_slow_tests; #[track_caller] fn check_diagnostics_no_bails(ra_fixture: &str) { @@ -1004,6 +1005,32 @@ fn f() { ); } + #[test] + fn exponential_match() { + if skip_slow_tests() { + return; + } + // Constructs a match where match checking takes exponential time. Ensures we bail early. + use std::fmt::Write; + let struct_arity = 50; + let mut code = String::new(); + write!(code, "struct BigStruct {{").unwrap(); + for i in 0..struct_arity { + write!(code, " field{i}: bool,").unwrap(); + } + write!(code, "}}").unwrap(); + write!(code, "fn big_match(s: BigStruct) {{").unwrap(); + write!(code, " match s {{").unwrap(); + for i in 0..struct_arity { + write!(code, " BigStruct {{ field{i}: true, ..}} => {{}},").unwrap(); + write!(code, " BigStruct {{ field{i}: false, ..}} => {{}},").unwrap(); + } + write!(code, " _ => {{}},").unwrap(); + write!(code, " }}").unwrap(); + write!(code, "}}").unwrap(); + check_diagnostics_no_bails(&code); + } + mod rust_unstable { use super::*; diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 34a0038295f..00352266ddb 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -7,7 +7,11 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // Diagnostic: need-mut // // This diagnostic is triggered on mutating an immutable variable. -pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagnostic { +pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option { + if d.span.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let fixes = (|| { if d.local.is_ref(ctx.sema.db) { // There is no simple way to add `mut` to `ref x` and `ref mut x` @@ -29,24 +33,30 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno use_range, )]) })(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - // FIXME: `E0384` is not the only error that this diagnostic handles - DiagnosticCode::RustcHardError("E0384"), - format!( - "cannot mutate immutable variable `{}`", - d.local.name(ctx.sema.db).display(ctx.sema.db) - ), - d.span, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + // FIXME: `E0384` is not the only error that this diagnostic handles + DiagnosticCode::RustcHardError("E0384"), + format!( + "cannot mutate immutable variable `{}`", + d.local.name(ctx.sema.db).display(ctx.sema.db) + ), + d.span, + ) + .with_fixes(fixes), ) - .with_fixes(fixes) } // Diagnostic: unused-mut // // This diagnostic is triggered when a mutable variable isn't actually mutated. -pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Diagnostic { +pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + if ast.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let fixes = (|| { let file_id = ast.file_id.file_id()?; let mut edit_builder = TextEdit::builder(); @@ -70,14 +80,16 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_mut"), - "variable does not need to be mutable", - ast, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), + "variable does not need to be mutable", + ast, + ) + .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. + .with_fixes(fixes), ) - .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive. - .with_fixes(fixes) } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs index 7a040e46e33..d831878044d 100644 --- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs +++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs @@ -12,7 +12,12 @@ use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, Diagnostics pub(crate) fn remove_trailing_return( ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn, -) -> Diagnostic { +) -> Option { + if d.return_expr.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } + let display_range = adjusted_display_range(ctx, d.return_expr, &|return_expr| { return_expr .syntax() @@ -20,12 +25,14 @@ pub(crate) fn remove_trailing_return( .and_then(ast::ExprStmt::cast) .map(|stmt| stmt.syntax().text_range()) }); - Diagnostic::new( - DiagnosticCode::Clippy("needless_return"), - "replace return ; with ", - display_range, + Some( + Diagnostic::new( + DiagnosticCode::Clippy("needless_return"), + "replace return ; with ", + display_range, + ) + .with_fixes(fixes(ctx, d)), ) - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option> { diff --git a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs index f68e6982385..448df1ca163 100644 --- a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs +++ b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs @@ -21,23 +21,30 @@ use crate::{ pub(crate) fn remove_unnecessary_else( ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse, -) -> Diagnostic { +) -> Option { + if d.if_expr.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } + let display_range = adjusted_display_range(ctx, d.if_expr, &|if_expr| { if_expr.else_token().as_ref().map(SyntaxToken::text_range) }); - Diagnostic::new( - DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), - "remove unnecessary else block", - display_range, + Some( + Diagnostic::new( + DiagnosticCode::Ra("remove-unnecessary-else", Severity::WeakWarning), + "remove unnecessary else block", + display_range, + ) + .experimental() + .with_fixes(fixes(ctx, d)), ) - .experimental() - .with_fixes(fixes(ctx, d)) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option> { let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id); let if_expr = d.if_expr.value.to_node(&root); - let if_expr = ctx.sema.original_ast_node(if_expr.clone())?; + let if_expr = ctx.sema.original_ast_node(if_expr)?; let mut indent = IndentLevel::from_node(if_expr.syntax()); let has_parent_if_expr = if_expr.syntax().parent().and_then(ast::IfExpr::cast).is_some(); diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index becc24ab21e..b9327f85567 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -8,6 +8,7 @@ use ide_db::{ source_change::SourceChange, RootDatabase, }; +use paths::Utf8Component; use syntax::{ ast::{self, edit::IndentLevel, HasModuleItem, HasName}, AstNode, TextRange, @@ -84,10 +85,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { // try resolving the relative difference of the paths as inline modules let mut current = root_module; - for ele in rel.as_ref().components() { + for ele in rel.as_utf8_path().components() { let seg = match ele { - std::path::Component::Normal(seg) => seg.to_str()?, - std::path::Component::RootDir => continue, + Utf8Component::Normal(seg) => seg, + Utf8Component::RootDir => continue, // shouldn't occur _ => continue 'crates, }; diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs index a9e1d07d7c5..cd251faab9a 100644 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -14,18 +14,24 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; pub(crate) fn unused_variables( ctx: &DiagnosticsContext<'_>, d: &hir::UnusedVariable, -) -> Diagnostic { +) -> Option { let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); + if ast.file_id.macro_file().is_some() { + // FIXME: Our infra can't handle allow from within macro expansions rn + return None; + } let diagnostic_range = ctx.sema.diagnostics_display_range(ast); let var_name = d.local.primary_source(ctx.sema.db).syntax().to_string(); - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_variables"), - "unused variable", - ast, + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_variables"), + "unused variable", + ast, + ) + .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) + .experimental(), ) - .with_fixes(fixes(&var_name, diagnostic_range, ast.file_id.is_macro())) - .experimental() } fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> Option> { @@ -47,7 +53,7 @@ fn fixes(var_name: &String, diagnostic_range: FileRange, is_in_marco: bool) -> O #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix, check_no_fix}; + use crate::tests::{check_diagnostics, check_fix}; #[test] fn unused_variables_simple() { @@ -193,7 +199,7 @@ fn main() { #[test] fn no_fix_for_marco() { - check_no_fix( + check_diagnostics( r#" macro_rules! my_macro { () => { @@ -202,7 +208,7 @@ macro_rules! my_macro { } fn main() { - $0my_macro!(); + my_macro!(); } "#, ); diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 0df6f0e0373..270cf844c65 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -330,7 +330,6 @@ pub fn diagnostics( } for diag in diags { - #[rustfmt::skip] let d = match diag { AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { @@ -361,7 +360,10 @@ pub fn diagnostics( AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), - AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), + AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { + Some(it) => it, + None => continue, + }, AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d), AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), @@ -385,12 +387,24 @@ pub fn diagnostics( AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), - AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), - AnyDiagnostic::UnusedVariable(d) => handlers::unused_variables::unused_variables(&ctx, &d), + AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { + Some(it) => it, + None => continue, + }, + AnyDiagnostic::UnusedVariable(d) => match handlers::unused_variables::unused_variables(&ctx, &d) { + Some(it) => it, + None => continue, + }, AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), - AnyDiagnostic::RemoveTrailingReturn(d) => handlers::remove_trailing_return::remove_trailing_return(&ctx, &d), - AnyDiagnostic::RemoveUnnecessaryElse(d) => handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d), + AnyDiagnostic::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) { + Some(it) => it, + None => continue, + }, + AnyDiagnostic::RemoveUnnecessaryElse(d) => match handlers::remove_unnecessary_else::remove_unnecessary_else(&ctx, &d) { + Some(it) => it, + None => continue, + }, }; res.push(d) } @@ -399,9 +413,9 @@ pub fn diagnostics( .iter_mut() .filter_map(|it| { Some(( - it.main_node - .map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) - .clone()?, + it.main_node.map(|ptr| { + ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id))) + })?, it, )) }) diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index dcaa2120892..bb5c2b79139 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -283,6 +283,10 @@ fn test_disabled_diagnostics() { #[test] fn minicore_smoke_test() { + if test_utils::skip_slow_tests() { + return; + } + fn check(minicore: MiniCore) { let source = minicore.source_code(); let mut config = DiagnosticsConfig::test_sample(); diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index aca7d613e11..a535015a27f 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -36,6 +36,7 @@ ide-ssr.workspace = true profile.workspace = true stdx.workspace = true syntax.workspace = true +span.workspace = true text-edit.workspace = true # ide should depend only on the top-level `hir` package. if you need # something from some `hir-xxx` subpackage, reexport the API via `hir`. diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index d10bdca50d8..64b4ccc5bd8 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -5,8 +5,6 @@ mod tests; mod intra_doc_links; -use std::ffi::OsStr; - use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions}; use stdx::format_to; @@ -134,8 +132,8 @@ pub(crate) fn remove_links(markdown: &str) -> String { pub(crate) fn external_docs( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> Option { let sema = &Semantics::new(db); let file = sema.parse(file_id).syntax().clone(); @@ -331,8 +329,8 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>) fn get_doc_links( db: &RootDatabase, def: Definition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> DocumentationLinks { let join_url = |base_url: Option, path: &str| -> Option { base_url.and_then(|url| url.join(path).ok()) @@ -479,15 +477,13 @@ fn map_links<'e>( fn get_doc_base_urls( db: &RootDatabase, def: Definition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> (Option, Option) { let local_doc = target_dir - .and_then(|path| path.to_str()) .and_then(|path| Url::parse(&format!("file:///{path}/")).ok()) .and_then(|it| it.join("doc/").ok()); let system_doc = sysroot - .and_then(|it| it.to_str()) .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/")) .and_then(|it| Url::parse(&it).ok()); diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 60e8d29a716..ac44908a902 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsStr, iter}; +use std::iter; use expect_test::{expect, Expect}; use hir::Semantics; @@ -18,10 +18,10 @@ use crate::{ fn check_external_docs( ra_fixture: &str, - target_dir: Option<&OsStr>, + target_dir: Option<&str>, expect_web_url: Option, expect_local_url: Option, - sysroot: Option<&OsStr>, + sysroot: Option<&str>, ) { let (analysis, position) = fixture::position(ra_fixture); let links = analysis.external_docs(position, target_dir, sysroot).unwrap(); @@ -127,10 +127,10 @@ fn external_docs_doc_builtin_type() { //- /main.rs crate:foo let x: u3$02 = 0; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]), Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -143,10 +143,10 @@ use foo$0::Foo; //- /lib.rs crate:foo pub struct Foo; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]), Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -157,10 +157,10 @@ fn external_docs_doc_url_std_crate() { //- /main.rs crate:std use self$0; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]), Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -171,10 +171,10 @@ fn external_docs_doc_url_struct() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new("/home/user/project")), + Some("/home/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -185,10 +185,10 @@ fn external_docs_doc_url_windows_backslash_path() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new(r"C:\Users\user\project")), + Some(r"C:\Users\user\project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } @@ -199,10 +199,10 @@ fn external_docs_doc_url_windows_slash_path() { //- /main.rs crate:foo pub struct Fo$0o; "#, - Some(OsStr::new(r"C:/Users/user/project")), + Some("C:/Users/user/project"), Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), - Some(OsStr::new("/sysroot")), + Some("/sysroot"), ); } diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs index 0e790e14205..813691540f9 100644 --- a/crates/ide/src/file_structure.rs +++ b/crates/ide/src/file_structure.rs @@ -134,15 +134,22 @@ fn structure_node(node: &SyntaxNode) -> Option { if let Some(type_param_list) = it.generic_param_list() { collapse_ws(type_param_list.syntax(), &mut detail); } - if let Some(param_list) = it.param_list() { + let has_self_param = if let Some(param_list) = it.param_list() { collapse_ws(param_list.syntax(), &mut detail); - } + param_list.self_param().is_some() + } else { + false + }; if let Some(ret_type) = it.ret_type() { detail.push(' '); collapse_ws(ret_type.syntax(), &mut detail); } - decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(SymbolKind::Function)) + decl_with_detail(&it, Some(detail), StructureNodeKind::SymbolKind(if has_self_param { + SymbolKind::Method + } else { + SymbolKind::Function + })) }, ast::Struct(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Struct)), ast::Union(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Union)), diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 8a12cbacccc..76e5e9dd928 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -337,6 +337,77 @@ impl Tr for S { const C: usize = 4; //^ } +"#, + ); + } + + #[test] + fn goto_adt_implementation_inside_block() { + check( + r#" +//- minicore: copy, derive +trait Bar {} + +fn test() { + #[derive(Copy)] + //^^^^^^^^^^^^^^^ + struct Foo$0; + + impl Foo {} + //^^^ + + trait Baz {} + + impl Bar for Foo {} + //^^^ + + impl Baz for Foo {} + //^^^ +} +"#, + ); + } + + #[test] + fn goto_trait_implementation_inside_block() { + check( + r#" +struct Bar; + +fn test() { + trait Foo$0 {} + + struct Baz; + + impl Foo for Bar {} + //^^^ + + impl Foo for Baz {} + //^^^ +} +"#, + ); + check( + r#" +struct Bar; + +fn test() { + trait Foo { + fn foo$0() {} + } + + struct Baz; + + impl Foo for Bar { + fn foo() {} + //^^^ + } + + impl Foo for Baz { + fn foo() {} + //^^^ + } +} "#, ); } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 8f4c629b581..822751c0e4c 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -33,6 +33,7 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, + pub max_struct_field_count: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 63777d49105..abedbff831a 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -410,6 +410,9 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } + Definition::Adt(Adt::Struct(struct_)) => { + struct_.display_limited(db, config.max_struct_field_count).to_string() + } _ => def.label(db), }; let docs = def.docs(db, famous_defs); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 4451e31870f..08925fcdff5 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -18,6 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, + max_struct_field_count: None, }; fn check_hover_no_result(ra_fixture: &str) { @@ -49,6 +50,28 @@ fn check(ra_fixture: &str, expect: Expect) { expect.assert_eq(&actual) } +#[track_caller] +fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { + links_in_hover: true, + max_struct_field_count: Some(count), + ..HOVER_BASE_CONFIG + }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + #[track_caller] fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); @@ -853,9 +876,7 @@ struct Foo$0 { field: u32 } ```rust // size = 4, align = 4 - struct Foo { - field: u32, - } + struct Foo ``` "#]], ); @@ -875,8 +896,74 @@ struct Foo$0 where u32: Copy { field: u32 } struct Foo where u32: Copy, - { - field: u32, + ``` + "#]], + ); +} + +#[test] +fn hover_record_struct_limit() { + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo { + a: u32, + b: i32, + c: i32, + } + ``` + "#]], + ); + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + struct Foo { + a: u32, + } + ``` + "#]], + ); + check_hover_struct_limit( + 3, + r#" + struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 16 (0x10), align = 4 + struct Foo { + a: u32, + b: i32, + c: i32, + /* … */ } ``` "#]], @@ -1344,9 +1431,7 @@ impl Thing { ``` ```rust - struct Thing { - x: u32, - } + struct Thing ``` "#]], ); @@ -1365,9 +1450,7 @@ impl Thing { ``` ```rust - struct Thing { - x: u32, - } + struct Thing ``` "#]], ); @@ -2599,7 +2682,7 @@ fn main() { let s$0t = S{ f1:0 }; } focus_range: 7..8, name: "S", kind: Struct, - description: "struct S {\n f1: u32,\n}", + description: "struct S", }, }, ], @@ -2645,7 +2728,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S {\n f1: T,\n}", + description: "struct S", }, }, ], @@ -2704,7 +2787,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; } focus_range: 24..25, name: "S", kind: Struct, - description: "struct S {\n f1: T,\n}", + description: "struct S", }, }, ], @@ -2957,7 +3040,7 @@ fn main() { let s$0t = foo(); } focus_range: 39..41, name: "S1", kind: Struct, - description: "struct S1 {}", + description: "struct S1", }, }, HoverGotoTypeData { @@ -2970,7 +3053,7 @@ fn main() { let s$0t = foo(); } focus_range: 52..54, name: "S2", kind: Struct, - description: "struct S2 {}", + description: "struct S2", }, }, ], @@ -3061,7 +3144,7 @@ fn foo(ar$0g: &impl Foo + Bar) {} focus_range: 36..37, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3161,7 +3244,7 @@ fn foo(ar$0g: &impl Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3198,7 +3281,7 @@ fn main() { let s$0t = foo(); } focus_range: 49..50, name: "B", kind: Struct, - description: "struct B {}", + description: "struct B", }, }, HoverGotoTypeData { @@ -3287,7 +3370,7 @@ fn foo(ar$0g: &dyn Foo) {} focus_range: 23..24, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -3322,7 +3405,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 50..51, name: "B", kind: Struct, - description: "struct B {}", + description: "struct B", }, }, HoverGotoTypeData { @@ -3361,7 +3444,7 @@ fn foo(a$0rg: &impl ImplTrait>>>) {} focus_range: 65..66, name: "S", kind: Struct, - description: "struct S {}", + description: "struct S", }, }, ], @@ -5105,6 +5188,32 @@ fn foo(e: E) { ); } +#[test] +fn hover_const_value() { + check( + r#" +pub enum AA { + BB, +} +const CONST: AA = AA::BB; +pub fn the_function() -> AA { + CON$0ST +} +"#, + expect![[r#" + *CONST* + + ```rust + test + ``` + + ```rust + const CONST: AA = BB + ``` + "#]], + ); +} + #[test] fn array_repeat_exp() { check( @@ -7747,3 +7856,25 @@ impl Iterator for S { "#]], ); } + +#[test] +fn hover_lifetime_regression_16963() { + check( + r#" +struct Pedro$0<'a> { + hola: &'a str +} +"#, + expect![[r#" + *Pedro* + + ```rust + test + ``` + + ```rust + struct Pedro<'a> + ``` + "#]], + ) +} diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 8311e770b4b..dda38ce77e0 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,5 +1,6 @@ use std::{ fmt::{self, Write}, + hash::{BuildHasher, BuildHasherDefault}, mem::take, }; @@ -8,7 +9,7 @@ use hir::{ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode { PreferPostfix, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InlayKind { Adjustment, BindingMode, @@ -132,7 +133,7 @@ pub enum InlayKind { RangeExclusive, } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayHintPosition { Before, After, @@ -151,13 +152,23 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, - pub needs_resolve: bool, +} + +impl std::hash::Hash for InlayHint { + fn hash(&self, state: &mut H) { + self.range.hash(state); + self.position.hash(state); + self.pad_left.hash(state); + self.pad_right.hash(state); + self.kind.hash(state); + self.label.hash(state); + self.text_edit.is_some().hash(state); + } } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -167,9 +178,9 @@ impl InlayHint { pad_right: false, } } + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -179,15 +190,19 @@ impl InlayHint { pad_right: false, } } + + pub fn needs_resolve(&self) -> bool { + self.text_edit.is_some() || self.label.needs_resolve() + } } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayTooltip { String(String), Markdown(String), } -#[derive(Default)] +#[derive(Default, Hash)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, } @@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel { } } +#[derive(Hash)] pub struct InlayHintLabelPart { pub text: String, /// Source location represented by this label part. The client will use this to fetch the part's @@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> { impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { - if self.location.is_some() { - never!("location link is already started"); - } + never!(self.location.is_some(), "location link is already started"); self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; let location = location.call_site(); @@ -425,11 +439,6 @@ fn ty_to_text_edit( Some(builder.finish()) } -pub enum RangeLimit { - Fixed(TextRange), - NearestParent(TextSize), -} - // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -451,7 +460,7 @@ pub enum RangeLimit { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option, + range_limit: Option, config: &InlayHintsConfig, ) -> Vec { let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); @@ -466,31 +475,13 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(RangeLimit::Fixed(range)) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() .filter(|descendant| range.intersect(descendant.text_range()).is_some()) .for_each(hints), }, - Some(RangeLimit::NearestParent(position)) => { - match file.token_at_offset(position).left_biased() { - Some(token) => { - if let Some(parent_block) = - token.parent_ancestors().find_map(ast::BlockExpr::cast) - { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = - token.parent_ancestors().find_map(ast::Item::cast) - { - parent_item.syntax().descendants().for_each(hints) - } else { - return acc; - } - } - None => return acc, - } - } None => file.descendants().for_each(hints), }; } @@ -498,6 +489,39 @@ pub(crate) fn inlay_hints( acc } +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + position: TextSize, + hash: u64, + config: &InlayHintsConfig, +) -> Option { + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let sema = Semantics::new(db); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); + match file.token_at_offset(position).left_biased() { + Some(token) => { + if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { + parent_block.syntax().descendants().for_each(hints) + } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { + parent_item.syntax().descendants().for_each(hints) + } else { + return None; + } + } + None => return None, + } + + acc.into_iter().find(|hint| BuildHasherDefault::::default().hash_one(hint) == hash) +} + fn hints( hints: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 631807d99a7..20128a286f2 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -147,7 +147,6 @@ pub(super) fn hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 45b51e35570..07b9f9cc1ff 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,7 +99,6 @@ pub(super) fn hints( None => pat.syntax().text_range(), }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: match type_ascriptable { Some(Some(t)) => text_range.cover(t.text_range()), _ => text_range, @@ -177,11 +176,7 @@ mod tests { use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::{ - fixture, - inlay_hints::{InlayHintsConfig, RangeLimit}, - ClosureReturnTypeHints, - }; + use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints}; use crate::inlay_hints::tests::{ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, @@ -404,7 +399,7 @@ fn main() { .inlay_hints( &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, - Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))), + Some(TextRange::new(TextSize::from(500), TextSize::from(600))), ) .unwrap(); let actual = diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs index 35504ffa785..f27390ee898 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/crates/ide/src/inlay_hints/binding_mode.rs @@ -50,7 +50,6 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::BindingMode, label: r.into(), @@ -69,7 +68,6 @@ pub(super) fn hints( hir::BindingMode::Ref(Mutability::Shared) => "ref", }; acc.push(InlayHint { - needs_resolve: false, range: pat.syntax().text_range(), kind: InlayKind::BindingMode, label: bm.into(), diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index b6063978e92..d86487d4b41 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -59,7 +59,6 @@ pub(super) fn hints( } let label = label_of_ty(famous_defs, config, &ty)?; acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), kind: InlayKind::Chaining, label, diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 2b68538c198..2cefd5acdc2 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,7 +109,6 @@ pub(super) fn hints( let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { - needs_resolve: linked_location.is_some(), range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index 2f8b959516d..f1b524e0880 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -32,7 +32,6 @@ pub(super) fn hints( let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = TextRange::new(range.end() - TextSize::from(1), range.end()); acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("move"), @@ -45,7 +44,6 @@ pub(super) fn hints( } }; acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -79,7 +77,6 @@ pub(super) fn hints( }), ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, label, @@ -91,7 +88,6 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(", "), @@ -103,7 +99,6 @@ pub(super) fn hints( } } acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index 204967cd7ca..3b41db0f13d 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,7 +64,6 @@ pub(super) fn hints( }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: param_list.syntax().text_range(), kind: InlayKind::Type, label, diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs index 06cce147d2a..202954100fb 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/crates/ide/src/inlay_hints/discriminant.rs @@ -79,7 +79,6 @@ fn variant_hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: match eq_token { Some(t) => range.cover(t.text_range()), _ => range, diff --git a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 6e5f23bed09..d3666754e2b 100644 --- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,7 +22,6 @@ pub(super) fn hints( } let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), @@ -184,7 +183,6 @@ pub(super) fn hints( let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); acc.push(InlayHint { - needs_resolve: false, range: angle_tok.text_range(), kind: InlayKind::Lifetime, label: format!( @@ -200,7 +198,6 @@ pub(super) fn hints( }); } (None, allocated_lifetimes) => acc.push(InlayHint { - needs_resolve: false, range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs index 5ba4e514e1f..31f0c790374 100644 --- a/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/crates/ide/src/inlay_hints/implicit_drop.rs @@ -105,7 +105,6 @@ pub(super) fn hints( pad_left: true, pad_right: true, kind: InlayKind::Drop, - needs_resolve: label.needs_resolve(), label, text_edit: None, }) diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs index f18e6421cbc..42223ddf580 100644 --- a/crates/ide/src/inlay_hints/implicit_static.rs +++ b/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,7 +31,6 @@ pub(super) fn hints( if ty.lifetime().is_none() { let t = ty.amp_token()?; acc.push(InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: "'static".into(), diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 418fc002a8b..96e845b2f32 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -57,7 +57,6 @@ pub(super) fn hints( let label = InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { - needs_resolve: label.needs_resolve(), range, kind: InlayKind::Parameter, label, diff --git a/crates/ide/src/inlay_hints/range_exclusive.rs b/crates/ide/src/inlay_hints/range_exclusive.rs index c4b0c199fc2..bfb92838857 100644 --- a/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/crates/ide/src/inlay_hints/range_exclusive.rs @@ -30,7 +30,6 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, - needs_resolve: false, } } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6955e14a10a..ad48d803895 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -58,8 +58,6 @@ mod view_item_tree; mod view_memory_layout; mod view_mir; -use std::ffi::OsStr; - use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::ChangeWithProcMacros; @@ -90,7 +88,7 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, - InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, RangeLimit, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -121,8 +119,8 @@ pub use ide_completion::{ }; pub use ide_db::{ base_db::{ - Cancelled, CrateGraph, CrateId, Edition, FileChange, FileId, FilePosition, FileRange, - SourceRoot, SourceRootId, + Cancelled, CrateGraph, CrateId, FileChange, FileId, FilePosition, FileRange, SourceRoot, + SourceRootId, }, documentation::Documentation, label::Label, @@ -137,6 +135,7 @@ pub use ide_diagnostics::{ Diagnostic, DiagnosticCode, DiagnosticsConfig, ExprFillDefaultMode, Severity, }; pub use ide_ssr::SsrError; +pub use span::Edition; pub use syntax::{TextRange, TextSize}; pub use text_edit::{Indel, TextEdit}; @@ -354,6 +353,10 @@ impl Analysis { self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id)) } + pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable> { + self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id)) + } + /// Renders the crate graph to GraphViz "dot" syntax. pub fn view_crate_graph(&self, full: bool) -> Cancellable> { self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) @@ -415,10 +418,19 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - range: Option, + range: Option, ) -> Cancellable> { self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config)) } + pub fn inlay_hints_resolve( + &self, + config: &InlayHintsConfig, + file_id: FileId, + position: TextSize, + hash: u64, + ) -> Cancellable> { + self.with_db(|db| inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config)) + } /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> { @@ -502,8 +514,8 @@ impl Analysis { pub fn external_docs( &self, position: FilePosition, - target_dir: Option<&OsStr>, - sysroot: Option<&OsStr>, + target_dir: Option<&str>, + sysroot: Option<&str>, ) -> Cancellable { self.with_db(|db| { doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default() diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index fe063081f79..3fef16df25e 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -167,6 +167,7 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, + max_struct_field_count: None, }; let tokens = tokens.filter(|token| { matches!( diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index c3d85e38936..8e7767c8e5d 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -10,7 +10,7 @@ use ide_db::{ debug::{DebugQueryTable, TableEntry}, Query, QueryTable, }, - CrateData, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId, + CompressedFileTextQuery, CrateData, FileId, ParseQuery, SourceDatabase, SourceRootId, }, symbol_index::ModuleSymbolsQuery, }; @@ -38,7 +38,7 @@ use triomphe::Arc; pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { let mut buf = String::new(); - format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(CompressedFileTextQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db))); format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db))); @@ -160,7 +160,7 @@ impl QueryCollect for ParseMacroExpansionQuery { type Collector = SyntaxTreeStats; } -impl QueryCollect for FileTextQuery { +impl QueryCollect for CompressedFileTextQuery { type Collector = FilesStats; } @@ -188,8 +188,8 @@ impl fmt::Display for FilesStats { } } -impl StatCollect> for FilesStats { - fn collect_entry(&mut self, _: FileId, value: Option>) { +impl StatCollect> for FilesStats { + fn collect_entry(&mut self, _: FileId, value: Option>) { self.total += 1; self.size += value.unwrap().len(); } diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 96c7c475594..e7346cbb992 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -178,6 +178,23 @@ fn keyword( T![do] | T![yeet] if parent_matches::(&token) => h | HlMod::ControlFlow, T![for] if parent_matches::(&token) => h | HlMod::ControlFlow, T![unsafe] => h | HlMod::Unsafe, + T![const] + if token.parent().map_or(false, |it| { + matches!( + it.kind(), + SyntaxKind::CONST + | SyntaxKind::FN + | SyntaxKind::IMPL + | SyntaxKind::BLOCK_EXPR + | SyntaxKind::CLOSURE_EXPR + | SyntaxKind::FN_PTR_TYPE + | SyntaxKind::TYPE_BOUND + | SyntaxKind::CONST_BLOCK_PAT + ) + }) => + { + h | HlMod::Const + } T![true] | T![false] => HlTag::BoolLiteral.into(), // crate is handled just as a token if it's in an `extern crate` T![crate] if parent_matches::(&token) => h, @@ -377,14 +394,17 @@ pub(super) fn highlight_def( if let Some(item) = func.as_assoc_item(db) { h |= HlMod::Associated; match func.self_param(db) { - Some(sp) => match sp.access(db) { - hir::Access::Exclusive => { - h |= HlMod::Mutable; - h |= HlMod::Reference; + Some(sp) => { + h.tag = HlTag::Symbol(SymbolKind::Method); + match sp.access(db) { + hir::Access::Exclusive => { + h |= HlMod::Mutable; + h |= HlMod::Reference; + } + hir::Access::Shared => h |= HlMod::Reference, + hir::Access::Owned => h |= HlMod::Consuming, } - hir::Access::Shared => h |= HlMod::Reference, - hir::Access::Owned => h |= HlMod::Consuming, - }, + } None => h |= HlMod::Static, } @@ -406,6 +426,9 @@ pub(super) fn highlight_def( if func.is_async(db) { h |= HlMod::Async; } + if func.is_const(db) { + h |= HlMod::Const; + } h } @@ -420,10 +443,11 @@ pub(super) fn highlight_def( } Definition::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)), Definition::Const(konst) => { - let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)); + let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const; if let Some(item) = konst.as_assoc_item(db) { h |= HlMod::Associated; + h |= HlMod::Static; match item.container(db) { hir::AssocItemContainer::Impl(i) => { if i.trait_(db).is_some() { @@ -445,6 +469,7 @@ pub(super) fn highlight_def( if let Some(item) = type_.as_assoc_item(db) { h |= HlMod::Associated; + h |= HlMod::Static; match item.container(db) { hir::AssocItemContainer::Impl(i) => { if i.trait_(db).is_some() { @@ -474,7 +499,7 @@ pub(super) fn highlight_def( Definition::GenericParam(it) => match it { hir::GenericParam::TypeParam(_) => Highlight::new(HlTag::Symbol(SymbolKind::TypeParam)), hir::GenericParam::ConstParam(_) => { - Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) + Highlight::new(HlTag::Symbol(SymbolKind::ConstParam)) | HlMod::Const } hir::GenericParam::LifetimeParam(_) => { Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) @@ -550,8 +575,7 @@ fn highlight_method_call( ) -> Option { let func = sema.resolve_method_call(method_call)?; - let mut h = SymbolKind::Function.into(); - h |= HlMod::Associated; + let mut h = SymbolKind::Method.into(); if func.is_unsafe_to_call(sema.db) || sema.is_unsafe_method_call(method_call) { h |= HlMod::Unsafe; @@ -647,7 +671,7 @@ fn highlight_name_ref_by_syntax( match parent.kind() { METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) .and_then(|it| highlight_method_call(sema, krate, &it)) - .unwrap_or_else(|| SymbolKind::Function.into()), + .unwrap_or_else(|| SymbolKind::Method.into()), FIELD_EXPR => { let h = HlTag::Symbol(SymbolKind::Field); let is_union = ast::FieldExpr::cast(parent) diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs index a6dca0541e5..e754b702dee 100644 --- a/crates/ide/src/syntax_highlighting/html.rs +++ b/crates/ide/src/syntax_highlighting/html.rs @@ -109,6 +109,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 5163a0de417..5c7a463ccdc 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -46,7 +46,7 @@ pub enum HlTag { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(u8)] pub enum HlMod { - /// Used for items in traits and impls. + /// Used for associated items. Associated = 0, /// Used with keywords like `async` and `await`. Async, @@ -54,6 +54,8 @@ pub enum HlMod { Attribute, /// Callable item or value. Callable, + /// Constant operation. + Const, /// Value that is being consumed in a function call Consuming, /// Used with keywords like `if` and `break`. @@ -82,7 +84,7 @@ pub enum HlMod { Public, /// Immutable reference. Reference, - /// Used for associated functions. + /// Used for associated items, except Methods. (Some languages call these static members) Static, /// Used for items in traits and trait impls. Trait, @@ -147,6 +149,7 @@ impl HlTag { SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", SymbolKind::Macro => "macro", + SymbolKind::Method => "method", SymbolKind::ProcMacro => "proc_macro", SymbolKind::Module => "module", SymbolKind::SelfParam => "self_keyword", @@ -211,6 +214,7 @@ impl HlMod { HlMod::Async, HlMod::Attribute, HlMod::Callable, + HlMod::Const, HlMod::Consuming, HlMod::ControlFlow, HlMod::CrateRoot, @@ -237,6 +241,7 @@ impl HlMod { HlMod::Attribute => "attribute", HlMod::Callable => "callable", HlMod::Consuming => "consuming", + HlMod::Const => "const", HlMod::ControlFlow => "control", HlMod::CrateRoot => "crate_root", HlMod::DefaultLibrary => "default_library", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html index 6994cb3d5c5..9c7f03fc158 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -50,15 +51,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd impl foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } trait t { fn t_is_static() {} - fn t_is_not_static(&self) {} + fn t_is_not_static(&self) {} } impl t for foo { pub fn is_static() {} - pub fn is_not_static(&self) {} + pub fn is_not_static(&self) {} } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index dc2d103b581..de902b5137d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -47,7 +48,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
#[allow(dead_code)]
 #[rustfmt::skip]
 #[proc_macros::identity]
-#[derive(Copy)]
+#[derive(Default)]
 /// This is a doc comment
 // This is a normal comment
 /// This is a doc comment
@@ -57,4 +58,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 // This is another normal comment
 #[derive(Copy, Unresolved)]
 // The reason for these being here is to test AttrIds
-struct Foo;
\ No newline at end of file +enum Foo { + #[default] + Bar +} \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index 093cc2358a6..eed3968a906 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -58,7 +59,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd foo!(Bar); fn func() { mod inner { - struct Innerest<const C: usize> { field: [(); {C}] } + struct Innerest<const C: usize> { field: [(); {C}] } } } } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html new file mode 100644 index 00000000000..cd6ffc2c3ae --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html @@ -0,0 +1,83 @@ + + +
macro_rules! id {
+    ($($tt:tt)*) => {
+        $($tt)*
+    };
+}
+const CONST_ITEM: *const () = &raw const ();
+const fn const_fn<const CONST_PARAM: ()>(const {}: const fn()) where (): const ConstTrait {
+    CONST_ITEM;
+    CONST_PARAM;
+    const {
+        const || {}
+    }
+    id!(
+        CONST_ITEM;
+        CONST_PARAM;
+        const {
+            const || {}
+        };
+        &raw const ();
+        const
+    );
+}
+trait ConstTrait {
+    const ASSOC_CONST: () = ();
+    const fn assoc_const_fn() {}
+}
+impl const ConstTrait for () {
+    const ASSOC_CONST: () = ();
+    const fn assoc_const_fn() {}
+}
+
+macro_rules! unsafe_deref {
+    () => {
+        *(&() as *const ())
+    };
+}
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html index 154b823fffb..63e1560b921 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -47,7 +48,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
extern crate foo;
 use core::iter;
 
-pub const NINETY_TWO: u8 = 92;
+pub const NINETY_TWO: u8 = 92;
 
 use foo as foooo;
 
@@ -56,13 +57,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 mod bar {
-    pub(in super) const FORTY_TWO: u8 = 42;
+    pub(in super) const FORTY_TWO: u8 = 42;
 
     mod baz {
-        use super::super::NINETY_TWO;
+        use super::super::NINETY_TWO;
         use crate::foooo::Point;
 
-        pub(in super::super) const TWENTY_NINE: u8 = 29;
+        pub(in super::super) const TWENTY_NINE: u8 = 29;
     }
 }
 
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 58613bf1510..366895ce7ec 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -48,5 +49,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn main() { let foo = Some(92); - let nums = iter::repeat(foo.unwrap()); + let nums = iter::repeat(foo.unwrap()); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 34274932afc..b2ca6e1ca4b 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -71,7 +72,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // KILLER WHALE /// Ishmael."; /// ``` - pub const bar: bool = true; + pub const bar: bool = true; /// Constructs a new `Foo`. /// @@ -81,7 +82,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// # #![allow(unused_mut)] /// let mut foo: Foo = Foo::new(); /// ``` - pub const fn new() -> Foo { + pub const fn new() -> Foo { Foo { bar: true } } @@ -109,25 +110,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ``` /// /// ```rust,no_run - /// let foobar = Foo::new().bar(); + /// let foobar = Foo::new().bar(); /// ``` /// /// ~~~rust,no_run /// // code block with tilde. - /// let foobar = Foo::new().bar(); + /// let foobar = Foo::new().bar(); /// ~~~ /// /// ``` /// // functions - /// fn foo<T, const X: usize>(arg: i32) { - /// let x: T = X; + /// fn foo<T, const X: usize>(arg: i32) { + /// let x: T = X; /// } /// ``` /// /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 729e4791f55..129b287e52f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 066fcfb1dfe..7ba1194d675 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -63,25 +64,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } trait Bar { - fn bar(&self) -> i32; + fn bar(&self) -> i32; } impl Bar for Foo { - fn bar(&self) -> i32 { + fn bar(&self) -> i32 { self.x } } impl Foo { - fn baz(mut self, f: Foo) -> i32 { - f.baz(self) + fn baz(mut self, f: Foo) -> i32 { + f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> i32 { + fn quop(&self) -> i32 { self.x } } @@ -94,15 +95,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } impl FooCopy { - fn baz(self, f: FooCopy) -> u32 { - f.baz(self) + fn baz(self, f: FooCopy) -> u32 { + f.baz(self) } - fn qux(&mut self) { + fn qux(&mut self) { self.x = 0; } - fn quop(&self) -> u32 { + fn quop(&self) -> u32 { self.x } } @@ -119,9 +120,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd loop {} } -fn const_param<const FOO: usize>() -> usize { - const_param::<{ FOO }>(); - FOO +fn const_param<const FOO: usize>() -> usize { + const_param::<{ FOO }>(); + FOO } use ops::Fn; @@ -148,17 +149,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let mut foo = Foo { x, y: x }; let foo2 = Foo { x, y: x }; - foo.quop(); - foo.qux(); - foo.baz(foo2); + foo.quop(); + foo.qux(); + foo.baz(foo2); let mut copy = FooCopy { x }; - copy.quop(); - copy.qux(); - copy.baz(copy); + copy.quop(); + copy.qux(); + copy.baz(copy); let a = |x| x; - let bar = Foo::baz; + let bar = Foo::baz; let baz = (-42,); let baz = -baz.0; @@ -178,7 +179,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd use Option::*; impl<T> Option<T> { - fn and<U>(self, other: Option<U>) -> Option<(T, U)> { + fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { None => unimplemented!(), Nope => Nope, @@ -200,12 +201,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn use_foo_items() { let bob = foo::Person { name: "Bob", - age: foo::consts::NUMBER, + age: foo::consts::NUMBER, }; let control_flow = foo::identity(foo::ControlFlow::Continue); - if control_flow.should_die() { + if control_flow.should_die() { foo::die!(); } } @@ -213,23 +214,23 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd pub enum Bool { True, False } impl Bool { - pub const fn to_primitive(self) -> bool { + pub const fn to_primitive(self) -> bool { true } } -const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); +const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); trait Baz { - type Qux; + type Qux; } fn baz<T>(t: T) where T: Baz, - <T as Baz>::Qux: Bar {} + <T as Baz>::Qux: Bar {} fn gp_shadows_trait<Baz: Bar>() { - Baz::bar; + Baz::bar; } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index 58a147dd80a..6c3fbcfcf41 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 22ae5c82a4b..7064a877070 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html index af779996593..8428b815800 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 32ac6a94d86..573b3d4bd52 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html index ef8a48ca1c1..947d1bf1e35 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html index a2ded15fd1b..0fe2b6f274d 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html index d123ee04976..c60b6ab27bb 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_operators.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html index 4429e5d933a..5d51b149789 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index b6458fa7ca0..7a07d17b271 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -171,9 +172,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd in(reg) i, ); - const CONSTANT: () = (): + const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 49b588baa58..15d6be6334c 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -40,6 +40,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword { color: #F0DFAF; font-weight: bold; } .control { font-style: italic; } .reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } @@ -65,7 +66,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd struct Struct { field: i32 } impl Struct { - unsafe fn unsafe_method(&self) {} + unsafe fn unsafe_method(&self) {} } #[repr(packed)] @@ -80,11 +81,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {} trait DoTheAutoref { - fn calls_autoref(&self); + fn calls_autoref(&self); } impl DoTheAutoref for u16 { - fn calls_autoref(&self) {} + fn calls_autoref(&self) {} } fn main() { @@ -106,7 +107,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd Union { b: 0 } => (), Union { a } => (), } - Struct { field: 0 }.unsafe_method(); + Struct { field: 0 }.unsafe_method(); // unsafe deref *x; @@ -123,6 +124,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let Packed { a: ref _a } = packed; // unsafe auto ref of packed field - packed.a.calls_autoref(); + packed.a.calls_autoref(); } } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 6fed7d783e8..c2990fd76ea 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -22,11 +22,11 @@ fn attributes() { check_highlighting( r#" //- proc_macros: identity -//- minicore: derive, copy +//- minicore: derive, copy, default #[allow(dead_code)] #[rustfmt::skip] #[proc_macros::identity] -#[derive(Copy)] +#[derive(Default)] /// This is a doc comment // This is a normal comment /// This is a doc comment @@ -36,7 +36,10 @@ fn attributes() { // This is another normal comment #[derive(Copy, Unresolved)] // The reason for these being here is to test AttrIds -struct Foo; +enum Foo { + #[default] + Bar +} "#, expect_file!["./test_data/highlight_attributes.html"], false, @@ -629,6 +632,52 @@ fn main() { ); } +#[test] +fn test_const_highlighting() { + check_highlighting( + r#" +macro_rules! id { + ($($tt:tt)*) => { + $($tt)* + }; +} +const CONST_ITEM: *const () = &raw const (); +const fn const_fn(const {}: const fn()) where (): const ConstTrait { + CONST_ITEM; + CONST_PARAM; + const { + const || {} + } + id!( + CONST_ITEM; + CONST_PARAM; + const { + const || {} + }; + &raw const (); + const + ); +} +trait ConstTrait { + const ASSOC_CONST: () = (); + const fn assoc_const_fn() {} +} +impl const ConstTrait for () { + const ASSOC_CONST: () = (); + const fn assoc_const_fn() {} +} + +macro_rules! unsafe_deref { + () => { + *(&() as *const ()) + }; +} +"#, + expect_file!["./test_data/highlight_const.html"], + false, + ); +} + #[test] fn test_highlight_doc_comment() { check_highlighting( @@ -1173,8 +1222,27 @@ fn benchmark_syntax_highlighting_parser() { .highlight(HL_CONFIG, file_id) .unwrap() .iter() - .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) + .filter(|it| { + matches!(it.highlight.tag, HlTag::Symbol(SymbolKind::Function | SymbolKind::Method)) + }) .count() }; assert_eq!(hash, 1169); } + +#[test] +fn highlight_trait_with_lifetimes_regression_16958() { + let (analysis, file_id) = fixture::file( + r#" +pub trait Deserialize<'de> { + fn deserialize(); +} + +fn f<'de, T: Deserialize<'de>>() { + T::deserialize(); +} +"# + .trim(), + ); + let _ = analysis.highlight(HL_CONFIG, file_id).unwrap(); +} diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs index ca471399703..99e24308607 100644 --- a/crates/ide/src/test_explorer.rs +++ b/crates/ide/src/test_explorer.rs @@ -7,7 +7,7 @@ use ide_db::{ }; use syntax::TextRange; -use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav}; +use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav}; #[derive(Debug)] pub enum TestItemKind { @@ -56,7 +56,12 @@ fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option }) } -fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String) -> Vec { +fn discover_tests_in_module( + db: &RootDatabase, + module: Module, + prefix_id: String, + only_in_this_file: bool, +) -> Vec { let sema = Semantics::new(db); let mut r = vec![]; @@ -64,9 +69,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String let module_name = c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned(); let module_id = format!("{prefix_id}::{module_name}"); - let module_children = discover_tests_in_module(db, c, module_id.clone()); + let module_children = discover_tests_in_module(db, c, module_id.clone(), only_in_this_file); if !module_children.is_empty() { - let nav = c.to_nav(db).call_site; + let nav = NavigationTarget::from_module_to_decl(sema.db, c).call_site; r.push(TestItem { id: module_id, kind: TestItemKind::Module, @@ -76,7 +81,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String text_range: Some(nav.focus_or_full_range()), runnable: None, }); - r.extend(module_children); + if !only_in_this_file || c.is_inline(db) { + r.extend(module_children); + } } } for def in module.declarations(db) { @@ -112,6 +119,55 @@ pub(crate) fn discover_tests_in_crate_by_test_id( discover_tests_in_crate(db, crate_id) } +pub(crate) fn discover_tests_in_file(db: &RootDatabase, file_id: FileId) -> Vec { + let sema = Semantics::new(db); + + let Some(module) = sema.file_to_module_def(file_id) else { return vec![] }; + let Some((mut tests, id)) = find_module_id_and_test_parents(&sema, module) else { + return vec![]; + }; + tests.extend(discover_tests_in_module(db, module, id, true)); + tests +} + +fn find_module_id_and_test_parents( + sema: &Semantics<'_, RootDatabase>, + module: Module, +) -> Option<(Vec, String)> { + let Some(parent) = module.parent(sema.db) else { + let name = module.krate().display_name(sema.db)?.to_string(); + return Some(( + vec![TestItem { + id: name.clone(), + kind: TestItemKind::Crate(module.krate().into()), + label: name.clone(), + parent: None, + file: None, + text_range: None, + runnable: None, + }], + name, + )); + }; + let (mut r, mut id) = find_module_id_and_test_parents(sema, parent)?; + let parent = Some(id.clone()); + id += "::"; + let module_name = &module.name(sema.db); + let module_name = module_name.as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]"); + id += module_name; + let nav = NavigationTarget::from_module_to_decl(sema.db, module).call_site; + r.push(TestItem { + id: id.clone(), + kind: TestItemKind::Module, + label: module_name.to_owned(), + parent, + file: Some(nav.file_id), + text_range: Some(nav.focus_or_full_range()), + runnable: None, + }); + Some((r, id)) +} + pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec { let crate_graph = db.crate_graph(); if !crate_graph[crate_id].origin.is_local() { @@ -133,6 +189,6 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V text_range: None, runnable: None, }]; - r.extend(discover_tests_in_module(db, module, crate_test_id)); + r.extend(discover_tests_in_module(db, module, crate_test_id, false)); r } diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index e87fc89fea2..d3eee0e02e4 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -175,9 +175,21 @@ fn on_opening_bracket_typed( } } - // If it's a statement in a block, we don't know how many statements should be included - if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { - return None; + if let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) { + let mut node = expr.syntax().clone(); + let all_prev_sib_attr = loop { + match node.prev_sibling() { + Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => { + node = sib + } + Some(_) => break false, + None => break true, + }; + }; + + if all_prev_sib_attr { + expr = parent; + } } // Insert the closing bracket right after the expression. @@ -824,6 +836,21 @@ fn f() { 0 => {()}, 1 => (), } +} + "#, + ); + type_char( + '{', + r#" +fn main() { + #[allow(unreachable_code)] + $0g(); +} + "#, + r#" +fn main() { + #[allow(unreachable_code)] + {g()}; } "#, ); diff --git a/crates/load-cargo/Cargo.toml b/crates/load-cargo/Cargo.toml index 05412e176b6..48e84a7b25e 100644 --- a/crates/load-cargo/Cargo.toml +++ b/crates/load-cargo/Cargo.toml @@ -20,6 +20,7 @@ tracing.workspace = true hir-expand.workspace = true ide-db.workspace = true +paths.workspace = true proc-macro-api.workspace = true project-model.workspace = true span.workspace = true diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index fe680e47fef..79d6fe36b56 100644 --- a/crates/load-cargo/src/lib.rs +++ b/crates/load-cargo/src/lib.rs @@ -38,7 +38,7 @@ pub fn load_workspace_at( load_config: &LoadCargoConfig, progress: &dyn Fn(String), ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { - let root = AbsPathBuf::assert(std::env::current_dir()?.join(root)); + let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root)); let root = ProjectManifest::discover_single(&root)?; let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?; @@ -387,7 +387,7 @@ fn expander_to_proc_macro( let name = From::from(expander.name()); let kind = match expander.kind() { proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive, - proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, + proc_macro_api::ProcMacroKind::Bang => ProcMacroKind::Bang, proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, }; let disabled = ignored_macros.iter().any(|replace| **replace == name); diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index ac7f0711784..4d5531ae307 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -23,7 +23,11 @@ fn benchmark_parse_macro_rules() { let _pt = bench("mbe parse macro rules"); rules .values() - .map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len()) + .map(|it| { + DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT, true) + .rules + .len() + }) .sum() }; assert_eq!(hash, 1144); @@ -51,10 +55,12 @@ fn benchmark_expand_macro_rules() { assert_eq!(hash, 69413); } -fn macro_rules_fixtures() -> FxHashMap> { +fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true))) + .map(|(id, tt)| { + (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT, true)) + }) .collect() } @@ -80,7 +86,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap> { /// Generate random invocation fixtures from rules fn invocation_fixtures( - rules: &FxHashMap>, + rules: &FxHashMap, ) -> Vec<(String, tt::Subtree)> { let mut seed = 123456789; let mut res = Vec::new(); @@ -128,11 +134,7 @@ fn invocation_fixtures( } return res; - fn collect_from_op( - op: &Op, - token_trees: &mut Vec>, - seed: &mut usize, - ) { + fn collect_from_op(op: &Op, token_trees: &mut Vec>, seed: &mut usize) { return match op { Op::Var { kind, .. } => match kind.as_ref() { Some(MetaVarKind::Ident) => token_trees.push(make_ident("foo")), diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs index 9366048fd95..2f2c0aa6ff5 100644 --- a/crates/mbe/src/expander.rs +++ b/crates/mbe/src/expander.rs @@ -6,22 +6,21 @@ mod matcher; mod transcriber; use rustc_hash::FxHashMap; +use span::Span; use syntax::SmolStr; -use tt::Span; use crate::{parser::MetaVarKind, ExpandError, ExpandResult}; -pub(crate) fn expand_rules( - rules: &[crate::Rule], - input: &tt::Subtree, - marker: impl Fn(&mut S) + Copy, - is_2021: bool, +pub(crate) fn expand_rules( + rules: &[crate::Rule], + input: &tt::Subtree, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, -) -> ExpandResult> { - let mut match_: Option<(matcher::Match, &crate::Rule)> = None; + call_site: Span, +) -> ExpandResult> { + let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { - let new_match = matcher::match_(&rule.lhs, input, is_2021); + let new_match = matcher::match_(&rule.lhs, input); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. @@ -110,30 +109,24 @@ pub(crate) fn expand_rules( /// In other words, `Bindings` is a *multi* mapping from `SmolStr` to /// `tt::TokenTree`, where the index to select a particular `TokenTree` among /// many is not a plain `usize`, but a `&[usize]`. -#[derive(Debug, Clone, PartialEq, Eq)] -struct Bindings { - inner: FxHashMap>, -} - -impl Default for Bindings { - fn default() -> Self { - Self { inner: Default::default() } - } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct Bindings { + inner: FxHashMap, } #[derive(Debug, Clone, PartialEq, Eq)] -enum Binding { - Fragment(Fragment), - Nested(Vec>), +enum Binding { + Fragment(Fragment), + Nested(Vec), Empty, Missing(MetaVarKind), } #[derive(Debug, Clone, PartialEq, Eq)] -enum Fragment { +enum Fragment { Empty, /// token fragments are just copy-pasted into the output - Tokens(tt::TokenTree), + Tokens(tt::TokenTree), /// Expr ast fragments are surrounded with `()` on insertion to preserve /// precedence. Note that this impl is different from the one currently in /// `rustc` -- `rustc` doesn't translate fragments into token trees at all. @@ -141,7 +134,7 @@ enum Fragment { /// At one point in time, we tried to use "fake" delimiters here à la /// proc-macro delimiter=none. As we later discovered, "none" delimiters are /// tricky to handle in the parser, and rustc doesn't handle those either. - Expr(tt::Subtree), + Expr(tt::Subtree), /// There are roughly two types of paths: paths in expression context, where a /// separator `::` between an identifier and its following generic argument list /// is mandatory, and paths in type context, where `::` can be omitted. @@ -151,5 +144,5 @@ enum Fragment { /// and is trasncribed as an expression-context path, verbatim transcription /// would cause a syntax error. We need to fix it up just before transcribing; /// see `transcriber::fix_up_and_push_path_tt()`. - Path(tt::Subtree), + Path(tt::Subtree), } diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index eea92cfba4c..3170834d54f 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -62,8 +62,9 @@ use std::rc::Rc; use smallvec::{smallvec, SmallVec}; +use span::Span; use syntax::SmolStr; -use tt::{DelimSpan, Span}; +use tt::DelimSpan; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, @@ -72,7 +73,7 @@ use crate::{ ExpandError, MetaTemplate, ValueResult, }; -impl Bindings { +impl Bindings { fn push_optional(&mut self, name: &SmolStr) { self.inner.insert(name.clone(), Binding::Fragment(Fragment::Empty)); } @@ -81,14 +82,14 @@ impl Bindings { self.inner.insert(name.clone(), Binding::Empty); } - fn bindings(&self) -> impl Iterator> { + fn bindings(&self) -> impl Iterator { self.inner.values() } } -#[derive(Clone, Debug, PartialEq, Eq)] -pub(super) struct Match { - pub(super) bindings: Bindings, +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub(super) struct Match { + pub(super) bindings: Bindings, /// We currently just keep the first error and count the rest to compare matches. pub(super) err: Option, pub(super) err_count: usize, @@ -98,19 +99,7 @@ pub(super) struct Match { pub(super) bound_count: usize, } -impl Default for Match { - fn default() -> Self { - Self { - bindings: Default::default(), - err: Default::default(), - err_count: Default::default(), - unmatched_tts: Default::default(), - bound_count: Default::default(), - } - } -} - -impl Match { +impl Match { fn add_err(&mut self, err: ExpandError) { let prev_err = self.err.take(); self.err = prev_err.or(Some(err)); @@ -119,16 +108,12 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_( - pattern: &MetaTemplate, - input: &tt::Subtree, - is_2021: bool, -) -> Match { - let mut res = match_loop(pattern, input, is_2021); +pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { + let mut res = match_loop(pattern, input); res.bound_count = count(res.bindings.bindings()); return res; - fn count<'a, S: 'a>(bindings: impl Iterator>) -> usize { + fn count<'a>(bindings: impl Iterator) -> usize { bindings .map(|it| match it { Binding::Fragment(_) => 1, @@ -141,10 +126,10 @@ pub(super) fn match_( } #[derive(Debug, Clone)] -enum BindingKind { +enum BindingKind { Empty(SmolStr), Optional(SmolStr), - Fragment(SmolStr, Fragment), + Fragment(SmolStr, Fragment), Missing(SmolStr, MetaVarKind), Nested(usize, usize), } @@ -158,18 +143,13 @@ enum LinkNode { Parent { idx: usize, len: usize }, } -struct BindingsBuilder { - nodes: Vec>>>>, +#[derive(Default)] +struct BindingsBuilder { + nodes: Vec>>>, nested: Vec>>, } -impl Default for BindingsBuilder { - fn default() -> Self { - Self { nodes: Default::default(), nested: Default::default() } - } -} - -impl BindingsBuilder { +impl BindingsBuilder { fn alloc(&mut self) -> BindingsIdx { let idx = self.nodes.len(); self.nodes.push(Vec::new()); @@ -206,7 +186,7 @@ impl BindingsBuilder { self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone())))); } - fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) { + fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &SmolStr, fragment: Fragment) { self.nodes[idx.0] .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment)))); } @@ -227,11 +207,11 @@ impl BindingsBuilder { idx.0 = new_idx; } - fn build(self, idx: &BindingsIdx) -> Bindings { + fn build(self, idx: &BindingsIdx) -> Bindings { self.build_inner(&self.nodes[idx.0]) } - fn build_inner(&self, link_nodes: &[LinkNode>>]) -> Bindings { + fn build_inner(&self, link_nodes: &[LinkNode>]) -> Bindings { let mut bindings = Bindings::default(); let mut nodes = Vec::new(); self.collect_nodes(link_nodes, &mut nodes); @@ -281,7 +261,7 @@ impl BindingsBuilder { &'a self, id: usize, len: usize, - nested_refs: &mut Vec<&'a [LinkNode>>]>, + nested_refs: &mut Vec<&'a [LinkNode>]>, ) { self.nested[id].iter().take(len).for_each(|it| match it { LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]), @@ -289,7 +269,7 @@ impl BindingsBuilder { }); } - fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec>) { + fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec) { let last = &self.nodes[idx]; let mut nested_refs: Vec<&[_]> = Vec::new(); self.nested[nested_idx].iter().for_each(|it| match *it { @@ -300,7 +280,7 @@ impl BindingsBuilder { nested.extend(nested_refs.into_iter().map(|iter| self.build_inner(iter))); } - fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { + fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) { self.nodes[id].iter().take(len).for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes), @@ -309,8 +289,8 @@ impl BindingsBuilder { fn collect_nodes<'a>( &'a self, - link_nodes: &'a [LinkNode>>], - nodes: &mut Vec<&'a BindingKind>, + link_nodes: &'a [LinkNode>], + nodes: &mut Vec<&'a BindingKind>, ) { link_nodes.iter().for_each(|it| match it { LinkNode::Node(it) => nodes.push(it), @@ -320,22 +300,22 @@ impl BindingsBuilder { } #[derive(Debug, Clone)] -struct MatchState<'t, S> { +struct MatchState<'t> { /// The position of the "dot" in this matcher - dot: OpDelimitedIter<'t, S>, + dot: OpDelimitedIter<'t>, /// Token subtree stack /// When matching against matchers with nested delimited submatchers (e.g., `pat ( pat ( .. ) /// pat ) pat`), we need to keep track of the matchers we are descending into. This stack does /// that where the bottom of the stack is the outermost matcher. - stack: SmallVec<[OpDelimitedIter<'t, S>; 4]>, + stack: SmallVec<[OpDelimitedIter<'t>; 4]>, /// The "parent" matcher position if we are in a repetition. That is, the matcher position just /// before we enter the repetition. - up: Option>>, + up: Option>>, /// The separator if we are in a repetition. - sep: Option>, + sep: Option, /// The KleeneOp of this sequence if we are in a repetition. sep_kind: Option, @@ -347,7 +327,7 @@ struct MatchState<'t, S> { bindings: BindingsIdx, /// Cached result of meta variable parsing - meta_result: Option<(TtIter<'t, S>, ExpandResult>>)>, + meta_result: Option<(TtIter<'t, Span>, ExpandResult>)>, /// Is error occurred in this state, will `poised` to "parent" is_error: bool, @@ -372,18 +352,17 @@ struct MatchState<'t, S> { /// - `bb_items`: the set of items that are waiting for the black-box parser. /// - `error_items`: the set of items in errors, used for error-resilient parsing #[inline] -fn match_loop_inner<'t, S: Span>( - src: TtIter<'t, S>, - stack: &[TtIter<'t, S>], - res: &mut Match, - bindings_builder: &mut BindingsBuilder, - cur_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - bb_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - next_items: &mut Vec>, - eof_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - error_items: &mut SmallVec<[MatchState<'t, S>; 1]>, - is_2021: bool, - delim_span: tt::DelimSpan, +fn match_loop_inner<'t>( + src: TtIter<'t, Span>, + stack: &[TtIter<'t, Span>], + res: &mut Match, + bindings_builder: &mut BindingsBuilder, + cur_items: &mut SmallVec<[MatchState<'t>; 1]>, + bb_items: &mut SmallVec<[MatchState<'t>; 1]>, + next_items: &mut Vec>, + eof_items: &mut SmallVec<[MatchState<'t>; 1]>, + error_items: &mut SmallVec<[MatchState<'t>; 1]>, + delim_span: tt::DelimSpan, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -494,7 +473,7 @@ fn match_loop_inner<'t, S: Span>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork, is_2021, delim_span); + let match_res = match_meta_var(kind, &mut fork, delim_span); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -607,10 +586,10 @@ fn match_loop_inner<'t, S: Span>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { +fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { let span = src.delimiter.delim_span(); let mut src = TtIter::new(src); - let mut stack: SmallVec<[TtIter<'_, S>; 1]> = SmallVec::new(); + let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); let mut res = Match::default(); let mut error_recover_item = None; @@ -647,7 +626,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: &mut next_items, &mut eof_items, &mut error_items, - is_2021, span, ); stdx::always!(cur_items.is_empty()); @@ -758,12 +736,11 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: } } -fn match_meta_var( +fn match_meta_var( kind: MetaVarKind, - input: &mut TtIter<'_, S>, - is_2021: bool, - delim_span: DelimSpan, -) -> ExpandResult>> { + input: &mut TtIter<'_, Span>, + delim_span: DelimSpan, +) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => { return input.expect_fragment(parser::PrefixEntryPoint::Path).map(|it| { @@ -771,8 +748,7 @@ fn match_meta_var( }); } MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, - MetaVarKind::Pat => parser::PrefixEntryPoint::Pat, + MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, MetaVarKind::Block => parser::PrefixEntryPoint::Block, @@ -846,7 +822,7 @@ fn match_meta_var( input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens)) } -fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { +fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { for op in pattern.iter() { match op { Op::Var { name, .. } => collector_fun(name.clone()), @@ -859,11 +835,11 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &Meta } } } -impl MetaTemplate { - fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_, S> { +impl MetaTemplate { + fn iter_delimited_with(&self, delimiter: tt::Delimiter) -> OpDelimitedIter<'_> { OpDelimitedIter { inner: &self.0, idx: 0, delimited: delimiter } } - fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_, S> { + fn iter_delimited(&self, span: tt::DelimSpan) -> OpDelimitedIter<'_> { OpDelimitedIter { inner: &self.0, idx: 0, @@ -873,27 +849,27 @@ impl MetaTemplate { } #[derive(Debug, Clone, Copy)] -enum OpDelimited<'a, S> { - Op(&'a Op), +enum OpDelimited<'a> { + Op(&'a Op), Open, Close, } #[derive(Debug, Clone, Copy)] -struct OpDelimitedIter<'a, S> { - inner: &'a [Op], - delimited: tt::Delimiter, +struct OpDelimitedIter<'a> { + inner: &'a [Op], + delimited: tt::Delimiter, idx: usize, } -impl<'a, S: Span> OpDelimitedIter<'a, S> { +impl<'a> OpDelimitedIter<'a> { fn is_eof(&self) -> bool { let len = self.inner.len() + if self.delimited.kind != tt::DelimiterKind::Invisible { 2 } else { 0 }; self.idx >= len } - fn peek(&self) -> Option> { + fn peek(&self) -> Option> { match self.delimited.kind { tt::DelimiterKind::Invisible => self.inner.get(self.idx).map(OpDelimited::Op), _ => match self.idx { @@ -909,8 +885,8 @@ impl<'a, S: Span> OpDelimitedIter<'a, S> { } } -impl<'a, S: Span> Iterator for OpDelimitedIter<'a, S> { - type Item = OpDelimited<'a, S>; +impl<'a> Iterator for OpDelimitedIter<'a> { + type Item = OpDelimited<'a>; fn next(&mut self) -> Option { let res = self.peek(); @@ -926,8 +902,8 @@ impl<'a, S: Span> Iterator for OpDelimitedIter<'a, S> { } } -impl TtIter<'_, S> { - fn expect_separator(&mut self, separator: &Separator) -> bool { +impl TtIter<'_, Span> { + fn expect_separator(&mut self, separator: &Separator) -> bool { let mut fork = self.clone(); let ok = match separator { Separator::Ident(lhs) => match fork.expect_ident_or_underscore() { @@ -957,7 +933,7 @@ impl TtIter<'_, S> { ok } - fn expect_tt(&mut self) -> Result, ()> { + fn expect_tt(&mut self) -> Result, ()> { if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = self.peek_n(0) { if punct.char == '\'' { self.expect_lifetime() @@ -976,7 +952,7 @@ impl TtIter<'_, S> { } } - fn expect_lifetime(&mut self) -> Result, ()> { + fn expect_lifetime(&mut self) -> Result, ()> { let punct = self.expect_single_punct()?; if punct.char != '\'' { return Err(()); @@ -997,7 +973,7 @@ impl TtIter<'_, S> { .into()) } - fn eat_char(&mut self, c: char) -> Option> { + fn eat_char(&mut self, c: char) -> Option> { let mut fork = self.clone(); match fork.expect_char(c) { Ok(_) => { diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 6d3055da286..5e6e45f1521 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/crates/mbe/src/expander/transcriber.rs @@ -1,8 +1,9 @@ //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` +use span::Span; use syntax::SmolStr; -use tt::{Delimiter, Span}; +use tt::Delimiter; use crate::{ expander::{Binding, Bindings, Fragment}, @@ -10,8 +11,8 @@ use crate::{ CountError, ExpandError, ExpandResult, MetaTemplate, }; -impl Bindings { - fn get(&self, name: &str) -> Result<&Binding, ExpandError> { +impl Bindings { + fn get(&self, name: &str) -> Result<&Binding, ExpandError> { match self.inner.get(name) { Some(binding) => Ok(binding), None => Err(ExpandError::UnresolvedBinding(Box::new(Box::from(name)))), @@ -21,10 +22,10 @@ impl Bindings { fn get_fragment( &self, name: &str, - mut span: S, + mut span: Span, nesting: &mut [NestingState], - marker: impl Fn(&mut S), - ) -> Result, ExpandError> { + marker: impl Fn(&mut Span), + ) -> Result { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) }; } @@ -134,15 +135,15 @@ impl Bindings { } } -pub(super) fn transcribe( - template: &MetaTemplate, - bindings: &Bindings, - marker: impl Fn(&mut S) + Copy, +pub(super) fn transcribe( + template: &MetaTemplate, + bindings: &Bindings, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, -) -> ExpandResult> { + call_site: Span, +) -> ExpandResult> { let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), new_meta_vars, call_site }; - let mut arena: Vec> = Vec::new(); + let mut arena: Vec> = Vec::new(); expand_subtree(&mut ctx, template, None, &mut arena, marker) } @@ -158,20 +159,20 @@ struct NestingState { } #[derive(Debug)] -struct ExpandCtx<'a, S> { - bindings: &'a Bindings, +struct ExpandCtx<'a> { + bindings: &'a Bindings, nesting: Vec, new_meta_vars: bool, - call_site: S, + call_site: Span, } -fn expand_subtree( - ctx: &mut ExpandCtx<'_, S>, - template: &MetaTemplate, - delimiter: Option>, - arena: &mut Vec>, - marker: impl Fn(&mut S) + Copy, -) -> ExpandResult> { +fn expand_subtree( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, + delimiter: Option>, + arena: &mut Vec>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult> { // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation let start_elements = arena.len(); let mut err = None; @@ -332,12 +333,12 @@ fn expand_subtree( } } -fn expand_var( - ctx: &mut ExpandCtx<'_, S>, +fn expand_var( + ctx: &mut ExpandCtx<'_>, v: &SmolStr, - id: S, - marker: impl Fn(&mut S), -) -> ExpandResult> { + id: Span, + marker: impl Fn(&mut Span), +) -> ExpandResult { // We already handle $crate case in mbe parser debug_assert!(v != "crate"); @@ -378,15 +379,15 @@ fn expand_var( } } -fn expand_repeat( - ctx: &mut ExpandCtx<'_, S>, - template: &MetaTemplate, +fn expand_repeat( + ctx: &mut ExpandCtx<'_>, + template: &MetaTemplate, kind: RepeatKind, - separator: &Option>, - arena: &mut Vec>, - marker: impl Fn(&mut S) + Copy, -) -> ExpandResult> { - let mut buf: Vec> = Vec::new(); + separator: &Option, + arena: &mut Vec>, + marker: impl Fn(&mut Span) + Copy, +) -> ExpandResult { + let mut buf: Vec> = Vec::new(); ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); // Dirty hack to make macro-expansion terminate. // This should be replaced by a proper macro-by-example implementation @@ -478,11 +479,7 @@ fn expand_repeat( ExpandResult { value: Fragment::Tokens(tt), err } } -fn push_fragment( - ctx: &ExpandCtx<'_, S>, - buf: &mut Vec>, - fragment: Fragment, -) { +fn push_fragment(ctx: &ExpandCtx<'_>, buf: &mut Vec>, fragment: Fragment) { match fragment { Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt), Fragment::Expr(sub) => { @@ -494,7 +491,7 @@ fn push_fragment( } } -fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { +fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { match tt.delimiter.kind { tt::DelimiterKind::Invisible => buf.extend(Vec::from(tt.token_trees)), _ => buf.push(tt.into()), @@ -504,10 +501,10 @@ fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) { /// Inserts the path separator `::` between an identifier and its following generic /// argument list, and then pushes into the buffer. See [`Fragment::Path`] for why /// we need this fixup. -fn fix_up_and_push_path_tt( - ctx: &ExpandCtx<'_, S>, - buf: &mut Vec>, - subtree: tt::Subtree, +fn fix_up_and_push_path_tt( + ctx: &ExpandCtx<'_>, + buf: &mut Vec>, + subtree: tt::Subtree, ) { stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible)); let mut prev_was_ident = false; @@ -546,11 +543,7 @@ fn fix_up_and_push_path_tt( /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth /// defined by the metavar expression. -fn count( - binding: &Binding, - depth_curr: usize, - depth_max: usize, -) -> Result { +fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> Result { match binding { Binding::Nested(bs) => { if depth_curr == depth_max { @@ -564,8 +557,8 @@ fn count( } } -fn count_old( - binding: &Binding, +fn count_old( + binding: &Binding, our_depth: usize, count_depth: Option, ) -> Result { diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 62fdce36892..3a853512660 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -17,8 +17,8 @@ mod tt_iter; #[cfg(test)] mod benchmark; +use span::{Edition, Span, SyntaxContextId}; use stdx::impl_from; -use tt::Span; use std::fmt; @@ -127,32 +127,29 @@ impl fmt::Display for CountError { /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident` /// and `$()*` have special meaning (see `Var` and `Repeat` data structures) #[derive(Clone, Debug, PartialEq, Eq)] -pub struct DeclarativeMacro { - rules: Box<[Rule]>, - // This is used for correctly determining the behavior of the pat fragment - // FIXME: This should be tracked by hygiene of the fragment identifier! - is_2021: bool, +pub struct DeclarativeMacro { + rules: Box<[Rule]>, err: Option>, } #[derive(Clone, Debug, PartialEq, Eq)] -struct Rule { - lhs: MetaTemplate, - rhs: MetaTemplate, +struct Rule { + lhs: MetaTemplate, + rhs: MetaTemplate, } -impl DeclarativeMacro { - pub fn from_err(err: ParseError, is_2021: bool) -> DeclarativeMacro { - DeclarativeMacro { rules: Box::default(), is_2021, err: Some(Box::new(err)) } +impl DeclarativeMacro { + pub fn from_err(err: ParseError) -> DeclarativeMacro { + DeclarativeMacro { rules: Box::default(), err: Some(Box::new(err)) } } /// The old, `macro_rules! m {}` flavor. pub fn parse_macro_rules( - tt: &tt::Subtree, - is_2021: bool, + tt: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, - ) -> DeclarativeMacro { + ) -> DeclarativeMacro { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. @@ -161,7 +158,7 @@ impl DeclarativeMacro { let mut err = None; while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -184,16 +181,16 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } /// The new, unstable `macro m {}` flavor. pub fn parse_macro2( - tt: &tt::Subtree, - is_2021: bool, + tt: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then) new_meta_vars: bool, - ) -> DeclarativeMacro { + ) -> DeclarativeMacro { let mut src = TtIter::new(tt); let mut rules = Vec::new(); let mut err = None; @@ -201,7 +198,7 @@ impl DeclarativeMacro { if tt::DelimiterKind::Brace == tt.delimiter.kind { cov_mark::hit!(parse_macro_def_rules); while src.len() > 0 { - let rule = match Rule::parse(&mut src, true, new_meta_vars) { + let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) { Ok(it) => it, Err(e) => { err = Some(Box::new(e)); @@ -220,7 +217,7 @@ impl DeclarativeMacro { } } else { cov_mark::hit!(parse_macro_def_simple); - match Rule::parse(&mut src, false, new_meta_vars) { + match Rule::parse(edition, &mut src, false, new_meta_vars) { Ok(rule) => { if src.len() != 0 { err = Some(Box::new(ParseError::expected("remaining tokens in macro def"))); @@ -240,7 +237,7 @@ impl DeclarativeMacro { } } - DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err } + DeclarativeMacro { rules: rules.into_boxed_slice(), err } } pub fn err(&self) -> Option<&ParseError> { @@ -249,18 +246,19 @@ impl DeclarativeMacro { pub fn expand( &self, - tt: &tt::Subtree, - marker: impl Fn(&mut S) + Copy, + tt: &tt::Subtree, + marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, - call_site: S, - ) -> ExpandResult> { - expander::expand_rules(&self.rules, tt, marker, self.is_2021, new_meta_vars, call_site) + call_site: Span, + ) -> ExpandResult> { + expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site) } } -impl Rule { +impl Rule { fn parse( - src: &mut TtIter<'_, S>, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + src: &mut TtIter<'_, Span>, expect_arrow: bool, new_meta_vars: bool, ) -> Result { @@ -271,14 +269,14 @@ impl Rule { } let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?; - let lhs = MetaTemplate::parse_pattern(lhs)?; - let rhs = MetaTemplate::parse_template(rhs, new_meta_vars)?; + let lhs = MetaTemplate::parse_pattern(edition, lhs)?; + let rhs = MetaTemplate::parse_template(edition, rhs, new_meta_vars)?; Ok(crate::Rule { lhs, rhs }) } } -fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { +fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { for op in pattern.iter() { match op { Op::Subtree { tokens, .. } => validate(tokens)?, diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index afdbbef2314..eaf2fd8c273 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -2,8 +2,8 @@ //! trees. use smallvec::{smallvec, SmallVec}; +use span::{Edition, Span, SyntaxContextId}; use syntax::SmolStr; -use tt::Span; use crate::{tt_iter::TtIter, ParseError}; @@ -21,30 +21,39 @@ use crate::{tt_iter::TtIter, ParseError}; /// Stuff to the right is a [`MetaTemplate`] template which is used to produce /// output. #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); +pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); -impl MetaTemplate { - pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result { - MetaTemplate::parse(pattern, Mode::Pattern, false) +impl MetaTemplate { + pub(crate) fn parse_pattern( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + pattern: &tt::Subtree, + ) -> Result { + MetaTemplate::parse(edition, pattern, Mode::Pattern, false) } pub(crate) fn parse_template( - template: &tt::Subtree, + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + template: &tt::Subtree, new_meta_vars: bool, ) -> Result { - MetaTemplate::parse(template, Mode::Template, new_meta_vars) + MetaTemplate::parse(edition, template, Mode::Template, new_meta_vars) } - pub(crate) fn iter(&self) -> impl Iterator> { + pub(crate) fn iter(&self) -> impl Iterator { self.0.iter() } - fn parse(tt: &tt::Subtree, mode: Mode, new_meta_vars: bool) -> Result { + fn parse( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + tt: &tt::Subtree, + mode: Mode, + new_meta_vars: bool, + ) -> Result { let mut src = TtIter::new(tt); let mut res = Vec::new(); while let Some(first) = src.peek_n(0) { - let op = next_op(first, &mut src, mode, new_meta_vars)?; + let op = next_op(edition, first, &mut src, mode, new_meta_vars)?; res.push(op); } @@ -53,15 +62,15 @@ impl MetaTemplate { } #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum Op { +pub(crate) enum Op { Var { name: SmolStr, kind: Option, - id: S, + id: Span, }, Ignore { name: SmolStr, - id: S, + id: Span, }, Index { depth: usize, @@ -75,17 +84,17 @@ pub(crate) enum Op { depth: Option, }, Repeat { - tokens: MetaTemplate, + tokens: MetaTemplate, kind: RepeatKind, - separator: Option>, + separator: Option, }, Subtree { - tokens: MetaTemplate, - delimiter: tt::Delimiter, + tokens: MetaTemplate, + delimiter: tt::Delimiter, }, - Literal(tt::Literal), - Punct(SmallVec<[tt::Punct; 3]>), - Ident(tt::Ident), + Literal(tt::Literal), + Punct(SmallVec<[tt::Punct; 3]>), + Ident(tt::Ident), } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -114,15 +123,15 @@ pub(crate) enum MetaVarKind { } #[derive(Clone, Debug, Eq)] -pub(crate) enum Separator { - Literal(tt::Literal), - Ident(tt::Ident), - Puncts(SmallVec<[tt::Punct; 3]>), +pub(crate) enum Separator { + Literal(tt::Literal), + Ident(tt::Ident), + Puncts(SmallVec<[tt::Punct; 3]>), } // Note that when we compare a Separator, we just care about its textual value. -impl PartialEq for Separator { - fn eq(&self, other: &Separator) -> bool { +impl PartialEq for Separator { + fn eq(&self, other: &Separator) -> bool { use Separator::*; match (self, other) { @@ -144,12 +153,13 @@ enum Mode { Template, } -fn next_op( - first_peeked: &tt::TokenTree, - src: &mut TtIter<'_, S>, +fn next_op( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + first_peeked: &tt::TokenTree, + src: &mut TtIter<'_, Span>, mode: Mode, new_meta_vars: bool, -) -> Result, ParseError> { +) -> Result { let res = match first_peeked { tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => { src.next().expect("first token already peeked"); @@ -162,7 +172,7 @@ fn next_op( tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind { tt::DelimiterKind::Parenthesis => { let (separator, kind) = parse_repeat(src)?; - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Repeat { tokens, separator, kind } } tt::DelimiterKind::Brace => match mode { @@ -189,13 +199,13 @@ fn next_op( Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span }) } tt::Leaf::Ident(ident) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = ident.text.clone(); let id = ident.span; Op::Var { name, kind, id } } tt::Leaf::Literal(lit) if is_boolean_literal(lit) => { - let kind = eat_fragment_kind(src, mode)?; + let kind = eat_fragment_kind(edition, src, mode)?; let name = lit.text.clone(); let id = lit.span; Op::Var { name, kind, id } @@ -233,15 +243,16 @@ fn next_op( tt::TokenTree::Subtree(subtree) => { src.next().expect("first token already peeked"); - let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?; + let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?; Op::Subtree { tokens, delimiter: subtree.delimiter } } }; Ok(res) } -fn eat_fragment_kind( - src: &mut TtIter<'_, S>, +fn eat_fragment_kind( + edition: impl Copy + Fn(SyntaxContextId) -> Edition, + src: &mut TtIter<'_, Span>, mode: Mode, ) -> Result, ParseError> { if let Mode::Pattern = mode { @@ -252,7 +263,10 @@ fn eat_fragment_kind( let kind = match ident.text.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, - "pat" => MetaVarKind::Pat, + "pat" => match edition(ident.span.ctx) { + Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam, + Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat, + }, "pat_param" => MetaVarKind::PatParam, "stmt" => MetaVarKind::Stmt, "block" => MetaVarKind::Block, @@ -271,13 +285,11 @@ fn eat_fragment_kind( Ok(None) } -fn is_boolean_literal(lit: &tt::Literal) -> bool { +fn is_boolean_literal(lit: &tt::Literal) -> bool { matches!(lit.text.as_str(), "true" | "false") } -fn parse_repeat( - src: &mut TtIter<'_, S>, -) -> Result<(Option>, RepeatKind), ParseError> { +fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, RepeatKind), ParseError> { let mut separator = Separator::Puncts(SmallVec::new()); for tt in src { let tt = match tt { @@ -314,7 +326,7 @@ fn parse_repeat( Err(ParseError::InvalidRepeat) } -fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Result, ()> { +fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, Span>) -> Result { let func = src.expect_ident()?; let args = src.expect_subtree()?; @@ -352,7 +364,7 @@ fn parse_metavar_expr(new_meta_vars: bool, src: &mut TtIter<'_, S>) -> Ok(op) } -fn parse_depth(src: &mut TtIter<'_, S>) -> Result { +fn parse_depth(src: &mut TtIter<'_, Span>) -> Result { if src.len() == 0 { Ok(0) } else if let tt::Leaf::Literal(lit) = src.expect_literal()? { @@ -363,7 +375,7 @@ fn parse_depth(src: &mut TtIter<'_, S>) -> Result { } } -fn try_eat_comma(src: &mut TtIter<'_, S>) -> bool { +fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) { let _ = src.next(); return true; diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 57d6082dd70..c934db6b715 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -1,5 +1,7 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. +use std::fmt; + use rustc_hash::{FxHashMap, FxHashSet}; use span::{SpanAnchor, SpanData, SpanMap}; use stdx::{never, non_empty_vec::NonEmptyVec}; @@ -9,30 +11,27 @@ use syntax::{ SyntaxKind::*, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T, }; -use tt::{ - buffer::{Cursor, TokenBuffer}, - Span, -}; +use tt::buffer::{Cursor, TokenBuffer}; use crate::{to_parser_input::to_parser_input, tt_iter::TtIter}; #[cfg(test)] mod tests; -pub trait SpanMapper { +pub trait SpanMapper { fn span_for(&self, range: TextRange) -> S; } impl SpanMapper> for SpanMap where - SpanData: Span, + SpanData: Copy, { fn span_for(&self, range: TextRange) -> SpanData { self.span_at(range.start()) } } -impl> SpanMapper for &SM { +impl> SpanMapper for &SM { fn span_for(&self, range: TextRange) -> S { SM::span_for(self, range) } @@ -78,8 +77,7 @@ pub fn syntax_node_to_token_tree( span: SpanData, ) -> tt::Subtree> where - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, SpanMap: SpanMapper>, { let mut c = Converter::new(node, map, Default::default(), Default::default(), span); @@ -98,8 +96,7 @@ pub fn syntax_node_to_token_tree_modified( ) -> tt::Subtree> where SpanMap: SpanMapper>, - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, { let mut c = Converter::new(node, map, append, remove, call_site); convert_tokens(&mut c) @@ -124,8 +121,7 @@ pub fn token_tree_to_syntax_node( entry_point: parser::TopEntryPoint, ) -> (Parse, SpanMap) where - SpanData: Span, - Ctx: Copy, + SpanData: Copy + fmt::Debug, { let buffer = match tt { tt::Subtree { @@ -161,7 +157,7 @@ pub fn parse_to_token_tree( text: &str, ) -> Option>> where - SpanData: Span, + SpanData: Copy + fmt::Debug, Ctx: Copy, { let lexed = parser::LexedStr::new(text); @@ -175,7 +171,7 @@ where /// Convert a string to a `TokenTree`. The passed span will be used for all spans of the produced subtree. pub fn parse_to_token_tree_static_span(span: S, text: &str) -> Option> where - S: Span, + S: Copy + fmt::Debug, { let lexed = parser::LexedStr::new(text); if lexed.errors().next().is_some() { @@ -186,11 +182,10 @@ where } /// Split token tree with separate expr: $($e:expr)SEP* -pub fn parse_exprs_with_sep( - tt: &tt::Subtree, - sep: char, - span: S, -) -> Vec> { +pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char, span: S) -> Vec> +where + S: Copy + fmt::Debug, +{ if tt.token_trees.is_empty() { return Vec::new(); } @@ -226,7 +221,8 @@ pub fn parse_exprs_with_sep( fn convert_tokens(conv: &mut C) -> tt::Subtree where C: TokenConverter, - S: Span, + S: Copy + fmt::Debug, + C::Token: fmt::Debug, { let entry = tt::SubtreeBuilder { delimiter: tt::Delimiter::invisible_spanned(conv.call_site()), @@ -485,7 +481,7 @@ struct StaticRawConverter<'a, S> { span: S, } -trait SrcToken: std::fmt::Debug { +trait SrcToken { fn kind(&self, ctx: &Ctx) -> SyntaxKind; fn to_char(&self, ctx: &Ctx) -> Option; @@ -525,7 +521,7 @@ impl SrcToken, S> for usize { } } -impl SrcToken, S> for usize { +impl SrcToken, S> for usize { fn kind(&self, ctx: &StaticRawConverter<'_, S>) -> SyntaxKind { ctx.lexed.kind(*self) } @@ -541,7 +537,7 @@ impl SrcToken, S> for usize { impl TokenConverter> for RawConverter<'_, Ctx> where - SpanData: Span, + SpanData: Copy, { type Token = usize; @@ -584,7 +580,7 @@ where impl TokenConverter for StaticRawConverter<'_, S> where - S: Span, + S: Copy, { type Token = usize; @@ -709,7 +705,7 @@ impl SynToken { } } -impl SrcToken, S> for SynToken { +impl SrcToken, S> for SynToken { fn kind(&self, _ctx: &Converter) -> SyntaxKind { match self { SynToken::Ordinary(token) => token.kind(), @@ -748,7 +744,7 @@ impl SrcToken, S> for SynToke impl TokenConverter for Converter where - S: Span, + S: Copy, SpanMap: SpanMapper, { type Token = SynToken; @@ -828,7 +824,7 @@ where struct TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Copy, { buf: String, cursor: Cursor<'a, SpanData>, @@ -839,7 +835,7 @@ where impl<'a, Ctx> TtTreeSink<'a, Ctx> where - SpanData: Span, + SpanData: Copy, { fn new(cursor: Cursor<'a, SpanData>) -> Self { TtTreeSink { @@ -871,7 +867,7 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> { impl TtTreeSink<'_, Ctx> where - SpanData: Span, + SpanData: Copy, { /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween. /// This occurs when a float literal is used as a field access. diff --git a/crates/mbe/src/to_parser_input.rs b/crates/mbe/src/to_parser_input.rs index 00a14f04686..3f70149aa5e 100644 --- a/crates/mbe/src/to_parser_input.rs +++ b/crates/mbe/src/to_parser_input.rs @@ -1,11 +1,13 @@ //! Convert macro-by-example tokens which are specific to macro expansion into a //! format that works for our parser. +use std::fmt; + use syntax::{SyntaxKind, SyntaxKind::*, T}; -use tt::{buffer::TokenBuffer, Span}; +use tt::buffer::TokenBuffer; -pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input { +pub(crate) fn to_parser_input(buffer: &TokenBuffer<'_, S>) -> parser::Input { let mut res = parser::Input::default(); let mut current = buffer.begin(); diff --git a/crates/mbe/src/tt_iter.rs b/crates/mbe/src/tt_iter.rs index f9913cb6f9b..e3d12d87078 100644 --- a/crates/mbe/src/tt_iter.rs +++ b/crates/mbe/src/tt_iter.rs @@ -1,9 +1,10 @@ //! A "Parser" structure for token trees. We use this when parsing a declarative //! macro definition into a list of patterns and templates. +use core::fmt; + use smallvec::{smallvec, SmallVec}; use syntax::SyntaxKind; -use tt::Span; use crate::{to_parser_input::to_parser_input, ExpandError, ExpandResult}; @@ -12,7 +13,7 @@ pub(crate) struct TtIter<'a, S> { pub(crate) inner: std::slice::Iter<'a, tt::TokenTree>, } -impl<'a, S: Span> TtIter<'a, S> { +impl<'a, S: Copy> TtIter<'a, S> { pub(crate) fn new(subtree: &'a tt::Subtree) -> TtIter<'a, S> { TtIter { inner: subtree.token_trees.iter() } } @@ -130,7 +131,12 @@ impl<'a, S: Span> TtIter<'a, S> { _ => Ok(smallvec![first]), } } + pub(crate) fn peek_n(&self, n: usize) -> Option<&'a tt::TokenTree> { + self.inner.as_slice().get(n) + } +} +impl<'a, S: Copy + fmt::Debug> TtIter<'a, S> { pub(crate) fn expect_fragment( &mut self, entry_point: parser::PrefixEntryPoint, @@ -185,10 +191,6 @@ impl<'a, S: Span> TtIter<'a, S> { }; ExpandResult { value: res, err } } - - pub(crate) fn peek_n(&self, n: usize) -> Option<&'a tt::TokenTree> { - self.inner.as_slice().get(n) - } } impl<'a, S> Iterator for TtIter<'a, S> { diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 72848a1f2b7..54ed5f0ba23 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -490,6 +490,18 @@ fn match_expr(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, MATCH_EXPR) } +// test_err match_arms_recovery +// fn foo() { +// match () { +// _ => (),, +// _ => , +// _ => (), +// => (), +// if true => (), +// _ => (), +// () if => (), +// } +// } pub(crate) fn match_arm_list(p: &mut Parser<'_>) { assert!(p.at(T!['{'])); let m = p.start(); @@ -511,6 +523,10 @@ pub(crate) fn match_arm_list(p: &mut Parser<'_>) { error_block(p, "expected match arm"); continue; } + if p.at(T![,]) { + p.err_and_bump("expected pattern"); + continue; + } match_arm(p); } p.expect(T!['}']); @@ -544,26 +560,30 @@ fn match_arm(p: &mut Parser<'_>) { // } attributes::outer_attrs(p); - patterns::pattern_top_r(p, TokenSet::EMPTY); + patterns::pattern_top_r(p, TokenSet::new(&[T![=], T![if]])); if p.at(T![if]) { match_guard(p); } p.expect(T![=>]); - let blocklike = match expr_stmt(p, None) { - Some((_, blocklike)) => blocklike, - None => BlockLike::NotBlock, - }; + if p.eat(T![,]) { + p.error("expected expression"); + } else { + let blocklike = match expr_stmt(p, None) { + Some((_, blocklike)) => blocklike, + None => BlockLike::NotBlock, + }; - // test match_arms_commas - // fn foo() { - // match () { - // _ => (), - // _ => {} - // _ => () - // } - // } - if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { - p.error("expected `,`"); + // test match_arms_commas + // fn foo() { + // match () { + // _ => (), + // _ => {} + // _ => () + // } + // } + if !p.eat(T![,]) && !blocklike.is_block() && !p.at(T!['}']) { + p.error("expected `,`"); + } } m.complete(p, MATCH_ARM); } @@ -579,7 +599,11 @@ fn match_guard(p: &mut Parser<'_>) -> CompletedMarker { assert!(p.at(T![if])); let m = p.start(); p.bump(T![if]); - expr(p); + if p.at(T![=]) { + p.error("expected expression"); + } else { + expr(p); + } m.complete(p, MATCH_GUARD) } diff --git a/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast new file mode 100644 index 00000000000..5b191945e45 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rast @@ -0,0 +1,113 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + ERROR + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + TUPLE_PAT + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 42: expected pattern +error 58: expected expression +error 85: expected pattern +error 100: expected pattern +error 145: expected expression diff --git a/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs new file mode 100644 index 00000000000..173103b2e37 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0034_match_arms_recovery.rs @@ -0,0 +1,11 @@ +fn foo() { + match () { + _ => (),, + _ => , + _ => (), + => (), + if true => (), + _ => (), + () if => (), + } +} diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml index 3d8752b5a82..59a4ad9a255 100644 --- a/crates/paths/Cargo.toml +++ b/crates/paths/Cargo.toml @@ -12,10 +12,14 @@ rust-version.workspace = true doctest = false [dependencies] +camino.workspace = true # Adding this dep sadly puts a lot of rust-analyzer crates after the # serde-derive crate. Even though we don't activate the derive feature here, # someone else in the crate graph certainly does! # serde.workspace = true +[features] +serde1 = ["camino/serde1"] + [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index a63d251c20d..2d3653401d2 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -1,4 +1,4 @@ -//! Thin wrappers around `std::path`, distinguishing between absolute and +//! Thin wrappers around `std::path`/`camino::path`, distinguishing between absolute and //! relative paths. #![warn(rust_2018_idioms, unused_lifetimes)] @@ -7,16 +7,24 @@ use std::{ borrow::Borrow, ffi::OsStr, fmt, ops, - path::{Component, Path, PathBuf}, + path::{Path, PathBuf}, }; -/// Wrapper around an absolute [`PathBuf`]. -#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct AbsPathBuf(PathBuf); +pub use camino::*; + +/// Wrapper around an absolute [`Utf8PathBuf`]. +#[derive(Debug, Clone, Ord, PartialOrd, Eq, Hash)] +pub struct AbsPathBuf(Utf8PathBuf); + +impl From for Utf8PathBuf { + fn from(AbsPathBuf(path_buf): AbsPathBuf) -> Utf8PathBuf { + path_buf + } +} impl From for PathBuf { fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf { - path_buf + path_buf.into() } } @@ -27,9 +35,21 @@ impl ops::Deref for AbsPathBuf { } } +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &Utf8Path { + self.0.as_path() + } +} + +impl AsRef for AbsPathBuf { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() + } +} + impl AsRef for AbsPathBuf { fn as_ref(&self) -> &Path { - self.0.as_path() + self.0.as_ref() } } @@ -45,9 +65,9 @@ impl Borrow for AbsPathBuf { } } -impl TryFrom for AbsPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result { +impl TryFrom for AbsPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result { if !path_buf.is_absolute() { return Err(path_buf); } @@ -55,16 +75,26 @@ impl TryFrom for AbsPathBuf { } } -impl TryFrom<&str> for AbsPathBuf { +impl TryFrom for AbsPathBuf { type Error = PathBuf; - fn try_from(path: &str) -> Result { - AbsPathBuf::try_from(PathBuf::from(path)) + fn try_from(path_buf: PathBuf) -> Result { + if !path_buf.is_absolute() { + return Err(path_buf); + } + Ok(AbsPathBuf(Utf8PathBuf::from_path_buf(path_buf)?)) } } -impl PartialEq for AbsPathBuf { - fn eq(&self, other: &AbsPath) -> bool { - self.as_path() == other +impl TryFrom<&str> for AbsPathBuf { + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result { + AbsPathBuf::try_from(Utf8PathBuf::from(path)) + } +} + +impl + ?Sized> PartialEq

for AbsPathBuf { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() } } @@ -74,19 +104,31 @@ impl AbsPathBuf { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: PathBuf) -> AbsPathBuf { + pub fn assert(path: Utf8PathBuf) -> AbsPathBuf { AbsPathBuf::try_from(path) - .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) + .unwrap_or_else(|path| panic!("expected absolute path, got {}", path)) + } + + /// Wrap the given absolute path in `AbsPathBuf` + /// + /// # Panics + /// + /// Panics if `path` is not absolute. + pub fn assert_utf8(path: PathBuf) -> AbsPathBuf { + AbsPathBuf::assert( + Utf8PathBuf::from_path_buf(path) + .unwrap_or_else(|path| panic!("expected utf8 path, got {}", path.display())), + ) } /// Coerces to an `AbsPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `AbsPathBuf`. pub fn as_path(&self) -> &AbsPath { AbsPath::assert(self.0.as_path()) } - /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. + /// Equivalent of [`Utf8PathBuf::pop`] for `AbsPathBuf`. /// /// Note that this won't remove the root component, so `self` will still be /// absolute. @@ -97,18 +139,36 @@ impl AbsPathBuf { impl fmt::Display for AbsPathBuf { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around an absolute [`Path`]. -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] +/// Wrapper around an absolute [`Utf8Path`]. +#[derive(Debug, Ord, PartialOrd, Eq, Hash)] #[repr(transparent)] -pub struct AbsPath(Path); +pub struct AbsPath(Utf8Path); + +impl + ?Sized> PartialEq

for AbsPath { + fn eq(&self, other: &P) -> bool { + self.0.as_std_path() == other.as_ref() + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef for AbsPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() + } +} + +impl AsRef for AbsPath { + fn as_ref(&self) -> &OsStr { + self.0.as_ref() } } @@ -120,9 +180,9 @@ impl ToOwned for AbsPath { } } -impl<'a> TryFrom<&'a Path> for &'a AbsPath { - type Error = &'a Path; - fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { +impl<'a> TryFrom<&'a Utf8Path> for &'a AbsPath { + type Error = &'a Utf8Path; + fn try_from(path: &'a Utf8Path) -> Result<&'a AbsPath, &'a Utf8Path> { if !path.is_absolute() { return Err(path); } @@ -136,24 +196,24 @@ impl AbsPath { /// # Panics /// /// Panics if `path` is not absolute. - pub fn assert(path: &Path) -> &AbsPath { + pub fn assert(path: &Utf8Path) -> &AbsPath { assert!(path.is_absolute()); - unsafe { &*(path as *const Path as *const AbsPath) } + unsafe { &*(path as *const Utf8Path as *const AbsPath) } } - /// Equivalent of [`Path::parent`] for `AbsPath`. + /// Equivalent of [`Utf8Path::parent`] for `AbsPath`. pub fn parent(&self) -> Option<&AbsPath> { self.0.parent().map(AbsPath::assert) } - /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards. - pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { + /// Equivalent of [`Utf8Path::join`] for `AbsPath` with an additional normalize step afterwards. + pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { self.join(path).normalize() } - /// Equivalent of [`Path::join`] for `AbsPath`. - pub fn join(&self, path: impl AsRef) -> AbsPathBuf { - self.as_ref().join(path).try_into().unwrap() + /// Equivalent of [`Utf8Path::join`] for `AbsPath`. + pub fn join(&self, path: impl AsRef) -> AbsPathBuf { + Utf8Path::join(self.as_ref(), path).try_into().unwrap() } /// Normalize the given path: @@ -172,7 +232,7 @@ impl AbsPath { AbsPathBuf(normalize_path(&self.0)) } - /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `AbsPath`. pub fn to_path_buf(&self) -> AbsPathBuf { AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() } @@ -181,7 +241,7 @@ impl AbsPath { panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430") } - /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. + /// Equivalent of [`Utf8Path::strip_prefix`] for `AbsPath`. /// /// Returns a relative path. pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { @@ -195,57 +255,61 @@ impl AbsPath { } pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { - Some(( - self.file_stem()?.to_str()?, - self.extension().and_then(|extension| extension.to_str()), - )) + Some((self.file_stem()?, self.extension())) } // region:delegate-methods - // Note that we deliberately don't implement `Deref` here. + // Note that we deliberately don't implement `Deref` here. // - // The problem with `Path` is that it directly exposes convenience IO-ing - // methods. For example, `Path::exists` delegates to `fs::metadata`. + // The problem with `Utf8Path` is that it directly exposes convenience IO-ing + // methods. For example, `Utf8Path::exists` delegates to `fs::metadata`. // // For `AbsPath`, we want to make sure that this is a POD type, and that all // IO goes via `fs`. That way, it becomes easier to mock IO when we need it. - pub fn file_name(&self) -> Option<&OsStr> { + pub fn file_name(&self) -> Option<&str> { self.0.file_name() } - pub fn extension(&self) -> Option<&OsStr> { + pub fn extension(&self) -> Option<&str> { self.0.extension() } - pub fn file_stem(&self) -> Option<&OsStr> { + pub fn file_stem(&self) -> Option<&str> { self.0.file_stem() } pub fn as_os_str(&self) -> &OsStr { self.0.as_os_str() } + pub fn as_str(&self) -> &str { + self.0.as_str() + } #[deprecated(note = "use Display instead")] - pub fn display(&self) -> std::path::Display<'_> { - self.0.display() + pub fn display(&self) -> ! { + unimplemented!() } #[deprecated(note = "use std::fs::metadata().is_ok() instead")] - pub fn exists(&self) -> bool { - self.0.exists() + pub fn exists(&self) -> ! { + unimplemented!() + } + + pub fn components(&self) -> Utf8Components<'_> { + self.0.components() } // endregion:delegate-methods } impl fmt::Display for AbsPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0.display(), f) + fmt::Display::fmt(&self.0, f) } } -/// Wrapper around a relative [`PathBuf`]. +/// Wrapper around a relative [`Utf8PathBuf`]. #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct RelPathBuf(PathBuf); +pub struct RelPathBuf(Utf8PathBuf); -impl From for PathBuf { - fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf { +impl From for Utf8PathBuf { + fn from(RelPathBuf(path_buf): RelPathBuf) -> Utf8PathBuf { path_buf } } @@ -257,15 +321,21 @@ impl ops::Deref for RelPathBuf { } } -impl AsRef for RelPathBuf { - fn as_ref(&self) -> &Path { +impl AsRef for RelPathBuf { + fn as_ref(&self) -> &Utf8Path { self.0.as_path() } } -impl TryFrom for RelPathBuf { - type Error = PathBuf; - fn try_from(path_buf: PathBuf) -> Result { +impl AsRef for RelPathBuf { + fn as_ref(&self) -> &Path { + self.0.as_ref() + } +} + +impl TryFrom for RelPathBuf { + type Error = Utf8PathBuf; + fn try_from(path_buf: Utf8PathBuf) -> Result { if !path_buf.is_relative() { return Err(path_buf); } @@ -274,65 +344,79 @@ impl TryFrom for RelPathBuf { } impl TryFrom<&str> for RelPathBuf { - type Error = PathBuf; - fn try_from(path: &str) -> Result { - RelPathBuf::try_from(PathBuf::from(path)) + type Error = Utf8PathBuf; + fn try_from(path: &str) -> Result { + RelPathBuf::try_from(Utf8PathBuf::from(path)) } } impl RelPathBuf { /// Coerces to a `RelPath` slice. /// - /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. + /// Equivalent of [`Utf8PathBuf::as_path`] for `RelPathBuf`. pub fn as_path(&self) -> &RelPath { RelPath::new_unchecked(self.0.as_path()) } } -/// Wrapper around a relative [`Path`]. +/// Wrapper around a relative [`Utf8Path`]. #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] #[repr(transparent)] -pub struct RelPath(Path); +pub struct RelPath(Utf8Path); + +impl AsRef for RelPath { + fn as_ref(&self) -> &Utf8Path { + &self.0 + } +} impl AsRef for RelPath { fn as_ref(&self) -> &Path { - &self.0 + self.0.as_ref() } } impl RelPath { /// Creates a new `RelPath` from `path`, without checking if it is relative. - pub fn new_unchecked(path: &Path) -> &RelPath { - unsafe { &*(path as *const Path as *const RelPath) } + pub fn new_unchecked(path: &Utf8Path) -> &RelPath { + unsafe { &*(path as *const Utf8Path as *const RelPath) } } - /// Equivalent of [`Path::to_path_buf`] for `RelPath`. + /// Equivalent of [`Utf8Path::to_path_buf`] for `RelPath`. pub fn to_path_buf(&self) -> RelPathBuf { RelPathBuf::try_from(self.0.to_path_buf()).unwrap() } + + pub fn as_utf8_path(&self) -> &Utf8Path { + self.as_ref() + } + + pub fn as_str(&self) -> &str { + self.0.as_str() + } } /// Taken from -fn normalize_path(path: &Path) -> PathBuf { +fn normalize_path(path: &Utf8Path) -> Utf8PathBuf { let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() { + let mut ret = if let Some(c @ Utf8Component::Prefix(..)) = components.peek().copied() { components.next(); - PathBuf::from(c.as_os_str()) + Utf8PathBuf::from(c.as_str()) } else { - PathBuf::new() + Utf8PathBuf::new() }; for component in components { match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); + Utf8Component::Prefix(..) => unreachable!(), + Utf8Component::RootDir => { + ret.push(component.as_str()); } - Component::CurDir => {} - Component::ParentDir => { + Utf8Component::CurDir => {} + Utf8Component::ParentDir => { ret.pop(); } - Component::Normal(c) => { + Utf8Component::Normal(c) => { ret.push(c); } } diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 978ad155609..f30f6a0f23b 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -23,7 +23,7 @@ snap = "1.1.0" indexmap = "2.1.0" # local deps -paths.workspace = true +paths = { workspace = true, features = ["serde1"] } tt.workspace = true stdx.workspace = true text-size.workspace = true diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 6b16711a8d8..fd491644648 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -35,8 +35,11 @@ pub use version::{read_dylib_info, read_version, RustCInfo}; #[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] pub enum ProcMacroKind { CustomDerive, - FuncLike, Attr, + // This used to be called FuncLike, so that's what the server expects currently. + #[serde(alias = "bang")] + #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))] + Bang, } /// A handle to an external process which load dylibs with macros (.so or .dll) diff --git a/crates/proc-macro-api/src/msg.rs b/crates/proc-macro-api/src/msg.rs index aa5aff455fd..ad0e1f187b6 100644 --- a/crates/proc-macro-api/src/msg.rs +++ b/crates/proc-macro-api/src/msg.rs @@ -1,11 +1,9 @@ //! Defines messages for cross-process message passing based on `ndjson` wire protocol pub(crate) mod flat; -use std::{ - io::{self, BufRead, Write}, - path::PathBuf, -}; +use std::io::{self, BufRead, Write}; +use paths::Utf8PathBuf; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::ProcMacroKind; @@ -27,7 +25,7 @@ pub const CURRENT_API_VERSION: u32 = RUST_ANALYZER_SPAN_SUPPORT; #[derive(Debug, Serialize, Deserialize)] pub enum Request { /// Since [`NO_VERSION_CHECK_VERSION`] - ListMacros { dylib_path: PathBuf }, + ListMacros { dylib_path: Utf8PathBuf }, /// Since [`NO_VERSION_CHECK_VERSION`] ExpandMacro(Box), /// Since [`VERSION_CHECK_VERSION`] @@ -89,7 +87,7 @@ pub struct ExpandMacro { /// Possible attributes for the attribute-like macros. pub attributes: Option, - pub lib: PathBuf, + pub lib: Utf8PathBuf, /// Environment variables to set during macro expansion. pub env: Vec<(String, String)>, @@ -273,7 +271,7 @@ mod tests { macro_body: FlatTree::new(&tt, CURRENT_API_VERSION, &mut span_data_table), macro_name: Default::default(), attributes: None, - lib: std::env::current_dir().unwrap(), + lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(), env: Default::default(), current_dir: Default::default(), has_global_spans: ExpnGlobals { diff --git a/crates/proc-macro-api/src/msg/flat.rs b/crates/proc-macro-api/src/msg/flat.rs index caf9e237fdd..99cdbd930e1 100644 --- a/crates/proc-macro-api/src/msg/flat.rs +++ b/crates/proc-macro-api/src/msg/flat.rs @@ -88,8 +88,6 @@ impl std::fmt::Debug for TokenId { } } -impl tt::Span for TokenId {} - #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { subtree: Vec, diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 72f95643c8b..35d48a15543 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -175,7 +175,7 @@ fn mk_child( env: &FxHashMap, null_stderr: bool, ) -> io::Result { - let mut cmd = Command::new(path.as_os_str()); + let mut cmd = Command::new(path); cmd.envs(env) .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) @@ -183,7 +183,7 @@ fn mk_child( .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() }); if cfg!(windows) { let mut path_var = std::ffi::OsString::new(); - path_var.push(path.parent().unwrap().parent().unwrap().as_os_str()); + path_var.push(path.parent().unwrap().parent().unwrap()); path_var.push("\\bin;"); path_var.push(std::env::var_os("PATH").unwrap_or_default()); cmd.env("PATH", path_var); diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index 52b4cced5f5..22c34ff1678 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -1,16 +1,11 @@ //! Handles dynamic library loading for proc macro -use std::{ - fmt, - fs::File, - io, - path::{Path, PathBuf}, -}; +use std::{fmt, fs::File, io}; use libloading::Library; use memmap2::Mmap; use object::Object; -use paths::AbsPath; +use paths::{AbsPath, Utf8Path, Utf8PathBuf}; use proc_macro::bridge; use proc_macro_api::{read_dylib_info, ProcMacroKind}; @@ -26,7 +21,7 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool { symbol.contains(NEW_REGISTRAR_SYMBOL) } -fn find_registrar_symbol(file: &Path) -> io::Result> { +fn find_registrar_symbol(file: &Utf8Path) -> io::Result> { let file = File::open(file)?; let buffer = unsafe { Mmap::map(&file)? }; @@ -62,12 +57,12 @@ fn find_registrar_symbol(file: &Path) -> io::Result> { /// /// It seems that on Windows that behaviour is default, so we do nothing in that case. #[cfg(windows)] -fn load_library(file: &Path) -> Result { +fn load_library(file: &Utf8Path) -> Result { unsafe { Library::new(file) } } #[cfg(unix)] -fn load_library(file: &Path) -> Result { +fn load_library(file: &Utf8Path) -> Result { use libloading::os::unix::Library as UnixLibrary; use std::os::raw::c_int; @@ -116,14 +111,14 @@ struct ProcMacroLibraryLibloading { } impl ProcMacroLibraryLibloading { - fn open(file: &Path) -> Result { + fn open(file: &Utf8Path) -> Result { let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| { - invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display())) + invalid_data_err(format!("Cannot find registrar symbol in file {file}")) })?; - let abs_file: &AbsPath = file.try_into().map_err(|_| { - invalid_data_err(format!("expected an absolute path, got {}", file.display())) - })?; + let abs_file: &AbsPath = file + .try_into() + .map_err(|_| invalid_data_err(format!("expected an absolute path, got {file}")))?; let version_info = read_dylib_info(abs_file)?; let lib = load_library(file).map_err(invalid_data_err)?; @@ -138,10 +133,10 @@ pub struct Expander { } impl Expander { - pub fn new(lib: &Path) -> Result { + pub fn new(lib: &Utf8Path) -> Result { // Some libraries for dynamic loading require canonicalized path even when it is // already absolute - let lib = lib.canonicalize()?; + let lib = lib.canonicalize_utf8()?; let lib = ensure_file_with_lock_free_access(&lib)?; @@ -176,30 +171,26 @@ impl Expander { /// Copy the dylib to temp directory to prevent locking in Windows #[cfg(windows)] -fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { +fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { use std::collections::hash_map::RandomState; - use std::ffi::OsString; use std::hash::{BuildHasher, Hasher}; if std::env::var("RA_DONT_COPY_PROC_MACRO_DLL").is_ok() { return Ok(path.to_path_buf()); } - let mut to = std::env::temp_dir(); + let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap(); let file_name = path.file_name().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidInput, - format!("File path is invalid: {}", path.display()), - ) + io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}")) })?; // Generate a unique number by abusing `HashMap`'s hasher. // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd. let t = RandomState::new().build_hasher().finish(); - let mut unique_name = OsString::from(t.to_string()); - unique_name.push(file_name); + let mut unique_name = t.to_string(); + unique_name.push_str(file_name); to.push(unique_name); std::fs::copy(path, &to).unwrap(); @@ -207,6 +198,6 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { } #[cfg(unix)] -fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { - Ok(path.to_path_buf()) +fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result { + Ok(path.to_owned()) } diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 831632c64c0..2472c1e3119 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -33,12 +33,11 @@ use std::{ collections::{hash_map::Entry, HashMap}, env, ffi::OsString, - fs, - path::{Path, PathBuf}, - thread, + fs, thread, time::SystemTime, }; +use paths::{Utf8Path, Utf8PathBuf}; use proc_macro_api::{ msg::{ self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals, @@ -53,7 +52,7 @@ use crate::server::TokenStream; // see `build.rs` include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); -trait ProcMacroSrvSpan: tt::Span { +trait ProcMacroSrvSpan: Copy { type Server: proc_macro::bridge::server::Server>; fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server; } @@ -81,7 +80,7 @@ impl ProcMacroSrvSpan for Span { #[derive(Default)] pub struct ProcMacroSrv { - expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, + expanders: HashMap<(Utf8PathBuf, SystemTime), dylib::Expander>, span_mode: SpanMode, } @@ -149,23 +148,22 @@ impl ProcMacroSrv { pub fn list_macros( &mut self, - dylib_path: &Path, + dylib_path: &Utf8Path, ) -> Result, String> { let expander = self.expander(dylib_path)?; Ok(expander.list_macros()) } - fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> { + fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> { let time = fs::metadata(path) .and_then(|it| it.modified()) - .map_err(|err| format!("Failed to get file metadata for {}: {err}", path.display()))?; + .map_err(|err| format!("Failed to get file metadata for {path}: {err}",))?; Ok(match self.expanders.entry((path.to_path_buf(), time)) { - Entry::Vacant(v) => { - v.insert(dylib::Expander::new(path).map_err(|err| { - format!("Cannot create expander for {}: {err}", path.display()) - })?) - } + Entry::Vacant(v) => v.insert( + dylib::Expander::new(path) + .map_err(|err| format!("Cannot create expander for {path}: {err}",))?, + ), Entry::Occupied(e) => e.into_mut(), }) } @@ -306,6 +304,6 @@ impl Drop for EnvSnapshot { mod tests; #[cfg(test)] -pub fn proc_macro_test_dylib_path() -> std::path::PathBuf { +pub fn proc_macro_test_dylib_path() -> paths::Utf8PathBuf { proc_macro_test::PROC_MACRO_TEST_LOCATION.into() } diff --git a/crates/proc-macro-srv/src/proc_macros.rs b/crates/proc-macro-srv/src/proc_macros.rs index 686d5b0438a..631fd84aa24 100644 --- a/crates/proc-macro-srv/src/proc_macros.rs +++ b/crates/proc-macro-srv/src/proc_macros.rs @@ -108,7 +108,7 @@ impl ProcMacros { (trait_name.to_string(), ProcMacroKind::CustomDerive) } bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) + (name.to_string(), ProcMacroKind::Bang) } bridge::client::ProcMacro::Attr { name, .. } => { (name.to_string(), ProcMacroKind::Attr) diff --git a/crates/proc-macro-srv/src/server/token_stream.rs b/crates/proc-macro-srv/src/server/token_stream.rs index 408db60e872..b1a448427c6 100644 --- a/crates/proc-macro-srv/src/server/token_stream.rs +++ b/crates/proc-macro-srv/src/server/token_stream.rs @@ -101,6 +101,8 @@ pub(super) struct TokenStreamBuilder { /// pub(super)lic implementation details for the `TokenStream` type, such as iterators. pub(super) mod token_stream { + use core::fmt; + use super::{TokenStream, TokenTree}; /// An iterator over `TokenStream`'s `TokenTree`s. @@ -122,7 +124,7 @@ pub(super) mod token_stream { /// /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to /// change these errors into `LexError`s later. - impl TokenStream { + impl TokenStream { pub(crate) fn from_str(src: &str, call_site: S) -> Result, String> { let subtree = mbe::parse_to_token_tree_static_span(call_site, src).ok_or("lexing error")?; diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs index 11b008fc0b4..63342825380 100644 --- a/crates/proc-macro-srv/src/tests/mod.rs +++ b/crates/proc-macro-srv/src/tests/mod.rs @@ -254,14 +254,14 @@ fn list_test_macros() { let res = list().join("\n"); expect![[r#" - fn_like_noop [FuncLike] - fn_like_panic [FuncLike] - fn_like_error [FuncLike] - fn_like_clone_tokens [FuncLike] - fn_like_mk_literals [FuncLike] - fn_like_mk_idents [FuncLike] - fn_like_span_join [FuncLike] - fn_like_span_ops [FuncLike] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_error [Bang] + fn_like_clone_tokens [Bang] + fn_like_mk_literals [Bang] + fn_like_mk_idents [Bang] + fn_like_span_join [Bang] + fn_like_span_ops [Bang] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index 924a4a89e21..097ee1f75cd 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -25,8 +25,9 @@ itertools.workspace = true # local deps base-db.workspace = true +span.workspace = true cfg.workspace = true -paths.workspace = true +paths = { workspace = true, features = ["serde1"] } stdx.workspace = true toolchain.workspace = true diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index 709fc037174..d40eb26063d 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -77,7 +77,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml").as_os_str()); + cmd.arg(workspace_root.join("Cargo.toml")); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -354,16 +354,11 @@ impl WorkspaceBuildScripts { } // cargo_metadata crate returns default (empty) path for // older cargos, which is not absolute, so work around that. - let out_dir = mem::take(&mut message.out_dir).into_os_string(); - if !out_dir.is_empty() { - let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir)); + let out_dir = mem::take(&mut message.out_dir); + if !out_dir.as_str().is_empty() { + let out_dir = AbsPathBuf::assert(out_dir); // inject_cargo_env(package, package_build_data); - // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() - if let Some(out_dir) = - out_dir.as_os_str().to_str().map(|s| s.to_owned()) - { - data.envs.push(("OUT_DIR".to_owned(), out_dir)); - } + data.envs.push(("OUT_DIR".to_owned(), out_dir.as_str().to_owned())); data.out_dir = Some(out_dir); data.cfgs = cfgs; } @@ -377,8 +372,8 @@ impl WorkspaceBuildScripts { if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) { - let filename = AbsPathBuf::assert(PathBuf::from(&filename)); - data.proc_macro_dylib_path = Some(filename); + let filename = AbsPath::assert(filename); + data.proc_macro_dylib_path = Some(filename.to_owned()); } } }); diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 53b41ea1e87..51c1b094f7b 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -1,17 +1,16 @@ //! See [`CargoWorkspace`]. use std::ops; -use std::path::PathBuf; use std::str::from_utf8; use anyhow::Context; -use base_db::Edition; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; use serde_json::from_value; +use span::Edition; use toolchain::Tool; use crate::{utf8_stdout, InvocationLocation, ManifestPath, Sysroot}; @@ -100,7 +99,7 @@ pub struct CargoConfig { pub invocation_strategy: InvocationStrategy, pub invocation_location: InvocationLocation, /// Optional path to use instead of `target` when building - pub target_dir: Option, + pub target_dir: Option, } pub type Package = Idx; @@ -262,7 +261,7 @@ impl CargoWorkspace { } } } - meta.current_dir(current_dir.as_os_str()); + meta.current_dir(current_dir); let mut other_options = vec![]; // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually @@ -351,7 +350,7 @@ impl CargoWorkspace { id: id.repr.clone(), name, version, - manifest: AbsPathBuf::assert(manifest_path.into()).try_into().unwrap(), + manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(), targets: Vec::new(), is_local, is_member, @@ -370,7 +369,7 @@ impl CargoWorkspace { let tgt = targets.alloc(TargetData { package: pkg, name, - root: AbsPathBuf::assert(src_path.into()), + root: AbsPathBuf::assert(src_path), kind: TargetKind::new(&kind), required_features, }); @@ -393,11 +392,9 @@ impl CargoWorkspace { packages[source].active_features.extend(node.features); } - let workspace_root = - AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string())); + let workspace_root = AbsPathBuf::assert(meta.workspace_root); - let target_directory = - AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string())); + let target_directory = AbsPathBuf::assert(meta.target_directory); CargoWorkspace { packages, targets, workspace_root, target_directory } } @@ -409,7 +406,7 @@ impl CargoWorkspace { pub fn target_by_root(&self, root: &AbsPath) -> Option { self.packages() .filter(|&pkg| self[pkg].is_member) - .find_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root)) + .find_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) .copied() } diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 5b91f5d8058..28696aa3277 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -127,8 +127,8 @@ impl ProjectManifest { .filter_map(Result::ok) .map(|it| it.path().join("Cargo.toml")) .filter(|it| it.exists()) - .map(AbsPathBuf::assert) - .filter_map(|it| it.try_into().ok()) + .map(AbsPathBuf::try_from) + .filter_map(|it| it.ok()?.try_into().ok()) .collect() } } diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index fba0aaa8ce9..512588cc8f8 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -49,12 +49,12 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition}; +use base_db::{CrateDisplayName, CrateId, CrateName, Dependency}; use la_arena::RawIdx; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::{de, Deserialize}; -use std::path::PathBuf; +use span::Edition; use crate::cfg_flag::CfgFlag; @@ -113,7 +113,7 @@ impl ProjectJson { .unwrap_or_else(|| root_module.starts_with(base)); let (include, exclude) = match crate_data.source { Some(src) => { - let absolutize = |dirs: Vec| { + let absolutize = |dirs: Vec| { dirs.into_iter().map(absolutize_on_base).collect::>() }; (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) @@ -176,15 +176,15 @@ impl ProjectJson { #[derive(Deserialize, Debug, Clone)] pub struct ProjectJsonData { - sysroot: Option, - sysroot_src: Option, + sysroot: Option, + sysroot_src: Option, crates: Vec, } #[derive(Deserialize, Debug, Clone)] struct CrateData { display_name: Option, - root_module: PathBuf, + root_module: Utf8PathBuf, edition: EditionData, #[serde(default)] version: Option, @@ -194,7 +194,7 @@ struct CrateData { target: Option, #[serde(default)] env: FxHashMap, - proc_macro_dylib_path: Option, + proc_macro_dylib_path: Option, is_workspace_member: Option, source: Option, #[serde(default)] @@ -238,8 +238,8 @@ struct DepData { #[derive(Deserialize, Debug, Clone)] struct CrateSource { - include_dirs: Vec, - exclude_dirs: Vec, + include_dirs: Vec, + exclude_dirs: Vec, } fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 3127bae8b0c..1142d6243d2 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -4,13 +4,13 @@ //! but we can't process `.rlib` and need source code instead. The source code //! is typically installed with `rustup component add rust-src` command. -use std::{env, fs, iter, ops, path::PathBuf, process::Command, sync::Arc}; +use std::{env, fs, iter, ops, process::Command, sync::Arc}; use anyhow::{format_err, Result}; use base_db::CrateName; use itertools::Itertools; use la_arena::{Arena, Idx}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use toolchain::{probe_for_binary, Tool}; @@ -419,7 +419,7 @@ fn discover_sysroot_dir( rustc.current_dir(current_dir).args(["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); let stdout = utf8_stdout(rustc)?; - Ok(AbsPathBuf::assert(PathBuf::from(stdout))) + Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout))) } fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index b9b1b701f6d..fc0b507b332 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -1,12 +1,9 @@ -use std::{ - ops::Deref, - path::{Path, PathBuf}, -}; +use std::ops::Deref; use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; use expect_test::{expect_file, ExpectFile}; -use paths::{AbsPath, AbsPathBuf}; +use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use triomphe::Arc; @@ -113,17 +110,16 @@ fn replace_root(s: &mut String, direction: bool) { fn replace_fake_sys_root(s: &mut String) { let fake_sysroot_path = get_test_path("fake-sysroot"); let fake_sysroot_path = if cfg!(windows) { - let normalized_path = - fake_sysroot_path.to_str().expect("expected str").replace('\\', r#"\\"#); + let normalized_path = fake_sysroot_path.as_str().replace('\\', r#"\\"#); format!(r#"{}\\"#, normalized_path) } else { - format!("{}/", fake_sysroot_path.to_str().expect("expected str")) + format!("{}/", fake_sysroot_path.as_str()) }; *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$") } -fn get_test_path(file: &str) -> PathBuf { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); +fn get_test_path(file: &str) -> Utf8PathBuf { + let base = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")); base.join("test_data").join(file) } @@ -139,7 +135,7 @@ fn get_fake_sysroot() -> Sysroot { fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { let mut root = "$ROOT$".to_owned(); replace_root(&mut root, true); - let path = Path::new(&root); + let path = Utf8Path::new(&root); let base = AbsPath::assert(path); ProjectJson::new(base, data) } @@ -268,7 +264,7 @@ fn smoke_test_real_sysroot_cargo() { let cargo_workspace = CargoWorkspace::new(meta); let sysroot = Ok(Sysroot::discover( - AbsPath::assert(Path::new(env!("CARGO_MANIFEST_DIR"))), + AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))), &Default::default(), true, ) diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 1a138b17bad..b8c5885108d 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -6,13 +6,14 @@ use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync}; use anyhow::{format_err, Context}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId, + LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; +use span::Edition; use stdx::always; use toolchain::Tool; use triomphe::Arc; @@ -718,19 +719,22 @@ impl ProjectWorkspace { ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered(); - let (mut crate_graph, proc_macros) = match self { + let ((mut crate_graph, proc_macros), sysroot) = match self { ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain: _, target_layout: _, - } => project_json_to_crate_graph( - rustc_cfg.clone(), - load, - project, - sysroot.as_ref().ok(), - extra_env, + } => ( + project_json_to_crate_graph( + rustc_cfg.clone(), + load, + project, + sysroot.as_ref().ok(), + extra_env, + ), + sysroot, ), ProjectWorkspace::Cargo { cargo, @@ -742,14 +746,17 @@ impl ProjectWorkspace { toolchain: _, target_layout: _, cargo_config_extra_env: _, - } => cargo_to_crate_graph( - load, - rustc.as_ref().map(|a| a.as_ref()).ok(), - cargo, - sysroot.as_ref().ok(), - rustc_cfg.clone(), - cfg_overrides, - build_scripts, + } => ( + cargo_to_crate_graph( + load, + rustc.as_ref().map(|a| a.as_ref()).ok(), + cargo, + sysroot.as_ref().ok(), + rustc_cfg.clone(), + cfg_overrides, + build_scripts, + ), + sysroot, ), ProjectWorkspace::DetachedFiles { files, @@ -757,11 +764,20 @@ impl ProjectWorkspace { rustc_cfg, toolchain: _, target_layout: _, - } => { - detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot.as_ref().ok()) - } + } => ( + detached_files_to_crate_graph( + rustc_cfg.clone(), + load, + files, + sysroot.as_ref().ok(), + ), + sysroot, + ), }; - if crate_graph.patch_cfg_if() { + + if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Workspace(_))) + && crate_graph.patch_cfg_if() + { tracing::debug!("Patched std to depend on cfg-if") } else { tracing::debug!("Did not patch std to depend on cfg-if") @@ -1077,6 +1093,8 @@ fn cargo_to_crate_graph( } } + let mut delayed_dev_deps = vec![]; + // Now add a dep edge from all targets of upstream to the lib // target of downstream. for pkg in cargo.packages() { @@ -1091,11 +1109,31 @@ fn cargo_to_crate_graph( continue; } + // If the dependency is a dev-dependency with both crates being member libraries of + // the workspace we delay adding the edge. The reason can be read up on in + // https://github.com/rust-lang/rust-analyzer/issues/14167 + // but in short, such an edge is able to cause some form of cycle in the crate graph + // for normal dependencies. If we do run into a cycle like this, we want to prefer + // the non dev-dependency edge, and so the easiest way to do that is by adding the + // dev-dependency edges last. + if dep.kind == DepKind::Dev + && matches!(kind, TargetKind::Lib { .. }) + && cargo[dep.pkg].is_member + && cargo[pkg].is_member + { + delayed_dev_deps.push((from, name.clone(), to)); + continue; + } + add_dep(crate_graph, from, name.clone(), to) } } } + for (from, name, to) in delayed_dev_deps { + add_dep(crate_graph, from, name, to); + } + if has_private { // If the user provided a path to rustc sources, we add all the rustc_private crates // and create dependencies on them for the crates which opt-in to that @@ -1151,7 +1189,6 @@ fn detached_files_to_crate_graph( }; let display_name = detached_file .file_stem() - .and_then(|os_str| os_str.to_str()) .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned())); let detached_file_crate = crate_graph.add_crate_root( file_id, @@ -1228,6 +1265,7 @@ fn handle_rustc_crates( let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else { continue; }; + let pkg_crates = &mut rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new); if let Some(file_id) = load(&rustc_workspace[tgt].root) { let crate_id = add_target_crate_root( crate_graph, @@ -1246,7 +1284,7 @@ fn handle_rustc_crates( if let Some(proc_macro) = libproc_macro { add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro); } - rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); + pkg_crates.push(crate_id); } } } @@ -1533,7 +1571,7 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) { // CARGO_BIN_NAME, CARGO_BIN_EXE_ let manifest_dir = package.manifest.parent(); - env.set("CARGO_MANIFEST_DIR", manifest_dir.as_os_str().to_string_lossy().into_owned()); + env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned()); // Not always right, but works for common cases. env.set("CARGO", "cargo".into()); diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index e6984d6f41b..6d70124188d 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -43,6 +43,7 @@ nohash-hasher.workspace = true always-assert = "0.2.0" walkdir = "2.3.2" semver.workspace = true +memchr = "2.7.1" cfg.workspace = true flycheck.workspace = true @@ -63,7 +64,7 @@ parser.workspace = true toolchain.workspace = true vfs-notify.workspace = true vfs.workspace = true -memchr = "2.7.1" +paths.workspace = true [target.'cfg(windows)'.dependencies] winapi = "0.3.9" diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index e747ec87b1c..78920f3abac 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -190,7 +190,7 @@ fn run_server() -> anyhow::Result<()> { Some(it) => it, None => { let cwd = env::current_dir()?; - AbsPathBuf::assert(cwd) + AbsPathBuf::assert_utf8(cwd) } }; diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 5c474908e7a..fdd77199aa0 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -70,7 +70,7 @@ impl flags::AnalysisStats { let mut db_load_sw = self.stop_watch(); - let path = AbsPathBuf::assert(env::current_dir()?.join(&self.path)); + let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(&self.path)); let manifest = ProjectManifest::discover_single(&path)?; let mut workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; @@ -279,7 +279,8 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &a in adts { - if db.generic_params(a.into()).iter().next().is_some() { + let generic_params = db.generic_params(a.into()); + if generic_params.iter().next().is_some() || generic_params.iter_lt().next().is_some() { // Data types with generics don't have layout. continue; } diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index bd2646126dc..79d6226debf 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -16,7 +16,7 @@ impl flags::Diagnostics { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { - let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(p)); + let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); ProcMacroServerChoice::Explicit(path) } else { ProcMacroServerChoice::Sysroot diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 3f68c5d053b..b3b8ab9a404 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -235,6 +235,7 @@ pub struct RunTests { #[derive(Debug)] pub struct RustcTests { pub rustc_repo: PathBuf, + pub filter: Option, } diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index f3f5ec1ebde..3ff9be7102f 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -283,7 +283,7 @@ impl flags::Lsif { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, }; - let path = AbsPathBuf::assert(env::current_dir()?.join(self.path)); + let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); let manifest = ProjectManifest::discover_single(&path)?; let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 84f2e600874..eeec13a14be 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -76,7 +76,7 @@ impl Tester { ); let workspace = ProjectWorkspace::DetachedFiles { - files: vec![tmp_file.clone()], + files: vec![tmp_file], sysroot, rustc_cfg: vec![], toolchain: None, diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 1061a433a58..aef2c1be224 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -27,7 +27,8 @@ impl flags::Scip { with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, }; - let root = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&self.path)).normalize(); + let root = + vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); let mut config = crate::config::Config::new( root.clone(), @@ -63,12 +64,7 @@ impl flags::Scip { special_fields: Default::default(), }) .into(), - project_root: format!( - "file://{}", - root.as_os_str() - .to_str() - .ok_or(anyhow::format_err!("Unable to normalize project_root path"))? - ), + project_root: format!("file://{root}"), text_document_encoding: scip_types::TextEncoding::UTF8.into(), special_fields: Default::default(), }; @@ -216,7 +212,7 @@ fn get_relative_filepath( rootpath: &vfs::AbsPathBuf, file_id: ide::FileId, ) -> Option { - Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_ref().to_str()?.to_owned()) + Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned()) } // SCIP Ranges have a (very large) optimization that ranges if they are on the same line diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index cbf15246590..7475a8e6e6d 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -7,11 +7,7 @@ //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. -use std::{ - fmt, iter, - ops::Not, - path::{Path, PathBuf}, -}; +use std::{fmt, iter, ops::Not}; use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; @@ -27,6 +23,7 @@ use ide_db::{ }; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; +use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, }; @@ -327,7 +324,7 @@ config_data! { /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may /// also need to add the folders to Code's `files.watcherExclude`. - files_excludeDirs: Vec = "[]", + files_excludeDirs: Vec = "[]", /// Controls file watching implementation. files_watcher: FilesWatcherDef = "\"client\"", @@ -378,6 +375,8 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = "\"both\"", + /// How many fields of a struct to display when hovering a struct. + hover_show_structFields: Option = "null", /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = "null", @@ -516,7 +515,7 @@ config_data! { /// This config takes a map of crate names with the exported proc-macro names to ignore as values. procMacro_ignored: FxHashMap, Box<[Box]>> = "{}", /// Internal config, path to proc-macro server executable. - procMacro_server: Option = "null", + procMacro_server: Option = "null", /// Exclude imports from find-all-references. references_excludeImports: bool = "false", @@ -864,7 +863,7 @@ impl Config { } let mut errors = Vec::new(); self.detached_files = - get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") + get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") .into_iter() .map(AbsPathBuf::assert) .collect(); @@ -956,7 +955,7 @@ impl Config { pub fn has_linked_projects(&self) -> bool { !self.data.linkedProjects.is_empty() } - pub fn linked_manifests(&self) -> impl Iterator + '_ { + pub fn linked_manifests(&self) -> impl Iterator + '_ { self.data.linkedProjects.iter().filter_map(|it| match it { ManifestOrProjectJson::Manifest(p) => Some(&**p), ManifestOrProjectJson::ProjectJson(_) => None, @@ -1014,6 +1013,17 @@ impl Config { ) } + pub fn did_change_watched_files_relative_pattern_support(&self) -> bool { + try_or_def!( + self.caps + .workspace + .as_ref()? + .did_change_watched_files + .as_ref()? + .relative_pattern_support? + ) + } + pub fn prefill_caches(&self) -> bool { self.data.cachePriming_enable } @@ -1410,9 +1420,11 @@ impl Config { } } - fn target_dir_from_config(&self) -> Option { + fn target_dir_from_config(&self) -> Option { self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir { - TargetDirectory::UseSubdirectory(true) => Some(PathBuf::from("target/rust-analyzer")), + TargetDirectory::UseSubdirectory(true) => { + Some(Utf8PathBuf::from("target/rust-analyzer")) + } TargetDirectory::UseSubdirectory(false) => None, TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()), TargetDirectory::Directory(_) => None, @@ -1691,6 +1703,7 @@ impl Config { }, keywords: self.data.hover_documentation_keywords_enable, max_trait_assoc_items_count: self.data.hover_show_traitAssocItems, + max_struct_field_count: self.data.hover_show_structFields, } } @@ -1951,7 +1964,7 @@ where #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] enum ManifestOrProjectJson { - Manifest(PathBuf), + Manifest(Utf8PathBuf), ProjectJson(ProjectJsonData), } @@ -2134,7 +2147,7 @@ pub enum MemoryLayoutHoverRenderKindDef { #[serde(untagged)] pub enum TargetDirectory { UseSubdirectory(bool), - Directory(PathBuf), + Directory(Utf8PathBuf), } macro_rules! _config_data { @@ -2263,7 +2276,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "type": "array", "items": { "type": "string" }, }, - "Vec" => set! { + "Vec" => set! { "type": "array", "items": { "type": "string" }, }, @@ -2291,7 +2304,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Option" => set! { "type": ["null", "string"], }, - "Option" => set! { + "Option" => set! { "type": ["null", "string"], }, "Option" => set! { @@ -2774,7 +2787,7 @@ mod tests { .unwrap(); assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true))); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("target/rust-analyzer"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -2793,10 +2806,10 @@ mod tests { .unwrap(); assert_eq!( config.data.cargo_targetDir, - Some(TargetDirectory::Directory(PathBuf::from("other_folder"))) + Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(PathBuf::from("other_folder"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } } diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 6e6cc53c251..7c4deac93f2 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -519,14 +519,13 @@ fn clippy_code_description(code: Option<&str>) -> Option anyhow::Result<()> { - for change in params.changes { + for change in params.changes.iter().unique_by(|&it| &it.uri) { if let Ok(path) = from_proto::abs_path(&change.uri) { state.loader.handle.invalidate(path); } diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 1d98457add3..77692ed3ae7 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -4,7 +4,6 @@ use std::{ fs, io::Write as _, - path::PathBuf, process::{self, Stdio}, }; @@ -12,8 +11,8 @@ use anyhow::Context; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit, - ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, + Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use itertools::Itertools; @@ -27,6 +26,7 @@ use lsp_types::{ SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, }; +use paths::Utf8PathBuf; use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; @@ -238,9 +238,12 @@ pub(crate) fn handle_discover_test( let (tests, scope) = match params.test_id { Some(id) => { let crate_id = id.split_once("::").map(|it| it.0).unwrap_or(&id); - (snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, vec![crate_id.to_owned()]) + ( + snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, + Some(vec![crate_id.to_owned()]), + ) } - None => (snap.analysis.discover_test_roots()?, vec![]), + None => (snap.analysis.discover_test_roots()?, None), }; for t in &tests { hack_recover_crate_name::insert_name(t.id.clone()); @@ -248,12 +251,13 @@ pub(crate) fn handle_discover_test( Ok(lsp_ext::DiscoverTestResults { tests: tests .into_iter() - .map(|t| { + .filter_map(|t| { let line_index = t.file.and_then(|f| snap.file_line_index(f).ok()); to_proto::test_item(&snap, t, line_index.as_ref()) }) .collect(), scope, + scope_file: None, }) } @@ -1465,7 +1469,7 @@ pub(crate) fn handle_inlay_hints( let inlay_hints_config = snap.config.inlay_hints(); Ok(Some( snap.analysis - .inlay_hints(&inlay_hints_config, file_id, Some(RangeLimit::Fixed(range)))? + .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() .map(|it| { to_proto::inlay_hint( @@ -1499,10 +1503,11 @@ pub(crate) fn handle_inlay_hints_resolve( let hint_position = from_proto::offset(&line_index, original_hint.position)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); - let resolve_hints = snap.analysis.inlay_hints( + let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - Some(RangeLimit::NearestParent(hint_position)), + hint_position, + resolve_data.hash, )?; let mut resolved_hints = resolve_hints @@ -1542,7 +1547,7 @@ pub(crate) fn handle_call_hierarchy_prepare( let RangeInfo { range: _, info: navs } = nav_info; let res = navs .into_iter() - .filter(|it| it.kind == Some(SymbolKind::Function)) + .filter(|it| matches!(it.kind, Some(SymbolKind::Function | SymbolKind::Method))) .map(|it| to_proto::call_hierarchy_item(&snap, it)) .collect::>>()?; @@ -1736,8 +1741,8 @@ pub(crate) fn handle_open_docs( _ => (None, None), }; - let sysroot = sysroot.map(|p| p.root().as_os_str()); - let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str()); + let sysroot = sysroot.map(|p| p.root().as_str()); + let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_str()); let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { return if snap.config.local_docs() { @@ -2042,7 +2047,7 @@ fn run_rustfmt( cmd } RustfmtConfig::CustomCommand { command, args } => { - let cmd = PathBuf::from(&command); + let cmd = Utf8PathBuf::from(&command); let workspace = CargoTargetSpec::for_file(snap, file_id)?; let mut cmd = match workspace { Some(spec) => { diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 1c5a862c703..2731e845f35 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -52,7 +52,7 @@ fn integrated_highlighting_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -112,7 +112,7 @@ fn integrated_completion_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; @@ -274,7 +274,7 @@ fn integrated_diagnostics_benchmark() { let file_id = { let file = workspace_to_load.join(file); - let path = VfsPath::from(AbsPathBuf::assert(file)); + let path = VfsPath::from(AbsPathBuf::assert_utf8(file)); vfs.file_id(&path).unwrap_or_else(|| panic!("can't find virtual file for {path}")) }; diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index 710ce7f8acb..eac982f1b27 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -194,7 +194,8 @@ pub struct TestItem { #[serde(rename_all = "camelCase")] pub struct DiscoverTestResults { pub tests: Vec, - pub scope: Vec, + pub scope: Option>, + pub scope_file: Option>, } pub enum DiscoverTest {} @@ -800,6 +801,7 @@ pub struct CompletionResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct InlayHintResolveData { pub file_id: u32, + pub hash: u64, } #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/crates/rust-analyzer/src/lsp/semantic_tokens.rs index c5081c4bea0..3e00222b752 100644 --- a/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -127,13 +127,14 @@ macro_rules! define_semantic_token_modifiers { define_semantic_token_modifiers![ standard { + ASYNC, DOCUMENTATION, DECLARATION, STATIC, DEFAULT_LIBRARY, } custom { - (ASYNC, "async"), + (ASSOCIATED, "associated"), (ATTRIBUTE_MODIFIER, "attribute"), (CALLABLE, "callable"), (CONSTANT, "constant"), diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index e77d0c13bf2..d8bb12528b9 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -1,7 +1,7 @@ //! Conversion of rust-analyzer specific types to lsp_types equivalents. use std::{ iter::once, - mem, path, + mem, sync::atomic::{AtomicU32, Ordering}, }; @@ -13,8 +13,9 @@ use ide::{ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; -use ide_db::rust_doc::format_docs; +use ide_db::{rust_doc::format_docs, FxHasher}; use itertools::Itertools; +use paths::{Utf8Component, Utf8Prefix}; use semver::VersionReq; use serde_json::to_value; use vfs::AbsPath; @@ -52,6 +53,7 @@ pub(crate) fn range(line_index: &LineIndex, range: TextRange) -> lsp_types::Rang pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { match symbol_kind { SymbolKind::Function => lsp_types::SymbolKind::FUNCTION, + SymbolKind::Method => lsp_types::SymbolKind::METHOD, SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, @@ -122,12 +124,12 @@ pub(crate) fn completion_item_kind( CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT, CompletionItemKind::InferredType => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD, - CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD, CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::REFERENCE, CompletionItemKind::Expression => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::SymbolKind(symbol) => match symbol { SymbolKind::Attribute => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::Method => lsp_types::CompletionItemKind::METHOD, SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, @@ -444,30 +446,42 @@ pub(crate) fn inlay_hint( fields_to_resolve: &InlayFieldsToResolve, line_index: &LineIndex, file_id: FileId, - inlay_hint: InlayHint, + mut inlay_hint: InlayHint, ) -> Cancellable { - let needs_resolve = inlay_hint.needs_resolve; - let (label, tooltip, mut something_to_resolve) = - inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; + let resolve_hash = inlay_hint.needs_resolve().then(|| { + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ) + }); + let mut something_to_resolve = false; let text_edits = if snap .config .visual_studio_code_version() // https://github.com/microsoft/vscode/issues/193124 .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && needs_resolve + && resolve_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); None } else { - inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it)) }; + let (label, tooltip) = inlay_hint_label( + snap, + fields_to_resolve, + &mut something_to_resolve, + resolve_hash.is_some(), + inlay_hint.label, + )?; - let data = if needs_resolve && something_to_resolve { - Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) - } else { - None + let data = match resolve_hash { + Some(hash) if something_to_resolve => Some( + to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash }).unwrap(), + ), + _ => None, }; Ok(lsp_types::InlayHint { @@ -492,15 +506,15 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, fields_to_resolve: &InlayFieldsToResolve, + something_to_resolve: &mut bool, needs_resolve: bool, mut label: InlayHintLabel, -) -> Cancellable<(lsp_types::InlayHintLabel, Option, bool)> { - let mut something_to_resolve = false; +) -> Cancellable<(lsp_types::InlayHintLabel, Option)> { let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - something_to_resolve |= tooltip.is_some(); + *something_to_resolve |= tooltip.is_some(); None } else { match tooltip { @@ -524,7 +538,7 @@ fn inlay_hint_label( .into_iter() .map(|part| { let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - something_to_resolve |= part.tooltip.is_some(); + *something_to_resolve |= part.tooltip.is_some(); None } else { match part.tooltip { @@ -543,7 +557,7 @@ fn inlay_hint_label( } }; let location = if needs_resolve && fields_to_resolve.resolve_label_location { - something_to_resolve |= part.linked_location.is_some(); + *something_to_resolve |= part.linked_location.is_some(); None } else { part.linked_location.map(|range| location(snap, range)).transpose()? @@ -559,7 +573,7 @@ fn inlay_hint_label( (lsp_types::InlayHintLabel::LabelParts(parts), None) } }; - Ok((label, tooltip, something_to_resolve)) + Ok((label, tooltip)) } static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); @@ -636,8 +650,7 @@ pub(crate) fn semantic_token_delta( fn semantic_token_type_and_modifiers( highlight: Highlight, ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { - let mut mods = semantic_tokens::ModifierSet::default(); - let type_ = match highlight.tag { + let ty = match highlight.tag { HlTag::Symbol(symbol) => match symbol { SymbolKind::Attribute => semantic_tokens::DECORATOR, SymbolKind::Derive => semantic_tokens::DERIVE, @@ -653,22 +666,10 @@ fn semantic_token_type_and_modifiers( SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD, SymbolKind::Local => semantic_tokens::VARIABLE, - SymbolKind::Function => { - if highlight.mods.contains(HlMod::Associated) { - semantic_tokens::METHOD - } else { - semantic_tokens::FUNCTION - } - } - SymbolKind::Const => { - mods |= semantic_tokens::CONSTANT; - mods |= semantic_tokens::STATIC; - semantic_tokens::VARIABLE - } - SymbolKind::Static => { - mods |= semantic_tokens::STATIC; - semantic_tokens::VARIABLE - } + SymbolKind::Method => semantic_tokens::METHOD, + SymbolKind::Function => semantic_tokens::FUNCTION, + SymbolKind::Const => semantic_tokens::VARIABLE, + SymbolKind::Static => semantic_tokens::VARIABLE, SymbolKind::Struct => semantic_tokens::STRUCT, SymbolKind::Enum => semantic_tokens::ENUM, SymbolKind::Variant => semantic_tokens::ENUM_MEMBER, @@ -715,12 +716,14 @@ fn semantic_token_type_and_modifiers( }, }; + let mut mods = semantic_tokens::ModifierSet::default(); for modifier in highlight.mods.iter() { let modifier = match modifier { - HlMod::Associated => continue, + HlMod::Associated => semantic_tokens::ASSOCIATED, HlMod::Async => semantic_tokens::ASYNC, HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, HlMod::Callable => semantic_tokens::CALLABLE, + HlMod::Const => semantic_tokens::CONSTANT, HlMod::Consuming => semantic_tokens::CONSUMING, HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, HlMod::CrateRoot => semantic_tokens::CRATE_ROOT, @@ -742,7 +745,7 @@ fn semantic_token_type_and_modifiers( mods |= modifier; } - (type_, mods) + (ty, mods) } pub(crate) fn folding_range( @@ -814,9 +817,9 @@ pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url /// When processing non-windows path, this is essentially the same as `Url::from_file_path`. pub(crate) fn url_from_abs_path(path: &AbsPath) -> lsp_types::Url { let url = lsp_types::Url::from_file_path(path).unwrap(); - match path.as_ref().components().next() { - Some(path::Component::Prefix(prefix)) - if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) => + match path.components().next() { + Some(Utf8Component::Prefix(prefix)) + if matches!(prefix.kind(), Utf8Prefix::Disk(_) | Utf8Prefix::VerbatimDisk(_)) => { // Need to lowercase driver letter } @@ -1514,8 +1517,8 @@ pub(crate) fn test_item( snap: &GlobalStateSnapshot, test_item: ide::TestItem, line_index: Option<&LineIndex>, -) -> lsp_ext::TestItem { - lsp_ext::TestItem { +) -> Option { + Some(lsp_ext::TestItem { id: test_item.id, label: test_item.label, kind: match test_item.kind { @@ -1530,9 +1533,9 @@ pub(crate) fn test_item( | project_model::TargetKind::Example | project_model::TargetKind::BuildScript | project_model::TargetKind::Other => lsp_ext::TestItemKind::Package, - project_model::TargetKind::Test | project_model::TargetKind::Bench => { - lsp_ext::TestItemKind::Test - } + project_model::TargetKind::Test => lsp_ext::TestItemKind::Test, + // benches are not tests needed to be shown in the test explorer + project_model::TargetKind::Bench => return None, } } ide::TestItemKind::Module => lsp_ext::TestItemKind::Module, @@ -1548,7 +1551,7 @@ pub(crate) fn test_item( .map(|f| lsp_types::TextDocumentIdentifier { uri: url(snap, f) }), range: line_index.and_then(|l| Some(range(l, test_item.text_range?))), runnable: test_item.runnable.and_then(|r| runnable(snap, r).ok()), - } + }) } pub(crate) mod command { @@ -2728,12 +2731,12 @@ struct ProcMacro { #[test] #[cfg(target_os = "windows")] fn test_lowercase_drive_letter() { - use std::path::Path; + use paths::Utf8Path; - let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap()); + let url = url_from_abs_path(Utf8Path::new("C:\\Test").try_into().unwrap()); assert_eq!(url.to_string(), "file:///c:/Test"); - let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap()); + let url = url_from_abs_path(Utf8Path::new(r#"\\localhost\C$\my_dir"#).try_into().unwrap()); assert_eq!(url.to_string(), "file://localhost/C$/my_dir"); } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ffe56e41435..38df3235125 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -9,9 +9,8 @@ use std::{ use always_assert::always; use crossbeam_channel::{never, select, Receiver}; use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; -use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; -use lsp_types::notification::Notification as _; +use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use stdx::thread::ThreadIntent; use vfs::FileId; @@ -533,31 +532,29 @@ impl GlobalState { let snapshot = self.snapshot(); move || { let tests = subscriptions - .into_iter() - .filter_map(|f| snapshot.analysis.crates_for(f).ok()) - .flatten() - .unique() - .filter_map(|c| snapshot.analysis.discover_tests_in_crate(c).ok()) + .iter() + .copied() + .filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok()) .flatten() .collect::>(); for t in &tests { hack_recover_crate_name::insert_name(t.id.clone()); } - let scope = tests - .iter() - .filter_map(|t| Some(t.id.split_once("::")?.0)) - .unique() - .map(|it| it.to_owned()) - .collect(); Task::DiscoverTest(lsp_ext::DiscoverTestResults { tests: tests .into_iter() - .map(|t| { + .filter_map(|t| { let line_index = t.file.and_then(|f| snapshot.file_line_index(f).ok()); to_proto::test_item(&snapshot, t, line_index.as_ref()) }) .collect(), - scope, + scope: None, + scope_file: Some( + subscriptions + .into_iter() + .map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) }) + .collect(), + ), }) } }); @@ -653,7 +650,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Building", state, msg, None, None); + self.report_progress("Building build-artifacts", state, msg, None, None); } } Task::LoadProcMacros(progress) => { @@ -669,7 +666,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Loading", state, msg, None, None); + self.report_progress("Loading proc-macros", state, msg, None, None); } } Task::BuildDepsHaveChanged => self.build_deps_changed = true, @@ -714,10 +711,9 @@ impl GlobalState { message += &format!( ": {}", match dir.strip_prefix(self.config.root_path()) { - Some(relative_path) => relative_path.as_ref(), + Some(relative_path) => relative_path.as_utf8_path(), None => dir.as_ref(), } - .display() ); } @@ -861,7 +857,7 @@ impl GlobalState { let title = if self.flycheck.len() == 1 { format!("{}", self.config.flycheck()) } else { - format!("cargo check (#{})", id + 1) + format!("{} (#{})", self.config.flycheck(), id + 1) }; self.report_progress( &title, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index c2725e1fad9..771a5599f6f 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -26,7 +26,6 @@ use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use proc_macro_api::ProcMacroServer; use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; -use rustc_hash::FxHashSet; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -185,10 +184,8 @@ impl GlobalState { message.push_str( "`rust-analyzer.linkedProjects` have been specified, which may be incorrect. Specified project paths:\n", ); - message.push_str(&format!( - " {}", - self.config.linked_manifests().map(|it| it.display()).format("\n ") - )); + message + .push_str(&format!(" {}", self.config.linked_manifests().format("\n "))); if self.config.has_linked_project_jsons() { message.push_str("\nAdditionally, one or more project jsons are specified") } @@ -431,27 +428,46 @@ impl GlobalState { } if let FilesWatcher::Client = self.config.files().watcher { - let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { - watchers: self - .workspaces - .iter() - .flat_map(|ws| ws.to_roots()) - .filter(|it| it.is_local) + let filter = + self.workspaces.iter().flat_map(|ws| ws.to_roots()).filter(|it| it.is_local); + + let watchers = if self.config.did_change_watched_files_relative_pattern_support() { + // When relative patterns are supported by the client, prefer using them + filter .flat_map(|root| { - root.include.into_iter().flat_map(|it| { - [ - format!("{it}/**/*.rs"), - format!("{it}/**/Cargo.toml"), - format!("{it}/**/Cargo.lock"), - ] + root.include.into_iter().flat_map(|base| { + [(base.clone(), "**/*.rs"), (base, "**/Cargo.{lock,toml}")] + }) + }) + .map(|(base, pat)| lsp_types::FileSystemWatcher { + glob_pattern: lsp_types::GlobPattern::Relative( + lsp_types::RelativePattern { + base_uri: lsp_types::OneOf::Right( + lsp_types::Url::from_file_path(base).unwrap(), + ), + pattern: pat.to_owned(), + }, + ), + kind: None, + }) + .collect() + } else { + // When they're not, integrate the base to make them into absolute patterns + filter + .flat_map(|root| { + root.include.into_iter().flat_map(|base| { + [format!("{base}/**/*.rs"), format!("{base}/**/Cargo.{{lock,toml}}")] }) }) .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern: lsp_types::GlobPattern::String(glob_pattern), kind: None, }) - .collect(), + .collect() }; + + let registration_options = + lsp_types::DidChangeWatchedFilesRegistrationOptions { watchers }; let registration = lsp_types::Registration { id: "workspace/didChangeWatchedFiles".to_owned(), method: "workspace/didChangeWatchedFiles".to_owned(), @@ -525,26 +541,23 @@ impl GlobalState { fn recreate_crate_graph(&mut self, cause: String) { // crate graph construction relies on these paths, record them so when one of them gets // deleted or created we trigger a reconstruction of the crate graph - let mut crate_graph_file_dependencies = FxHashSet::default(); + let mut crate_graph_file_dependencies = mem::take(&mut self.crate_graph_file_dependencies); + self.report_progress( + "Building CrateGraph", + crate::lsp::utils::Progress::Begin, + None, + None, + None, + ); let (crate_graph, proc_macro_paths, layouts, toolchains) = { // Create crate graph from all the workspaces let vfs = &mut self.vfs.write().0; - let loader = &mut self.loader; let load = |path: &AbsPath| { - let _p = tracing::span!(tracing::Level::DEBUG, "switch_workspaces::load").entered(); let vfs_path = vfs::VfsPath::from(path.to_path_buf()); crate_graph_file_dependencies.insert(vfs_path.clone()); - match vfs.file_id(&vfs_path) { - Some(file_id) => Some(file_id), - None => { - // FIXME: Consider not loading this here? - let contents = loader.handle.load_sync(path); - vfs.set_file_contents(vfs_path.clone(), contents); - vfs.file_id(&vfs_path) - } - } + vfs.file_id(&vfs_path) }; ws_to_crate_graph(&self.workspaces, self.config.extra_env(), load) @@ -564,6 +577,13 @@ impl GlobalState { change.set_toolchains(toolchains); self.analysis_host.apply_change(change); self.crate_graph_file_dependencies = crate_graph_file_dependencies; + self.report_progress( + "Building CrateGraph", + crate::lsp::utils::Progress::End, + None, + None, + None, + ); self.process_changes(); self.reload_flycheck(); @@ -732,6 +752,8 @@ pub fn ws_to_crate_graph( }); proc_macro_paths.push(crate_proc_macros); } + crate_graph.shrink_to_fit(); + proc_macro_paths.shrink_to_fit(); (crate_graph, proc_macro_paths, layouts, toolchains) } @@ -739,7 +761,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"]; - let file_name = match path.file_name().unwrap_or_default().to_str() { + let file_name = match path.file_name() { Some(it) => it, None => return false, }; @@ -754,18 +776,18 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) // .cargo/config{.toml} if path.extension().unwrap_or_default() != "rs" { let is_cargo_config = matches!(file_name, "config.toml" | "config") - && path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false); + && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false); return is_cargo_config; } - if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_str().ends_with(it)) { return true; } let parent = match path.parent() { Some(it) => it, None => return false, }; - if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.as_str().ends_with(it)) { return true; } if file_name == "main.rs" { @@ -773,7 +795,7 @@ pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) Some(it) => it, None => return false, }; - if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_ref().ends_with(it)) { + if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.as_str().ends_with(it)) { return true; } } diff --git a/crates/rust-analyzer/tests/crate_graph.rs b/crates/rust-analyzer/tests/crate_graph.rs index efd42fadf7e..cf38032b941 100644 --- a/crates/rust-analyzer/tests/crate_graph.rs +++ b/crates/rust-analyzer/tests/crate_graph.rs @@ -60,7 +60,7 @@ fn get_fake_sysroot() -> Sysroot { let sysroot_path = get_fake_sysroot_path(); // 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_dir = AbsPathBuf::assert_utf8(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); Sysroot::load(sysroot_dir, Some(Ok(sysroot_src_dir)), false) } diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 960f5b531d4..439b006977d 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -903,6 +903,7 @@ fn out_dirs_check() { } #[test] +#[cfg(not(windows))] // windows requires elevated permissions to create symlinks fn root_contains_symlink_out_dirs_check() { out_dirs_check_impl(true); } @@ -917,7 +918,7 @@ fn resolve_proc_macro() { } let sysroot = project_model::Sysroot::discover_no_source( - &AbsPathBuf::assert(std::env::current_dir().unwrap()), + &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()), &Default::default(), ) .unwrap(); @@ -1002,7 +1003,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { }, "procMacro": { "enable": true, - "server": proc_macro_server_path.as_path().as_ref(), + "server": proc_macro_server_path.as_path().as_str(), } })) .root("foo") @@ -1039,7 +1040,7 @@ fn test_will_rename_files_same_level() { let tmp_dir = TestDir::new(); let tmp_dir_path = tmp_dir.path().to_owned(); - let tmp_dir_str = tmp_dir_path.to_str().unwrap(); + let tmp_dir_str = tmp_dir_path.as_str(); let base_path = PathBuf::from(format!("file://{tmp_dir_str}")); let code = r#" @@ -1084,7 +1085,7 @@ use crate::old_folder::nested::foo as bar; "documentChanges": [ { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ @@ -1141,7 +1142,7 @@ use crate::old_folder::nested::foo as bar; "documentChanges": [ { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ @@ -1162,7 +1163,7 @@ use crate::old_folder::nested::foo as bar; }, { "textDocument": { - "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").to_str().unwrap().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), + "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), "version": null }, "edits": [ diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 1d831b8b105..8bbe6ff3724 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -1,7 +1,6 @@ use std::{ cell::{Cell, RefCell}, fs, - path::{Path, PathBuf}, sync::Once, time::Duration, }; @@ -9,6 +8,7 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url}; +use paths::{Utf8Path, Utf8PathBuf}; use rust_analyzer::{config::Config, lsp, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; @@ -21,7 +21,7 @@ use crate::testdir::TestDir; pub(crate) struct Project<'a> { fixture: &'a str, tmp_dir: Option, - roots: Vec, + roots: Vec, config: serde_json::Value, root_dir_contains_symlink: bool, } @@ -359,7 +359,7 @@ impl Server { self.client.sender.send(Message::Notification(not)).unwrap(); } - pub(crate) fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Utf8Path { self.dir.path() } } diff --git a/crates/rust-analyzer/tests/slow-tests/testdir.rs b/crates/rust-analyzer/tests/slow-tests/testdir.rs index b3ee7fa3d03..d113bd51278 100644 --- a/crates/rust-analyzer/tests/slow-tests/testdir.rs +++ b/crates/rust-analyzer/tests/slow-tests/testdir.rs @@ -1,11 +1,12 @@ use std::{ fs, io, - path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}, }; +use paths::{Utf8Path, Utf8PathBuf}; + pub(crate) struct TestDir { - path: PathBuf, + path: Utf8PathBuf, keep: bool, } @@ -51,10 +52,13 @@ impl TestDir { #[cfg(target_os = "windows")] std::os::windows::fs::symlink_dir(path, &symlink_path).unwrap(); - return TestDir { path: symlink_path, keep: false }; + return TestDir { + path: Utf8PathBuf::from_path_buf(symlink_path).unwrap(), + keep: false, + }; } - return TestDir { path, keep: false }; + return TestDir { path: Utf8PathBuf::from_path_buf(path).unwrap(), keep: false }; } panic!("Failed to create a temporary directory") } @@ -64,7 +68,7 @@ impl TestDir { self.keep = true; self } - pub(crate) fn path(&self) -> &Path { + pub(crate) fn path(&self) -> &Utf8Path { &self.path } } @@ -79,7 +83,7 @@ impl Drop for TestDir { let actual_path = filetype.is_symlink().then(|| fs::read_link(&self.path).unwrap()); if let Some(actual_path) = actual_path { - remove_dir_all(&actual_path).unwrap_or_else(|err| { + remove_dir_all(Utf8Path::from_path(&actual_path).unwrap()).unwrap_or_else(|err| { panic!( "failed to remove temporary link to directory {}: {err}", actual_path.display() @@ -88,18 +92,18 @@ impl Drop for TestDir { } remove_dir_all(&self.path).unwrap_or_else(|err| { - panic!("failed to remove temporary directory {}: {err}", self.path.display()) + panic!("failed to remove temporary directory {}: {err}", self.path) }); } } #[cfg(not(windows))] -fn remove_dir_all(path: &Path) -> io::Result<()> { +fn remove_dir_all(path: &Utf8Path) -> io::Result<()> { fs::remove_dir_all(path) } #[cfg(windows)] -fn remove_dir_all(path: &Path) -> io::Result<()> { +fn remove_dir_all(path: &Utf8Path) -> io::Result<()> { for _ in 0..99 { if fs::remove_dir_all(path).is_ok() { return Ok(()); diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs index 6b849ce3738..c9109c72d0d 100644 --- a/crates/span/src/lib.rs +++ b/crates/span/src/lib.rs @@ -16,6 +16,56 @@ pub use self::{ pub use syntax::{TextRange, TextSize}; pub use vfs::FileId; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Edition { + Edition2015, + Edition2018, + Edition2021, + Edition2024, +} + +impl Edition { + pub const CURRENT: Edition = Edition::Edition2021; + pub const DEFAULT: Edition = Edition::Edition2015; +} + +#[derive(Debug)] +pub struct ParseEditionError { + invalid_input: String, +} + +impl std::error::Error for ParseEditionError {} +impl fmt::Display for ParseEditionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid edition: {:?}", self.invalid_input) + } +} + +impl std::str::FromStr for Edition { + type Err = ParseEditionError; + + fn from_str(s: &str) -> Result { + let res = match s { + "2015" => Edition::Edition2015, + "2018" => Edition::Edition2018, + "2021" => Edition::Edition2021, + "2024" => Edition::Edition2024, + _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), + }; + Ok(res) + } +} + +impl fmt::Display for Edition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Edition::Edition2015 => "2015", + Edition::Edition2018 => "2018", + Edition::Edition2021 => "2021", + Edition::Edition2024 => "2024", + }) + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct FilePosition { pub file_id: FileId, diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 9a8d73cf7ff..1809ca7dea5 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -33,12 +33,8 @@ text-edit.workspace = true [dev-dependencies] rayon.workspace = true expect-test = "1.4.0" -proc-macro2 = "1.0.47" -quote = "1.0.20" -ungrammar = "1.16.1" test-utils.workspace = true -sourcegen.workspace = true [features] in-rust-tree = [] diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index c3d8e97c436..e1765b25fd8 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -414,7 +414,7 @@ StmtList = '}' RefExpr = - Attr* '&' ('raw' | 'mut' | 'const') Expr + Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr TryExpr = Attr* Expr '?' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 75971861aa8..c82bc4151ac 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -7,6 +7,807 @@ use crate::{ SyntaxNode, SyntaxToken, T, }; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Abi { + pub(crate) syntax: SyntaxNode, +} +impl Abi { + pub fn extern_token(&self) -> Option { support::token(&self.syntax, T![extern]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArgList { + pub(crate) syntax: SyntaxNode, +} +impl ArgList { + pub fn args(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for ArrayExpr {} +impl ArrayExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn exprs(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayType { + pub(crate) syntax: SyntaxNode, +} +impl ArrayType { + pub fn const_arg(&self) -> Option { support::child(&self.syntax) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AsmExpr {} +impl AsmExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AssocItemList { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AssocItemList {} +impl AssocItemList { + pub fn assoc_items(&self) -> AstChildren { support::children(&self.syntax) } + pub fn l_curly_token(&self) -> Option { support::token(&self.syntax, T!['{']) } + pub fn r_curly_token(&self) -> Option { support::token(&self.syntax, T!['}']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AssocTypeArg { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasTypeBounds for AssocTypeArg {} +impl AssocTypeArg { + pub fn const_arg(&self) -> Option { support::child(&self.syntax) } + pub fn generic_arg_list(&self) -> Option { support::child(&self.syntax) } + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + pub fn param_list(&self) -> Option { support::child(&self.syntax) } + pub fn ret_type(&self) -> Option { support::child(&self.syntax) } + pub fn ty(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Attr { + pub(crate) syntax: SyntaxNode, +} +impl Attr { + pub fn meta(&self) -> Option { support::child(&self.syntax) } + pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + pub fn l_brack_token(&self) -> Option { support::token(&self.syntax, T!['[']) } + pub fn r_brack_token(&self) -> Option { support::token(&self.syntax, T![']']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AwaitExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for AwaitExpr {} +impl AwaitExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn dot_token(&self) -> Option { support::token(&self.syntax, T![.]) } + pub fn await_token(&self) -> Option { support::token(&self.syntax, T![await]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BecomeExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for BecomeExpr {} +impl BecomeExpr { + pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn become_token(&self) -> Option { support::token(&self.syntax, T![become]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BinExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for BinExpr {} +impl BinExpr {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BlockExpr { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasAttrs for BlockExpr {} +impl BlockExpr { + pub fn label(&self) -> Option

::Metadata; +} +impl AggregateRawPtr<*mut T> for *mut P { + type Metadata =

::Metadata; +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff new file mode 100644 index 00000000000..02934d4c01e --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff @@ -0,0 +1,95 @@ +- // MIR for `make_pointers` before LowerIntrinsics ++ // MIR for `make_pointers` after LowerIntrinsics + + fn make_pointers(_1: *const u8, _2: *mut (), _3: usize) -> () { + debug a => _1; + debug b => _2; + debug n => _3; + let mut _0: (); + let _4: *const i32; + let mut _5: *const u8; + let mut _6: (); + let mut _8: *mut (); + let mut _9: (); + let mut _11: *const u8; + let mut _12: usize; + let mut _14: *mut (); + let mut _15: usize; + scope 1 { + debug _thin_const => _4; + let _7: *mut u8; + scope 2 { + debug _thin_mut => _7; + let _10: *const [u16]; + scope 3 { + debug _slice_const => _10; + let _13: *mut [u64]; + scope 4 { + debug _slice_mut => _13; + } + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; + StorageLive(_6); + _6 = (); +- _4 = aggregate_raw_ptr::<*const i32, *const u8, ()>(move _5, move _6) -> [return: bb1, unwind unreachable]; ++ _4 = *const i32 from (move _5, move _6); ++ goto -> bb1; + } + + bb1: { + StorageDead(_6); + StorageDead(_5); + StorageLive(_7); + StorageLive(_8); + _8 = _2; + StorageLive(_9); + _9 = (); +- _7 = aggregate_raw_ptr::<*mut u8, *mut (), ()>(move _8, move _9) -> [return: bb2, unwind unreachable]; ++ _7 = *mut u8 from (move _8, move _9); ++ goto -> bb2; + } + + bb2: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = _1; + StorageLive(_12); + _12 = _3; +- _10 = aggregate_raw_ptr::<*const [u16], *const u8, usize>(move _11, move _12) -> [return: bb3, unwind unreachable]; ++ _10 = *const [u16] from (move _11, move _12); ++ goto -> bb3; + } + + bb3: { + StorageDead(_12); + StorageDead(_11); + StorageLive(_13); + StorageLive(_14); + _14 = _2; + StorageLive(_15); + _15 = _3; +- _13 = aggregate_raw_ptr::<*mut [u64], *mut (), usize>(move _14, move _15) -> [return: bb4, unwind unreachable]; ++ _13 = *mut [u64] from (move _14, move _15); ++ goto -> bb4; + } + + bb4: { + StorageDead(_15); + StorageDead(_14); + _0 = const (); + StorageDead(_13); + StorageDead(_10); + StorageDead(_7); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff new file mode 100644 index 00000000000..02934d4c01e --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff @@ -0,0 +1,95 @@ +- // MIR for `make_pointers` before LowerIntrinsics ++ // MIR for `make_pointers` after LowerIntrinsics + + fn make_pointers(_1: *const u8, _2: *mut (), _3: usize) -> () { + debug a => _1; + debug b => _2; + debug n => _3; + let mut _0: (); + let _4: *const i32; + let mut _5: *const u8; + let mut _6: (); + let mut _8: *mut (); + let mut _9: (); + let mut _11: *const u8; + let mut _12: usize; + let mut _14: *mut (); + let mut _15: usize; + scope 1 { + debug _thin_const => _4; + let _7: *mut u8; + scope 2 { + debug _thin_mut => _7; + let _10: *const [u16]; + scope 3 { + debug _slice_const => _10; + let _13: *mut [u64]; + scope 4 { + debug _slice_mut => _13; + } + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; + StorageLive(_6); + _6 = (); +- _4 = aggregate_raw_ptr::<*const i32, *const u8, ()>(move _5, move _6) -> [return: bb1, unwind unreachable]; ++ _4 = *const i32 from (move _5, move _6); ++ goto -> bb1; + } + + bb1: { + StorageDead(_6); + StorageDead(_5); + StorageLive(_7); + StorageLive(_8); + _8 = _2; + StorageLive(_9); + _9 = (); +- _7 = aggregate_raw_ptr::<*mut u8, *mut (), ()>(move _8, move _9) -> [return: bb2, unwind unreachable]; ++ _7 = *mut u8 from (move _8, move _9); ++ goto -> bb2; + } + + bb2: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = _1; + StorageLive(_12); + _12 = _3; +- _10 = aggregate_raw_ptr::<*const [u16], *const u8, usize>(move _11, move _12) -> [return: bb3, unwind unreachable]; ++ _10 = *const [u16] from (move _11, move _12); ++ goto -> bb3; + } + + bb3: { + StorageDead(_12); + StorageDead(_11); + StorageLive(_13); + StorageLive(_14); + _14 = _2; + StorageLive(_15); + _15 = _3; +- _13 = aggregate_raw_ptr::<*mut [u64], *mut (), usize>(move _14, move _15) -> [return: bb4, unwind unreachable]; ++ _13 = *mut [u64] from (move _14, move _15); ++ goto -> bb4; + } + + bb4: { + StorageDead(_15); + StorageDead(_14); + _0 = const (); + StorageDead(_13); + StorageDead(_10); + StorageDead(_7); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 693425c0041..12e526ab074 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -248,3 +248,13 @@ pub fn three_way_compare_signed(a: i16, b: i16) { pub fn three_way_compare_unsigned(a: u32, b: u32) { let _x = core::intrinsics::three_way_compare(a, b); } + +// EMIT_MIR lower_intrinsics.make_pointers.LowerIntrinsics.diff +pub fn make_pointers(a: *const u8, b: *mut (), n: usize) { + use std::intrinsics::aggregate_raw_ptr; + + let _thin_const: *const i32 = aggregate_raw_ptr(a, ()); + let _thin_mut: *mut u8 = aggregate_raw_ptr(b, ()); + let _slice_const: *const [u16] = aggregate_raw_ptr(a, n); + let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n); +} From de64ff76f8abd65222f9f6db56e7652976c5339b Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 11 Apr 2024 17:01:27 -0700 Subject: [PATCH 156/185] Use it in the library, and `InstSimplify` it away in the easy places --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 22 ++++++- .../rustc_const_eval/src/interpret/operand.rs | 11 ++++ .../rustc_const_eval/src/interpret/step.rs | 17 ++++- library/core/src/intrinsics.rs | 1 + library/core/src/ptr/metadata.rs | 32 ++++++--- library/core/tests/ptr.rs | 8 +++ library/core/tests/slice.rs | 13 ++++ ...ked_range.PreCodegen.after.panic-abort.mir | 60 +++++++++++++++-- ...ed_range.PreCodegen.after.panic-unwind.mir | 60 +++++++++++++++-- ..._to_slice.PreCodegen.after.panic-abort.mir | 66 +++++++++++++++++-- ...to_slice.PreCodegen.after.panic-unwind.mir | 66 +++++++++++++++++-- 11 files changed, 328 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 6725a6d9e38..7823d4c249a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -9,7 +9,7 @@ use crate::MemFlags; use rustc_hir as hir; use rustc_middle::mir; -use rustc_middle::mir::Operand; +use rustc_middle::mir::{AggregateKind, Operand}; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt}; @@ -720,6 +720,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: OperandValue::Immediate(static_), layout } } mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand), + mir::Rvalue::Aggregate(box mir::AggregateKind::RawPtr(..), ref fields) => { + let ty = rvalue.ty(self.mir, self.cx.tcx()); + let layout = self.cx.layout_of(self.monomorphize(ty)); + let [data, meta] = &*fields.raw else { + bug!("RawPtr fields: {fields:?}"); + }; + let data = self.codegen_operand(bx, data); + let meta = self.codegen_operand(bx, meta); + match (data.val, meta.val) { + (p @ OperandValue::Immediate(_), OperandValue::ZeroSized) => { + OperandRef { val: p, layout } + } + (OperandValue::Immediate(p), OperandValue::Immediate(m)) => { + OperandRef { val: OperandValue::Pair(p, m), layout } + } + _ => bug!("RawPtr operands {data:?} {meta:?}"), + } + } mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { // According to `rvalue_creates_operand`, only ZST // aggregate rvalues are allowed to be operands. @@ -1032,6 +1050,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) => // (*) true, + // This always produces a `ty::RawPtr`, so will be Immediate or Pair + mir::Rvalue::Aggregate(box AggregateKind::RawPtr(..), ..) => true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { let ty = rvalue.ty(self.mir, self.cx.tcx()); diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 718c91b2f76..d4a6dab9ac3 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -603,6 +603,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(self.read_immediate(op)?.to_scalar()) } + pub fn read_mem_place_meta( + &self, + op: &impl Readable<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + Ok(if op.layout().is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.read_scalar(op)?) + }) + } + // Pointer-sized reads are fairly common and need target layout access, so we wrap them in // convenience functions. diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index c3f26da8a79..460cd377c8f 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,7 +9,7 @@ use rustc_middle::mir; use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; -use super::{ImmTy, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar}; +use super::{ImmTy, Immediate, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar}; use crate::util; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -303,6 +303,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::RawPtr(..) => { + // Trying to `project_field` into pointers tends not to work, + // so build the `Immediate` from the parts directly. + let [data, meta] = &operands.raw else { + bug!("{kind:?} should have 2 operands, had {operands:?}"); + }; + let data = self.eval_operand(data, None)?; + let data = self.read_pointer(&data)?; + let meta = self.eval_operand(meta, None)?; + let meta = self.read_mem_place_meta(&meta)?; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self); + let ptr = ImmTy::from_immediate(ptr_imm, dest.layout); + self.copy_op(&ptr, dest)?; + return Ok(()); + } _ => (FIRST_VARIANT, dest.clone(), None), }; if active_field_index.is_some() { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index b49409a9f42..751f2210d0c 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2786,6 +2786,7 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { /// change the possible layouts of pointers. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] #[cfg(not(bootstrap))] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 25a06f121cd..1226c8e2419 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,6 +2,8 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; +#[cfg(not(bootstrap))] +use crate::intrinsics::aggregate_raw_ptr; use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. @@ -113,10 +115,17 @@ pub const fn from_raw_parts( data_pointer: *const (), metadata: ::Metadata, ) -> *const T { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr } + #[cfg(bootstrap)] + { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents have the same memory layouts. Only std can make this + // guarantee. + unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr } + } + #[cfg(not(bootstrap))] + { + aggregate_raw_ptr(data_pointer, metadata) + } } /// Performs the same functionality as [`from_raw_parts`], except that a @@ -130,10 +139,17 @@ pub const fn from_raw_parts_mut( data_pointer: *mut (), metadata: ::Metadata, ) -> *mut T { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr } + #[cfg(bootstrap)] + { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents have the same memory layouts. Only std can make this + // guarantee. + unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr } + } + #[cfg(not(bootstrap))] + { + aggregate_raw_ptr(data_pointer, metadata) + } } #[repr(C)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 2c82eda9a58..7b55c2bf8a8 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1163,3 +1163,11 @@ fn test_null_array_as_slice() { assert!(ptr.is_null()); assert_eq!(ptr.len(), 4); } + +#[test] +fn test_ptr_from_raw_parts_in_const() { + const EMPTY_SLICE_PTR: *const [i32] = + std::ptr::slice_from_raw_parts(std::ptr::without_provenance(123), 456); + assert_eq!(EMPTY_SLICE_PTR.addr(), 123); + assert_eq!(EMPTY_SLICE_PTR.len(), 456); +} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index c5743eda3e8..ffe8ffcc7f2 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -2678,3 +2678,16 @@ fn test_get_many_mut_duplicate() { let mut v = vec![1, 2, 3, 4, 5]; assert!(v.get_many_mut([1, 3, 3, 4]).is_err()); } + +#[test] +fn test_slice_from_raw_parts_in_const() { + static FANCY: i32 = 4; + static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) }; + assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY)); + assert_eq!(FANCY_SLICE.len(), 1); + + const EMPTY_SLICE: &[i32] = + unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) }; + assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); + assert_eq!(EMPTY_SLICE.len(), 0); +} diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir index 79c6a05b48d..8446324c663 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir @@ -4,16 +4,66 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - debug slice => _1; debug index => _2; let mut _0: *const [u32]; + let mut _3: usize; + let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { debug self => _1; - debug index => _2; + debug ((index: std::ops::Range).0: usize) => _3; + debug ((index: std::ops::Range).1: usize) => _4; + scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { + debug ((self: std::ops::Range).0: usize) => _3; + debug ((self: std::ops::Range).1: usize) => _4; + debug slice => _1; + let _5: usize; + let mut _6: *const u32; + let mut _7: *const u32; + scope 3 { + debug new_len => _5; + scope 6 (inlined std::ptr::const_ptr::::as_ptr) { + debug self => _1; + } + scope 7 (inlined std::ptr::const_ptr::::add) { + debug self => _6; + debug count => _3; + } + scope 8 (inlined slice_from_raw_parts::) { + debug data => _7; + debug len => _5; + let mut _8: *const (); + scope 9 (inlined std::ptr::const_ptr::::cast::<()>) { + debug self => _7; + } + scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) { + debug data_pointer => _8; + debug metadata => _5; + } + } + } + scope 4 (inlined std::ptr::const_ptr::::len) { + debug self => _1; + scope 5 (inlined std::ptr::metadata::<[u32]>) { + debug ptr => _1; + } + } + } } bb0: { - _0 = as SliceIndex<[u32]>>::get_unchecked(move _2, move _1) -> [return: bb1, unwind unreachable]; - } - - bb1: { + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = SubUnchecked(_4, _3); + StorageLive(_7); + StorageLive(_6); + _6 = _1 as *const u32 (PtrToPtr); + _7 = Offset(_6, _3); + StorageDead(_6); + StorageLive(_8); + _8 = _7 as *const () (PtrToPtr); + _0 = *const [u32] from (_8, _5); + StorageDead(_8); + StorageDead(_7); + StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir index 5231f858d04..8446324c663 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir @@ -4,16 +4,66 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - debug slice => _1; debug index => _2; let mut _0: *const [u32]; + let mut _3: usize; + let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { debug self => _1; - debug index => _2; + debug ((index: std::ops::Range).0: usize) => _3; + debug ((index: std::ops::Range).1: usize) => _4; + scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { + debug ((self: std::ops::Range).0: usize) => _3; + debug ((self: std::ops::Range).1: usize) => _4; + debug slice => _1; + let _5: usize; + let mut _6: *const u32; + let mut _7: *const u32; + scope 3 { + debug new_len => _5; + scope 6 (inlined std::ptr::const_ptr::::as_ptr) { + debug self => _1; + } + scope 7 (inlined std::ptr::const_ptr::::add) { + debug self => _6; + debug count => _3; + } + scope 8 (inlined slice_from_raw_parts::) { + debug data => _7; + debug len => _5; + let mut _8: *const (); + scope 9 (inlined std::ptr::const_ptr::::cast::<()>) { + debug self => _7; + } + scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) { + debug data_pointer => _8; + debug metadata => _5; + } + } + } + scope 4 (inlined std::ptr::const_ptr::::len) { + debug self => _1; + scope 5 (inlined std::ptr::metadata::<[u32]>) { + debug ptr => _1; + } + } + } } bb0: { - _0 = as SliceIndex<[u32]>>::get_unchecked(move _2, move _1) -> [return: bb1, unwind continue]; - } - - bb1: { + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = SubUnchecked(_4, _3); + StorageLive(_7); + StorageLive(_6); + _6 = _1 as *const u32 (PtrToPtr); + _7 = Offset(_6, _3); + StorageDead(_6); + StorageLive(_8); + _8 = _7 as *const () (PtrToPtr); + _0 = *const [u32] from (_8, _5); + StorageDead(_8); + StorageDead(_7); + StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index df8d5c3836f..18728d543ad 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -3,12 +3,70 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { debug v => _1; let mut _0: &[u8]; - - bb0: { - _0 = as Deref>::deref(move _1) -> [return: bb1, unwind unreachable]; + scope 1 (inlined as Deref>::deref) { + debug self => _1; + let mut _4: *const u8; + let mut _5: usize; + scope 2 (inlined Vec::::as_ptr) { + debug self => _1; + let mut _2: &alloc::raw_vec::RawVec; + scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { + debug self => _2; + let mut _3: std::ptr::NonNull; + scope 4 (inlined Unique::::as_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _3; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 5 (inlined NonNull::::as_ptr) { + debug self => _3; + } + } + } + } + scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) { + debug data => _4; + debug len => _5; + let _7: *const [u8]; + scope 7 (inlined core::ub_checks::check_language_ub) { + scope 8 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 9 (inlined std::mem::size_of::) { + } + scope 10 (inlined align_of::) { + } + scope 11 (inlined slice_from_raw_parts::) { + debug data => _4; + debug len => _5; + let mut _6: *const (); + scope 12 (inlined std::ptr::const_ptr::::cast::<()>) { + debug self => _4; + } + scope 13 (inlined std::ptr::from_raw_parts::<[u8]>) { + debug data_pointer => _6; + debug metadata => _5; + } + } + } } - bb1: { + bb0: { + StorageLive(_4); + StorageLive(_2); + _2 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_3); + _3 = ((((*_1).0: alloc::raw_vec::RawVec).0: std::ptr::Unique).0: std::ptr::NonNull); + _4 = (_3.0: *const u8); + StorageDead(_3); + StorageDead(_2); + StorageLive(_5); + _5 = ((*_1).1: usize); + StorageLive(_6); + _6 = _4 as *const () (PtrToPtr); + _7 = *const [u8] from (_6, _5); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + _0 = &(*_7); return; } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index d26afef4653..18728d543ad 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -3,12 +3,70 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { debug v => _1; let mut _0: &[u8]; - - bb0: { - _0 = as Deref>::deref(move _1) -> [return: bb1, unwind continue]; + scope 1 (inlined as Deref>::deref) { + debug self => _1; + let mut _4: *const u8; + let mut _5: usize; + scope 2 (inlined Vec::::as_ptr) { + debug self => _1; + let mut _2: &alloc::raw_vec::RawVec; + scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { + debug self => _2; + let mut _3: std::ptr::NonNull; + scope 4 (inlined Unique::::as_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _3; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 5 (inlined NonNull::::as_ptr) { + debug self => _3; + } + } + } + } + scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) { + debug data => _4; + debug len => _5; + let _7: *const [u8]; + scope 7 (inlined core::ub_checks::check_language_ub) { + scope 8 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 9 (inlined std::mem::size_of::) { + } + scope 10 (inlined align_of::) { + } + scope 11 (inlined slice_from_raw_parts::) { + debug data => _4; + debug len => _5; + let mut _6: *const (); + scope 12 (inlined std::ptr::const_ptr::::cast::<()>) { + debug self => _4; + } + scope 13 (inlined std::ptr::from_raw_parts::<[u8]>) { + debug data_pointer => _6; + debug metadata => _5; + } + } + } } - bb1: { + bb0: { + StorageLive(_4); + StorageLive(_2); + _2 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_3); + _3 = ((((*_1).0: alloc::raw_vec::RawVec).0: std::ptr::Unique).0: std::ptr::NonNull); + _4 = (_3.0: *const u8); + StorageDead(_3); + StorageDead(_2); + StorageLive(_5); + _5 = ((*_1).1: usize); + StorageLive(_6); + _6 = _4 as *const () (PtrToPtr); + _7 = *const [u8] from (_6, _5); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + _0 = &(*_7); return; } } From 9520cebfc50947f04280a6dbf288dfb5475c1637 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 14 Apr 2024 11:12:32 -0700 Subject: [PATCH 157/185] =?UTF-8?q?InstSimplify=20`from=5Fraw=5Fparts(p,?= =?UTF-8?q?=20())`=20=E2=86=92=20`p=20as=20=5F`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rustc_mir_transform/src/instsimplify.rs | 36 ++++++++++++++++++- tests/mir-opt/instsimplify/casts.rs | 9 +++++ ...e_add_fat.PreCodegen.after.panic-abort.mir | 10 +----- ..._add_fat.PreCodegen.after.panic-unwind.mir | 10 +----- ..._add_thin.PreCodegen.after.panic-abort.mir | 12 +------ ...add_thin.PreCodegen.after.panic-unwind.mir | 12 +------ 6 files changed, 48 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index ff786d44d6a..bae959963b5 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -36,6 +36,7 @@ impl<'tcx> MirPass<'tcx> for InstSimplify { ctx.simplify_bool_cmp(&statement.source_info, rvalue); ctx.simplify_ref_deref(&statement.source_info, rvalue); ctx.simplify_len(&statement.source_info, rvalue); + ctx.simplify_ptr_aggregate(&statement.source_info, rvalue); ctx.simplify_cast(rvalue); } _ => {} @@ -58,8 +59,17 @@ struct InstSimplifyContext<'tcx, 'a> { impl<'tcx> InstSimplifyContext<'tcx, '_> { fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { + self.should_simplify_custom(source_info, "Rvalue", rvalue) + } + + fn should_simplify_custom( + &self, + source_info: &SourceInfo, + label: &str, + value: impl std::fmt::Debug, + ) -> bool { self.tcx.consider_optimizing(|| { - format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}") + format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}") }) } @@ -147,6 +157,30 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } + /// Transform "Aggregate(RawPtr, \[p, ()\])" ==> "Cast(PtrToPtr, p)". + fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue + { + let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx); + if meta_ty.is_unit() { + // The mutable borrows we're holding prevent printing `rvalue` here + if !self.should_simplify_custom( + source_info, + "Aggregate::RawPtr", + (&pointee_ty, *mutability, &fields), + ) { + return; + } + + let mut fields = std::mem::take(fields); + let _meta = fields.pop().unwrap(); + let data = fields.pop().unwrap(); + let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability); + *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty); + } + } + } + fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue { let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); diff --git a/tests/mir-opt/instsimplify/casts.rs b/tests/mir-opt/instsimplify/casts.rs index b3bc34af5b7..a7786fa570f 100644 --- a/tests/mir-opt/instsimplify/casts.rs +++ b/tests/mir-opt/instsimplify/casts.rs @@ -1,6 +1,7 @@ //@ test-mir-pass: InstSimplify //@ compile-flags: -Zinline-mir #![crate_type = "lib"] +#![feature(core_intrinsics)] #[inline(always)] fn generic_cast(x: *const T) -> *const U { @@ -23,3 +24,11 @@ pub fn roundtrip(x: *const u8) -> *const u8 { // CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); x as *mut u8 as *const u8 } + +// EMIT_MIR casts.roundtrip.InstSimplify.diff +pub fn cast_thin_via_aggregate(x: *const u8) -> *const () { + // CHECK-LABEL: fn cast_thin_via_aggregate( + // CHECK: _2 = _1; + // CHECK: _0 = move _2 as *const () (PtrToPtr); + std::intrinsics::aggregate_raw_ptr(x, ()) +} diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir index 81474306eec..37f50d25fc8 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -28,8 +28,6 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { debug data_pointer => _5; debug metadata => _7; - let mut _8: std::ptr::metadata::PtrComponents<[u32]>; - let mut _9: std::ptr::metadata::PtrRepr<[u32]>; } } } @@ -47,13 +45,7 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); StorageDead(_6); - StorageLive(_9); - StorageLive(_8); - _8 = std::ptr::metadata::PtrComponents::<[u32]> { data_pointer: _5, metadata: _7 }; - _9 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _8 }; - StorageDead(_8); - _0 = (_9.0: *const [u32]); - StorageDead(_9); + _0 = *const [u32] from (_5, _7); StorageDead(_7); StorageDead(_5); StorageDead(_4); diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir index 81474306eec..37f50d25fc8 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -28,8 +28,6 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { debug data_pointer => _5; debug metadata => _7; - let mut _8: std::ptr::metadata::PtrComponents<[u32]>; - let mut _9: std::ptr::metadata::PtrRepr<[u32]>; } } } @@ -47,13 +45,7 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); StorageDead(_6); - StorageLive(_9); - StorageLive(_8); - _8 = std::ptr::metadata::PtrComponents::<[u32]> { data_pointer: _5, metadata: _7 }; - _9 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: move _8 }; - StorageDead(_8); - _0 = (_9.0: *const [u32]); - StorageDead(_9); + _0 = *const [u32] from (_5, _7); StorageDead(_7); StorageDead(_5); StorageDead(_4); diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir index d86f2d1106a..9551f30c7e9 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir @@ -26,29 +26,19 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { scope 6 (inlined std::ptr::from_raw_parts::) { debug data_pointer => _5; debug metadata => const (); - let mut _6: std::ptr::metadata::PtrComponents; - let mut _7: std::ptr::metadata::PtrRepr; } } } bb0: { - StorageLive(_4); StorageLive(_3); _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); StorageLive(_5); _5 = _4 as *const () (PtrToPtr); - StorageLive(_7); - StorageLive(_6); - _6 = std::ptr::metadata::PtrComponents:: { data_pointer: _5, metadata: const () }; - _7 = std::ptr::metadata::PtrRepr:: { const_ptr: move _6 }; - StorageDead(_6); - _0 = (_7.0: *const u32); - StorageDead(_7); + _0 = _4 as *const u32 (PtrToPtr); StorageDead(_5); - StorageDead(_4); return; } } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir index d86f2d1106a..9551f30c7e9 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir @@ -26,29 +26,19 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { scope 6 (inlined std::ptr::from_raw_parts::) { debug data_pointer => _5; debug metadata => const (); - let mut _6: std::ptr::metadata::PtrComponents; - let mut _7: std::ptr::metadata::PtrRepr; } } } bb0: { - StorageLive(_4); StorageLive(_3); _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); StorageLive(_5); _5 = _4 as *const () (PtrToPtr); - StorageLive(_7); - StorageLive(_6); - _6 = std::ptr::metadata::PtrComponents:: { data_pointer: _5, metadata: const () }; - _7 = std::ptr::metadata::PtrRepr:: { const_ptr: move _6 }; - StorageDead(_6); - _0 = (_7.0: *const u32); - StorageDead(_7); + _0 = _4 as *const u32 (PtrToPtr); StorageDead(_5); - StorageDead(_4); return; } } From 5e1d16ca5515ae2134b69c68e7b05f6fead90f06 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 11 Apr 2024 21:45:05 -0700 Subject: [PATCH 158/185] Also handle AggregateKind::RawPtr in cg_cranelift --- compiler/rustc_codegen_cranelift/src/base.rs | 13 +++++++++++++ .../src/value_and_place.rs | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index f07421431da..f428c4c7c0d 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -813,6 +813,19 @@ fn codegen_stmt<'tcx>( ); lval.write_cvalue(fx, val); } + Rvalue::Aggregate(ref kind, ref operands) + if matches!(**kind, AggregateKind::RawPtr(..)) => + { + let ty = to_place_and_rval.1.ty(&fx.mir.local_decls, fx.tcx); + let layout = fx.layout_of(fx.monomorphize(ty)); + let [data, meta] = &*operands.raw else { + bug!("RawPtr fields: {operands:?}"); + }; + let data = codegen_operand(fx, data); + let meta = codegen_operand(fx, meta); + let ptr_val = CValue::pointer_from_data_and_meta(data, meta, layout); + lval.write_cvalue(fx, ptr_val); + } Rvalue::Aggregate(ref kind, ref operands) => { let (variant_index, variant_dest, active_field_index) = match **kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index ad863903cee..8d52fd9d7a8 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -94,6 +94,23 @@ impl<'tcx> CValue<'tcx> { CValue(CValueInner::ByValPair(value, extra), layout) } + /// For `AggregateKind::RawPtr`, create a pointer from its parts. + /// + /// Panics if the `layout` is not a raw pointer. + pub(crate) fn pointer_from_data_and_meta( + data: CValue<'tcx>, + meta: CValue<'tcx>, + layout: TyAndLayout<'tcx>, + ) -> CValue<'tcx> { + assert!(layout.ty.is_unsafe_ptr()); + let inner = match (data.0, meta.0) { + (CValueInner::ByVal(p), CValueInner::ByVal(m)) => CValueInner::ByValPair(p, m), + (p @ CValueInner::ByVal(_), CValueInner::ByRef(..)) if meta.1.is_zst() => p, + _ => bug!("RawPtr operands {data:?} {meta:?}"), + }; + CValue(inner, layout) + } + pub(crate) fn layout(&self) -> TyAndLayout<'tcx> { self.1 } From bb8d6f790b63797a816a4007e8ab002ed2906de7 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 12 Apr 2024 10:43:51 -0700 Subject: [PATCH 159/185] Address PR feedback --- .../rustc_const_eval/src/interpret/operand.rs | 11 ----------- compiler/rustc_const_eval/src/interpret/step.rs | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index d4a6dab9ac3..718c91b2f76 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -603,17 +603,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(self.read_immediate(op)?.to_scalar()) } - pub fn read_mem_place_meta( - &self, - op: &impl Readable<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, MemPlaceMeta> { - Ok(if op.layout().is_zst() { - MemPlaceMeta::None - } else { - MemPlaceMeta::Meta(self.read_scalar(op)?) - }) - } - // Pointer-sized reads are fairly common and need target layout access, so we wrap them in // convenience functions. diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 460cd377c8f..b29034e991e 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,7 +9,9 @@ use rustc_middle::mir; use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; -use super::{ImmTy, Immediate, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar}; +use super::{ + ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar, +}; use crate::util; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -304,15 +306,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (variant_index, variant_dest, active_field_index) } mir::AggregateKind::RawPtr(..) => { - // Trying to `project_field` into pointers tends not to work, - // so build the `Immediate` from the parts directly. + // Pointers don't have "fields" in the normal sense, so the + // projection-based code below would either fail in projection + // or in type mismatches. Instead, build an `Immediate` from + // the parts and write that to the destination. let [data, meta] = &operands.raw else { bug!("{kind:?} should have 2 operands, had {operands:?}"); }; let data = self.eval_operand(data, None)?; let data = self.read_pointer(&data)?; let meta = self.eval_operand(meta, None)?; - let meta = self.read_mem_place_meta(&meta)?; + let meta = if meta.layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.read_scalar(&meta)?) + }; let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self); let ptr = ImmTy::from_immediate(ptr_imm, dest.layout); self.copy_op(&ptr, dest)?; From 1398fe7a5e91992bd8887496f066370ba48d7278 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 16 Apr 2024 23:55:12 -0700 Subject: [PATCH 160/185] Address more PR feedback --- compiler/rustc_mir_transform/src/instsimplify.rs | 6 +++--- library/core/src/intrinsics.rs | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index bae959963b5..fd768cc96ae 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -121,7 +121,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { if a.const_.ty().is_bool() { a.const_.try_to_bool() } else { None } } - /// Transform "&(*a)" ==> "a". + /// Transform `&(*a)` ==> `a`. fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Ref(_, _, place) = rvalue { if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { @@ -141,7 +141,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } - /// Transform "Len([_; N])" ==> "N". + /// Transform `Len([_; N])` ==> `N`. fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(self.local_decls, self.tcx).ty; @@ -157,7 +157,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } - /// Transform "Aggregate(RawPtr, \[p, ()\])" ==> "Cast(PtrToPtr, p)". + /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 751f2210d0c..92f1bd27408 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2791,7 +2791,8 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { #[rustc_intrinsic_must_be_overridden] #[cfg(not(bootstrap))] pub const fn aggregate_raw_ptr, D, M>(_data: D, _meta: M) -> P { - // No fallback because `libcore` doesn't want to know the layout + // To implement a fallback we'd have to assume the layout of the pointer, + // but the whole point of this intrinsic is that we shouldn't do that. unreachable!() } From 5e785b1420d8d26f360a33919700868c877cbbbc Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 21 Apr 2024 11:24:54 -0700 Subject: [PATCH 161/185] Update tests after 123949 --- ...yte_add_fat.PreCodegen.after.panic-abort.mir | 10 ---------- ...te_add_fat.PreCodegen.after.panic-unwind.mir | 10 ---------- ...te_add_thin.PreCodegen.after.panic-abort.mir | 14 -------------- ...e_add_thin.PreCodegen.after.panic-unwind.mir | 14 -------------- ...ecked_range.PreCodegen.after.panic-abort.mir | 17 ----------------- ...cked_range.PreCodegen.after.panic-unwind.mir | 17 ----------------- 6 files changed, 82 deletions(-) diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir index 37f50d25fc8..db0c84bd560 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -5,29 +5,19 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { debug n => _2; let mut _0: *const [u32]; scope 1 (inlined std::ptr::const_ptr::::byte_add) { - debug self => _1; - debug count => _2; let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { - debug self => _1; } scope 3 (inlined std::ptr::const_ptr::::add) { - debug self => _3; - debug count => _2; } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { - debug self => _4; - debug meta => _1; let mut _5: *const (); let mut _7: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { - debug ptr => _1; let mut _6: std::ptr::metadata::PtrRepr<[u32]>; } scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { - debug data_pointer => _5; - debug metadata => _7; } } } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir index 37f50d25fc8..db0c84bd560 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -5,29 +5,19 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { debug n => _2; let mut _0: *const [u32]; scope 1 (inlined std::ptr::const_ptr::::byte_add) { - debug self => _1; - debug count => _2; let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { - debug self => _1; } scope 3 (inlined std::ptr::const_ptr::::add) { - debug self => _3; - debug count => _2; } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { - debug self => _4; - debug meta => _1; let mut _5: *const (); let mut _7: usize; scope 5 (inlined std::ptr::metadata::<[u32]>) { - debug ptr => _1; let mut _6: std::ptr::metadata::PtrRepr<[u32]>; } scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { - debug data_pointer => _5; - debug metadata => _7; } } } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir index 9551f30c7e9..766bd29ef41 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir @@ -5,27 +5,16 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { debug n => _2; let mut _0: *const u32; scope 1 (inlined std::ptr::const_ptr::::byte_add) { - debug self => _1; - debug count => _2; let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { - debug self => _1; } scope 3 (inlined std::ptr::const_ptr::::add) { - debug self => _3; - debug count => _2; } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { - debug self => _4; - debug meta => _1; - let mut _5: *const (); scope 5 (inlined std::ptr::metadata::) { - debug ptr => _1; } scope 6 (inlined std::ptr::from_raw_parts::) { - debug data_pointer => _5; - debug metadata => const (); } } } @@ -35,10 +24,7 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); - StorageLive(_5); - _5 = _4 as *const () (PtrToPtr); _0 = _4 as *const u32 (PtrToPtr); - StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir index 9551f30c7e9..766bd29ef41 100644 --- a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir @@ -5,27 +5,16 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { debug n => _2; let mut _0: *const u32; scope 1 (inlined std::ptr::const_ptr::::byte_add) { - debug self => _1; - debug count => _2; let mut _3: *const u8; let mut _4: *const u8; scope 2 (inlined std::ptr::const_ptr::::cast::) { - debug self => _1; } scope 3 (inlined std::ptr::const_ptr::::add) { - debug self => _3; - debug count => _2; } scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { - debug self => _4; - debug meta => _1; - let mut _5: *const (); scope 5 (inlined std::ptr::metadata::) { - debug ptr => _1; } scope 6 (inlined std::ptr::from_raw_parts::) { - debug data_pointer => _5; - debug metadata => const (); } } } @@ -35,10 +24,7 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { _3 = _1 as *const u8 (PtrToPtr); _4 = Offset(_3, _2); StorageDead(_3); - StorageLive(_5); - _5 = _4 as *const () (PtrToPtr); _0 = _4 as *const u32 (PtrToPtr); - StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir index 8446324c663..018ff6c357d 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir @@ -7,42 +7,25 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - let mut _3: usize; let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { - debug self => _1; - debug ((index: std::ops::Range).0: usize) => _3; - debug ((index: std::ops::Range).1: usize) => _4; scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { - debug ((self: std::ops::Range).0: usize) => _3; - debug ((self: std::ops::Range).1: usize) => _4; - debug slice => _1; let _5: usize; let mut _6: *const u32; let mut _7: *const u32; scope 3 { - debug new_len => _5; scope 6 (inlined std::ptr::const_ptr::::as_ptr) { - debug self => _1; } scope 7 (inlined std::ptr::const_ptr::::add) { - debug self => _6; - debug count => _3; } scope 8 (inlined slice_from_raw_parts::) { - debug data => _7; - debug len => _5; let mut _8: *const (); scope 9 (inlined std::ptr::const_ptr::::cast::<()>) { - debug self => _7; } scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) { - debug data_pointer => _8; - debug metadata => _5; } } } scope 4 (inlined std::ptr::const_ptr::::len) { - debug self => _1; scope 5 (inlined std::ptr::metadata::<[u32]>) { - debug ptr => _1; } } } diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir index 8446324c663..018ff6c357d 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir @@ -7,42 +7,25 @@ fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) - let mut _3: usize; let mut _4: usize; scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { - debug self => _1; - debug ((index: std::ops::Range).0: usize) => _3; - debug ((index: std::ops::Range).1: usize) => _4; scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { - debug ((self: std::ops::Range).0: usize) => _3; - debug ((self: std::ops::Range).1: usize) => _4; - debug slice => _1; let _5: usize; let mut _6: *const u32; let mut _7: *const u32; scope 3 { - debug new_len => _5; scope 6 (inlined std::ptr::const_ptr::::as_ptr) { - debug self => _1; } scope 7 (inlined std::ptr::const_ptr::::add) { - debug self => _6; - debug count => _3; } scope 8 (inlined slice_from_raw_parts::) { - debug data => _7; - debug len => _5; let mut _8: *const (); scope 9 (inlined std::ptr::const_ptr::::cast::<()>) { - debug self => _7; } scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) { - debug data_pointer => _8; - debug metadata => _5; } } } scope 4 (inlined std::ptr::const_ptr::::len) { - debug self => _1; scope 5 (inlined std::ptr::metadata::<[u32]>) { - debug ptr => _1; } } } From 3aaa3941fd62fb4aeea559eafe8a6aa6472eb87d Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 21 Apr 2024 15:43:43 -0300 Subject: [PATCH 162/185] Move some tests --- src/tools/tidy/src/issues.txt | 68 +++++++++---------- src/tools/tidy/src/ui_tests.rs | 2 +- .../issue-19129-1.rs | 0 .../issue-19129-2.rs | 0 .../issue-20763-1.rs | 0 .../issue-20763-2.rs | 0 .../issue-40402-1.rs | 0 .../issue-40402-1.stderr | 0 .../issue-40402-2.rs | 0 .../issue-40402-2.stderr | 0 .../ui/{issues => closures}/issue-22864-1.rs | 0 .../ui/{issues => closures}/issue-22864-2.rs | 0 tests/ui/{issues => closures}/issue-5239-1.rs | 0 .../{issues => closures}/issue-5239-1.stderr | 0 tests/ui/{issues => closures}/issue-5239-2.rs | 0 .../issue-32122-1.fixed | 0 .../issue-32122-1.rs | 0 .../issue-32122-1.stderr | 0 .../issue-32122-2.fixed | 0 .../issue-32122-2.rs | 0 .../issue-32122-2.stderr | 0 tests/ui/{issues => consts}/issue-19244-1.rs | 0 .../{issues => consts}/issue-19244-1.stderr | 0 tests/ui/{issues => consts}/issue-19244-2.rs | 0 .../{issues => consts}/issue-19244-2.stderr | 0 .../issue-71676-1.fixed | 0 .../issue-71676-1.rs | 0 .../issue-71676-1.stderr | 0 .../issue-71676-2.rs | 0 .../issue-71676-2.stderr | 0 .../auxiliary/issue-19340-1.rs | 0 tests/ui/{issues => enum}/issue-19340-1.rs | 0 tests/ui/{issues => enum}/issue-19340-2.rs | 0 tests/ui/{issues => enum}/issue-23304-1.rs | 0 tests/ui/{issues => enum}/issue-23304-2.rs | 0 tests/ui/{issues => expr}/issue-22933-1.rs | 0 tests/ui/{issues => expr}/issue-22933-2.rs | 0 .../ui/{issues => expr}/issue-22933-2.stderr | 0 tests/ui/{issues => macros}/issue-11692-1.rs | 0 .../{issues => macros}/issue-11692-1.stderr | 0 tests/ui/{issues => macros}/issue-11692-2.rs | 0 .../{issues => macros}/issue-11692-2.stderr | 0 tests/ui/{issues => nll}/issue-57362-1.rs | 0 tests/ui/{issues => nll}/issue-57362-1.stderr | 0 tests/ui/{issues => nll}/issue-57362-2.rs | 0 tests/ui/{issues => nll}/issue-57362-2.stderr | 0 tests/ui/{issues => parser}/issue-12187-1.rs | 0 .../{issues => parser}/issue-12187-1.stderr | 0 tests/ui/{issues => parser}/issue-12187-2.rs | 0 .../{issues => parser}/issue-12187-2.stderr | 0 .../ui/{issues => recursion}/issue-23122-1.rs | 0 .../issue-23122-1.stderr | 0 .../ui/{issues => recursion}/issue-23122-2.rs | 0 .../issue-23122-2.stderr | 0 tests/ui/{issues => resolve}/issue-3214.rs | 0 .../ui/{issues => resolve}/issue-3214.stderr | 0 tests/ui/{issues => type}/issue-7607-1.rs | 0 tests/ui/{issues => type}/issue-7607-1.stderr | 0 tests/ui/{issues => type}/issue-7607-2.rs | 0 59 files changed, 35 insertions(+), 35 deletions(-) rename tests/ui/{issues => associated-types}/issue-19129-1.rs (100%) rename tests/ui/{issues => associated-types}/issue-19129-2.rs (100%) rename tests/ui/{issues => associated-types}/issue-20763-1.rs (100%) rename tests/ui/{issues => associated-types}/issue-20763-2.rs (100%) rename tests/ui/{issues/issue-40402-ref-hints => binding}/issue-40402-1.rs (100%) rename tests/ui/{issues/issue-40402-ref-hints => binding}/issue-40402-1.stderr (100%) rename tests/ui/{issues/issue-40402-ref-hints => binding}/issue-40402-2.rs (100%) rename tests/ui/{issues/issue-40402-ref-hints => binding}/issue-40402-2.stderr (100%) rename tests/ui/{issues => closures}/issue-22864-1.rs (100%) rename tests/ui/{issues => closures}/issue-22864-2.rs (100%) rename tests/ui/{issues => closures}/issue-5239-1.rs (100%) rename tests/ui/{issues => closures}/issue-5239-1.stderr (100%) rename tests/ui/{issues => closures}/issue-5239-2.rs (100%) rename tests/ui/{issues/issue-32122-deref-coercions-composition => coercion}/issue-32122-1.fixed (100%) rename tests/ui/{issues/issue-32122-deref-coercions-composition => coercion}/issue-32122-1.rs (100%) rename tests/ui/{issues/issue-32122-deref-coercions-composition => coercion}/issue-32122-1.stderr (100%) rename tests/ui/{issues/issue-32122-deref-coercions-composition => coercion}/issue-32122-2.fixed (100%) rename tests/ui/{issues/issue-32122-deref-coercions-composition => coercion}/issue-32122-2.rs (100%) rename tests/ui/{issues/issue-32122-deref-coercions-composition => coercion}/issue-32122-2.stderr (100%) rename tests/ui/{issues => consts}/issue-19244-1.rs (100%) rename tests/ui/{issues => consts}/issue-19244-1.stderr (100%) rename tests/ui/{issues => consts}/issue-19244-2.rs (100%) rename tests/ui/{issues => consts}/issue-19244-2.stderr (100%) rename tests/ui/{issues/issue-71676-suggest-deref => deref-patterns}/issue-71676-1.fixed (100%) rename tests/ui/{issues/issue-71676-suggest-deref => deref-patterns}/issue-71676-1.rs (100%) rename tests/ui/{issues/issue-71676-suggest-deref => deref-patterns}/issue-71676-1.stderr (100%) rename tests/ui/{issues/issue-71676-suggest-deref => deref-patterns}/issue-71676-2.rs (100%) rename tests/ui/{issues/issue-71676-suggest-deref => deref-patterns}/issue-71676-2.stderr (100%) rename tests/ui/{issues => enum}/auxiliary/issue-19340-1.rs (100%) rename tests/ui/{issues => enum}/issue-19340-1.rs (100%) rename tests/ui/{issues => enum}/issue-19340-2.rs (100%) rename tests/ui/{issues => enum}/issue-23304-1.rs (100%) rename tests/ui/{issues => enum}/issue-23304-2.rs (100%) rename tests/ui/{issues => expr}/issue-22933-1.rs (100%) rename tests/ui/{issues => expr}/issue-22933-2.rs (100%) rename tests/ui/{issues => expr}/issue-22933-2.stderr (100%) rename tests/ui/{issues => macros}/issue-11692-1.rs (100%) rename tests/ui/{issues => macros}/issue-11692-1.stderr (100%) rename tests/ui/{issues => macros}/issue-11692-2.rs (100%) rename tests/ui/{issues => macros}/issue-11692-2.stderr (100%) rename tests/ui/{issues => nll}/issue-57362-1.rs (100%) rename tests/ui/{issues => nll}/issue-57362-1.stderr (100%) rename tests/ui/{issues => nll}/issue-57362-2.rs (100%) rename tests/ui/{issues => nll}/issue-57362-2.stderr (100%) rename tests/ui/{issues => parser}/issue-12187-1.rs (100%) rename tests/ui/{issues => parser}/issue-12187-1.stderr (100%) rename tests/ui/{issues => parser}/issue-12187-2.rs (100%) rename tests/ui/{issues => parser}/issue-12187-2.stderr (100%) rename tests/ui/{issues => recursion}/issue-23122-1.rs (100%) rename tests/ui/{issues => recursion}/issue-23122-1.stderr (100%) rename tests/ui/{issues => recursion}/issue-23122-2.rs (100%) rename tests/ui/{issues => recursion}/issue-23122-2.stderr (100%) rename tests/ui/{issues => resolve}/issue-3214.rs (100%) rename tests/ui/{issues => resolve}/issue-3214.stderr (100%) rename tests/ui/{issues => type}/issue-7607-1.rs (100%) rename tests/ui/{issues => type}/issue-7607-1.stderr (100%) rename tests/ui/{issues => type}/issue-7607-2.rs (100%) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 211dc347b0f..dff15265fae 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -74,8 +74,12 @@ ui/associated-type-bounds/issue-99828.rs ui/associated-type-bounds/return-type-notation/issue-120208-higher-ranked-const.rs ui/associated-types/issue-18655.rs ui/associated-types/issue-19081.rs +ui/associated-types/issue-19129-1.rs +ui/associated-types/issue-19129-2.rs ui/associated-types/issue-19883.rs ui/associated-types/issue-20005.rs +ui/associated-types/issue-20763-1.rs +ui/associated-types/issue-20763-2.rs ui/associated-types/issue-20825-2.rs ui/associated-types/issue-20825.rs ui/associated-types/issue-21363.rs @@ -281,6 +285,8 @@ ui/auxiliary/issue-18502.rs ui/auxiliary/issue-24106.rs ui/auxiliary/issue-76387.rs ui/bench/issue-32062.rs +ui/binding/issue-40402-1.rs +ui/binding/issue-40402-2.rs ui/binding/issue-53114-borrow-checks.rs ui/binding/issue-53114-safety-checks.rs ui/binop/issue-25916.rs @@ -431,12 +437,16 @@ ui/closures/issue-111932.rs ui/closures/issue-113087.rs ui/closures/issue-11873.rs ui/closures/issue-1460.rs +ui/closures/issue-22864-1.rs +ui/closures/issue-22864-2.rs ui/closures/issue-23012-supertrait-signature-inference.rs ui/closures/issue-25439.rs ui/closures/issue-41366.rs ui/closures/issue-42463.rs ui/closures/issue-46742.rs ui/closures/issue-48109.rs +ui/closures/issue-5239-1.rs +ui/closures/issue-5239-2.rs ui/closures/issue-52437.rs ui/closures/issue-67123.rs ui/closures/issue-6801.rs @@ -482,6 +492,8 @@ ui/coercion/issue-101066.rs ui/coercion/issue-14589.rs ui/coercion/issue-26905-rpass.rs ui/coercion/issue-26905.rs +ui/coercion/issue-32122-1.rs +ui/coercion/issue-32122-2.rs ui/coercion/issue-36007.rs ui/coercion/issue-37655.rs ui/coercion/issue-3794.rs @@ -721,6 +733,8 @@ ui/consts/issue-17718-references.rs ui/consts/issue-17718.rs ui/consts/issue-17756.rs ui/consts/issue-18294.rs +ui/consts/issue-19244-1.rs +ui/consts/issue-19244-2.rs ui/consts/issue-19244.rs ui/consts/issue-21562.rs ui/consts/issue-21721.rs @@ -845,6 +859,8 @@ ui/cycle-trait/issue-12511.rs ui/debuginfo/issue-105386-debuginfo-ub.rs ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs ui/deprecation/issue-84637-deprecated-associated-function.rs +ui/deref-patterns/issue-71676-1.rs +ui/deref-patterns/issue-71676-2.rs ui/derived-errors/issue-30580.rs ui/derived-errors/issue-31997-1.rs ui/derived-errors/issue-31997.rs @@ -952,7 +968,12 @@ ui/enum-discriminant/issue-70453-polymorphic-ctfe.rs ui/enum-discriminant/issue-70509-partial_eq.rs ui/enum-discriminant/issue-72554.rs ui/enum-discriminant/issue-90038.rs +ui/enum/auxiliary/issue-19340-1.rs ui/enum/issue-1821.rs +ui/enum/issue-19340-1.rs +ui/enum/issue-19340-2.rs +ui/enum/issue-23304-1.rs +ui/enum/issue-23304-2.rs ui/enum/issue-42747.rs ui/enum/issue-67945-1.rs ui/enum/issue-67945-2.rs @@ -965,6 +986,8 @@ ui/errors/issue-104621-extern-not-file.rs ui/errors/issue-89280-emitter-overflow-splice-lines.rs ui/errors/issue-99572-impl-trait-on-pointer.rs ui/expr/if/issue-4201.rs +ui/expr/issue-22933-1.rs +ui/expr/issue-22933-2.rs ui/extern/auxiliary/issue-80074-macro-2.rs ui/extern/auxiliary/issue-80074-macro.rs ui/extern/issue-10025.rs @@ -1400,7 +1423,6 @@ ui/issues/auxiliary/issue-18711.rs ui/issues/auxiliary/issue-18913-1.rs ui/issues/auxiliary/issue-18913-2.rs ui/issues/auxiliary/issue-19293.rs -ui/issues/auxiliary/issue-19340-1.rs ui/issues/auxiliary/issue-20389.rs ui/issues/auxiliary/issue-21202.rs ui/issues/auxiliary/issue-2170-lib.rs @@ -1492,8 +1514,6 @@ ui/issues/issue-11592.rs ui/issues/issue-11677.rs ui/issues/issue-11680.rs ui/issues/issue-11681.rs -ui/issues/issue-11692-1.rs -ui/issues/issue-11692-2.rs ui/issues/issue-11709.rs ui/issues/issue-11740.rs ui/issues/issue-11771.rs @@ -1504,8 +1524,6 @@ ui/issues/issue-11958.rs ui/issues/issue-12033.rs ui/issues/issue-12041.rs ui/issues/issue-12127.rs -ui/issues/issue-12187-1.rs -ui/issues/issue-12187-2.rs ui/issues/issue-12285.rs ui/issues/issue-12567.rs ui/issues/issue-12612.rs @@ -1713,14 +1731,8 @@ ui/issues/issue-19097.rs ui/issues/issue-19098.rs ui/issues/issue-19100.rs ui/issues/issue-19127.rs -ui/issues/issue-19129-1.rs -ui/issues/issue-19129-2.rs ui/issues/issue-19135.rs -ui/issues/issue-19244-1.rs -ui/issues/issue-19244-2.rs ui/issues/issue-19293.rs -ui/issues/issue-19340-1.rs -ui/issues/issue-19340-2.rs ui/issues/issue-19367.rs ui/issues/issue-19380.rs ui/issues/issue-19398.rs @@ -1761,8 +1773,6 @@ ui/issues/issue-20644.rs ui/issues/issue-20676.rs ui/issues/issue-20714.rs ui/issues/issue-2074.rs -ui/issues/issue-20763-1.rs -ui/issues/issue-20763-2.rs ui/issues/issue-20772.rs ui/issues/issue-20797.rs ui/issues/issue-20803.rs @@ -1835,15 +1845,11 @@ ui/issues/issue-22789.rs ui/issues/issue-2281-part1.rs ui/issues/issue-22814.rs ui/issues/issue-2284.rs -ui/issues/issue-22864-1.rs -ui/issues/issue-22864-2.rs ui/issues/issue-22872.rs ui/issues/issue-22874.rs ui/issues/issue-2288.rs ui/issues/issue-22886.rs ui/issues/issue-22894.rs -ui/issues/issue-22933-1.rs -ui/issues/issue-22933-2.rs ui/issues/issue-22992-2.rs ui/issues/issue-22992.rs ui/issues/issue-23024.rs @@ -1854,8 +1860,6 @@ ui/issues/issue-23073.rs ui/issues/issue-2311-2.rs ui/issues/issue-2311.rs ui/issues/issue-2312.rs -ui/issues/issue-23122-1.rs -ui/issues/issue-23122-2.rs ui/issues/issue-2316-c.rs ui/issues/issue-23173.rs ui/issues/issue-23189.rs @@ -1863,8 +1867,6 @@ ui/issues/issue-23217.rs ui/issues/issue-23253.rs ui/issues/issue-23261.rs ui/issues/issue-23281.rs -ui/issues/issue-23304-1.rs -ui/issues/issue-23304-2.rs ui/issues/issue-23311.rs ui/issues/issue-23336.rs ui/issues/issue-23354-2.rs @@ -2092,9 +2094,6 @@ ui/issues/issue-31910.rs ui/issues/issue-32004.rs ui/issues/issue-32008.rs ui/issues/issue-32086.rs -ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs -ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs -ui/issues/issue-3214.rs ui/issues/issue-3220.rs ui/issues/issue-32292.rs ui/issues/issue-32324.rs @@ -2243,8 +2242,6 @@ ui/issues/issue-4025.rs ui/issues/issue-40288-2.rs ui/issues/issue-40288.rs ui/issues/issue-40350.rs -ui/issues/issue-40402-ref-hints/issue-40402-1.rs -ui/issues/issue-40402-ref-hints/issue-40402-2.rs ui/issues/issue-40408.rs ui/issues/issue-40610.rs ui/issues/issue-40749.rs @@ -2438,8 +2435,6 @@ ui/issues/issue-51947.rs ui/issues/issue-52049.rs ui/issues/issue-52126-assign-op-invariance.rs ui/issues/issue-52262.rs -ui/issues/issue-5239-1.rs -ui/issues/issue-5239-2.rs ui/issues/issue-52489.rs ui/issues/issue-52533.rs ui/issues/issue-52717.rs @@ -2491,8 +2486,6 @@ ui/issues/issue-57162.rs ui/issues/issue-5718.rs ui/issues/issue-57198-pass.rs ui/issues/issue-57271.rs -ui/issues/issue-57362-1.rs -ui/issues/issue-57362-2.rs ui/issues/issue-57399-self-return-impl-trait.rs ui/issues/issue-5741.rs ui/issues/issue-5754.rs @@ -2578,8 +2571,6 @@ ui/issues/issue-70724-add_type_neq_err_label-unwrap.rs ui/issues/issue-70746.rs ui/issues/issue-7092.rs ui/issues/issue-71406.rs -ui/issues/issue-71676-suggest-deref/issue-71676-1.rs -ui/issues/issue-71676-suggest-deref/issue-71676-2.rs ui/issues/issue-7178.rs ui/issues/issue-72002.rs ui/issues/issue-72076.rs @@ -2600,8 +2591,6 @@ ui/issues/issue-7563.rs ui/issues/issue-75704.rs ui/issues/issue-7575.rs ui/issues/issue-76042.rs -ui/issues/issue-7607-1.rs -ui/issues/issue-7607-2.rs ui/issues/issue-76077-inaccesible-private-fields/issue-76077-1.rs ui/issues/issue-76077-inaccesible-private-fields/issue-76077.rs ui/issues/issue-76191.rs @@ -2851,6 +2840,8 @@ ui/macros/issue-109237.rs ui/macros/issue-111749.rs ui/macros/issue-112342-1.rs ui/macros/issue-112342-2.rs +ui/macros/issue-11692-1.rs +ui/macros/issue-11692-2.rs ui/macros/issue-118048.rs ui/macros/issue-118786.rs ui/macros/issue-16098.rs @@ -3162,6 +3153,8 @@ ui/nll/issue-57265-return-type-wf-check.rs ui/nll/issue-57280-1-flipped.rs ui/nll/issue-57280-1.rs ui/nll/issue-57280.rs +ui/nll/issue-57362-1.rs +ui/nll/issue-57362-2.rs ui/nll/issue-57642-higher-ranked-subtype.rs ui/nll/issue-57843.rs ui/nll/issue-57960.rs @@ -3218,6 +3211,8 @@ ui/packed/issue-27060.rs ui/packed/issue-46152.rs ui/panics/issue-47429-short-backtraces.rs ui/parser/issue-116781.rs +ui/parser/issue-12187-1.rs +ui/parser/issue-12187-2.rs ui/parser/issues/auxiliary/issue-21146-inc.rs ui/parser/issues/auxiliary/issue-89971-outer-attr-following-inner-attr-ice.rs ui/parser/issues/auxiliary/issue-94340-inc.rs @@ -3613,6 +3608,8 @@ ui/reachable/issue-11225-1.rs ui/reachable/issue-11225-2.rs ui/reachable/issue-11225-3.rs ui/reachable/issue-948.rs +ui/recursion/issue-23122-1.rs +ui/recursion/issue-23122-2.rs ui/recursion/issue-23302-1.rs ui/recursion/issue-23302-2.rs ui/recursion/issue-23302-3.rs @@ -3692,6 +3689,7 @@ ui/resolve/issue-30535.rs ui/resolve/issue-3099-a.rs ui/resolve/issue-3099-b.rs ui/resolve/issue-31845.rs +ui/resolve/issue-3214.rs ui/resolve/issue-33876.rs ui/resolve/issue-35675.rs ui/resolve/issue-3907-2.rs @@ -4203,6 +4201,8 @@ ui/type/issue-102598.rs ui/type/issue-103271.rs ui/type/issue-58355.rs ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs +ui/type/issue-7607-1.rs +ui/type/issue-7607-2.rs ui/type/issue-91268.rs ui/type/issue-94187-verbose-type-name.rs ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 7136bc4d8f2..f9985a75703 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -17,7 +17,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: usize = 1720; +const ISSUES_ENTRY_LIMIT: usize = 1676; const ROOT_ENTRY_LIMIT: usize = 859; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ diff --git a/tests/ui/issues/issue-19129-1.rs b/tests/ui/associated-types/issue-19129-1.rs similarity index 100% rename from tests/ui/issues/issue-19129-1.rs rename to tests/ui/associated-types/issue-19129-1.rs diff --git a/tests/ui/issues/issue-19129-2.rs b/tests/ui/associated-types/issue-19129-2.rs similarity index 100% rename from tests/ui/issues/issue-19129-2.rs rename to tests/ui/associated-types/issue-19129-2.rs diff --git a/tests/ui/issues/issue-20763-1.rs b/tests/ui/associated-types/issue-20763-1.rs similarity index 100% rename from tests/ui/issues/issue-20763-1.rs rename to tests/ui/associated-types/issue-20763-1.rs diff --git a/tests/ui/issues/issue-20763-2.rs b/tests/ui/associated-types/issue-20763-2.rs similarity index 100% rename from tests/ui/issues/issue-20763-2.rs rename to tests/ui/associated-types/issue-20763-2.rs diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.rs b/tests/ui/binding/issue-40402-1.rs similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-1.rs rename to tests/ui/binding/issue-40402-1.rs diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/tests/ui/binding/issue-40402-1.stderr similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr rename to tests/ui/binding/issue-40402-1.stderr diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-2.rs b/tests/ui/binding/issue-40402-2.rs similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-2.rs rename to tests/ui/binding/issue-40402-2.rs diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr b/tests/ui/binding/issue-40402-2.stderr similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr rename to tests/ui/binding/issue-40402-2.stderr diff --git a/tests/ui/issues/issue-22864-1.rs b/tests/ui/closures/issue-22864-1.rs similarity index 100% rename from tests/ui/issues/issue-22864-1.rs rename to tests/ui/closures/issue-22864-1.rs diff --git a/tests/ui/issues/issue-22864-2.rs b/tests/ui/closures/issue-22864-2.rs similarity index 100% rename from tests/ui/issues/issue-22864-2.rs rename to tests/ui/closures/issue-22864-2.rs diff --git a/tests/ui/issues/issue-5239-1.rs b/tests/ui/closures/issue-5239-1.rs similarity index 100% rename from tests/ui/issues/issue-5239-1.rs rename to tests/ui/closures/issue-5239-1.rs diff --git a/tests/ui/issues/issue-5239-1.stderr b/tests/ui/closures/issue-5239-1.stderr similarity index 100% rename from tests/ui/issues/issue-5239-1.stderr rename to tests/ui/closures/issue-5239-1.stderr diff --git a/tests/ui/issues/issue-5239-2.rs b/tests/ui/closures/issue-5239-2.rs similarity index 100% rename from tests/ui/issues/issue-5239-2.rs rename to tests/ui/closures/issue-5239-2.rs diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.fixed b/tests/ui/coercion/issue-32122-1.fixed similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.fixed rename to tests/ui/coercion/issue-32122-1.fixed diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs b/tests/ui/coercion/issue-32122-1.rs similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs rename to tests/ui/coercion/issue-32122-1.rs diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.stderr b/tests/ui/coercion/issue-32122-1.stderr similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.stderr rename to tests/ui/coercion/issue-32122-1.stderr diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.fixed b/tests/ui/coercion/issue-32122-2.fixed similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.fixed rename to tests/ui/coercion/issue-32122-2.fixed diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs b/tests/ui/coercion/issue-32122-2.rs similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs rename to tests/ui/coercion/issue-32122-2.rs diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.stderr b/tests/ui/coercion/issue-32122-2.stderr similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.stderr rename to tests/ui/coercion/issue-32122-2.stderr diff --git a/tests/ui/issues/issue-19244-1.rs b/tests/ui/consts/issue-19244-1.rs similarity index 100% rename from tests/ui/issues/issue-19244-1.rs rename to tests/ui/consts/issue-19244-1.rs diff --git a/tests/ui/issues/issue-19244-1.stderr b/tests/ui/consts/issue-19244-1.stderr similarity index 100% rename from tests/ui/issues/issue-19244-1.stderr rename to tests/ui/consts/issue-19244-1.stderr diff --git a/tests/ui/issues/issue-19244-2.rs b/tests/ui/consts/issue-19244-2.rs similarity index 100% rename from tests/ui/issues/issue-19244-2.rs rename to tests/ui/consts/issue-19244-2.rs diff --git a/tests/ui/issues/issue-19244-2.stderr b/tests/ui/consts/issue-19244-2.stderr similarity index 100% rename from tests/ui/issues/issue-19244-2.stderr rename to tests/ui/consts/issue-19244-2.stderr diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.fixed b/tests/ui/deref-patterns/issue-71676-1.fixed similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.fixed rename to tests/ui/deref-patterns/issue-71676-1.fixed diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.rs b/tests/ui/deref-patterns/issue-71676-1.rs similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.rs rename to tests/ui/deref-patterns/issue-71676-1.rs diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.stderr b/tests/ui/deref-patterns/issue-71676-1.stderr similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.stderr rename to tests/ui/deref-patterns/issue-71676-1.stderr diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.rs b/tests/ui/deref-patterns/issue-71676-2.rs similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.rs rename to tests/ui/deref-patterns/issue-71676-2.rs diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.stderr b/tests/ui/deref-patterns/issue-71676-2.stderr similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.stderr rename to tests/ui/deref-patterns/issue-71676-2.stderr diff --git a/tests/ui/issues/auxiliary/issue-19340-1.rs b/tests/ui/enum/auxiliary/issue-19340-1.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-19340-1.rs rename to tests/ui/enum/auxiliary/issue-19340-1.rs diff --git a/tests/ui/issues/issue-19340-1.rs b/tests/ui/enum/issue-19340-1.rs similarity index 100% rename from tests/ui/issues/issue-19340-1.rs rename to tests/ui/enum/issue-19340-1.rs diff --git a/tests/ui/issues/issue-19340-2.rs b/tests/ui/enum/issue-19340-2.rs similarity index 100% rename from tests/ui/issues/issue-19340-2.rs rename to tests/ui/enum/issue-19340-2.rs diff --git a/tests/ui/issues/issue-23304-1.rs b/tests/ui/enum/issue-23304-1.rs similarity index 100% rename from tests/ui/issues/issue-23304-1.rs rename to tests/ui/enum/issue-23304-1.rs diff --git a/tests/ui/issues/issue-23304-2.rs b/tests/ui/enum/issue-23304-2.rs similarity index 100% rename from tests/ui/issues/issue-23304-2.rs rename to tests/ui/enum/issue-23304-2.rs diff --git a/tests/ui/issues/issue-22933-1.rs b/tests/ui/expr/issue-22933-1.rs similarity index 100% rename from tests/ui/issues/issue-22933-1.rs rename to tests/ui/expr/issue-22933-1.rs diff --git a/tests/ui/issues/issue-22933-2.rs b/tests/ui/expr/issue-22933-2.rs similarity index 100% rename from tests/ui/issues/issue-22933-2.rs rename to tests/ui/expr/issue-22933-2.rs diff --git a/tests/ui/issues/issue-22933-2.stderr b/tests/ui/expr/issue-22933-2.stderr similarity index 100% rename from tests/ui/issues/issue-22933-2.stderr rename to tests/ui/expr/issue-22933-2.stderr diff --git a/tests/ui/issues/issue-11692-1.rs b/tests/ui/macros/issue-11692-1.rs similarity index 100% rename from tests/ui/issues/issue-11692-1.rs rename to tests/ui/macros/issue-11692-1.rs diff --git a/tests/ui/issues/issue-11692-1.stderr b/tests/ui/macros/issue-11692-1.stderr similarity index 100% rename from tests/ui/issues/issue-11692-1.stderr rename to tests/ui/macros/issue-11692-1.stderr diff --git a/tests/ui/issues/issue-11692-2.rs b/tests/ui/macros/issue-11692-2.rs similarity index 100% rename from tests/ui/issues/issue-11692-2.rs rename to tests/ui/macros/issue-11692-2.rs diff --git a/tests/ui/issues/issue-11692-2.stderr b/tests/ui/macros/issue-11692-2.stderr similarity index 100% rename from tests/ui/issues/issue-11692-2.stderr rename to tests/ui/macros/issue-11692-2.stderr diff --git a/tests/ui/issues/issue-57362-1.rs b/tests/ui/nll/issue-57362-1.rs similarity index 100% rename from tests/ui/issues/issue-57362-1.rs rename to tests/ui/nll/issue-57362-1.rs diff --git a/tests/ui/issues/issue-57362-1.stderr b/tests/ui/nll/issue-57362-1.stderr similarity index 100% rename from tests/ui/issues/issue-57362-1.stderr rename to tests/ui/nll/issue-57362-1.stderr diff --git a/tests/ui/issues/issue-57362-2.rs b/tests/ui/nll/issue-57362-2.rs similarity index 100% rename from tests/ui/issues/issue-57362-2.rs rename to tests/ui/nll/issue-57362-2.rs diff --git a/tests/ui/issues/issue-57362-2.stderr b/tests/ui/nll/issue-57362-2.stderr similarity index 100% rename from tests/ui/issues/issue-57362-2.stderr rename to tests/ui/nll/issue-57362-2.stderr diff --git a/tests/ui/issues/issue-12187-1.rs b/tests/ui/parser/issue-12187-1.rs similarity index 100% rename from tests/ui/issues/issue-12187-1.rs rename to tests/ui/parser/issue-12187-1.rs diff --git a/tests/ui/issues/issue-12187-1.stderr b/tests/ui/parser/issue-12187-1.stderr similarity index 100% rename from tests/ui/issues/issue-12187-1.stderr rename to tests/ui/parser/issue-12187-1.stderr diff --git a/tests/ui/issues/issue-12187-2.rs b/tests/ui/parser/issue-12187-2.rs similarity index 100% rename from tests/ui/issues/issue-12187-2.rs rename to tests/ui/parser/issue-12187-2.rs diff --git a/tests/ui/issues/issue-12187-2.stderr b/tests/ui/parser/issue-12187-2.stderr similarity index 100% rename from tests/ui/issues/issue-12187-2.stderr rename to tests/ui/parser/issue-12187-2.stderr diff --git a/tests/ui/issues/issue-23122-1.rs b/tests/ui/recursion/issue-23122-1.rs similarity index 100% rename from tests/ui/issues/issue-23122-1.rs rename to tests/ui/recursion/issue-23122-1.rs diff --git a/tests/ui/issues/issue-23122-1.stderr b/tests/ui/recursion/issue-23122-1.stderr similarity index 100% rename from tests/ui/issues/issue-23122-1.stderr rename to tests/ui/recursion/issue-23122-1.stderr diff --git a/tests/ui/issues/issue-23122-2.rs b/tests/ui/recursion/issue-23122-2.rs similarity index 100% rename from tests/ui/issues/issue-23122-2.rs rename to tests/ui/recursion/issue-23122-2.rs diff --git a/tests/ui/issues/issue-23122-2.stderr b/tests/ui/recursion/issue-23122-2.stderr similarity index 100% rename from tests/ui/issues/issue-23122-2.stderr rename to tests/ui/recursion/issue-23122-2.stderr diff --git a/tests/ui/issues/issue-3214.rs b/tests/ui/resolve/issue-3214.rs similarity index 100% rename from tests/ui/issues/issue-3214.rs rename to tests/ui/resolve/issue-3214.rs diff --git a/tests/ui/issues/issue-3214.stderr b/tests/ui/resolve/issue-3214.stderr similarity index 100% rename from tests/ui/issues/issue-3214.stderr rename to tests/ui/resolve/issue-3214.stderr diff --git a/tests/ui/issues/issue-7607-1.rs b/tests/ui/type/issue-7607-1.rs similarity index 100% rename from tests/ui/issues/issue-7607-1.rs rename to tests/ui/type/issue-7607-1.rs diff --git a/tests/ui/issues/issue-7607-1.stderr b/tests/ui/type/issue-7607-1.stderr similarity index 100% rename from tests/ui/issues/issue-7607-1.stderr rename to tests/ui/type/issue-7607-1.stderr diff --git a/tests/ui/issues/issue-7607-2.rs b/tests/ui/type/issue-7607-2.rs similarity index 100% rename from tests/ui/issues/issue-7607-2.rs rename to tests/ui/type/issue-7607-2.rs From 67748015631ebbb798c6b8f2693fead06d06ce87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 21 Apr 2024 21:01:04 +0200 Subject: [PATCH 163/185] crashes: add a couple more ICE tests --- tests/crashes/123664.rs | 4 ++++ tests/crashes/123955.rs | 6 ++++++ tests/crashes/124092.rs | 7 +++++++ tests/crashes/124182.rs | 22 ++++++++++++++++++++++ tests/crashes/124189.rs | 14 ++++++++++++++ tests/crashes/124207.rs | 9 +++++++++ 6 files changed, 62 insertions(+) create mode 100644 tests/crashes/123664.rs create mode 100644 tests/crashes/123955.rs create mode 100644 tests/crashes/124092.rs create mode 100644 tests/crashes/124182.rs create mode 100644 tests/crashes/124189.rs create mode 100644 tests/crashes/124207.rs diff --git a/tests/crashes/123664.rs b/tests/crashes/123664.rs new file mode 100644 index 00000000000..80c415fe07b --- /dev/null +++ b/tests/crashes/123664.rs @@ -0,0 +1,4 @@ +//@ known-bug: #123664 +#![feature(generic_const_exprs, effects)] +const fn with_positive() {} +pub fn main() {} diff --git a/tests/crashes/123955.rs b/tests/crashes/123955.rs new file mode 100644 index 00000000000..fdd58c84794 --- /dev/null +++ b/tests/crashes/123955.rs @@ -0,0 +1,6 @@ +//@ known-bug: #123955 +//@ compile-flags: -Clto -Zvirtual-function-elimination +//@ only-x86_64 +pub fn main() { + _ = Box::new(()) as Box; +} diff --git a/tests/crashes/124092.rs b/tests/crashes/124092.rs new file mode 100644 index 00000000000..c03db384e76 --- /dev/null +++ b/tests/crashes/124092.rs @@ -0,0 +1,7 @@ +//@ known-bug: #124092 +//@ compile-flags: -Zvirtual-function-elimination=true -Clto=true +//@ only-x86_64 +const X: for<'b> fn(&'b ()) = |&()| (); +fn main() { + let dyn_debug = Box::new(X) as Box as Box; +} diff --git a/tests/crashes/124182.rs b/tests/crashes/124182.rs new file mode 100644 index 00000000000..46948207df3 --- /dev/null +++ b/tests/crashes/124182.rs @@ -0,0 +1,22 @@ +//@ known-bug: #124182 +struct LazyLock { + data: (Copy, fn() -> T), +} + +impl LazyLock { + pub const fn new(f: fn() -> T) -> LazyLock { + LazyLock { data: (None, f) } + } +} + +struct A(Option); + +impl Default for A { + fn default() -> Self { + A(None) + } +} + +static EMPTY_SET: LazyLock> = LazyLock::new(A::default); + +fn main() {} diff --git a/tests/crashes/124189.rs b/tests/crashes/124189.rs new file mode 100644 index 00000000000..7c193ec1bce --- /dev/null +++ b/tests/crashes/124189.rs @@ -0,0 +1,14 @@ +//@ known-bug: #124189 +trait Trait { + type Type; +} + +impl Trait for T { + type Type = (); +} + +fn f(_: <&Copy as Trait>::Type) {} + +fn main() { + f(()); +} diff --git a/tests/crashes/124207.rs b/tests/crashes/124207.rs new file mode 100644 index 00000000000..a4e1c551890 --- /dev/null +++ b/tests/crashes/124207.rs @@ -0,0 +1,9 @@ +//@ known-bug: #124207 +#![feature(transmutability)] +#![feature(type_alias_impl_trait)] +trait OpaqueTrait {} +type OpaqueType = impl OpaqueTrait; +trait AnotherTrait {} +impl> AnotherTrait for T {} +impl AnotherTrait for OpaqueType {} +pub fn main() {} From 28f60ff9a4f11432ad5c2910a706522f04e0f3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 21 Apr 2024 21:27:42 +0200 Subject: [PATCH 164/185] add test for #121413 Fixes #121413 --- .../consts/const_refs_to_static-ice-121413.rs | 16 +++++++ .../const_refs_to_static-ice-121413.stderr | 46 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/ui/consts/const_refs_to_static-ice-121413.rs create mode 100644 tests/ui/consts/const_refs_to_static-ice-121413.stderr diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.rs b/tests/ui/consts/const_refs_to_static-ice-121413.rs new file mode 100644 index 00000000000..8a24fb799b6 --- /dev/null +++ b/tests/ui/consts/const_refs_to_static-ice-121413.rs @@ -0,0 +1,16 @@ +// ICE: ImmTy { imm: Scalar(alloc1), ty: *const dyn Sync } input to a fat-to-thin cast (*const dyn Sync -> *const usize +// or with -Zextra-const-ub-checks: expected wide pointer extra data (e.g. slice length or trait object vtable) +// issue: rust-lang/rust#121413 +//@ compile-flags: -Zextra-const-ub-checks +// ignore-tidy-linelength +#![feature(const_refs_to_static)] +const REF_INTERIOR_MUT: &usize = { + static FOO: Sync = AtomicUsize::new(0); + //~^ ERROR failed to resolve: use of undeclared type `AtomicUsize` + //~| WARN trait objects without an explicit `dyn` are deprecated + //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + unsafe { &*(&FOO as *const _ as *const usize) } +}; +pub fn main() {} diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr new file mode 100644 index 00000000000..c977c698a92 --- /dev/null +++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr @@ -0,0 +1,46 @@ +error[E0433]: failed to resolve: use of undeclared type `AtomicUsize` + --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^^^^^^^^ use of undeclared type `AtomicUsize` + | +help: consider importing this struct + | +LL + use std::sync::atomic::AtomicUsize; + | + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: `#[warn(bare_trait_objects)]` on by default +help: if this is an object-safe trait, use `dyn` + | +LL | static FOO: dyn Sync = AtomicUsize::new(0); + | +++ + +error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` + +error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` + = note: constant expressions must have a statically known size + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0277, E0433. +For more information about an error, try `rustc --explain E0277`. From 751f662b703d27e028769722a8a47e02d3948b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 21 Apr 2024 21:31:21 +0200 Subject: [PATCH 165/185] add test for ice #121463 Fixes #121463 --- ...T-struct-pattern-box-pattern-ice-121463.rs | 12 +++++++++++ ...ruct-pattern-box-pattern-ice-121463.stderr | 21 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs create mode 100644 tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr diff --git a/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs new file mode 100644 index 00000000000..cf927e34fb4 --- /dev/null +++ b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs @@ -0,0 +1,12 @@ +// issue rust-lang/rust#121463 +// ICE non-ADT in struct pattern +#![feature(box_patterns)] + +fn main() { + let mut a = E::StructVar { boxed: Box::new(5_i32) }; + //~^ ERROR failed to resolve: use of undeclared type `E` + match a { + E::StructVar { box boxed } => { } + //~^ ERROR failed to resolve: use of undeclared type `E` + } +} diff --git a/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr new file mode 100644 index 00000000000..349546606a5 --- /dev/null +++ b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr @@ -0,0 +1,21 @@ +error[E0433]: failed to resolve: use of undeclared type `E` + --> $DIR/non-ADT-struct-pattern-box-pattern-ice-121463.rs:6:17 + | +LL | let mut a = E::StructVar { boxed: Box::new(5_i32) }; + | ^ + | | + | use of undeclared type `E` + | help: a trait with a similar name exists: `Eq` + +error[E0433]: failed to resolve: use of undeclared type `E` + --> $DIR/non-ADT-struct-pattern-box-pattern-ice-121463.rs:9:9 + | +LL | E::StructVar { box boxed } => { } + | ^ + | | + | use of undeclared type `E` + | help: a trait with a similar name exists: `Eq` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. From a42adf2f9549eedb715b027e3ba7794bddab127e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sun, 21 Apr 2024 21:43:47 +0200 Subject: [PATCH 166/185] add test for ICE caused by using feature(generic_const_exprs) #114463 Fixes #114463 --- .../cannot-convert-refree-ice-114463.rs | 10 ++++++++++ .../cannot-convert-refree-ice-114463.stderr | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr diff --git a/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs new file mode 100644 index 00000000000..2ce998e9fd9 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs @@ -0,0 +1,10 @@ +// issue: rust-lang/rust#114463 +// ICE cannot convert `ReFree ..` to a region vid +#![feature(generic_const_exprs)] +//~^ WARN the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes +fn bug<'a>() { + [(); (|_: &'a u8| (), 0).1]; + //~^ ERROR cannot capture late-bound lifetime in constant +} + +pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr new file mode 100644 index 00000000000..e4845405ec8 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr @@ -0,0 +1,19 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/cannot-convert-refree-ice-114463.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: cannot capture late-bound lifetime in constant + --> $DIR/cannot-convert-refree-ice-114463.rs:6:16 + | +LL | fn bug<'a>() { + | -- lifetime defined here +LL | [(); (|_: &'a u8| (), 0).1]; + | ^^ + +error: aborting due to 1 previous error; 1 warning emitted + From 9989d009c4e21835df16ff4d849a8b0e811a953f Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 21 Apr 2024 21:35:17 -0400 Subject: [PATCH 167/185] Update stdarch submodule --- library/stdarch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch b/library/stdarch index 7df81ba8c3e..c0257c1660e 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 7df81ba8c3e2d02c2ace0c5a6f4f32d800c09e56 +Subproject commit c0257c1660e78c80ad1b9136fcc5555b14da5b4c From 9470e0555075a0c4f2d09097cf1ae80a38587435 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 21 Apr 2024 18:46:52 -0700 Subject: [PATCH 168/185] bootstrap: Promote some build_steps comments to docs --- src/bootstrap/src/core/build_steps/check.rs | 2 +- src/bootstrap/src/core/build_steps/clippy.rs | 2 +- src/bootstrap/src/core/build_steps/compile.rs | 16 ++++++++-------- src/bootstrap/src/core/build_steps/dist.rs | 12 ++++++------ src/bootstrap/src/core/build_steps/doc.rs | 2 +- src/bootstrap/src/core/build_steps/install.rs | 4 ++-- src/bootstrap/src/core/build_steps/llvm.rs | 8 ++++---- src/bootstrap/src/core/build_steps/test.rs | 10 +++++----- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 927d72e8ccb..8235d4634b7 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -394,7 +394,7 @@ macro_rules! tool_check_step { impl Step for $name { type Output = (); const ONLY_HOSTS: bool = true; - // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken + /// don't ever check out-of-tree tools by default, they'll fail when toolstate is broken const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 33323ec1e5d..01b5e99116f 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -24,7 +24,7 @@ use super::compile::std_cargo; use super::tool::prepare_tool_cargo; use super::tool::SourceType; -// Disable the most spammy clippy lints +/// Disable the most spammy clippy lints const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[ "many_single_char_names", // there are a lot in stdarch "collapsible_if", diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 734c64ad4df..c20653b5dfa 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -835,13 +835,13 @@ impl Rustc { } impl Step for Rustc { - // We return the stage of the "actual" compiler (not the uplifted one). - // - // By "actual" we refer to the uplifting logic where we may not compile the requested stage; - // instead, we uplift it from the previous stages. Which can lead to bootstrap failures in - // specific situations where we request stage X from other steps. However we may end up - // uplifting it from stage Y, causing the other stage to fail when attempting to link with - // stage X which was never actually built. + /// We return the stage of the "actual" compiler (not the uplifted one). + /// + /// By "actual" we refer to the uplifting logic where we may not compile the requested stage; + /// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in + /// specific situations where we request stage X from other steps. However we may end up + /// uplifting it from stage Y, causing the other stage to fail when attempting to link with + /// stage X which was never actually built. type Output = u32; const ONLY_HOSTS: bool = true; const DEFAULT: bool = false; @@ -1302,7 +1302,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { impl Step for CodegenBackend { type Output = (); const ONLY_HOSTS: bool = true; - // Only the backends specified in the `codegen-backends` entry of `config.toml` are built. + /// Only the backends specified in the `codegen-backends` entry of `config.toml` are built. const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 22482ba4796..0eca20901b7 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2272,9 +2272,9 @@ impl Step for LlvmBitcodeLinker { } } -// Tarball intended for internal consumption to ease rustc/std development. -// -// Should not be considered stable by end users. +/// Tarball intended for internal consumption to ease rustc/std development. +/// +/// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct RustDev { pub target: TargetSelection, @@ -2356,9 +2356,9 @@ impl Step for RustDev { } } -// Tarball intended for internal consumption to ease rustc/std development. -// -// Should not be considered stable by end users. +/// Tarball intended for internal consumption to ease rustc/std development. +/// +/// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Bootstrap { pub target: TargetSelection, diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index a0acdd9013a..38c48bd9570 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -506,7 +506,7 @@ impl Step for SharedAssets { run.never() } - // Generate shared resources used by other pieces of documentation. + /// Generate shared resources used by other pieces of documentation. fn run(self, builder: &Builder<'_>) -> Self::Output { let out = builder.doc_out(self.target); diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 294f2a11b74..6a75f35c93a 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -20,8 +20,8 @@ const SHELL: &str = "bash"; #[cfg(not(target_os = "illumos"))] const SHELL: &str = "sh"; -// We have to run a few shell scripts, which choke quite a bit on both `\` -// characters and on `C:\` paths, so normalize both of them away. +/// We have to run a few shell scripts, which choke quite a bit on both `\` +/// characters and on `C:\` paths, so normalize both of them away. fn sanitize_sh(path: &Path) -> String { let path = path.to_str().unwrap().replace('\\', "/"); return change_drive(unc_to_lfs(&path)).unwrap_or(path); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index fd513f23206..d4473e24039 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -56,14 +56,14 @@ impl LlvmBuildStatus { } } -// Linker flags to pass to LLVM's CMake invocation. +/// Linker flags to pass to LLVM's CMake invocation. #[derive(Debug, Clone, Default)] struct LdFlags { - // CMAKE_EXE_LINKER_FLAGS + /// CMAKE_EXE_LINKER_FLAGS exe: OsString, - // CMAKE_SHARED_LINKER_FLAGS + /// CMAKE_SHARED_LINKER_FLAGS shared: OsString, - // CMAKE_MODULE_LINKER_FLAGS + /// CMAKE_MODULE_LINKER_FLAGS module: OsString, } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 1a94c2a0948..ca10a128b98 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1433,8 +1433,8 @@ host_test!(RustdocJson { path: "tests/rustdoc-json", mode: "rustdoc-json", suite host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" }); -// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` -// tests. +/// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` +/// tests. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct RunMake { pub compiler: Compiler, @@ -1527,10 +1527,10 @@ impl Coverage { impl Step for Coverage { type Output = (); - // We rely on the individual CoverageMap/CoverageRun steps to run themselves. + /// We rely on the individual CoverageMap/CoverageRun steps to run themselves. const DEFAULT: bool = false; - // When manually invoked, try to run as much as possible. - // Compiletest will automatically skip the "coverage-run" tests if necessary. + /// When manually invoked, try to run as much as possible. + /// Compiletest will automatically skip the "coverage-run" tests if necessary. const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { From f31e4aef0b4e0ed02864dfd5ca5615960c85837c Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Mon, 22 Apr 2024 08:16:47 +0530 Subject: [PATCH 169/185] Add comma at one place in `abs()` documentation --- library/core/src/num/int_macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index ab0c633ca0a..de25b999bde 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -3199,7 +3199,7 @@ macro_rules! int_impl { /// that code in debug mode will trigger a panic on this case and /// optimized code will return #[doc = concat!("`", stringify!($SelfT), "::MIN`")] - /// without a panic. If you do not want this behavior consider + /// without a panic. If you do not want this behavior, consider /// using [`unsigned_abs`](Self::unsigned_abs) instead. /// /// # Examples From c373ec07c42f9c403c3eed5de431907e7ccbd49d Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 22 Apr 2024 12:11:07 +0200 Subject: [PATCH 170/185] Improve ICE message for forbidden dep-graph reads. --- .../rustc_query_system/src/dep_graph/graph.rs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 534937003eb..af70e2a4264 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -459,7 +459,8 @@ impl DepGraph { } TaskDepsRef::Ignore => return, TaskDepsRef::Forbid => { - panic!("Illegal read of: {dep_node_index:?}") + // Reading is forbidden in this context. ICE with a useful error message. + panic_on_forbidden_read(data, dep_node_index) } }; let task_deps = &mut *task_deps; @@ -1366,3 +1367,41 @@ pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: Option< eprintln!("end of try_mark_green dep node stack"); } + +#[cold] +#[inline(never)] +fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> ! { + // We have to do an expensive reverse-lookup of the DepNode that + // corresponds to `dep_node_index`, but that's OK since we are about + // to ICE anyway. + let mut dep_node = None; + + // First try to find the dep node among those that already existed in the + // previous session + for (prev_index, index) in data.current.prev_index_to_index.lock().iter_enumerated() { + if index == &Some(dep_node_index) { + dep_node = Some(data.previous.index_to_node(prev_index)); + break; + } + } + + if dep_node.is_none() { + // Try to find it among the new nodes + for shard in data.current.new_node_to_index.lock_shards() { + if let Some((node, _)) = shard.iter().find(|(_, index)| **index == dep_node_index) { + dep_node = Some(*node); + break; + } + } + } + + let dep_node = dep_node.map_or_else( + || format!("with index {:?}", dep_node_index), + |dep_node| format!("`{:?}`", dep_node), + ); + + panic!( + "Error: trying to record dependency on DepNode {dep_node} in a \ + context that does not allow it (e.g. during query deserialization)." + ) +} From 4f7a47798eb3455aed74fde5cd8e81927d2db07a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Apr 2024 11:41:40 +1000 Subject: [PATCH 171/185] coverage: Branch coverage test for let-else --- tests/coverage/branch/let-else.cov-map | 18 ++++++++++++ tests/coverage/branch/let-else.coverage | 37 +++++++++++++++++++++++++ tests/coverage/branch/let-else.rs | 35 +++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 tests/coverage/branch/let-else.cov-map create mode 100644 tests/coverage/branch/let-else.coverage create mode 100644 tests/coverage/branch/let-else.rs diff --git a/tests/coverage/branch/let-else.cov-map b/tests/coverage/branch/let-else.cov-map new file mode 100644 index 00000000000..ad987bd6bb1 --- /dev/null +++ b/tests/coverage/branch/let-else.cov-map @@ -0,0 +1,18 @@ +Function name: let_else::let_else +Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Expression(0, Sub)) at (prev + 3, 14) to (start + 0, 15) + = (c1 - c2) +- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24) +- Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15) +- Code(Expression(0, Sub)) at (prev + 4, 5) to (start + 0, 11) + = (c1 - c2) +- Code(Expression(1, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c2 + (c1 - c2)) + diff --git a/tests/coverage/branch/let-else.coverage b/tests/coverage/branch/let-else.coverage new file mode 100644 index 00000000000..83730e1dfba --- /dev/null +++ b/tests/coverage/branch/let-else.coverage @@ -0,0 +1,37 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 3|fn let_else(value: Option<&str>) { + LL| 3| no_merge!(); + LL| | + LL| 3| let Some(x) = value else { + ^2 + LL| 1| say("none"); + LL| 1| return; + LL| | }; + LL| | + LL| 2| say(x); + LL| 3|} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | let_else(Some("x")); + LL| | let_else(Some("x")); + LL| | let_else(None); + LL| |} + LL| | + LL| |// FIXME(#124118) Actually instrument let-else for branch coverage. + diff --git a/tests/coverage/branch/let-else.rs b/tests/coverage/branch/let-else.rs new file mode 100644 index 00000000000..af0665d8241 --- /dev/null +++ b/tests/coverage/branch/let-else.rs @@ -0,0 +1,35 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn let_else(value: Option<&str>) { + no_merge!(); + + let Some(x) = value else { + say("none"); + return; + }; + + say(x); +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} + +#[coverage(off)] +fn main() { + let_else(Some("x")); + let_else(Some("x")); + let_else(None); +} + +// FIXME(#124118) Actually instrument let-else for branch coverage. From 7f432dfb23f264f5c368464f849663a518750a93 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Apr 2024 11:41:40 +1000 Subject: [PATCH 172/185] coverage: Branch coverage test for if-let and let-chains --- tests/coverage/branch/if-let.cov-map | 41 ++++++++++++++++++ tests/coverage/branch/if-let.coverage | 62 +++++++++++++++++++++++++++ tests/coverage/branch/if-let.rs | 58 +++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 tests/coverage/branch/if-let.cov-map create mode 100644 tests/coverage/branch/if-let.coverage create mode 100644 tests/coverage/branch/if-let.rs diff --git a/tests/coverage/branch/if-let.cov-map b/tests/coverage/branch/if-let.cov-map new file mode 100644 index 00000000000..c12df8d9801 --- /dev/null +++ b/tests/coverage/branch/if-let.cov-map @@ -0,0 +1,41 @@ +Function name: if_let::if_let +Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 18) + = (c1 - c2) +- Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27) +- Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6) + = (c1 - c2) +- Code(Counter(2)) at (prev + 2, 12) to (start + 2, 6) +- Code(Expression(1, Add)) at (prev + 3, 5) to (start + 1, 2) + = (c2 + (c1 - c2)) + +Function name: if_let::if_let_chain +Raw bytes (52): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 08, 01, 17, 01, 00, 33, 02, 01, 11, 00, 12, 01, 00, 16, 00, 17, 0d, 01, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 51) +- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 18) + = (c0 - c1) +- Code(Counter(0)) at (prev + 0, 22) to (start + 0, 23) +- Code(Counter(3)) at (prev + 1, 21) to (start + 0, 22) +- Code(Expression(0, Sub)) at (prev + 0, 26) to (start + 0, 27) + = (c0 - c1) +- Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6) +- Code(Expression(3, Add)) at (prev + 3, 12) to (start + 2, 6) + = (c1 + c2) +- Code(Expression(2, Add)) at (prev + 3, 5) to (start + 1, 2) + = ((c1 + c2) + c3) + diff --git a/tests/coverage/branch/if-let.coverage b/tests/coverage/branch/if-let.coverage new file mode 100644 index 00000000000..f30c5d34eca --- /dev/null +++ b/tests/coverage/branch/if-let.coverage @@ -0,0 +1,62 @@ + LL| |#![feature(coverage_attribute, let_chains)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 3|fn if_let(input: Option<&str>) { + LL| 3| no_merge!(); + LL| | + LL| 3| if let Some(x) = input { + ^2 + LL| 2| say(x); + LL| 2| } else { + LL| 1| say("none"); + LL| 1| } + LL| 3| say("done"); + LL| 3|} + LL| | + LL| 15|fn if_let_chain(a: Option<&str>, b: Option<&str>) { + LL| 15| if let Some(x) = a + ^12 + LL| 12| && let Some(y) = b + ^8 + LL| 8| { + LL| 8| say(x); + LL| 8| say(y); + LL| 8| } else { + LL| 7| say("not both"); + LL| 7| } + LL| 15| say("done"); + LL| 15|} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | if_let(Some("x")); + LL| | if_let(Some("x")); + LL| | if_let(None); + LL| | + LL| | for _ in 0..8 { + LL| | if_let_chain(Some("a"), Some("b")); + LL| | } + LL| | for _ in 0..4 { + LL| | if_let_chain(Some("a"), None); + LL| | } + LL| | for _ in 0..2 { + LL| | if_let_chain(None, Some("b")); + LL| | } + LL| | if_let_chain(None, None); + LL| |} + LL| | + LL| |// FIXME(#124118) Actually instrument if-let and let-chains for branch coverage. + diff --git a/tests/coverage/branch/if-let.rs b/tests/coverage/branch/if-let.rs new file mode 100644 index 00000000000..13db00a82b1 --- /dev/null +++ b/tests/coverage/branch/if-let.rs @@ -0,0 +1,58 @@ +#![feature(coverage_attribute, let_chains)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn if_let(input: Option<&str>) { + no_merge!(); + + if let Some(x) = input { + say(x); + } else { + say("none"); + } + say("done"); +} + +fn if_let_chain(a: Option<&str>, b: Option<&str>) { + if let Some(x) = a + && let Some(y) = b + { + say(x); + say(y); + } else { + say("not both"); + } + say("done"); +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} + +#[coverage(off)] +fn main() { + if_let(Some("x")); + if_let(Some("x")); + if_let(None); + + for _ in 0..8 { + if_let_chain(Some("a"), Some("b")); + } + for _ in 0..4 { + if_let_chain(Some("a"), None); + } + for _ in 0..2 { + if_let_chain(None, Some("b")); + } + if_let_chain(None, None); +} + +// FIXME(#124118) Actually instrument if-let and let-chains for branch coverage. From 3de87feba23c6b9df520f284b51390c02ea8d12a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Apr 2024 12:32:11 +1000 Subject: [PATCH 173/185] coverage: Branch coverage tests for match arms --- tests/coverage/branch/match-arms.cov-map | 92 ++++++++++++++++ tests/coverage/branch/match-arms.coverage | 105 +++++++++++++++++++ tests/coverage/branch/match-arms.rs | 90 ++++++++++++++++ tests/coverage/branch/match-trivial.cov-map | 17 +++ tests/coverage/branch/match-trivial.coverage | 49 +++++++++ tests/coverage/branch/match-trivial.rs | 48 +++++++++ 6 files changed, 401 insertions(+) create mode 100644 tests/coverage/branch/match-arms.cov-map create mode 100644 tests/coverage/branch/match-arms.coverage create mode 100644 tests/coverage/branch/match-arms.rs create mode 100644 tests/coverage/branch/match-trivial.cov-map create mode 100644 tests/coverage/branch/match-trivial.coverage create mode 100644 tests/coverage/branch/match-trivial.rs diff --git a/tests/coverage/branch/match-arms.cov-map b/tests/coverage/branch/match-arms.cov-map new file mode 100644 index 00000000000..1f17f11baaa --- /dev/null +++ b/tests/coverage/branch/match-arms.cov-map @@ -0,0 +1,92 @@ +Function name: match_arms::guards +Raw bytes (88): 0x[01, 01, 08, 07, 15, 0b, 11, 0f, 0d, 00, 09, 17, 25, 1b, 21, 1f, 1d, 03, 19, 0c, 01, 30, 01, 01, 10, 29, 03, 0b, 00, 10, 19, 01, 11, 00, 29, 20, 19, 09, 00, 17, 00, 1b, 1d, 01, 11, 00, 29, 20, 1d, 0d, 00, 17, 00, 1b, 21, 01, 11, 00, 29, 20, 21, 11, 00, 17, 00, 1b, 25, 01, 11, 00, 29, 20, 25, 15, 00, 17, 00, 1b, 03, 01, 0e, 00, 18, 13, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 8 +- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(5) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3) +- expression 3 operands: lhs = Zero, rhs = Counter(2) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(9) +- expression 5 operands: lhs = Expression(6, Add), rhs = Counter(8) +- expression 6 operands: lhs = Expression(7, Add), rhs = Counter(7) +- expression 7 operands: lhs = Expression(0, Add), rhs = Counter(6) +Number of file 0 mappings: 12 +- Code(Counter(0)) at (prev + 48, 1) to (start + 1, 16) +- Code(Counter(10)) at (prev + 3, 11) to (start + 0, 16) +- Code(Counter(6)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(6), false: Counter(2) } at (prev + 0, 23) to (start + 0, 27) + true = c6 + false = c2 +- Code(Counter(7)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(7), false: Counter(3) } at (prev + 0, 23) to (start + 0, 27) + true = c7 + false = c3 +- Code(Counter(8)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(8), false: Counter(4) } at (prev + 0, 23) to (start + 0, 27) + true = c8 + false = c4 +- Code(Counter(9)) at (prev + 1, 17) to (start + 0, 41) +- Branch { true: Counter(9), false: Counter(5) } at (prev + 0, 23) to (start + 0, 27) + true = c9 + false = c5 +- Code(Expression(0, Add)) at (prev + 1, 14) to (start + 0, 24) + = ((((Zero + c2) + c3) + c4) + c5) +- Code(Expression(4, Add)) at (prev + 3, 5) to (start + 1, 2) + = ((((((((Zero + c2) + c3) + c4) + c5) + c6) + c7) + c8) + c9) + +Function name: match_arms::match_arms +Raw bytes (51): 0x[01, 01, 06, 05, 07, 0b, 11, 09, 0d, 13, 02, 17, 09, 11, 0d, 07, 01, 18, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 21, 0d, 01, 11, 00, 21, 09, 01, 11, 00, 21, 02, 01, 11, 00, 21, 0f, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(1), rhs = Expression(1, Add) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(4) +- expression 2 operands: lhs = Counter(2), rhs = Counter(3) +- expression 3 operands: lhs = Expression(4, Add), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(2) +- expression 5 operands: lhs = Counter(4), rhs = Counter(3) +Number of file 0 mappings: 7 +- Code(Counter(0)) at (prev + 24, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16) +- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 33) +- Code(Counter(3)) at (prev + 1, 17) to (start + 0, 33) +- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 33) +- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 33) + = (c1 - ((c2 + c3) + c4)) +- Code(Expression(3, Add)) at (prev + 3, 5) to (start + 1, 2) + = (((c4 + c3) + c2) + (c1 - ((c2 + c3) + c4))) + +Function name: match_arms::or_patterns +Raw bytes (75): 0x[01, 01, 0d, 11, 0d, 05, 2f, 33, 11, 09, 0d, 09, 2a, 05, 2f, 33, 11, 09, 0d, 03, 27, 09, 2a, 05, 2f, 33, 11, 09, 0d, 09, 01, 25, 01, 01, 10, 05, 03, 0b, 00, 10, 11, 01, 11, 00, 12, 0d, 00, 1e, 00, 1f, 03, 00, 24, 00, 2e, 09, 01, 11, 00, 12, 2a, 00, 1e, 00, 1f, 27, 00, 24, 00, 2e, 23, 03, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 13 +- expression 0 operands: lhs = Counter(4), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Expression(11, Add) +- expression 2 operands: lhs = Expression(12, Add), rhs = Counter(4) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +- expression 4 operands: lhs = Counter(2), rhs = Expression(10, Sub) +- expression 5 operands: lhs = Counter(1), rhs = Expression(11, Add) +- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(4) +- expression 7 operands: lhs = Counter(2), rhs = Counter(3) +- expression 8 operands: lhs = Expression(0, Add), rhs = Expression(9, Add) +- expression 9 operands: lhs = Counter(2), rhs = Expression(10, Sub) +- expression 10 operands: lhs = Counter(1), rhs = Expression(11, Add) +- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(4) +- expression 12 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 9 +- Code(Counter(0)) at (prev + 37, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 11) to (start + 0, 16) +- Code(Counter(4)) at (prev + 1, 17) to (start + 0, 18) +- Code(Counter(3)) at (prev + 0, 30) to (start + 0, 31) +- Code(Expression(0, Add)) at (prev + 0, 36) to (start + 0, 46) + = (c4 + c3) +- Code(Counter(2)) at (prev + 1, 17) to (start + 0, 18) +- Code(Expression(10, Sub)) at (prev + 0, 30) to (start + 0, 31) + = (c1 - ((c2 + c3) + c4)) +- Code(Expression(9, Add)) at (prev + 0, 36) to (start + 0, 46) + = (c2 + (c1 - ((c2 + c3) + c4))) +- Code(Expression(8, Add)) at (prev + 3, 5) to (start + 1, 2) + = ((c4 + c3) + (c2 + (c1 - ((c2 + c3) + c4)))) + diff --git a/tests/coverage/branch/match-arms.coverage b/tests/coverage/branch/match-arms.coverage new file mode 100644 index 00000000000..ea8a6f97ab1 --- /dev/null +++ b/tests/coverage/branch/match-arms.coverage @@ -0,0 +1,105 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |// Tests for branch coverage of various kinds of match arms. + LL| | + LL| |// Helper macro to prevent start-of-function spans from being merged into + LL| |// spans on the lines we care about. + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| |#[derive(Clone, Copy, Debug)] + LL| |enum Enum { + LL| | A(u32), + LL| | B(u32), + LL| | C(u32), + LL| | D(u32), + LL| |} + LL| | + LL| 15|fn match_arms(value: Enum) { + LL| 15| no_merge!(); + LL| | + LL| 15| match value { + LL| 8| Enum::D(d) => consume(d), + LL| 4| Enum::C(c) => consume(c), + LL| 2| Enum::B(b) => consume(b), + LL| 1| Enum::A(a) => consume(a), + LL| | } + LL| | + LL| 15| consume(0); + LL| 15|} + LL| | + LL| 15|fn or_patterns(value: Enum) { + LL| 15| no_merge!(); + LL| | + LL| 15| match value { + LL| 12| Enum::D(x) | Enum::C(x) => consume(x), + ^8 ^4 + LL| 3| Enum::B(y) | Enum::A(y) => consume(y), + ^2 ^1 + LL| | } + LL| | + LL| 15| consume(0); + LL| 15|} + LL| | + LL| 45|fn guards(value: Enum, cond: bool) { + LL| 45| no_merge!(); + LL| | + LL| 3| match value { + LL| 8| Enum::D(d) if cond => consume(d), + ------------------ + | Branch (LL:23): [True: 8, False: 16] + ------------------ + LL| 4| Enum::C(c) if cond => consume(c), + ------------------ + | Branch (LL:23): [True: 4, False: 8] + ------------------ + LL| 2| Enum::B(b) if cond => consume(b), + ------------------ + | Branch (LL:23): [True: 2, False: 4] + ------------------ + LL| 1| Enum::A(a) if cond => consume(a), + ------------------ + | Branch (LL:23): [True: 1, False: 2] + ------------------ + LL| 30| _ => consume(0), + LL| | } + LL| | + LL| 45| consume(0); + LL| 45|} + LL| | + LL| |#[coverage(off)] + LL| |fn consume(x: T) { + LL| | core::hint::black_box(x); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | #[coverage(off)] + LL| | fn call_everything(e: Enum) { + LL| | match_arms(e); + LL| | or_patterns(e); + LL| | for cond in [false, false, true] { + LL| | guards(e, cond); + LL| | } + LL| | } + LL| | + LL| | call_everything(Enum::A(0)); + LL| | for b in 0..2 { + LL| | call_everything(Enum::B(b)); + LL| | } + LL| | for c in 0..4 { + LL| | call_everything(Enum::C(c)); + LL| | } + LL| | for d in 0..8 { + LL| | call_everything(Enum::D(d)); + LL| | } + LL| |} + LL| | + LL| |// FIXME(#124118) Actually instrument match arms for branch coverage. + diff --git a/tests/coverage/branch/match-arms.rs b/tests/coverage/branch/match-arms.rs new file mode 100644 index 00000000000..63151f59ffe --- /dev/null +++ b/tests/coverage/branch/match-arms.rs @@ -0,0 +1,90 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +// Tests for branch coverage of various kinds of match arms. + +// Helper macro to prevent start-of-function spans from being merged into +// spans on the lines we care about. +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +#[derive(Clone, Copy, Debug)] +enum Enum { + A(u32), + B(u32), + C(u32), + D(u32), +} + +fn match_arms(value: Enum) { + no_merge!(); + + match value { + Enum::D(d) => consume(d), + Enum::C(c) => consume(c), + Enum::B(b) => consume(b), + Enum::A(a) => consume(a), + } + + consume(0); +} + +fn or_patterns(value: Enum) { + no_merge!(); + + match value { + Enum::D(x) | Enum::C(x) => consume(x), + Enum::B(y) | Enum::A(y) => consume(y), + } + + consume(0); +} + +fn guards(value: Enum, cond: bool) { + no_merge!(); + + match value { + Enum::D(d) if cond => consume(d), + Enum::C(c) if cond => consume(c), + Enum::B(b) if cond => consume(b), + Enum::A(a) if cond => consume(a), + _ => consume(0), + } + + consume(0); +} + +#[coverage(off)] +fn consume(x: T) { + core::hint::black_box(x); +} + +#[coverage(off)] +fn main() { + #[coverage(off)] + fn call_everything(e: Enum) { + match_arms(e); + or_patterns(e); + for cond in [false, false, true] { + guards(e, cond); + } + } + + call_everything(Enum::A(0)); + for b in 0..2 { + call_everything(Enum::B(b)); + } + for c in 0..4 { + call_everything(Enum::C(c)); + } + for d in 0..8 { + call_everything(Enum::D(d)); + } +} + +// FIXME(#124118) Actually instrument match arms for branch coverage. diff --git a/tests/coverage/branch/match-trivial.cov-map b/tests/coverage/branch/match-trivial.cov-map new file mode 100644 index 00000000000..1136a529b25 --- /dev/null +++ b/tests/coverage/branch/match-trivial.cov-map @@ -0,0 +1,17 @@ +Function name: match_trivial::_uninhabited (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 16, 01, 01, 10] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 22, 1) to (start + 1, 16) + +Function name: match_trivial::trivial +Raw bytes (14): 0x[01, 01, 00, 02, 01, 1e, 01, 01, 10, 05, 03, 0b, 05, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 30, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 11) to (start + 5, 2) + diff --git a/tests/coverage/branch/match-trivial.coverage b/tests/coverage/branch/match-trivial.coverage new file mode 100644 index 00000000000..4ffb172e1b6 --- /dev/null +++ b/tests/coverage/branch/match-trivial.coverage @@ -0,0 +1,49 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |// When instrumenting match expressions for branch coverage, make sure we don't + LL| |// cause an ICE or produce weird coverage output for matches with <2 arms. + LL| | + LL| |// Helper macro to prevent start-of-function spans from being merged into + LL| |// spans on the lines we care about. + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| |enum Uninhabited {} + LL| |enum Trivial { + LL| | Value, + LL| |} + LL| | + LL| 0|fn _uninhabited(x: Uninhabited) { + LL| 0| no_merge!(); + LL| | + LL| | match x {} + LL| | + LL| | consume("done"); + LL| |} + LL| | + LL| 1|fn trivial(x: Trivial) { + LL| 1| no_merge!(); + LL| | + LL| 1| match x { + LL| 1| Trivial::Value => consume("trivial"), + LL| 1| } + LL| 1| + LL| 1| consume("done"); + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn consume(x: T) { + LL| | core::hint::black_box(x); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | trivial(Trivial::Value); + LL| |} + diff --git a/tests/coverage/branch/match-trivial.rs b/tests/coverage/branch/match-trivial.rs new file mode 100644 index 00000000000..db8887a26b7 --- /dev/null +++ b/tests/coverage/branch/match-trivial.rs @@ -0,0 +1,48 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +// When instrumenting match expressions for branch coverage, make sure we don't +// cause an ICE or produce weird coverage output for matches with <2 arms. + +// Helper macro to prevent start-of-function spans from being merged into +// spans on the lines we care about. +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +enum Uninhabited {} +enum Trivial { + Value, +} + +fn _uninhabited(x: Uninhabited) { + no_merge!(); + + match x {} + + consume("done"); +} + +fn trivial(x: Trivial) { + no_merge!(); + + match x { + Trivial::Value => consume("trivial"), + } + + consume("done"); +} + +#[coverage(off)] +fn consume(x: T) { + core::hint::black_box(x); +} + +#[coverage(off)] +fn main() { + trivial(Trivial::Value); +} From da37b14121d0fed6e49a4ed27abd2b9e7fc1c486 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Apr 2024 14:50:25 +1000 Subject: [PATCH 174/185] coverage: Move mir-opt coverage tests into a subdirectory --- .../instrument_coverage.bar.InstrumentCoverage.diff | 0 .../instrument_coverage.main.InstrumentCoverage.diff | 0 tests/mir-opt/{ => coverage}/instrument_coverage.rs | 0 .../instrument_coverage_cleanup.main.CleanupPostBorrowck.diff | 0 .../instrument_coverage_cleanup.main.InstrumentCoverage.diff | 0 tests/mir-opt/{ => coverage}/instrument_coverage_cleanup.rs | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename tests/mir-opt/{ => coverage}/instrument_coverage.bar.InstrumentCoverage.diff (100%) rename tests/mir-opt/{ => coverage}/instrument_coverage.main.InstrumentCoverage.diff (100%) rename tests/mir-opt/{ => coverage}/instrument_coverage.rs (100%) rename tests/mir-opt/{ => coverage}/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff (100%) rename tests/mir-opt/{ => coverage}/instrument_coverage_cleanup.main.InstrumentCoverage.diff (100%) rename tests/mir-opt/{ => coverage}/instrument_coverage_cleanup.rs (100%) diff --git a/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff similarity index 100% rename from tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff rename to tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff similarity index 100% rename from tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff rename to tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage.rs b/tests/mir-opt/coverage/instrument_coverage.rs similarity index 100% rename from tests/mir-opt/instrument_coverage.rs rename to tests/mir-opt/coverage/instrument_coverage.rs diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff similarity index 100% rename from tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff rename to tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff similarity index 100% rename from tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff rename to tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs similarity index 100% rename from tests/mir-opt/instrument_coverage_cleanup.rs rename to tests/mir-opt/coverage/instrument_coverage_cleanup.rs From a892c2387e64ba080a6503d422931f3a0916826a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 19 Apr 2024 15:35:23 +1000 Subject: [PATCH 175/185] coverage: Add a mir-opt test for branch coverage of match arms --- ...ch_match_arms.main.InstrumentCoverage.diff | 138 ++++++++++++++++++ tests/mir-opt/coverage/branch_match_arms.rs | 27 ++++ 2 files changed, 165 insertions(+) create mode 100644 tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff create mode 100644 tests/mir-opt/coverage/branch_match_arms.rs diff --git a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff new file mode 100644 index 00000000000..e60f71f47b1 --- /dev/null +++ b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff @@ -0,0 +1,138 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); + let mut _1: Enum; + let mut _2: isize; + let _3: u32; + let mut _4: u32; + let _5: u32; + let mut _6: u32; + let _7: u32; + let mut _8: u32; + let _9: u32; + let mut _10: u32; + scope 1 { + debug d => _3; + } + scope 2 { + debug c => _5; + } + scope 3 { + debug b => _7; + } + scope 4 { + debug a => _9; + } + ++ coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) }; ++ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) }; ++ coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) }; ++ coverage ExpressionId(3) => Expression { lhs: Counter(3), op: Add, rhs: Counter(2) }; ++ coverage ExpressionId(4) => Expression { lhs: Expression(3), op: Add, rhs: Counter(1) }; ++ coverage ExpressionId(5) => Expression { lhs: Expression(4), op: Add, rhs: Expression(2) }; ++ coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1 - 15:21; ++ coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:16:17 - 16:33; ++ coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17 - 17:33; ++ coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:18:17 - 18:33; ++ coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17 - 19:33; ++ coverage Code(Expression(5)) => $DIR/branch_match_arms.rs:21:1 - 21:2; ++ + bb0: { ++ Coverage::CounterIncrement(0); + StorageLive(_1); + _1 = Enum::A(const 0_u32); + PlaceMention(_1); + _2 = discriminant(_1); + switchInt(move _2) -> [0: bb5, 1: bb4, 2: bb3, 3: bb2, otherwise: bb1]; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb2: { ++ Coverage::CounterIncrement(3); + falseEdge -> [real: bb6, imaginary: bb3]; + } + + bb3: { ++ Coverage::CounterIncrement(2); + falseEdge -> [real: bb8, imaginary: bb4]; + } + + bb4: { ++ Coverage::CounterIncrement(1); + falseEdge -> [real: bb10, imaginary: bb5]; + } + + bb5: { ++ Coverage::ExpressionUsed(2); + StorageLive(_9); + _9 = ((_1 as A).0: u32); + StorageLive(_10); + _10 = _9; + _0 = consume(move _10) -> [return: bb12, unwind: bb14]; + } + + bb6: { + StorageLive(_3); + _3 = ((_1 as D).0: u32); + StorageLive(_4); + _4 = _3; + _0 = consume(move _4) -> [return: bb7, unwind: bb14]; + } + + bb7: { + StorageDead(_4); + StorageDead(_3); + goto -> bb13; + } + + bb8: { + StorageLive(_5); + _5 = ((_1 as C).0: u32); + StorageLive(_6); + _6 = _5; + _0 = consume(move _6) -> [return: bb9, unwind: bb14]; + } + + bb9: { + StorageDead(_6); + StorageDead(_5); + goto -> bb13; + } + + bb10: { + StorageLive(_7); + _7 = ((_1 as B).0: u32); + StorageLive(_8); + _8 = _7; + _0 = consume(move _8) -> [return: bb11, unwind: bb14]; + } + + bb11: { + StorageDead(_8); + StorageDead(_7); + goto -> bb13; + } + + bb12: { + StorageDead(_10); + StorageDead(_9); + goto -> bb13; + } + + bb13: { ++ Coverage::ExpressionUsed(5); + StorageDead(_1); + return; + } + + bb14 (cleanup): { + resume; + } + } + diff --git a/tests/mir-opt/coverage/branch_match_arms.rs b/tests/mir-opt/coverage/branch_match_arms.rs new file mode 100644 index 00000000000..18764b38d6e --- /dev/null +++ b/tests/mir-opt/coverage/branch_match_arms.rs @@ -0,0 +1,27 @@ +#![feature(coverage_attribute)] +//@ test-mir-pass: InstrumentCoverage +//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime -Zcoverage-options=branch +// skip-filecheck + +enum Enum { + A(u32), + B(u32), + C(u32), + D(u32), +} + +// EMIT_MIR branch_match_arms.main.InstrumentCoverage.diff +fn main() { + match Enum::A(0) { + Enum::D(d) => consume(d), + Enum::C(c) => consume(c), + Enum::B(b) => consume(b), + Enum::A(a) => consume(a), + } +} + +#[inline(never)] +#[coverage(off)] +fn consume(x: u32) { + core::hint::black_box(x); +} From 97bf5536827ea7a1ba6b7cf856dd2b22184d2527 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Apr 2024 12:06:03 +1000 Subject: [PATCH 176/185] coverage: Detach MC/DC branch spans from regular branch spans MC/DC's reliance on the existing branch coverage types is making it much harder to improve branch coverage. --- compiler/rustc_middle/src/mir/coverage.rs | 4 +- compiler/rustc_middle/src/mir/pretty.rs | 2 +- .../rustc_mir_build/src/build/coverageinfo.rs | 47 +++++------- .../rustc_mir_transform/src/coverage/mod.rs | 16 +++- .../rustc_mir_transform/src/coverage/spans.rs | 10 ++- .../src/coverage/spans/from_mir.rs | 74 ++++++++++++++----- 6 files changed, 101 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index b1d0c815ae0..04011fd4194 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -314,7 +314,9 @@ impl Default for ConditionInfo { #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct MCDCBranchSpan { pub span: Span, - pub condition_info: ConditionInfo, + /// If `None`, this actually represents a normal branch span inserted for + /// code that was too complex for MC/DC. + pub condition_info: Option, pub true_marker: BlockMarkerId, pub false_marker: BlockMarkerId, } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8291404ebf3..0c9ceae63c1 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -491,7 +491,7 @@ fn write_coverage_branch_info( writeln!( w, "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", - condition_info.condition_id + condition_info.map(|info| info.condition_id) )?; } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 57f809ef7cf..15963c1234f 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -22,6 +22,7 @@ pub(crate) struct BranchInfoBuilder { num_block_markers: usize, branch_spans: Vec, + mcdc_branch_spans: Vec, mcdc_decision_spans: Vec, mcdc_state: Option, @@ -95,13 +96,7 @@ impl BranchInfoBuilder { } } - fn record_conditions_operation(&mut self, logical_op: LogicalOp, span: Span) { - if let Some(mcdc_state) = self.mcdc_state.as_mut() { - mcdc_state.record_conditions(logical_op, span); - } - } - - fn fetch_condition_info( + fn fetch_mcdc_condition_info( &mut self, tcx: TyCtxt<'_>, true_marker: BlockMarkerId, @@ -121,14 +116,9 @@ impl BranchInfoBuilder { _ => { // Do not generate mcdc mappings and statements for decisions with too many conditions. let rebase_idx = self.mcdc_branch_spans.len() - decision.conditions_num + 1; - let to_normal_branches = self.mcdc_branch_spans.split_off(rebase_idx); - self.branch_spans.extend(to_normal_branches.into_iter().map( - |MCDCBranchSpan { span, true_marker, false_marker, .. }| BranchSpan { - span, - true_marker, - false_marker, - }, - )); + for branch in &mut self.mcdc_branch_spans[rebase_idx..] { + branch.condition_info = None; + } // ConditionInfo of this branch shall also be reset. condition_info = None; @@ -157,7 +147,7 @@ impl BranchInfoBuilder { branch_spans, mcdc_branch_spans, mcdc_decision_spans, - .. + mcdc_state: _, } = self; if num_block_markers == 0 { @@ -355,27 +345,30 @@ impl Builder<'_, '_> { let true_marker = inject_branch_marker(then_block); let false_marker = inject_branch_marker(else_block); - if let Some(condition_info) = - branch_info.fetch_condition_info(self.tcx, true_marker, false_marker) - { + if branch_info.mcdc_state.is_some() { + let condition_info = + branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker); branch_info.mcdc_branch_spans.push(MCDCBranchSpan { span: source_info.span, condition_info, true_marker, false_marker, }); - } else { - branch_info.branch_spans.push(BranchSpan { - span: source_info.span, - true_marker, - false_marker, - }); + return; } + + branch_info.branch_spans.push(BranchSpan { + span: source_info.span, + true_marker, + false_marker, + }); } pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) { - if let Some(branch_info) = self.coverage_branch_info.as_mut() { - branch_info.record_conditions_operation(logical_op, span); + if let Some(branch_info) = self.coverage_branch_info.as_mut() + && let Some(mcdc_state) = branch_info.mcdc_state.as_mut() + { + mcdc_state.record_conditions(logical_op, span); } } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 9e8648b0f93..d1516605fb6 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -149,13 +149,21 @@ fn create_mappings<'tcx>( true_term: term_for_bcb(true_bcb), false_term: term_for_bcb(false_bcb), }, - BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => { - MappingKind::MCDCBranch { + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => { + MappingKind::Branch { true_term: term_for_bcb(true_bcb), false_term: term_for_bcb(false_bcb), - mcdc_params: condition_info, } } + BcbMappingKind::MCDCBranch { + true_bcb, + false_bcb, + condition_info: Some(mcdc_params), + } => MappingKind::MCDCBranch { + true_term: term_for_bcb(true_bcb), + false_term: term_for_bcb(false_bcb), + mcdc_params, + }, BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => { MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num }) } @@ -249,7 +257,7 @@ fn inject_mcdc_statements<'tcx>( for (true_bcb, false_bcb, condition_id) in coverage_spans.all_bcb_mappings().filter_map(|mapping| match mapping.kind { BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => { - Some((true_bcb, false_bcb, condition_info.condition_id)) + Some((true_bcb, false_bcb, condition_info?.condition_id)) } _ => None, }) diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index a4cd8a38c66..8d2241783f6 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -21,7 +21,9 @@ pub(super) enum BcbMappingKind { MCDCBranch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock, - condition_info: ConditionInfo, + /// If `None`, this actually represents a normal branch mapping inserted + /// for code that was too complex for MC/DC. + condition_info: Option, }, /// Associates a mcdc decision with its join BCB. MCDCDecision { end_bcbs: BTreeSet, bitmap_idx: u32, conditions_num: u16 }, @@ -85,6 +87,12 @@ pub(super) fn generate_coverage_spans( })); mappings.extend(from_mir::extract_branch_mappings( + mir_body, + hir_info, + basic_coverage_blocks, + )); + + mappings.extend(from_mir::extract_mcdc_mappings( mir_body, hir_info.body_span, basic_coverage_blocks, diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index b9919a2ae88..3c64591d43e 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -366,15 +366,10 @@ impl SpanFromMir { } } -pub(super) fn extract_branch_mappings( +fn resolve_block_markers( + branch_info: &mir::coverage::BranchInfo, mir_body: &mir::Body<'_>, - body_span: Span, - basic_coverage_blocks: &CoverageGraph, -) -> Vec { - let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { - return vec![]; - }; - +) -> IndexVec> { let mut block_markers = IndexVec::>::from_elem_n( None, branch_info.num_block_markers, @@ -389,6 +384,58 @@ pub(super) fn extract_branch_mappings( } } + block_markers +} + +// FIXME: There is currently a lot of redundancy between +// `extract_branch_mappings` and `extract_mcdc_mappings`. This is needed so +// that they can each be modified without interfering with the other, but in +// the long term we should try to bring them together again when branch coverage +// and MC/DC coverage support are more mature. + +pub(super) fn extract_branch_mappings( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] }; + + let block_markers = resolve_block_markers(branch_info, mir_body); + + branch_info + .branch_spans + .iter() + .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + let (span, _) = + unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?; + + let bcb_from_marker = + |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + + let true_bcb = bcb_from_marker(true_marker)?; + let false_bcb = bcb_from_marker(false_marker)?; + + Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }) + }) + .collect::>() +} + +pub(super) fn extract_mcdc_mappings( + mir_body: &mir::Body<'_>, + body_span: Span, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { + return vec![]; + }; + + let block_markers = resolve_block_markers(branch_info, mir_body); + let bcb_from_marker = |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); @@ -406,12 +453,6 @@ pub(super) fn extract_branch_mappings( Some((span, true_bcb, false_bcb)) }; - let branch_filter_map = |&BranchSpan { span: raw_span, true_marker, false_marker }| { - check_branch_bcb(raw_span, true_marker, false_marker).map(|(span, true_bcb, false_bcb)| { - BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span } - }) - }; - let mcdc_branch_filter_map = |&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| { check_branch_bcb(raw_span, true_marker, false_marker).map( @@ -446,10 +487,7 @@ pub(super) fn extract_branch_mappings( }) }; - branch_info - .branch_spans - .iter() - .filter_map(branch_filter_map) + std::iter::empty() .chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map)) .chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map)) .collect::>() From b5a22be6a3ce55e4bbac62767cda62c38d47e162 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Apr 2024 12:20:09 +1000 Subject: [PATCH 177/185] coverage: Move some helper code into `BranchInfoBuilder` --- .../rustc_mir_build/src/build/coverageinfo.rs | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 15963c1234f..9e9ccd3dc2d 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -7,13 +7,13 @@ use rustc_middle::mir::coverage::{ BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan, MCDCDecisionSpan, }; -use rustc_middle::mir::{self, BasicBlock, UnOp}; +use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; use rustc_span::Span; -use crate::build::Builder; +use crate::build::{Builder, CFG}; use crate::errors::MCDCExceedsConditionNumLimit; pub(crate) struct BranchInfoBuilder { @@ -134,12 +134,42 @@ impl BranchInfoBuilder { condition_info } + fn add_two_way_branch<'tcx>( + &mut self, + cfg: &mut CFG<'tcx>, + source_info: SourceInfo, + true_block: BasicBlock, + false_block: BasicBlock, + ) { + let true_marker = self.inject_block_marker(cfg, source_info, true_block); + let false_marker = self.inject_block_marker(cfg, source_info, false_block); + + self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker }); + } + fn next_block_marker_id(&mut self) -> BlockMarkerId { let id = BlockMarkerId::from_usize(self.num_block_markers); self.num_block_markers += 1; id } + fn inject_block_marker( + &mut self, + cfg: &mut CFG<'_>, + source_info: SourceInfo, + block: BasicBlock, + ) -> BlockMarkerId { + let id = self.next_block_marker_id(); + + let marker_statement = mir::Statement { + source_info, + kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), + }; + cfg.push(block, marker_statement); + + id + } + pub(crate) fn into_done(self) -> Option> { let Self { nots: _, @@ -315,7 +345,7 @@ impl Builder<'_, '_> { mut else_block: BasicBlock, ) { // Bail out if branch coverage is not enabled for this function. - let Some(branch_info) = self.coverage_branch_info.as_ref() else { return }; + let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; // If this condition expression is nested within one or more `!` expressions, // replace it with the enclosing `!` collected by `visit_unary_not`. @@ -325,27 +355,15 @@ impl Builder<'_, '_> { std::mem::swap(&mut then_block, &mut else_block); } } - let source_info = self.source_info(self.thir[expr_id].span); - // Now that we have `source_info`, we can upgrade to a &mut reference. - let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut"); - - let mut inject_branch_marker = |block: BasicBlock| { - let id = branch_info.next_block_marker_id(); - - let marker_statement = mir::Statement { - source_info, - kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), - }; - self.cfg.push(block, marker_statement); - - id - }; - - let true_marker = inject_branch_marker(then_block); - let false_marker = inject_branch_marker(else_block); + let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; + // Separate path for handling branches when MC/DC is enabled. if branch_info.mcdc_state.is_some() { + let mut inject_block_marker = + |block| branch_info.inject_block_marker(&mut self.cfg, source_info, block); + let true_marker = inject_block_marker(then_block); + let false_marker = inject_block_marker(else_block); let condition_info = branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker); branch_info.mcdc_branch_spans.push(MCDCBranchSpan { @@ -357,11 +375,7 @@ impl Builder<'_, '_> { return; } - branch_info.branch_spans.push(BranchSpan { - span: source_info.span, - true_marker, - false_marker, - }); + branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block); } pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) { From 2b6adb06fb8307f19bd42d2fce3ad338dc6112ef Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 21 Apr 2024 13:21:58 +1000 Subject: [PATCH 178/185] coverage: Separate branch pairs from other mapping kinds This clears the way for larger changes to how branches are handled by the coverage instrumentor, in order to support branch coverage for more language constructs. --- .../rustc_mir_transform/src/coverage/mod.rs | 22 +++++++---- .../rustc_mir_transform/src/coverage/spans.rs | 37 +++++++++++++------ .../src/coverage/spans/from_mir.rs | 10 ++--- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d1516605fb6..0b15c52c281 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -9,7 +9,7 @@ mod tests; use self::counters::{CounterIncrementSite, CoverageCounters}; use self::graph::{BasicCoverageBlock, CoverageGraph}; -use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans}; +use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans}; use crate::MirPass; @@ -141,14 +141,10 @@ fn create_mappings<'tcx>( let mut mappings = Vec::new(); - mappings.extend(coverage_spans.all_bcb_mappings().filter_map( + mappings.extend(coverage_spans.mappings.iter().filter_map( |BcbMapping { kind: bcb_mapping_kind, span }| { let kind = match *bcb_mapping_kind { BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), - BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch { - true_term: term_for_bcb(true_bcb), - false_term: term_for_bcb(false_bcb), - }, BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => { MappingKind::Branch { true_term: term_for_bcb(true_bcb), @@ -173,6 +169,16 @@ fn create_mappings<'tcx>( }, )); + mappings.extend(coverage_spans.branch_pairs.iter().filter_map( + |&BcbBranchPair { span, true_bcb, false_bcb }| { + let true_term = term_for_bcb(true_bcb); + let false_term = term_for_bcb(false_bcb); + let kind = MappingKind::Branch { true_term, false_term }; + let code_region = make_code_region(source_map, file_name, span, body_span)?; + Some(Mapping { kind, code_region }) + }, + )); + mappings } @@ -241,7 +247,7 @@ fn inject_mcdc_statements<'tcx>( // Inject test vector update first because `inject_statement` always insert new statement at head. for (end_bcbs, bitmap_idx) in - coverage_spans.all_bcb_mappings().filter_map(|mapping| match &mapping.kind { + coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind { BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => { Some((end_bcbs, *bitmap_idx)) } @@ -255,7 +261,7 @@ fn inject_mcdc_statements<'tcx>( } for (true_bcb, false_bcb, condition_id) in - coverage_spans.all_bcb_mappings().filter_map(|mapping| match mapping.kind { + coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind { BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => { Some((true_bcb, false_bcb, condition_info?.condition_id)) } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 8d2241783f6..88f18b72085 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -15,8 +15,10 @@ mod from_mir; pub(super) enum BcbMappingKind { /// Associates an ordinary executable code span with its corresponding BCB. Code(BasicCoverageBlock), - /// Associates a branch span with BCBs for its true and false arms. - Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, + + // Ordinary branch mappings are stored separately, so they don't have a + // variant in this enum. + // /// Associates a mcdc branch span with condition info besides fields for normal branch. MCDCBranch { true_bcb: BasicCoverageBlock, @@ -35,9 +37,20 @@ pub(super) struct BcbMapping { pub(super) span: Span, } +/// This is separate from [`BcbMappingKind`] to help prepare for larger changes +/// that will be needed for improved branch coverage in the future. +/// (See .) +#[derive(Debug)] +pub(super) struct BcbBranchPair { + pub(super) span: Span, + pub(super) true_bcb: BasicCoverageBlock, + pub(super) false_bcb: BasicCoverageBlock, +} + pub(super) struct CoverageSpans { bcb_has_mappings: BitSet, - mappings: Vec, + pub(super) mappings: Vec, + pub(super) branch_pairs: Vec, test_vector_bitmap_bytes: u32, } @@ -46,10 +59,6 @@ impl CoverageSpans { self.bcb_has_mappings.contains(bcb) } - pub(super) fn all_bcb_mappings(&self) -> impl Iterator { - self.mappings.iter() - } - pub(super) fn test_vector_bitmap_bytes(&self) -> u32 { self.test_vector_bitmap_bytes } @@ -65,6 +74,7 @@ pub(super) fn generate_coverage_spans( basic_coverage_blocks: &CoverageGraph, ) -> Option { let mut mappings = vec![]; + let mut branch_pairs = vec![]; if hir_info.is_async_fn { // An async function desugars into a function that returns a future, @@ -86,7 +96,7 @@ pub(super) fn generate_coverage_spans( BcbMapping { kind: BcbMappingKind::Code(bcb), span } })); - mappings.extend(from_mir::extract_branch_mappings( + branch_pairs.extend(from_mir::extract_branch_pairs( mir_body, hir_info, basic_coverage_blocks, @@ -99,7 +109,7 @@ pub(super) fn generate_coverage_spans( )); } - if mappings.is_empty() { + if mappings.is_empty() && branch_pairs.is_empty() { return None; } @@ -112,8 +122,7 @@ pub(super) fn generate_coverage_spans( for BcbMapping { kind, span: _ } in &mappings { match *kind { BcbMappingKind::Code(bcb) => insert(bcb), - BcbMappingKind::Branch { true_bcb, false_bcb } - | BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => { + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => { insert(true_bcb); insert(false_bcb); } @@ -126,8 +135,12 @@ pub(super) fn generate_coverage_spans( } } } + for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs { + insert(true_bcb); + insert(false_bcb); + } - Some(CoverageSpans { bcb_has_mappings, mappings, test_vector_bitmap_bytes }) + Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes }) } #[derive(Debug)] diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 3c64591d43e..64f21d74b1c 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -13,7 +13,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; -use crate::coverage::spans::{BcbMapping, BcbMappingKind}; +use crate::coverage::spans::{BcbBranchPair, BcbMapping, BcbMappingKind}; use crate::coverage::ExtractedHirInfo; /// Traverses the MIR body to produce an initial collection of coverage-relevant @@ -388,16 +388,16 @@ fn resolve_block_markers( } // FIXME: There is currently a lot of redundancy between -// `extract_branch_mappings` and `extract_mcdc_mappings`. This is needed so +// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so // that they can each be modified without interfering with the other, but in // the long term we should try to bring them together again when branch coverage // and MC/DC coverage support are more mature. -pub(super) fn extract_branch_mappings( +pub(super) fn extract_branch_pairs( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, -) -> Vec { +) -> Vec { let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] }; let block_markers = resolve_block_markers(branch_info, mir_body); @@ -420,7 +420,7 @@ pub(super) fn extract_branch_mappings( let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; - Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }) + Some(BcbBranchPair { span, true_bcb, false_bcb }) }) .collect::>() } From 0881e3e531b6fd879107d754f1e807cd591f2d48 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 22 Apr 2024 15:41:08 +0300 Subject: [PATCH 179/185] Exhaustivelly match TyKind in consider_builtin_async_destruct_candidate --- compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index e4d961a7f0c..7dda3411806 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -855,7 +855,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { goal.predicate.self_ty() ), - _ => bug!( + ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!( "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}" ), }; From a9c7465997973b8d2ca24a5150548a2023cd3823 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 22 Apr 2024 15:42:07 +0300 Subject: [PATCH 180/185] Fix copy-paste typo in the comment within consider_builtin_async_destruct_candidate --- compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 7dda3411806..c662ab23c53 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -840,8 +840,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { | ty::Tuple(_) | ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env), - // We do not call `Ty::discriminant_ty` on alias, param, or placeholder - // types, which return `::Discriminant` + // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder + // types, which return `::AsyncDestructor` // (or ICE in the case of placeholders). Projecting a type to itself // is never really productive. ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { From 67980dd6fb11917d23d01a19c2cf4cfc3978aac8 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 22 Apr 2024 15:44:01 +0300 Subject: [PATCH 181/185] Fix typo in the has_surface_drop's doc comment Co-authored-by: Oli Scherer --- compiler/rustc_middle/src/ty/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2d83b9f8ef1..542014dcce7 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1328,7 +1328,7 @@ impl<'tcx> Ty<'tcx> { ) } - /// Checks whether values of this type `T` implements the `AsyncDrop` + /// Checks whether values of this type `T` implements the `Drop` /// trait. pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self)) From 6146a51f17f19b557c2baf11a1ae04cbafdd89bb Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Mon, 22 Apr 2024 14:54:28 +0200 Subject: [PATCH 182/185] Add more context to the forbidden dep-graph read ICE error message. --- compiler/rustc_query_system/src/dep_graph/graph.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index af70e2a4264..2b3fa7f6cfa 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1402,6 +1402,10 @@ fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepN panic!( "Error: trying to record dependency on DepNode {dep_node} in a \ - context that does not allow it (e.g. during query deserialization)." + context that does not allow it (e.g. during query deserialization). \ + The most common case of recording a dependency on a DepNode `foo` is \ + when the correspondng query `foo` is invoked. Invoking queries is not \ + allowed as part of loading something from the incremental on-disk cache. \ + See ." ) } From 33e68aadc975c60bf975789f70156e67082e8910 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 21 Apr 2024 18:41:45 +0200 Subject: [PATCH 183/185] Stabilize generic `NonZero`. --- compiler/rustc_attr/src/lib.rs | 1 - compiler/rustc_const_eval/src/lib.rs | 1 - compiler/rustc_data_structures/src/lib.rs | 1 - compiler/rustc_errors/src/lib.rs | 1 - compiler/rustc_feature/src/lib.rs | 1 - compiler/rustc_hir_analysis/src/lib.rs | 1 - compiler/rustc_interface/src/lib.rs | 1 - compiler/rustc_lint/src/lib.rs | 1 - compiler/rustc_metadata/src/lib.rs | 1 - compiler/rustc_middle/src/lib.rs | 1 - compiler/rustc_passes/src/lib.rs | 1 - compiler/rustc_query_impl/src/lib.rs | 1 - compiler/rustc_query_system/src/lib.rs | 1 - compiler/rustc_serialize/src/lib.rs | 1 - compiler/rustc_session/src/lib.rs | 1 - library/alloc/src/lib.rs | 1 - library/alloc/tests/lib.rs | 1 - library/core/src/array/mod.rs | 5 ++- library/core/src/iter/traits/double_ended.rs | 3 +- library/core/src/iter/traits/iterator.rs | 6 ++- library/core/src/num/mod.rs | 8 ++-- library/core/src/num/nonzero.rs | 10 ++--- library/core/tests/lib.rs | 1 - library/proc_macro/src/lib.rs | 1 - library/std/src/lib.rs | 1 - library/std/src/num.rs | 3 +- library/std/src/process.rs | 3 +- library/test/src/lib.rs | 1 - src/tools/miri/src/bin/miri.rs | 1 - src/tools/miri/src/lib.rs | 1 - tests/codegen/array-equality.rs | 1 - tests/codegen/enum/enum-debug-niche-2.rs | 2 +- tests/codegen/function-arguments.rs | 1 - tests/codegen/intrinsics/transmute-niched.rs | 1 - tests/codegen/issues/issue-119422.rs | 1 - tests/codegen/loads.rs | 1 - tests/codegen/option-as-slice.rs | 1 - tests/codegen/option-niche-eq.rs | 1 - tests/codegen/slice-ref-equality.rs | 1 - tests/codegen/transmute-optimized.rs | 1 - tests/debuginfo/msvc-pretty-enums.rs | 1 - tests/debuginfo/numeric-types.rs | 1 - .../instsimplify/combine_transmutes.rs | 1 - tests/ui/abi/compatibility.rs | 1 - tests/ui/consts/const-eval/raw-bytes.rs | 2 +- tests/ui/consts/const-eval/ub-nonnull.rs | 2 +- tests/ui/consts/const-eval/valid-const.rs | 1 - tests/ui/consts/tuple-struct-constructors.rs | 1 - .../intrinsics/panic-uninitialized-zeroed.rs | 1 - tests/ui/issues/issue-64593.rs | 1 - tests/ui/layout/unsafe-cell-hides-niche.rs | 1 - .../ui/layout/zero-sized-array-enum-niche.rs | 1 - .../layout/zero-sized-array-enum-niche.stderr | 8 ++-- tests/ui/lint/clashing-extern-fn.rs | 1 - tests/ui/lint/clashing-extern-fn.stderr | 38 +++++++++---------- tests/ui/lint/invalid_value.rs | 2 +- tests/ui/lint/lint-ctypes-enum.rs | 1 - tests/ui/lint/lint-ctypes-enum.stderr | 22 +++++------ .../overflowing-neg-nonzero.rs | 1 - tests/ui/print_type_sizes/niche-filling.rs | 1 - .../ui/structs-enums/enum-null-pointer-opt.rs | 1 - .../enum-null-pointer-opt.stderr | 2 +- tests/ui/structs-enums/type-sizes.rs | 1 - .../core-std-import-order-issue-83564.rs | 1 - .../core-std-import-order-issue-83564.stderr | 2 +- .../next-solver/specialization-transmute.rs | 1 - .../specialization-transmute.stderr | 12 +++--- 67 files changed, 69 insertions(+), 111 deletions(-) diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index fada69c4e6d..dd87a5c4dc3 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -7,7 +7,6 @@ #![allow(internal_features)] #![feature(rustdoc_internals)] #![doc(rust_logo)] -#![feature(generic_nonzero)] #![feature(let_chains)] #[macro_use] diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 50420aaec04..d27d42737cd 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -11,7 +11,6 @@ Rust MIR: a lowered representation of Rust. #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(slice_ptr_get)] #![feature(strict_provenance)] diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index b82a9a909e6..2b799d6f5d3 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -20,7 +20,6 @@ #![feature(cfg_match)] #![feature(core_intrinsics)] #![feature(extend_one)] -#![feature(generic_nonzero)] #![feature(hash_raw_entry)] #![feature(hasher_prefixfree_extras)] #![feature(lazy_cell)] diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8d6b22a9fa9..e3363e89594 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -15,7 +15,6 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![feature(extract_if)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index cb28bb4e8e4..36ef8fe7816 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -12,7 +12,6 @@ //! symbol to the `accepted` or `removed` modules respectively. #![allow(internal_features)] -#![feature(generic_nonzero)] #![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(lazy_cell)] diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c374f9762d6..66b86c9eb02 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -63,7 +63,6 @@ This API is completely unstable and subject to change. #![feature(rustdoc_internals)] #![allow(internal_features)] #![feature(control_flow_enum)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(is_sorted)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index d0ce23dacb5..75df006a56f 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,4 @@ #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(lazy_cell)] #![feature(let_chains)] #![feature(thread_spawn_unchecked)] diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 31c80c4d75b..086c3834410 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -32,7 +32,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(let_chains)] diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index f133a2f5f73..7dd03407bd7 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -6,7 +6,6 @@ #![feature(error_iter)] #![feature(extract_if)] #![feature(coroutines)] -#![feature(generic_nonzero)] #![feature(iter_from_coroutine)] #![feature(let_chains)] #![feature(if_let_guard)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e52a5863fd0..aadaca18326 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -35,7 +35,6 @@ #![feature(const_type_name)] #![feature(discriminant_kind)] #![feature(coroutines)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(inline_const)] #![feature(iter_from_coroutine)] diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index e03052bcfed..bce29e2af09 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -8,7 +8,6 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![allow(internal_features)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(try_blocks)] diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 3373835d813..914481d712e 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -3,7 +3,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(generic_nonzero)] #![feature(min_specialization)] #![feature(rustc_attrs)] #![allow(rustc::potential_query_instability, unused_parens)] diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 6a959a99e5d..416f556f57d 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,6 +1,5 @@ #![feature(assert_matches)] #![feature(core_intrinsics)] -#![feature(generic_nonzero)] #![feature(hash_raw_entry)] #![feature(min_specialization)] #![feature(let_chains)] diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 5a9403e0a85..3e1d8f3828b 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -11,7 +11,6 @@ #![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(const_option)] #![feature(core_intrinsics)] -#![feature(generic_nonzero)] #![feature(inline_const)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index c63af90a7f3..58e1394c090 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(lazy_cell)] #![feature(option_get_or_insert_default)] diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index dec04d7e421..88faf5a9c7d 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -126,7 +126,6 @@ #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] -#![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hint_assert_unchecked)] #![feature(inline_const)] diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index a34bce66496..b5175a8487f 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -14,7 +14,6 @@ #![feature(core_intrinsics)] #![feature(extract_if)] #![feature(exact_size_is_empty)] -#![feature(generic_nonzero)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] #![feature(new_uninit)] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 2a447aafe72..05874ab6c4c 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -512,7 +512,8 @@ impl [T; N] { /// # Examples /// /// ``` - /// #![feature(array_try_map, generic_nonzero)] + /// #![feature(array_try_map)] + /// /// let a = ["1", "2", "3"]; /// let b = a.try_map(|v| v.parse::()).unwrap().map(|v| v + 1); /// assert_eq!(b, [2, 3, 4]); @@ -522,8 +523,10 @@ impl [T; N] { /// assert!(b.is_err()); /// /// use std::num::NonZero; + /// /// let z = [1, 2, 0, 3, 4]; /// assert_eq!(z.try_map(NonZero::new), None); + /// /// let a = [1, 2, 3]; /// let b = a.try_map(NonZero::new); /// let c = b.map(|x| x.map(NonZero::get)); diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 35092ec51cd..3b126785728 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -118,7 +118,8 @@ pub trait DoubleEndedIterator: Iterator { /// Basic usage: /// /// ``` - /// #![feature(generic_nonzero, iter_advance_by)] + /// #![feature(iter_advance_by)] + /// /// use std::num::NonZero; /// /// let a = [3, 4, 5, 6]; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 95c03043e3e..7c1c6122efe 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -288,7 +288,8 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(generic_nonzero, iter_advance_by)] + /// #![feature(iter_advance_by)] + /// /// use std::num::NonZero; /// /// let a = [1, 2, 3, 4]; @@ -2939,7 +2940,8 @@ pub trait Iterator { /// This also supports other types which implement [`Try`], not just [`Result`]. /// /// ``` - /// #![feature(generic_nonzero, try_find)] + /// #![feature(try_find)] + /// /// use std::num::NonZero; /// /// let a = [3, 5, 7, 4, 9, 0, 11u32]; diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9ebbb4ffe80..443401c5dba 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -67,15 +67,15 @@ pub use error::ParseIntError; )] pub use nonzero::ZeroablePrimitive; -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")] pub use nonzero::NonZero; -#[stable(feature = "nonzero", since = "1.28.0")] -pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; - #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; +#[stable(feature = "nonzero", since = "1.28.0")] +pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + #[stable(feature = "try_from", since = "1.34.0")] pub use error::TryFromIntError; diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 62ea7abf652..b9d771ba359 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -105,12 +105,11 @@ impl_zeroable_primitive!( /// For example, `Option>` is the same size as `u32`: /// /// ``` -/// #![feature(generic_nonzero)] -/// use core::mem::size_of; +/// use core::{mem::size_of, num::NonZero}; /// -/// assert_eq!(size_of::>>(), size_of::()); +/// assert_eq!(size_of::>>(), size_of::()); /// ``` -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] #[rustc_diagnostic_item = "NonZero"] @@ -562,7 +561,8 @@ macro_rules! nonzero_integer { /// Basic usage: /// /// ``` - /// #![feature(generic_nonzero, non_zero_count_ones)] + /// #![feature(non_zero_count_ones)] + /// /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { /// # use std::num::*; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index e741149e7ce..7bd962fa260 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -42,7 +42,6 @@ #![feature(float_minimum_maximum)] #![feature(future_join)] #![feature(generic_assert_internals)] -#![feature(generic_nonzero)] #![feature(array_try_from_fn)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c8db028b651..1ceff2e506c 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -26,7 +26,6 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] #![feature(new_uninit)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 241a443fab9..aa908f0499f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -335,7 +335,6 @@ #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] #![feature(fmt_internals)] -#![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_assert_unchecked)] diff --git a/library/std/src/num.rs b/library/std/src/num.rs index 1343fdfd1df..fbe68f7e303 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -23,11 +23,12 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} )] pub use core::num::ZeroablePrimitive; -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")] pub use core::num::NonZero; #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; + #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 69cc61b30ef..4a73a9be88b 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1865,7 +1865,8 @@ impl ExitStatusError { /// # Examples /// /// ``` - /// #![feature(exit_status_error, generic_nonzero)] + /// #![feature(exit_status_error)] + /// /// # if cfg!(unix) { /// use std::num::NonZero; /// use std::process::Command; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index f3c22061d25..f3a26e25938 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -17,7 +17,6 @@ #![unstable(feature = "test", issue = "50297")] #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] -#![feature(generic_nonzero)] #![feature(rustdoc_internals)] #![feature(internal_output_capture)] #![feature(staged_api)] diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index db2cd01ce0b..0070d1f3ebc 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(rustc_private, stmt_expr_attributes)] #![allow( clippy::manual_range_contains, diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 2e19c9ff713..e1c0da9118d 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -2,7 +2,6 @@ #![feature(cell_update)] #![feature(const_option)] #![feature(float_gamma)] -#![feature(generic_nonzero)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] diff --git a/tests/codegen/array-equality.rs b/tests/codegen/array-equality.rs index 5b85da1d4a0..bc5425c7a4f 100644 --- a/tests/codegen/array-equality.rs +++ b/tests/codegen/array-equality.rs @@ -1,7 +1,6 @@ //@ compile-flags: -O -Z merge-functions=disabled //@ only-x86_64 #![crate_type = "lib"] -#![feature(generic_nonzero)] // CHECK-LABEL: @array_eq_value #[no_mangle] diff --git a/tests/codegen/enum/enum-debug-niche-2.rs b/tests/codegen/enum/enum-debug-niche-2.rs index 25871885e7e..58f43fe3ec6 100644 --- a/tests/codegen/enum/enum-debug-niche-2.rs +++ b/tests/codegen/enum/enum-debug-niche-2.rs @@ -7,7 +7,7 @@ // CHECK: {{.*}}DICompositeType{{.*}}tag: DW_TAG_variant_part,{{.*}}size: 32,{{.*}} // CHECK: {{.*}}DIDerivedType{{.*}}tag: DW_TAG_member,{{.*}}name: "Placeholder",{{.*}}extraData: i128 4294967295{{[,)].*}} // CHECK: {{.*}}DIDerivedType{{.*}}tag: DW_TAG_member,{{.*}}name: "Error",{{.*}}extraData: i128 0{{[,)].*}} -#![feature(generic_nonzero, never_type)] +#![feature(never_type)] #[derive(Copy, Clone)] pub struct Entity { diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs index 468ec0a7753..2b27dab078d 100644 --- a/tests/codegen/function-arguments.rs +++ b/tests/codegen/function-arguments.rs @@ -1,7 +1,6 @@ //@ compile-flags: -O -C no-prepopulate-passes #![crate_type = "lib"] #![feature(dyn_star)] -#![feature(generic_nonzero)] #![feature(allocator_api)] use std::mem::MaybeUninit; diff --git a/tests/codegen/intrinsics/transmute-niched.rs b/tests/codegen/intrinsics/transmute-niched.rs index b5e0da1b2f5..f5b7bd2efea 100644 --- a/tests/codegen/intrinsics/transmute-niched.rs +++ b/tests/codegen/intrinsics/transmute-niched.rs @@ -3,7 +3,6 @@ //@ [DBG] compile-flags: -C opt-level=0 -C no-prepopulate-passes //@ only-64bit (so I don't need to worry about usize) #![crate_type = "lib"] -#![feature(generic_nonzero)] use std::mem::transmute; use std::num::NonZero; diff --git a/tests/codegen/issues/issue-119422.rs b/tests/codegen/issues/issue-119422.rs index 19480b4dc9e..aa56bfe79ac 100644 --- a/tests/codegen/issues/issue-119422.rs +++ b/tests/codegen/issues/issue-119422.rs @@ -4,7 +4,6 @@ //@ compile-flags: -O --edition=2021 -Zmerge-functions=disabled //@ only-64bit (because the LLVM type of i64 for usize shows up) #![crate_type = "lib"] -#![feature(generic_nonzero)] use core::ptr::NonNull; use core::num::NonZero; diff --git a/tests/codegen/loads.rs b/tests/codegen/loads.rs index ba4de77ce6f..e3e2f757770 100644 --- a/tests/codegen/loads.rs +++ b/tests/codegen/loads.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0 -O #![crate_type = "lib"] -#![feature(generic_nonzero)] use std::mem::MaybeUninit; use std::num::NonZero; diff --git a/tests/codegen/option-as-slice.rs b/tests/codegen/option-as-slice.rs index c5b1eafaccb..65637a2495d 100644 --- a/tests/codegen/option-as-slice.rs +++ b/tests/codegen/option-as-slice.rs @@ -1,7 +1,6 @@ //@ compile-flags: -O -Z randomize-layout=no //@ only-x86_64 #![crate_type = "lib"] -#![feature(generic_nonzero)] extern crate core; diff --git a/tests/codegen/option-niche-eq.rs b/tests/codegen/option-niche-eq.rs index 8b8044e9b75..7b955332fd3 100644 --- a/tests/codegen/option-niche-eq.rs +++ b/tests/codegen/option-niche-eq.rs @@ -1,7 +1,6 @@ //@ compile-flags: -O -Zmerge-functions=disabled //@ min-llvm-version: 18 #![crate_type = "lib"] -#![feature(generic_nonzero)] extern crate core; use core::cmp::Ordering; diff --git a/tests/codegen/slice-ref-equality.rs b/tests/codegen/slice-ref-equality.rs index 7ab70108fe0..1153d7817b2 100644 --- a/tests/codegen/slice-ref-equality.rs +++ b/tests/codegen/slice-ref-equality.rs @@ -1,6 +1,5 @@ //@ compile-flags: -O -Zmerge-functions=disabled #![crate_type = "lib"] -#![feature(generic_nonzero)] use std::num::NonZero; diff --git a/tests/codegen/transmute-optimized.rs b/tests/codegen/transmute-optimized.rs index 1a5f53e625a..8e5bcb2340e 100644 --- a/tests/codegen/transmute-optimized.rs +++ b/tests/codegen/transmute-optimized.rs @@ -1,6 +1,5 @@ //@ compile-flags: -O -Z merge-functions=disabled #![crate_type = "lib"] -#![feature(generic_nonzero)] // This tests that LLVM can optimize based on the niches in the source or // destination types for transmutes. diff --git a/tests/debuginfo/msvc-pretty-enums.rs b/tests/debuginfo/msvc-pretty-enums.rs index cfac14a22c4..0293ec0ec39 100644 --- a/tests/debuginfo/msvc-pretty-enums.rs +++ b/tests/debuginfo/msvc-pretty-enums.rs @@ -132,7 +132,6 @@ // cdb-command: dx -r2 arbitrary_discr2,d // cdb-check: arbitrary_discr2,d : Def [Type: enum2$] // cdb-check: [+0x[...]] __0 : 5678 [Type: unsigned int] -#![feature(generic_nonzero)] #![feature(rustc_attrs)] #![feature(repr128)] #![feature(arbitrary_enum_discriminant)] diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs index 1ff72d34fbd..98bc31e8855 100644 --- a/tests/debuginfo/numeric-types.rs +++ b/tests/debuginfo/numeric-types.rs @@ -237,7 +237,6 @@ // lldb-command:v nz_usize // lldb-check:[...] 122 { __0 = { 0 = 122 } } -#![feature(generic_nonzero)] use std::num::*; use std::sync::atomic::*; diff --git a/tests/mir-opt/instsimplify/combine_transmutes.rs b/tests/mir-opt/instsimplify/combine_transmutes.rs index a1274dd1b40..0be7466001f 100644 --- a/tests/mir-opt/instsimplify/combine_transmutes.rs +++ b/tests/mir-opt/instsimplify/combine_transmutes.rs @@ -3,7 +3,6 @@ #![crate_type = "lib"] #![feature(core_intrinsics)] #![feature(custom_mir)] -#![feature(generic_nonzero)] use std::intrinsics::mir::*; use std::mem::{MaybeUninit, ManuallyDrop, transmute}; diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 3ee4542810c..373d1cce1d7 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -64,7 +64,6 @@ [csky] needs-llvm-components: csky */ #![feature(rustc_attrs, unsized_fn_params, transparent_unions)] -#![cfg_attr(host, feature(generic_nonzero))] #![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)] #![allow(unused, improper_ctypes_definitions, internal_features)] diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index e5dfd5ca293..2fbf135c997 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -3,7 +3,7 @@ // ignore-tidy-linelength //@ normalize-stderr-test "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼" -> "╾ALLOC_ID$1╼" #![allow(invalid_value)] -#![feature(generic_nonzero, never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] +#![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] use std::mem; use std::alloc::Layout; diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs index 76bd5248ffd..10d304436f8 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.rs +++ b/tests/ui/consts/const-eval/ub-nonnull.rs @@ -2,7 +2,7 @@ //@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" #![allow(invalid_value)] // make sure we cannot allow away the errors tested here -#![feature(generic_nonzero, rustc_attrs, ptr_metadata)] +#![feature(rustc_attrs, ptr_metadata)] use std::mem; use std::ptr::NonNull; diff --git a/tests/ui/consts/const-eval/valid-const.rs b/tests/ui/consts/const-eval/valid-const.rs index 15d3e883456..777484c6b09 100644 --- a/tests/ui/consts/const-eval/valid-const.rs +++ b/tests/ui/consts/const-eval/valid-const.rs @@ -1,7 +1,6 @@ //@ check-pass // // Some constants that *are* valid -#![feature(generic_nonzero)] use std::mem; use std::ptr::NonNull; diff --git a/tests/ui/consts/tuple-struct-constructors.rs b/tests/ui/consts/tuple-struct-constructors.rs index d2f25aeec9b..e645b574075 100644 --- a/tests/ui/consts/tuple-struct-constructors.rs +++ b/tests/ui/consts/tuple-struct-constructors.rs @@ -1,7 +1,6 @@ //@ run-pass // // https://github.com/rust-lang/rust/issues/41898 -#![feature(generic_nonzero)] use std::num::NonZero; diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index b1ac7528d58..67b9832d601 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -7,7 +7,6 @@ // // This test checks panic emitted from `mem::{uninitialized,zeroed}`. #![allow(deprecated, invalid_value)] -#![feature(generic_nonzero)] #![feature(never_type)] use std::{ diff --git a/tests/ui/issues/issue-64593.rs b/tests/ui/issues/issue-64593.rs index 091c3a2f316..e28b9577347 100644 --- a/tests/ui/issues/issue-64593.rs +++ b/tests/ui/issues/issue-64593.rs @@ -1,6 +1,5 @@ //@ check-pass #![deny(improper_ctypes)] -#![feature(generic_nonzero)] pub struct Error(std::num::NonZero); diff --git a/tests/ui/layout/unsafe-cell-hides-niche.rs b/tests/ui/layout/unsafe-cell-hides-niche.rs index 568eb819be2..fe51c9925e8 100644 --- a/tests/ui/layout/unsafe-cell-hides-niche.rs +++ b/tests/ui/layout/unsafe-cell-hides-niche.rs @@ -6,7 +6,6 @@ //@ check-pass //@ compile-flags: --crate-type=lib //@ only-x86 -#![feature(generic_nonzero)] #![feature(repr_simd)] use std::cell::{UnsafeCell, RefCell, Cell}; diff --git a/tests/ui/layout/zero-sized-array-enum-niche.rs b/tests/ui/layout/zero-sized-array-enum-niche.rs index 058f5923487..0c37c0f010e 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.rs +++ b/tests/ui/layout/zero-sized-array-enum-niche.rs @@ -1,6 +1,5 @@ //@ normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" #![crate_type = "lib"] -#![feature(generic_nonzero)] #![feature(rustc_attrs)] // Various tests around the behavior of zero-sized arrays and diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index af049125de4..ee34cfdfb0d 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -98,7 +98,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:14:1 + --> $DIR/zero-sized-array-enum-niche.rs:13:1 | LL | type AlignedResult = Result<[u32; 0], bool>; | ^^^^^^^^^^^^^^^^^^ @@ -227,7 +227,7 @@ error: layout_of(MultipleAlignments) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:22:1 + --> $DIR/zero-sized-array-enum-niche.rs:21:1 | LL | enum MultipleAlignments { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -332,7 +332,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:38:1 + --> $DIR/zero-sized-array-enum-niche.rs:37:1 | LL | type NicheLosesToTagged = Result<[u32; 0], Packed>>; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -441,7 +441,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:45:1 + --> $DIR/zero-sized-array-enum-niche.rs:44:1 | LL | type NicheWinsOverTagged = Result<[u32; 0], Packed>; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs index cb63af0ea42..728dfabb393 100644 --- a/tests/ui/lint/clashing-extern-fn.rs +++ b/tests/ui/lint/clashing-extern-fn.rs @@ -2,7 +2,6 @@ //@ aux-build:external_extern_fn.rs #![crate_type = "lib"] #![warn(clashing_extern_declarations)] -#![feature(generic_nonzero)] mod redeclared_different_signature { mod a { diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 86ee789aeb2..43c8cdead9f 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -1,5 +1,5 @@ warning: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:430:55 + --> $DIR/clashing-extern-fn.rs:429:55 | LL | fn hidden_niche_transparent_no_niche() -> Option; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -9,7 +9,7 @@ LL | fn hidden_niche_transparent_no_niche() -> Option>>`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:434:46 + --> $DIR/clashing-extern-fn.rs:433:46 | LL | fn hidden_niche_unsafe_cell() -> Option>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -18,7 +18,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option $DIR/clashing-extern-fn.rs:15:13 + --> $DIR/clashing-extern-fn.rs:14:13 | LL | fn clash(x: u8); | --------------- `clash` previously declared here @@ -35,7 +35,7 @@ LL | #![warn(clashing_extern_declarations)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `extern_link_name` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:53:9 + --> $DIR/clashing-extern-fn.rs:52:9 | LL | #[link_name = "extern_link_name"] | --------------------------------- `extern_link_name` previously declared here @@ -47,7 +47,7 @@ LL | fn extern_link_name(x: u32); found `unsafe extern "C" fn(u32)` warning: `some_other_extern_link_name` redeclares `some_other_new_name` with a different signature - --> $DIR/clashing-extern-fn.rs:56:9 + --> $DIR/clashing-extern-fn.rs:55:9 | LL | fn some_other_new_name(x: i16); | ------------------------------ `some_other_new_name` previously declared here @@ -59,7 +59,7 @@ LL | #[link_name = "some_other_new_name"] found `unsafe extern "C" fn(u32)` warning: `other_both_names_different` redeclares `link_name_same` with a different signature - --> $DIR/clashing-extern-fn.rs:60:9 + --> $DIR/clashing-extern-fn.rs:59:9 | LL | #[link_name = "link_name_same"] | ------------------------------- `link_name_same` previously declared here @@ -71,7 +71,7 @@ LL | #[link_name = "link_name_same"] found `unsafe extern "C" fn(u32)` warning: `different_mod` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:73:9 + --> $DIR/clashing-extern-fn.rs:72:9 | LL | fn different_mod(x: u8); | ----------------------- `different_mod` previously declared here @@ -83,7 +83,7 @@ LL | fn different_mod(x: u64); found `unsafe extern "C" fn(u64)` warning: `variadic_decl` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:83:9 + --> $DIR/clashing-extern-fn.rs:82:9 | LL | fn variadic_decl(x: u8, ...); | ---------------------------- `variadic_decl` previously declared here @@ -95,7 +95,7 @@ LL | fn variadic_decl(x: u8); found `unsafe extern "C" fn(u8)` warning: `weigh_banana` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:143:13 + --> $DIR/clashing-extern-fn.rs:142:13 | LL | fn weigh_banana(count: *const Banana) -> u64; | -------------------------------------------- `weigh_banana` previously declared here @@ -107,7 +107,7 @@ LL | fn weigh_banana(count: *const Banana) -> u64; found `unsafe extern "C" fn(*const three::Banana) -> u64` warning: `draw_point` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:172:13 + --> $DIR/clashing-extern-fn.rs:171:13 | LL | fn draw_point(p: Point); | ----------------------- `draw_point` previously declared here @@ -119,7 +119,7 @@ LL | fn draw_point(p: Point); found `unsafe extern "C" fn(sameish_members::b::Point)` warning: `origin` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:198:13 + --> $DIR/clashing-extern-fn.rs:197:13 | LL | fn origin() -> Point3; | --------------------- `origin` previously declared here @@ -131,7 +131,7 @@ LL | fn origin() -> Point3; found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3` warning: `transparent_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:221:13 + --> $DIR/clashing-extern-fn.rs:220:13 | LL | fn transparent_incorrect() -> T; | ------------------------------- `transparent_incorrect` previously declared here @@ -143,7 +143,7 @@ LL | fn transparent_incorrect() -> isize; found `unsafe extern "C" fn() -> isize` warning: `missing_return_type` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:260:13 + --> $DIR/clashing-extern-fn.rs:259:13 | LL | fn missing_return_type() -> usize; | --------------------------------- `missing_return_type` previously declared here @@ -155,7 +155,7 @@ LL | fn missing_return_type(); found `unsafe extern "C" fn()` warning: `non_zero_usize` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:278:13 + --> $DIR/clashing-extern-fn.rs:277:13 | LL | fn non_zero_usize() -> core::num::NonZero; | ------------------------------------------------ `non_zero_usize` previously declared here @@ -167,7 +167,7 @@ LL | fn non_zero_usize() -> usize; found `unsafe extern "C" fn() -> usize` warning: `non_null_ptr` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:280:13 + --> $DIR/clashing-extern-fn.rs:279:13 | LL | fn non_null_ptr() -> core::ptr::NonNull; | ---------------------------------------------- `non_null_ptr` previously declared here @@ -179,7 +179,7 @@ LL | fn non_null_ptr() -> *const usize; found `unsafe extern "C" fn() -> *const usize` warning: `option_non_zero_usize_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:374:13 + --> $DIR/clashing-extern-fn.rs:373:13 | LL | fn option_non_zero_usize_incorrect() -> usize; | --------------------------------------------- `option_non_zero_usize_incorrect` previously declared here @@ -191,7 +191,7 @@ LL | fn option_non_zero_usize_incorrect() -> isize; found `unsafe extern "C" fn() -> isize` warning: `option_non_null_ptr_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:376:13 + --> $DIR/clashing-extern-fn.rs:375:13 | LL | fn option_non_null_ptr_incorrect() -> *const usize; | -------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here @@ -203,7 +203,7 @@ LL | fn option_non_null_ptr_incorrect() -> *const isize; found `unsafe extern "C" fn() -> *const isize` warning: `hidden_niche_transparent_no_niche` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:430:13 + --> $DIR/clashing-extern-fn.rs:429:13 | LL | fn hidden_niche_transparent_no_niche() -> usize; | ----------------------------------------------- `hidden_niche_transparent_no_niche` previously declared here @@ -215,7 +215,7 @@ LL | fn hidden_niche_transparent_no_niche() -> Option Option` warning: `hidden_niche_unsafe_cell` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:434:13 + --> $DIR/clashing-extern-fn.rs:433:13 | LL | fn hidden_niche_unsafe_cell() -> usize; | -------------------------------------- `hidden_niche_unsafe_cell` previously declared here diff --git a/tests/ui/lint/invalid_value.rs b/tests/ui/lint/invalid_value.rs index 1d2f23aaaf6..29e8e6cfef6 100644 --- a/tests/ui/lint/invalid_value.rs +++ b/tests/ui/lint/invalid_value.rs @@ -2,7 +2,7 @@ // in a lint. #![allow(deprecated)] #![deny(invalid_value)] -#![feature(generic_nonzero, never_type, rustc_attrs)] +#![feature(never_type, rustc_attrs)] use std::mem::{self, MaybeUninit}; use std::ptr::NonNull; diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 3157b6e240a..c60290f8553 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] #![deny(improper_ctypes)] -#![feature(generic_nonzero)] #![feature(ptr_internals)] #![feature(transparent_unions)] diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index 48be3eb5a56..103fda8d402 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:61:13 + --> $DIR/lint-ctypes-enum.rs:60:13 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:10:1 + --> $DIR/lint-ctypes-enum.rs:9:1 | LL | enum U { | ^^^^^^ @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:62:13 + --> $DIR/lint-ctypes-enum.rs:61:13 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:13:1 + --> $DIR/lint-ctypes-enum.rs:12:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:63:13 + --> $DIR/lint-ctypes-enum.rs:62:13 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,13 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:17:1 + --> $DIR/lint-ctypes-enum.rs:16:1 | LL | enum T { | ^^^^^^ error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:75:23 + --> $DIR/lint-ctypes-enum.rs:74:23 | LL | fn nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -54,7 +54,7 @@ LL | fn nonzero_u128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:82:23 + --> $DIR/lint-ctypes-enum.rs:81:23 | LL | fn nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -62,7 +62,7 @@ LL | fn nonzero_i128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:87:28 + --> $DIR/lint-ctypes-enum.rs:86:28 | LL | fn transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -71,7 +71,7 @@ LL | fn transparent_union(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:89:20 + --> $DIR/lint-ctypes-enum.rs:88:20 | LL | fn repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +80,7 @@ LL | fn repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), NonZero>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:90:20 + --> $DIR/lint-ctypes-enum.rs:89:20 | LL | fn no_result(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs index bda5cef979e..8aa0d04e500 100644 --- a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs +++ b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs @@ -3,7 +3,6 @@ //@ ignore-emscripten no processes //@ compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] -#![feature(generic_nonzero)] use std::num::NonZero; diff --git a/tests/ui/print_type_sizes/niche-filling.rs b/tests/ui/print_type_sizes/niche-filling.rs index 07da1cff27a..5dda0da8458 100644 --- a/tests/ui/print_type_sizes/niche-filling.rs +++ b/tests/ui/print_type_sizes/niche-filling.rs @@ -15,7 +15,6 @@ // ^-- needed because `--pass check` does not emit the output needed. // FIXME: consider using an attribute instead of side-effects. #![allow(dead_code)] -#![feature(generic_nonzero)] #![feature(rustc_attrs)] use std::num::NonZero; diff --git a/tests/ui/structs-enums/enum-null-pointer-opt.rs b/tests/ui/structs-enums/enum-null-pointer-opt.rs index a8418943ba4..7a9e1b3c1e4 100644 --- a/tests/ui/structs-enums/enum-null-pointer-opt.rs +++ b/tests/ui/structs-enums/enum-null-pointer-opt.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(generic_nonzero)] #![feature(transparent_unions)] use std::mem::size_of; diff --git a/tests/ui/structs-enums/enum-null-pointer-opt.stderr b/tests/ui/structs-enums/enum-null-pointer-opt.stderr index fca62bd1c80..64e93ffaffd 100644 --- a/tests/ui/structs-enums/enum-null-pointer-opt.stderr +++ b/tests/ui/structs-enums/enum-null-pointer-opt.stderr @@ -1,5 +1,5 @@ warning: method `dummy` is never used - --> $DIR/enum-null-pointer-opt.rs:11:18 + --> $DIR/enum-null-pointer-opt.rs:10:18 | LL | trait Trait { fn dummy(&self) { } } | ----- ^^^^^ diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 50491d5ef3e..9c933a9ef1c 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -2,7 +2,6 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] -#![feature(generic_nonzero)] #![feature(never_type)] #![feature(pointer_is_aligned_to)] #![feature(strict_provenance)] diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.rs b/tests/ui/suggestions/core-std-import-order-issue-83564.rs index 62b9b246cc8..6f2bdd7a38a 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.rs +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.rs @@ -2,7 +2,6 @@ // // This is a regression test for #83564. // For some reason, Rust 2018 or higher is required to reproduce the bug. -#![feature(generic_nonzero)] fn main() { //~^ HELP consider importing one of these items diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.stderr index 56e10b9340c..8665cc6d87c 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared type `NonZero` - --> $DIR/core-std-import-order-issue-83564.rs:9:14 + --> $DIR/core-std-import-order-issue-83564.rs:8:14 | LL | let _x = NonZero::new(5u32).unwrap(); | ^^^^^^^ use of undeclared type `NonZero` diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 17c55fb4d49..caa3bfc552e 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Znext-solver //~^ ERROR cannot normalize `::Id: '_` -#![feature(generic_nonzero)] #![feature(specialization)] //~^ WARN the feature `specialization` is incomplete diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index 65e33700325..76ae08fdb7a 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-transmute.rs:4:12 + --> $DIR/specialization-transmute.rs:3:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -11,31 +11,31 @@ LL | #![feature(specialization)] error: cannot normalize `::Id: '_` error[E0284]: type annotations needed: cannot satisfy `::Id == _` - --> $DIR/specialization-transmute.rs:16:23 + --> $DIR/specialization-transmute.rs:15:23 | LL | fn intu(&self) -> &Self::Id { | ^^^^^^^^^ cannot satisfy `::Id == _` error[E0284]: type annotations needed: cannot satisfy `T <: ::Id` - --> $DIR/specialization-transmute.rs:18:9 + --> $DIR/specialization-transmute.rs:17:9 | LL | self | ^^^^ cannot satisfy `T <: ::Id` error[E0284]: type annotations needed: cannot satisfy `::Id == Option>` - --> $DIR/specialization-transmute.rs:29:13 + --> $DIR/specialization-transmute.rs:28:13 | LL | let s = transmute::>>(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id == Option>` | note: required by a bound in `transmute` - --> $DIR/specialization-transmute.rs:22:25 + --> $DIR/specialization-transmute.rs:21:25 | LL | fn transmute, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:14:23 + --> $DIR/specialization-transmute.rs:13:23 | LL | default type Id = T; | ^ cannot infer type for associated type `::Id` From 9ed562f9e71b6ed8e30024395936ee58f1a3ca52 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:00:54 -0400 Subject: [PATCH 184/185] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/reference | 2 +- src/doc/rustc-dev-guide | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book b/src/doc/book index 3131aa4642c..d207d894cc5 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 3131aa4642c627a24f523c82566b94a7d920f68c +Subproject commit d207d894cc5e1d496ab99beeacd1a420e5d4d238 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index eb3eb80e106..0c68e90acaa 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit eb3eb80e106d03250c1fb7c5666b1c8c59672862 +Subproject commit 0c68e90acaae5a611f8f5098a3c2980de9845ab2 diff --git a/src/doc/reference b/src/doc/reference index 55694913b13..5854fcc2865 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 55694913b1301cc809f9bf4a1ad1b3d6920efbd9 +Subproject commit 5854fcc286557ad3ab34d325073d11d8118096b6 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index b77a34bd463..07425fed36b 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit b77a34bd46399687b4ce6a17198e9f316c988794 +Subproject commit 07425fed36b00e60341c5e29e28d37d40cbd4451 From 8d5c47f17fd8cb45ef40ae2b339c52eae807f8d4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 22 Apr 2024 19:50:09 +0200 Subject: [PATCH 185/185] miri libstd tests: test windows-msvc instead of windows-gnu --- src/bootstrap/mk/Makefile.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index d6e60d52d63..3cfd0240794 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -76,10 +76,11 @@ check-aux: $(BOOTSTRAP) miri --stage 2 library/std \ --doc -- \ --skip fs:: --skip net:: --skip process:: --skip sys::pal:: - # Also test some very target-specific modules on other targets. + # Also test some very target-specific modules on other targets + # (making sure to cover an i686 target as well). $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ $(BOOTSTRAP) miri --stage 2 library/std \ - --target aarch64-apple-darwin,i686-pc-windows-gnu \ + --target aarch64-apple-darwin,i686-pc-windows-msvc \ --no-doc -- \ time:: sync:: thread:: env:: dist: