about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-12-04 09:00:08 +0100
committerLukas Wirth <lukastw97@gmail.com>2024-12-06 12:49:03 +0100
commit2c91563eb5f0556696f6cf3192c9757474723a3e (patch)
tree3eca2c9ea69cd33c7a4072d2f91f8ac2a2616ff7 /src
parent6ef7f8e01409884d160769d6a62ba3d82dfb0207 (diff)
downloadrust-2c91563eb5f0556696f6cf3192c9757474723a3e.tar.gz
rust-2c91563eb5f0556696f6cf3192c9757474723a3e.zip
Add implict unsafety inlay hints for extern blocks
Diffstat (limited to 'src')
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs138
2 files changed, 157 insertions, 2 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 1ae8bfa9b6e..aa99ba49bc8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -29,6 +29,7 @@ mod closing_brace;
 mod closure_captures;
 mod closure_ret;
 mod discriminant;
+mod extern_block;
 mod generic_param;
 mod implicit_drop;
 mod implicit_static;
@@ -116,6 +117,7 @@ pub(crate) fn inlay_hints(
 #[derive(Default)]
 struct InlayHintCtx {
     lifetime_stacks: Vec<Vec<SmolStr>>,
+    extern_block_parent: Option<ast::ExternBlock>,
 }
 
 pub(crate) fn inlay_hints_resolve(
@@ -174,12 +176,18 @@ fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<S
                     .unwrap_or_default();
                 ctx.lifetime_stacks.push(params);
             }
+            if let Some(node) = ast::ExternBlock::cast(node.clone()) {
+                ctx.extern_block_parent = Some(node);
+            }
             Some(node)
         }
         WalkEvent::Leave(n) => {
             if ast::AnyHasGenericParams::can_cast(n.kind()) {
                 ctx.lifetime_stacks.pop();
             }
+            if ast::ExternBlock::can_cast(n.kind()) {
+                ctx.extern_block_parent = None;
+            }
             None
         }
     }
@@ -234,12 +242,20 @@ fn hints(
             ast::Item(it) => match it {
                 ast::Item::Fn(it) => {
                     implicit_drop::hints(hints, famous_defs, config, file_id, &it);
+                    if let Some(extern_block) = &ctx.extern_block_parent {
+                        extern_block::fn_hints(hints, famous_defs, config, file_id, &it, extern_block);
+                    }
                     lifetime::fn_hints(hints, ctx, famous_defs, config, file_id, it)
                 },
-                // static type elisions
-                ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)),
+                ast::Item::Static(it) => {
+                    if let Some(extern_block) = &ctx.extern_block_parent {
+                        extern_block::static_hints(hints, famous_defs, config, file_id, &it, extern_block);
+                    }
+                    implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it))
+                },
                 ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)),
                 ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
+                ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, file_id, it),
                 _ => None,
             },
             // FIXME: trait object type elisions
@@ -368,6 +384,7 @@ pub enum InlayKind {
     Type,
     Drop,
     RangeExclusive,
+    ExternUnsafety,
 }
 
 #[derive(Debug, Hash)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
new file mode 100644
index 00000000000..4cc4925cda6
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/extern_block.rs
@@ -0,0 +1,138 @@
+//! Extern block hints
+use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
+use span::EditionedFileId;
+use syntax::{ast, AstNode, SyntaxToken};
+
+use crate::{InlayHint, InlayHintsConfig};
+
+pub(super) fn extern_block_hints(
+    acc: &mut Vec<InlayHint>,
+    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    _config: &InlayHintsConfig,
+    _file_id: EditionedFileId,
+    extern_block: ast::ExternBlock,
+) -> Option<()> {
+    if extern_block.unsafe_token().is_some() {
+        return None;
+    }
+    let abi = extern_block.abi()?;
+    acc.push(InlayHint {
+        range: abi.syntax().text_range(),
+        position: crate::InlayHintPosition::Before,
+        pad_left: false,
+        pad_right: true,
+        kind: crate::InlayKind::ExternUnsafety,
+        label: crate::InlayHintLabel::from("unsafe"),
+        text_edit: Some(TextEdit::insert(abi.syntax().text_range().start(), "unsafe ".to_owned())),
+        resolve_parent: Some(extern_block.syntax().text_range()),
+    });
+    Some(())
+}
+
+pub(super) fn fn_hints(
+    acc: &mut Vec<InlayHint>,
+    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    _config: &InlayHintsConfig,
+    _file_id: EditionedFileId,
+    fn_: &ast::Fn,
+    extern_block: &ast::ExternBlock,
+) -> Option<()> {
+    let implicit_unsafe = fn_.safe_token().is_none() && fn_.unsafe_token().is_none();
+    if !implicit_unsafe {
+        return None;
+    }
+    let fn_ = fn_.fn_token()?;
+    acc.push(item_hint(extern_block, fn_));
+    Some(())
+}
+
+pub(super) fn static_hints(
+    acc: &mut Vec<InlayHint>,
+    FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+    _config: &InlayHintsConfig,
+    _file_id: EditionedFileId,
+    static_: &ast::Static,
+    extern_block: &ast::ExternBlock,
+) -> Option<()> {
+    let implicit_unsafe = static_.safe_token().is_none() && static_.unsafe_token().is_none();
+    if !implicit_unsafe {
+        return None;
+    }
+    let static_ = static_.static_token()?;
+    acc.push(item_hint(extern_block, static_));
+    Some(())
+}
+
+fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
+    InlayHint {
+        range: token.text_range(),
+        position: crate::InlayHintPosition::Before,
+        pad_left: false,
+        pad_right: true,
+        kind: crate::InlayKind::ExternUnsafety,
+        label: crate::InlayHintLabel::from("unsafe"),
+        text_edit: {
+            let mut builder = TextEdit::builder();
+            builder.insert(token.text_range().start(), "unsafe ".to_owned());
+            if extern_block.unsafe_token().is_none() {
+                if let Some(abi) = extern_block.abi() {
+                    builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned());
+                }
+            }
+            Some(builder.finish())
+        },
+        resolve_parent: Some(extern_block.syntax().text_range()),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG};
+
+    #[test]
+    fn unadorned() {
+        check_with_config(
+            DISABLED_CONFIG,
+            r#"
+  extern "C" {
+//^^^^^^^^^^ unsafe
+    static FOO: ();
+ // ^^^^^^ unsafe
+    pub static FOO: ();
+     // ^^^^^^unsafe
+    unsafe static FOO: ();
+    safe static FOO: ();
+    fn foo();
+ // ^^ unsafe
+    pub fn foo();
+     // ^^ unsafe
+    unsafe fn foo();
+    safe fn foo();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn adorned() {
+        check_with_config(
+            DISABLED_CONFIG,
+            r#"
+unsafe extern "C" {
+    static FOO: ();
+ // ^^^^^^ unsafe
+    pub static FOO: ();
+     // ^^^^^^unsafe
+    unsafe static FOO: ();
+    safe static FOO: ();
+    fn foo();
+ // ^^ unsafe
+    pub fn foo();
+     // ^^ unsafe
+    unsafe fn foo();
+    safe fn foo();
+}
+"#,
+        );
+    }
+}