ide_diagnostics/handlers/
trait_impl_incorrect_safety.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use hir::InFile;
use syntax::ast;

use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};

// Diagnostic: trait-impl-incorrect-safety
//
// Diagnoses incorrect safety annotations of trait impls.
pub(crate) fn trait_impl_incorrect_safety(
    ctx: &DiagnosticsContext<'_>,
    d: &hir::TraitImplIncorrectSafety,
) -> Diagnostic {
    Diagnostic::new(
        DiagnosticCode::Ra("trait-impl-incorrect-safety", Severity::Error),
        if d.should_be_safe {
            "unsafe impl for safe trait"
        } else {
            "impl for unsafe trait needs to be unsafe"
        },
        adjusted_display_range::<ast::Impl>(
            ctx,
            InFile { file_id: d.file_id, value: d.impl_ },
            &|impl_| {
                if d.should_be_safe {
                    Some(match (impl_.unsafe_token(), impl_.impl_token()) {
                        (None, None) => return None,
                        (None, Some(t)) | (Some(t), None) => t.text_range(),
                        (Some(t1), Some(t2)) => t1.text_range().cover(t2.text_range()),
                    })
                } else {
                    impl_.impl_token().map(|t| t.text_range())
                }
            },
        ),
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::check_diagnostics;

    #[test]
    fn simple() {
        check_diagnostics(
            r#"
trait Safe {}
unsafe trait Unsafe {}

  impl Safe for () {}

  impl Unsafe for () {}
//^^^^  error: impl for unsafe trait needs to be unsafe

  unsafe impl Safe for () {}
//^^^^^^^^^^^ error: unsafe impl for safe trait

  unsafe impl Unsafe for () {}
"#,
        );
    }

    #[test]
    fn drop_may_dangle() {
        check_diagnostics(
            r#"
#[lang = "drop"]
trait Drop {}
struct S<T>;
struct L<'l>;

  impl<T> Drop for S<T> {}

  impl<#[may_dangle] T> Drop for S<T> {}
//^^^^ error: impl for unsafe trait needs to be unsafe

  unsafe impl<T> Drop for S<T> {}
//^^^^^^^^^^^ error: unsafe impl for safe trait

  unsafe impl<#[may_dangle] T> Drop for S<T> {}

  impl<'l> Drop for L<'l> {}

  impl<#[may_dangle] 'l> Drop for L<'l> {}
//^^^^ error: impl for unsafe trait needs to be unsafe

  unsafe impl<'l> Drop for L<'l> {}
//^^^^^^^^^^^ error: unsafe impl for safe trait

  unsafe impl<#[may_dangle] 'l> Drop for L<'l> {}
"#,
        );
    }

    #[test]
    fn negative() {
        check_diagnostics(
            r#"
trait Trait {}

  impl !Trait for () {}

  unsafe impl !Trait for () {}
//^^^^^^^^^^^ error: unsafe impl for safe trait

unsafe trait UnsafeTrait {}

  impl !UnsafeTrait for () {}

  unsafe impl !UnsafeTrait for () {}
//^^^^^^^^^^^ error: unsafe impl for safe trait

"#,
        );
    }

    #[test]
    fn inherent() {
        check_diagnostics(
            r#"
struct S;

  impl S {}

  unsafe impl S {}
//^^^^^^^^^^^ error: unsafe impl for safe trait
"#,
        );
    }
}