Unnamed repository; edit this file 'description' to name the repository.
d---------
d---------
'>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_.syntax_node_ptr() },
            &|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
"#,
        );
    }
}