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