I'd rather see a dedicated lint name to be #[allow]ed, unless #[externally_constructed] has behavior beyond suppressing the lint. Don't introduce new(-looking) mechanisms when old ones will do.
Alternately: why are your repr(C) structs pub with private fields? The C side of the bridge won’t have private fields.
(I think there are valid answers to this, like “this would require exposing a bunch of other types that the rest of my Rust code doesn’t care about”, but it still seems worth considering.)
The fields could have safety invariants, which in C are expressed in documentation and in Rust (currently) must be expressed via making the fields private, since fields can't (yet) be declared unsafe-to-write.
A common case is representing incomplete/opaque types used in FFI. Ideally these would be extern type but until those are stable the pattern is to use private fields to block incorrect creation of the type.
(Very oddly, I'm getting the warning on a single type in my project, despite having multiple such types. I've yet to investigate what's different about it, but I would guess it's to due with the types' usage in extern fn.)
But I agree that the "correct" way to suppress the lint is just #[allow] rather than a separate new attribute.