Rollup merge of #144243 - lnicola:sync-from-ra, r=lnicola

Subtree update of `rust-analyzer`

r? ```@ghost```
This commit is contained in:
许杰友 Jieyou Xu (Joe) 2025-07-22 00:54:31 +08:00 committed by GitHub
commit 1bcd093474
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 278 additions and 28 deletions

41
Cargo.lock generated
View File

@ -1268,7 +1268,7 @@ dependencies = [
"expect-test",
"intern",
"parser",
"ra-ap-rustc_lexer",
"ra-ap-rustc_lexer 0.122.0",
"rustc-hash 2.1.1",
"smallvec",
"span",
@ -1504,7 +1504,7 @@ dependencies = [
"drop_bomb",
"edition",
"expect-test",
"ra-ap-rustc_lexer",
"ra-ap-rustc_lexer 0.122.0",
"rustc-literal-escaper",
"stdx",
"tracing",
@ -1614,7 +1614,7 @@ dependencies = [
"object",
"paths",
"proc-macro-test",
"ra-ap-rustc_lexer",
"ra-ap-rustc_lexer 0.122.0",
"span",
"syntax-bridge",
"tt",
@ -1756,9 +1756,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_abi"
version = "0.121.0"
version = "0.122.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee51482d1c9d3e538acda8cce723db8eea1a81540544bf362bf4c3d841b2329"
checksum = "fb01e1fec578003c85481c1cad4ff8cd8195b07c2dc85ae3f716108507ae15d5"
dependencies = [
"bitflags 2.9.1",
"ra-ap-rustc_hashes",
@ -1768,18 +1768,18 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_hashes"
version = "0.121.0"
version = "0.122.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19c8f1e0c28e24e1b4c55dc08058c6c9829df2204497d4034259f491d348c204"
checksum = "e0ec056e72a472ffef8761ce96ece6c626eb07368c09d0105b6df30d27d07673"
dependencies = [
"rustc-stable-hash",
]
[[package]]
name = "ra-ap-rustc_index"
version = "0.121.0"
version = "0.122.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f33f429cec6b92fa2c7243883279fb29dd233fdc3e94099aff32aa91aa87f50"
checksum = "0fcdd1001db0295e59052e9f53aeda588bbe81e362534f4687d41bd44777b5a7"
dependencies = [
"ra-ap-rustc_index_macros",
"smallvec",
@ -1787,9 +1787,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index_macros"
version = "0.121.0"
version = "0.122.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9b55910dbe1fe7ef34bdc1d1bcb41e99b377eb680ea58a1218d95d6b4152257"
checksum = "728d64dd98e25530b32e3f7c7c1e844e52722b269360daa1cdeba9dff9727a26"
dependencies = [
"proc-macro2",
"quote",
@ -1807,21 +1807,32 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "ra-ap-rustc_lexer"
version = "0.122.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "415f0821f512608d825b3215489a6a6a2c18ed9f0045953d514e7ec23d4b90ab"
dependencies = [
"memchr",
"unicode-properties",
"unicode-xid",
]
[[package]]
name = "ra-ap-rustc_parse_format"
version = "0.121.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81057891bc2063ad9e353f29462fbc47a0f5072560af34428ae9313aaa5e9d97"
dependencies = [
"ra-ap-rustc_lexer",
"ra-ap-rustc_lexer 0.121.0",
"rustc-literal-escaper",
]
[[package]]
name = "ra-ap-rustc_pattern_analysis"
version = "0.121.0"
version = "0.122.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe21a3542980d56d2435e96c2720773cac1c63fd4db666417e414729da192eb3"
checksum = "4657fcfdfe06e2a02ec8180d4e7c95aecf4811ba50367e363d1a2300b7623284"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.1.1",
@ -2581,7 +2592,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"intern",
"ra-ap-rustc_lexer",
"ra-ap-rustc_lexer 0.122.0",
"stdx",
"text-size",
]

View File

@ -89,11 +89,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
edition = { path = "./crates/edition", version = "0.0.0" }
ra-ap-rustc_lexer = { version = "0.121", default-features = false }
ra-ap-rustc_lexer = { version = "0.122", default-features = false }
ra-ap-rustc_parse_format = { version = "0.121", default-features = false }
ra-ap-rustc_index = { version = "0.121", default-features = false }
ra-ap-rustc_abi = { version = "0.121", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.121", default-features = false }
ra-ap-rustc_index = { version = "0.122", default-features = false }
ra-ap-rustc_abi = { version = "0.122", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.122", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.

View File

@ -68,6 +68,11 @@ impl CfgExpr {
next_cfg_expr(&mut tt.iter()).unwrap_or(CfgExpr::Invalid)
}
#[cfg(feature = "tt")]
pub fn parse_from_iter<S: Copy>(tt: &mut tt::iter::TtIter<'_, S>) -> CfgExpr {
next_cfg_expr(tt).unwrap_or(CfgExpr::Invalid)
}
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
match self {
@ -96,7 +101,14 @@ fn next_cfg_expr<S: Copy>(it: &mut tt::iter::TtIter<'_, S>) -> Option<CfgExpr> {
};
let ret = match it.peek() {
Some(TtElement::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
Some(TtElement::Leaf(tt::Leaf::Punct(punct)))
// Don't consume on e.g. `=>`.
if punct.char == '='
&& (punct.spacing == tt::Spacing::Alone
|| it.remaining().flat_tokens().get(1).is_none_or(|peek2| {
!matches!(peek2, tt::TokenTree::Leaf(tt::Leaf::Punct(_)))
})) =>
{
match it.remaining().flat_tokens().get(1) {
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
it.next();

View File

@ -550,3 +550,51 @@ fn main() { "\"hello\""; }
"##]],
);
}
#[test]
fn cfg_select() {
check(
r#"
#[rustc_builtin_macro]
pub macro cfg_select($($tt:tt)*) {}
cfg_select! {
false => { fn false_1() {} }
any(false, true) => { fn true_1() {} }
}
cfg_select! {
false => { fn false_2() {} }
_ => { fn true_2() {} }
}
cfg_select! {
false => { fn false_3() {} }
}
cfg_select! {
false
}
cfg_select! {
false =>
}
"#,
expect![[r#"
#[rustc_builtin_macro]
pub macro cfg_select($($tt:tt)*) {}
fn true_1() {}
fn true_2() {}
/* error: none of the predicates in this `cfg_select` evaluated to true */
/* error: expected `=>` after cfg expression */
/* error: expected a token tree after `=>` */
"#]],
);
}

View File

@ -127,6 +127,7 @@ register_builtin! {
(asm, Asm) => asm_expand,
(global_asm, GlobalAsm) => global_asm_expand,
(naked_asm, NakedAsm) => naked_asm_expand,
(cfg_select, CfgSelect) => cfg_select_expand,
(cfg, Cfg) => cfg_expand,
(core_panic, CorePanic) => panic_expand,
(std_panic, StdPanic) => panic_expand,
@ -355,6 +356,71 @@ fn naked_asm_expand(
ExpandResult::ok(expanded)
}
fn cfg_select_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
let loc = db.lookup_intern_macro_call(id);
let cfg_options = loc.krate.cfg_options(db);
let mut iter = tt.iter();
let mut expand_to = None;
while let Some(next) = iter.peek() {
let active = if let tt::TtElement::Leaf(tt::Leaf::Ident(ident)) = next
&& ident.sym == sym::underscore
{
iter.next();
true
} else {
cfg_options.check(&CfgExpr::parse_from_iter(&mut iter)) != Some(false)
};
match iter.expect_glued_punct() {
Ok(it) if it.len() == 2 && it[0].char == '=' && it[1].char == '>' => {}
_ => {
let err_span = iter.peek().map(|it| it.first_span()).unwrap_or(span);
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
ExpandError::other(err_span, "expected `=>` after cfg expression"),
);
}
}
let expand_to_if_active = match iter.next() {
Some(tt::TtElement::Subtree(_, tt)) => tt.remaining(),
_ => {
let err_span = iter.peek().map(|it| it.first_span()).unwrap_or(span);
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
ExpandError::other(err_span, "expected a token tree after `=>`"),
);
}
};
if expand_to.is_none() && active {
expand_to = Some(expand_to_if_active);
}
}
match expand_to {
Some(expand_to) => {
let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter {
kind: tt::DelimiterKind::Invisible,
open: span,
close: span,
});
builder.extend_with_tt(expand_to);
ExpandResult::ok(builder.build())
}
None => ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
ExpandError::other(
span,
"none of the predicates in this `cfg_select` evaluated to true",
),
),
}
}
fn cfg_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,

View File

@ -10,7 +10,7 @@ use syntax::{
use crate::{
AssistId,
assist_context::{AssistContext, Assists, SourceChangeBuilder},
utils::generate_trait_impl_text,
utils::generate_trait_impl_text_intransitive,
};
// Assist: generate_deref
@ -150,7 +150,7 @@ fn generate_edit(
),
};
let strukt_adt = ast::Adt::Struct(strukt);
let deref_impl = generate_trait_impl_text(
let deref_impl = generate_trait_impl_text_intransitive(
&strukt_adt,
&trait_path.display(db, edition).to_string(),
&impl_code,
@ -227,6 +227,28 @@ impl core::ops::Deref for B {
);
}
#[test]
fn test_generate_record_deref_with_generic() {
check_assist(
generate_deref,
r#"
//- minicore: deref
struct A<T>($0T);
"#,
r#"
struct A<T>(T);
impl<T> core::ops::Deref for A<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
"#,
);
}
#[test]
fn test_generate_record_deref_short_path() {
check_assist(

View File

@ -134,6 +134,9 @@ fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option
if trait_ == famous.core_borrow_Borrow().as_ref() {
return Some("BorrowMut");
}
if trait_ == famous.core_ops_Deref().as_ref() {
return Some("DerefMut");
}
None
}
@ -142,6 +145,7 @@ fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
"index" => "index_mut",
"as_ref" => "as_mut",
"borrow" => "borrow_mut",
"deref" => "deref_mut",
_ => return None,
};
Some((name, new_name))
@ -258,6 +262,39 @@ impl core::convert::AsRef<i32> for Foo {
&self.0
}
}
"#,
);
check_assist(
generate_mut_trait_impl,
r#"
//- minicore: deref
struct Foo(i32);
impl core::ops::Deref$0 for Foo {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
"#,
r#"
struct Foo(i32);
$0impl core::ops::DerefMut for Foo {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl core::ops::Deref for Foo {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
"#,
);
}

View File

@ -567,6 +567,7 @@ pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
///
/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
// FIXME: migrate remaining uses to `generate_trait_impl`
#[allow(dead_code)]
pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
generate_impl_text_inner(adt, Some(trait_text), true, code)
}

View File

@ -531,7 +531,7 @@ impl<'a> FindUsages<'a> {
node.token_at_offset(offset)
.find(|it| {
// `name` is stripped of raw ident prefix. See the comment on name retrieval below.
it.text().trim_start_matches("r#") == name
it.text().trim_start_matches('\'').trim_start_matches("r#") == name
})
.into_iter()
.flat_map(move |token| {
@ -938,7 +938,12 @@ impl<'a> FindUsages<'a> {
})
};
// We need to search without the `r#`, hence `as_str` access.
self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr())
// We strip `'` from lifetimes and labels as otherwise they may not match with raw-escaped ones,
// e.g. if we search `'foo` we won't find `'r#foo`.
self.def
.name(sema.db)
.or_else(self_kw_refs)
.map(|it| it.as_str().trim_start_matches('\'').to_smolstr())
}
};
let name = match &name {

View File

@ -48,7 +48,6 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
let mut res = vec![];
let mut visited_comments = FxHashSet::default();
let mut visited_nodes = FxHashSet::default();
let mut merged_fn_bodies = FxHashSet::default();
// regions can be nested, here is a LIFO buffer
let mut region_starts: Vec<TextSize> = vec![];
@ -73,7 +72,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
continue;
}
if let Some(body) = fn_node.body() {
if fn_node.body().is_some() {
res.push(Fold {
range: TextRange::new(
node.text_range().start(),
@ -81,7 +80,6 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
),
kind: FoldKind::Function,
});
merged_fn_bodies.insert(body.syntax().text_range());
continue;
}
}

View File

@ -3088,4 +3088,42 @@ fn main() {
"#]],
);
}
#[test]
fn raw_labels_and_lifetimes() {
check(
r#"
fn foo<'r#fn>(s: &'r#fn str) {
let _a: &'r#fn str = s;
let _b: &'r#fn str;
'r#break$0: {
break 'r#break;
}
}
"#,
expect![[r#"
'r#break Label FileId(0) 87..96 87..95
FileId(0) 113..121
"#]],
);
check(
r#"
fn foo<'r#fn$0>(s: &'r#fn str) {
let _a: &'r#fn str = s;
let _b: &'r#fn str;
'r#break: {
break 'r#break;
}
}
"#,
expect![[r#"
'r#fn LifetimeParam FileId(0) 7..12
FileId(0) 18..23
FileId(0) 44..49
FileId(0) 72..77
"#]],
);
}
}

View File

@ -156,6 +156,7 @@ define_symbols! {
cfg_attr,
cfg_eval,
cfg,
cfg_select,
char,
clone,
Clone,

View File

@ -217,6 +217,17 @@ pub enum TtElement<'a, S> {
Subtree(&'a Subtree<S>, TtIter<'a, S>),
}
impl<S: Copy + fmt::Debug> fmt::Debug for TtElement<'_, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Leaf(leaf) => f.debug_tuple("Leaf").field(leaf).finish(),
Self::Subtree(subtree, inner) => {
f.debug_tuple("Subtree").field(subtree).field(inner).finish()
}
}
}
}
impl<S: Copy> TtElement<'_, S> {
#[inline]
pub fn first_span(&self) -> S {

View File

@ -1 +1 @@
a9fb6103b05c6ad6eee6bed4c0bb5a2e8e1024c6
e05ab47e6c418fb2b9faa2eae9a7e70c65c98eaa