//! Extern block hints use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit}; use syntax::{AstNode, SyntaxToken, ast}; use crate::{InlayHint, InlayHintsConfig}; pub(super) fn extern_block_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig<'_>, extern_block: ast::ExternBlock, ) -> Option<()> { if extern_block.unsafe_token().is_some() { return None; } let abi = extern_block.abi()?; sema.to_def(&extern_block)?; 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(config.lazy_text_edit(|| { 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, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig<'_>, 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_token = fn_.fn_token()?; if sema.to_def(fn_).is_some_and(|def| def.extern_block(sema.db).is_some()) { acc.push(item_hint(config, extern_block, fn_token)); } Some(()) } pub(super) fn static_hints( acc: &mut Vec, FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig<'_>, 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_token = static_.static_token()?; if sema.to_def(static_).is_some_and(|def| def.extern_block(sema.db).is_some()) { acc.push(item_hint(config, extern_block, static_token)); } Some(()) } fn item_hint( config: &InlayHintsConfig<'_>, 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: Some(config.lazy_text_edit(|| { let mut builder = TextEdit::builder(); builder.insert(token.text_range().start(), "unsafe ".to_owned()); if extern_block.unsafe_token().is_none() && let Some(abi) = extern_block.abi() { builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned()); } builder.finish() })), resolve_parent: Some(extern_block.syntax().text_range()), } } #[cfg(test)] mod tests { use crate::inlay_hints::tests::{DISABLED_CONFIG, check_with_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(); } "#, ); } }