Skip to content

Commit 19465b9

Browse files
committed
Add implict unsafety inlay hints for extern blocks
1 parent 2fd0654 commit 19465b9

File tree

2 files changed

+157
-2
lines changed

2 files changed

+157
-2
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mod closing_brace;
2929
mod closure_captures;
3030
mod closure_ret;
3131
mod discriminant;
32+
mod extern_block;
3233
mod generic_param;
3334
mod implicit_drop;
3435
mod implicit_static;
@@ -116,6 +117,7 @@ pub(crate) fn inlay_hints(
116117
#[derive(Default)]
117118
struct InlayHintCtx {
118119
lifetime_stacks: Vec<Vec<SmolStr>>,
120+
extern_block_parent: Option<ast::ExternBlock>,
119121
}
120122

121123
pub(crate) fn inlay_hints_resolve(
@@ -174,12 +176,18 @@ fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<S
174176
.unwrap_or_default();
175177
ctx.lifetime_stacks.push(params);
176178
}
179+
if let Some(node) = ast::ExternBlock::cast(node.clone()) {
180+
ctx.extern_block_parent = Some(node);
181+
}
177182
Some(node)
178183
}
179184
WalkEvent::Leave(n) => {
180185
if ast::AnyHasGenericParams::can_cast(n.kind()) {
181186
ctx.lifetime_stacks.pop();
182187
}
188+
if ast::ExternBlock::can_cast(n.kind()) {
189+
ctx.extern_block_parent = None;
190+
}
183191
None
184192
}
185193
}
@@ -234,12 +242,20 @@ fn hints(
234242
ast::Item(it) => match it {
235243
ast::Item::Fn(it) => {
236244
implicit_drop::hints(hints, famous_defs, config, file_id, &it);
245+
if let Some(extern_block) = &ctx.extern_block_parent {
246+
extern_block::fn_hints(hints, famous_defs, config, file_id, &it, extern_block);
247+
}
237248
lifetime::fn_hints(hints, ctx, famous_defs, config, file_id, it)
238249
},
239-
// static type elisions
240-
ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)),
250+
ast::Item::Static(it) => {
251+
if let Some(extern_block) = &ctx.extern_block_parent {
252+
extern_block::static_hints(hints, famous_defs, config, file_id, &it, extern_block);
253+
}
254+
implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it))
255+
},
241256
ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)),
242257
ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
258+
ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, file_id, it),
243259
_ => None,
244260
},
245261
// FIXME: trait object type elisions
@@ -368,6 +384,7 @@ pub enum InlayKind {
368384
Type,
369385
Drop,
370386
RangeExclusive,
387+
ExternUnsafety,
371388
}
372389

373390
#[derive(Debug, Hash)]
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//! Extern block hints
2+
use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
3+
use span::EditionedFileId;
4+
use syntax::{ast, AstNode, SyntaxToken};
5+
6+
use crate::{InlayHint, InlayHintsConfig};
7+
8+
pub(super) fn extern_block_hints(
9+
acc: &mut Vec<InlayHint>,
10+
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
11+
_config: &InlayHintsConfig,
12+
_file_id: EditionedFileId,
13+
extern_block: ast::ExternBlock,
14+
) -> Option<()> {
15+
if extern_block.unsafe_token().is_some() {
16+
return None;
17+
}
18+
let abi = extern_block.abi()?;
19+
acc.push(InlayHint {
20+
range: abi.syntax().text_range(),
21+
position: crate::InlayHintPosition::Before,
22+
pad_left: false,
23+
pad_right: true,
24+
kind: crate::InlayKind::ExternUnsafety,
25+
label: crate::InlayHintLabel::from("unsafe"),
26+
text_edit: Some(TextEdit::insert(abi.syntax().text_range().start(), "unsafe ".to_owned())),
27+
resolve_parent: Some(extern_block.syntax().text_range()),
28+
});
29+
Some(())
30+
}
31+
32+
pub(super) fn fn_hints(
33+
acc: &mut Vec<InlayHint>,
34+
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
35+
_config: &InlayHintsConfig,
36+
_file_id: EditionedFileId,
37+
fn_: &ast::Fn,
38+
extern_block: &ast::ExternBlock,
39+
) -> Option<()> {
40+
let implicit_unsafe = fn_.safe_token().is_none() && fn_.unsafe_token().is_none();
41+
if !implicit_unsafe {
42+
return None;
43+
}
44+
let fn_ = fn_.fn_token()?;
45+
acc.push(item_hint(extern_block, fn_));
46+
Some(())
47+
}
48+
49+
pub(super) fn static_hints(
50+
acc: &mut Vec<InlayHint>,
51+
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
52+
_config: &InlayHintsConfig,
53+
_file_id: EditionedFileId,
54+
static_: &ast::Static,
55+
extern_block: &ast::ExternBlock,
56+
) -> Option<()> {
57+
let implicit_unsafe = static_.safe_token().is_none() && static_.unsafe_token().is_none();
58+
if !implicit_unsafe {
59+
return None;
60+
}
61+
let static_ = static_.static_token()?;
62+
acc.push(item_hint(extern_block, static_));
63+
Some(())
64+
}
65+
66+
fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
67+
InlayHint {
68+
range: token.text_range(),
69+
position: crate::InlayHintPosition::Before,
70+
pad_left: false,
71+
pad_right: true,
72+
kind: crate::InlayKind::ExternUnsafety,
73+
label: crate::InlayHintLabel::from("unsafe"),
74+
text_edit: {
75+
let mut builder = TextEdit::builder();
76+
builder.insert(token.text_range().start(), "unsafe ".to_owned());
77+
if extern_block.unsafe_token().is_none() {
78+
if let Some(abi) = extern_block.abi() {
79+
builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned());
80+
}
81+
}
82+
Some(builder.finish())
83+
},
84+
resolve_parent: Some(extern_block.syntax().text_range()),
85+
}
86+
}
87+
88+
#[cfg(test)]
89+
mod tests {
90+
use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG};
91+
92+
#[test]
93+
fn unadorned() {
94+
check_with_config(
95+
DISABLED_CONFIG,
96+
r#"
97+
extern "C" {
98+
//^^^^^^^^^^ unsafe
99+
static FOO: ();
100+
// ^^^^^^ unsafe
101+
pub static FOO: ();
102+
// ^^^^^^unsafe
103+
unsafe static FOO: ();
104+
safe static FOO: ();
105+
fn foo();
106+
// ^^ unsafe
107+
pub fn foo();
108+
// ^^ unsafe
109+
unsafe fn foo();
110+
safe fn foo();
111+
}
112+
"#,
113+
);
114+
}
115+
116+
#[test]
117+
fn adorned() {
118+
check_with_config(
119+
DISABLED_CONFIG,
120+
r#"
121+
unsafe extern "C" {
122+
static FOO: ();
123+
// ^^^^^^ unsafe
124+
pub static FOO: ();
125+
// ^^^^^^unsafe
126+
unsafe static FOO: ();
127+
safe static FOO: ();
128+
fn foo();
129+
// ^^ unsafe
130+
pub fn foo();
131+
// ^^ unsafe
132+
unsafe fn foo();
133+
safe fn foo();
134+
}
135+
"#,
136+
);
137+
}
138+
}

0 commit comments

Comments
 (0)