Merge pull request #18610 from Veykril/push-kynytqktmnxq

Add implict unsafety inlay hints for extern blocks
This commit is contained in:
Lukas Wirth 2024-12-06 12:01:49 +00:00 committed by GitHub
commit 632ca53df4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 157 additions and 2 deletions

View file

@ -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)]

View file

@ -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();
}
"#,
);
}
}