Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18610 from Veykril/push-kynytqktmnxq
Add implict unsafety inlay hints for extern blocks
Lukas Wirth 2024-12-06
parent cddaf74 · parent 19465b9 · commit 632ca53
-rw-r--r--crates/ide/src/inlay_hints.rs21
-rw-r--r--crates/ide/src/inlay_hints/extern_block.rs138
2 files changed, 157 insertions, 2 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 1ae8bfa9b6..aa99ba49bc 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/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/crates/ide/src/inlay_hints/extern_block.rs b/crates/ide/src/inlay_hints/extern_block.rs
new file mode 100644
index 0000000000..4cc4925cda
--- /dev/null
+++ b/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();
+}
+"#,
+ );
+ }
+}