mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Add implict unsafety inlay hints for extern blocks
This commit is contained in:
parent
2fd06545a6
commit
19465b94f5
2 changed files with 157 additions and 2 deletions
|
@ -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)]
|
||||
|
|
138
crates/ide/src/inlay_hints/extern_block.rs
Normal file
138
crates/ide/src/inlay_hints/extern_block.rs
Normal 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();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue