Repository: rust
Issue: `unsafe_op_in_unsafe_fn` not triggered for raw pointer dereferencing inside `addr_of`/`addr_of_mut`
Labels: A-diagnostics,T-compiler

### Code

```Rust
#![forbid(unsafe_op_in_unsafe_fn)]

use core::ptr::addr_of;
use core::ptr::addr_of_mut;

pub struct Foo {
    field: u8
}

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    addr_of!((*ptr).field)
}

pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
    addr_of_mut!((*ptr).field)
}
```


### Current output

```Shell
<No error>
```


### Desired output

```Shell
error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
  --> src/lib.rs:11:5
   |
11 |     addr_of!((*ptr).field)
   |     ^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
   |
   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
   = note: this error originates in the macro `addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
note: the lint level is defined here
  --> src/lib.rs:1:11
   |
1  | #![forbid(unsafe_op_in_unsafe_fn)]
   |           ^^^^^^^^^^^^^^^^^^^^^^

error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
  --> src/lib.rs:15:5
   |
15 |     addr_of_mut!((*ptr).field)
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
   |
   = note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
   = note: this error originates in the macro `addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0133`.
```


### Rationale and extra context

When removing the `unsafe` keywords in the function declarations of the sample code, the compiler emits the errors found in the “desired output” box (except for the note on the `unsafe_op_in_unsafe_fn` lint). Hence, I was expecting the operations to be unsafe.[^why-unsafe-op] However, with `forbid(unsafe_op_in_unsafe_fn)` and the functions marked `unsafe`, no errors are emitted at all.

[^why-unsafe-op]: I’m not a hundred percent sure about this point as we effectively perform pointer arithmetic only. Conceptually, I would like to understand `addr_of!((*ptr).field)` as not actually dereferencing `ptr`. But the [docs](https://doc.rust-lang.org/core/ptr/macro.addr_of.html) explicitly state that “the `expr` in `addr_of!(expr)` is still subject to all the usual rules. In particular, `addr_of!(*ptr::null())` is Undefined Behavior because it dereferences a null pointer.”

### Other cases

_No response_

### Anything else?

Note that the `unsafe_op_in_unsafe_fn` lint is not entirely broken for the `expr` of `addr_of!(expr)`. The following snippet emits an error for calling an unsafe function (but not for dereferencing the raw pointer):

```rust
#![forbid(unsafe_op_in_unsafe_fn)]

pub struct Foo {
    field: u8
}

unsafe fn unsafe_id<T>(x: T) -> T { x }

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    core::ptr::addr_of!((*unsafe_id(ptr)).field)
}
```

[Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=577055919ab678e1df8abc6a56cede27)

---

Note furthermore that inlining the `addr_of!()`/`addr_of_mut!()` macros (using `#![feature(raw_ref_op)]`) result in the expected behavior:

```rust
#![feature(raw_ref_op)]
#![forbid(unsafe_op_in_unsafe_fn)]

pub struct Foo {
    field: u8
}

pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
    &raw const (*ptr).field
}

pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
    &raw mut (*ptr).field
}
```

[Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=f7da796ab7ab202e623c8e80b9541500)