Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/tests/closure_captures.rs')
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs231
1 files changed, 190 insertions, 41 deletions
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs
index 782331a136..22cef3505b 100644
--- a/crates/hir-ty/src/tests/closure_captures.rs
+++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -1,11 +1,15 @@
use base_db::salsa::InternKey;
use expect_test::{expect, Expect};
use hir_def::db::DefDatabase;
+use hir_expand::files::InFileWrapper;
use itertools::Itertools;
+use span::{HirFileId, TextRange};
+use syntax::{AstNode, AstPtr};
use test_fixture::WithFixture;
use crate::db::{HirDatabase, InternedClosureId};
use crate::display::HirDisplay;
+use crate::mir::MirSpan;
use crate::test_db::TestDB;
use super::visit_module;
@@ -30,45 +34,69 @@ fn check_closure_captures(ra_fixture: &str, expect: Expect) {
.expect("failed to map closure to SyntaxNode")
.value
.text_range();
- captures.iter().flat_map(move |capture| {
+ captures.iter().map(move |capture| {
+ fn text_range<N: AstNode>(
+ db: &TestDB,
+ syntax: InFileWrapper<HirFileId, AstPtr<N>>,
+ ) -> TextRange {
+ let root = syntax.file_syntax(db);
+ syntax.value.to_node(&root).syntax().text_range()
+ }
+
// FIXME: Deduplicate this with hir::Local::sources().
let (body, source_map) = db.body_with_source_map(closure.0);
- let local_text_ranges = match body.self_param.zip(source_map.self_param_syntax()) {
+ let local_text_range = match body.self_param.zip(source_map.self_param_syntax()) {
Some((param, source)) if param == capture.local() => {
- vec![source.file_syntax(db).text_range()]
+ format!("{:?}", text_range(db, source))
}
_ => source_map
.patterns_for_binding(capture.local())
.iter()
.map(|&definition| {
- let src = source_map.pat_syntax(definition).unwrap();
- src.file_syntax(db).text_range()
+ text_range(db, source_map.pat_syntax(definition).unwrap())
})
- .collect(),
+ .map(|it| format!("{it:?}"))
+ .join(", "),
};
let place = capture.display_place(closure.0, db);
let capture_ty = capture.ty.skip_binders().display_test(db).to_string();
- local_text_ranges.into_iter().map(move |local_text_range| {
- (
- closure_text_range,
- local_text_range,
- place.clone(),
- capture_ty.clone(),
- capture.kind(),
- )
- })
+ let spans = capture
+ .spans()
+ .iter()
+ .flat_map(|span| match *span {
+ MirSpan::ExprId(expr) => {
+ vec![text_range(db, source_map.expr_syntax(expr).unwrap())]
+ }
+ MirSpan::PatId(pat) => {
+ vec![text_range(db, source_map.pat_syntax(pat).unwrap())]
+ }
+ MirSpan::BindingId(binding) => source_map
+ .patterns_for_binding(binding)
+ .iter()
+ .map(|pat| text_range(db, source_map.pat_syntax(*pat).unwrap()))
+ .collect(),
+ MirSpan::SelfParam => {
+ vec![text_range(db, source_map.self_param_syntax().unwrap())]
+ }
+ MirSpan::Unknown => Vec::new(),
+ })
+ .sorted_by_key(|it| it.start())
+ .map(|it| format!("{it:?}"))
+ .join(",");
+
+ (closure_text_range, local_text_range, spans, place, capture_ty, capture.kind())
})
}));
}
captures_info.sort_unstable_by_key(|(closure_text_range, local_text_range, ..)| {
- (closure_text_range.start(), local_text_range.start())
+ (closure_text_range.start(), local_text_range.clone())
});
let rendered = captures_info
.iter()
- .map(|(closure_text_range, local_text_range, place, capture_ty, capture_kind)| {
+ .map(|(closure_text_range, local_text_range, spans, place, capture_ty, capture_kind)| {
format!(
- "{closure_text_range:?};{local_text_range:?} {capture_kind:?} {place} {capture_ty}"
+ "{closure_text_range:?};{local_text_range};{spans} {capture_kind:?} {place} {capture_ty}"
)
})
.join("\n");
@@ -86,7 +114,7 @@ fn main() {
let closure = || { let b = *a; };
}
"#,
- expect!["53..71;0..75 ByRef(Shared) *a &'? bool"],
+ expect!["53..71;20..21;66..68 ByRef(Shared) *a &'? bool"],
);
}
@@ -100,7 +128,7 @@ fn main() {
let closure = || { let &mut ref b = a; };
}
"#,
- expect!["53..79;0..83 ByRef(Shared) *a &'? bool"],
+ expect!["53..79;20..21;67..72 ByRef(Shared) *a &'? bool"],
);
check_closure_captures(
r#"
@@ -110,7 +138,7 @@ fn main() {
let closure = || { let &mut ref mut b = a; };
}
"#,
- expect!["53..83;0..87 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+ expect!["53..83;20..21;67..76 ByRef(Mut { kind: Default }) *a &'? mut bool"],
);
}
@@ -124,7 +152,7 @@ fn main() {
let closure = || { *a = false; };
}
"#,
- expect!["53..71;0..75 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+ expect!["53..71;20..21;58..60 ByRef(Mut { kind: Default }) *a &'? mut bool"],
);
}
@@ -138,7 +166,7 @@ fn main() {
let closure = || { let ref mut b = *a; };
}
"#,
- expect!["53..79;0..83 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+ expect!["53..79;20..21;62..71 ByRef(Mut { kind: Default }) *a &'? mut bool"],
);
}
@@ -152,7 +180,7 @@ fn main() {
let closure = || { let _ = *a else { return; }; };
}
"#,
- expect!["53..88;0..92 ByRef(Shared) *a &'? bool"],
+ expect!["53..88;20..21;66..68 ByRef(Shared) *a &'? bool"],
);
}
@@ -167,7 +195,7 @@ fn main() {
let closure = || { let b = a; };
}
"#,
- expect!["67..84;0..88 ByValue a NonCopy"],
+ expect!["67..84;36..37;80..81 ByValue a NonCopy"],
);
}
@@ -184,8 +212,8 @@ fn main() {
}
"#,
expect![[r#"
- 71..89;0..135 ByRef(Shared) a &'? NonCopy
- 109..131;0..135 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
+ 71..89;36..41;84..86 ByRef(Shared) a &'? NonCopy
+ 109..131;36..41;122..128 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
);
}
@@ -200,7 +228,7 @@ fn main() {
let closure = || { let b = a.a; };
}
"#,
- expect!["92..111;0..115 ByRef(Shared) a.a &'? i32"],
+ expect!["92..111;50..51;105..108 ByRef(Shared) a.a &'? i32"],
);
}
@@ -221,9 +249,9 @@ fn main() {
}
"#,
expect![[r#"
- 133..212;0..216 ByRef(Shared) a.a &'? i32
- 133..212;0..216 ByRef(Mut { kind: Default }) a.b &'? mut i32
- 133..212;0..216 ByValue a.c NonCopy"#]],
+ 133..212;87..92;154..158 ByRef(Shared) a.a &'? i32
+ 133..212;87..92;176..184 ByRef(Mut { kind: Default }) a.b &'? mut i32
+ 133..212;87..92;202..205 ByValue a.c NonCopy"#]],
);
}
@@ -244,8 +272,8 @@ fn main() {
}
"#,
expect![[r#"
- 123..133;0..168 ByRef(Shared) a &'? Foo
- 153..164;0..168 ByRef(Mut { kind: Default }) a &'? mut Foo"#]],
+ 123..133;92..97;126..127 ByRef(Shared) a &'? Foo
+ 153..164;92..97;156..157 ByRef(Mut { kind: Default }) a &'? mut Foo"#]],
);
}
@@ -262,11 +290,6 @@ fn main() {
*a = false;
let b = &mut a;
};
- // Max ByRef(Mut { kind: ClosureCapture })
- let closure = || {
- let b = *a;
- let c = &mut *a;
- };
// Max ByValue
let mut a = NonCopy;
let closure = || {
@@ -277,8 +300,134 @@ fn main() {
}
"#,
expect![[r#"
- 113..167;0..430 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool
- 234..289;0..430 ByRef(Mut { kind: Default }) *a &'? mut bool
- 353..426;0..430 ByValue a NonCopy"#]],
+ 113..167;36..41;127..128,154..160 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool
+ 231..304;196..201;252..253,276..277,296..297 ByValue a NonCopy"#]],
+ );
+}
+
+#[test]
+fn let_underscore() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+fn main() {
+ let mut a = true;
+ let closure = || { let _ = a; };
+}
+"#,
+ expect![""],
+ );
+}
+
+#[test]
+fn match_wildcard() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+struct NonCopy;
+fn main() {
+ let mut a = NonCopy;
+ let closure = || match a {
+ _ => {}
+ };
+ let closure = || match a {
+ ref b => {}
+ };
+ let closure = || match a {
+ ref mut b => {}
+ };
+}
+"#,
+ expect![[r#"
+ 125..163;36..41;134..135 ByRef(Shared) a &'? NonCopy
+ 183..225;36..41;192..193 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
+ );
+}
+
+#[test]
+fn multiple_bindings() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+fn main() {
+ let mut a = false;
+ let mut closure = || { let (b | b) = a; };
+}
+"#,
+ expect!["57..80;20..25;76..77,76..77 ByRef(Shared) a &'? bool"],
+ );
+}
+
+#[test]
+fn multiple_usages() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+fn main() {
+ let mut a = false;
+ let mut closure = || {
+ let b = &a;
+ let c = &a;
+ let d = &mut a;
+ a = true;
+ };
+}
+"#,
+ expect!["57..149;20..25;78..80,98..100,118..124,134..135 ByRef(Mut { kind: Default }) a &'? mut bool"],
+ );
+}
+
+#[test]
+fn ref_then_deref() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+fn main() {
+ let mut a = false;
+ let mut closure = || { let b = *&mut a; };
+}
+"#,
+ expect!["57..80;20..25;71..77 ByRef(Mut { kind: Default }) a &'? mut bool"],
+ );
+}
+
+#[test]
+fn ref_of_ref() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+fn main() {
+ let mut a = &false;
+ let closure = || { let b = &a; };
+ let closure = || { let b = &mut a; };
+ let a = &mut false;
+ let closure = || { let b = &a; };
+ let closure = || { let b = &mut a; };
+}
+"#,
+ expect![[r#"
+ 54..72;20..25;67..69 ByRef(Shared) a &'? &'? bool
+ 92..114;20..25;105..111 ByRef(Mut { kind: Default }) a &'? mut &'? bool
+ 158..176;124..125;171..173 ByRef(Shared) a &'? &'? mut bool
+ 196..218;124..125;209..215 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool"#]],
+ );
+}
+
+#[test]
+fn multiple_capture_usages() {
+ check_closure_captures(
+ r#"
+//- minicore:copy
+struct A { a: i32, b: bool }
+fn main() {
+ let mut a = A { a: 123, b: false };
+ let closure = |$0| {
+ let b = a.b;
+ a = A { a: 456, b: true };
+ };
+ closure();
+}
+"#,
+ expect!["99..165;49..54;120..121,133..134 ByRef(Mut { kind: Default }) a &'? mut A"],
);
}