ide/inlay_hints/
extern_block.rs

1//! Extern block hints
2use 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}