Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/inlay_hints/closure_captures.rs')
-rw-r--r--crates/ide/src/inlay_hints/closure_captures.rs207
1 files changed, 207 insertions, 0 deletions
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
new file mode 100644
index 0000000000..9d5defcbb7
--- /dev/null
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -0,0 +1,207 @@
+//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
+use ide_db::{base_db::FileId, famous_defs::FamousDefs};
+use syntax::ast::{self, AstNode};
+use text_edit::{TextRange, TextSize};
+
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+
+pub(super) fn hints(
+ acc: &mut Vec<InlayHint>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ config: &InlayHintsConfig,
+ _file_id: FileId,
+ closure: ast::ClosureExpr,
+) -> Option<()> {
+ if !config.closure_capture_hints {
+ return None;
+ }
+ let ty = &sema.type_of_expr(&closure.clone().into())?.original;
+ let c = ty.as_closure()?;
+ let captures = c.captured_items(sema.db);
+
+ if captures.is_empty() {
+ return None;
+ }
+
+ let move_kw_range = match closure.move_token() {
+ Some(t) => t.text_range(),
+ None => {
+ let range = closure.syntax().first_token()?.prev_token()?.text_range();
+ let range = TextRange::new(range.end() - TextSize::from(1), range.end());
+ acc.push(InlayHint {
+ range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple("move", None, None),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ range
+ }
+ };
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::from("("),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ let last = captures.len() - 1;
+ for (idx, capture) in captures.into_iter().enumerate() {
+ let local = capture.local();
+ let source = local.primary_source(sema.db);
+
+ // force cache the source file, otherwise sema lookup will potentially panic
+ _ = sema.parse_or_expand(source.file());
+
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple(
+ format!(
+ "{}{}",
+ match capture.kind() {
+ hir::CaptureKind::SharedRef => "&",
+ hir::CaptureKind::UniqueSharedRef => "&unique ",
+ hir::CaptureKind::MutableRef => "&mut ",
+ hir::CaptureKind::Move => "",
+ },
+ capture.display_place(sema.db)
+ ),
+ None,
+ source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
+ ),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+
+ if idx != last {
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple(", ", None, None),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ }
+ }
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::from(")"),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
+ });
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
+ InlayHintsConfig,
+ };
+
+ #[test]
+ fn all_capture_kinds() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+
+
+#[derive(Copy, Clone)]
+struct Copy;
+
+struct NonCopy;
+
+fn main() {
+ let foo = Copy;
+ let bar = NonCopy;
+ let mut baz = NonCopy;
+ let qux = &mut NonCopy;
+ || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ bar
+// ^ , $
+// ^ baz
+// ^ , $
+// ^ qux
+// ^ )
+ foo;
+ bar;
+ baz;
+ qux;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ &bar
+// ^ , $
+// ^ &baz
+// ^ , $
+// ^ &qux
+// ^ )
+ &foo;
+ &bar;
+ &baz;
+ &qux;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ )
+ &mut baz;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ , $
+// ^ &mut *qux
+// ^ )
+ baz = NonCopy;
+ *qux = NonCopy;
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_token() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+fn main() {
+ let foo = u32;
+ move || {
+// ^^^^ (
+// ^^^^ foo
+// ^^^^ )
+ foo;
+ };
+}
+"#,
+ );
+ }
+}