Skip to content

Commit e5f2e0e

Browse files
committed
lint: port improper ctypes diagnostics
Signed-off-by: David Wood <david.wood@huawei.com>
1 parent 7a9bef4 commit e5f2e0e

File tree

2 files changed

+124
-63
lines changed

2 files changed

+124
-63
lines changed

compiler/rustc_error_messages/locales/en-US/lint.ftl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,61 @@ lint-overflowing-literal = literal out of range for `{$ty}`
172172
.note = the literal `{$lit}` does not fit into the type `{$ty}` and will be converted to `{$ty}::INFINITY`
173173
174174
lint-unused-comparisons = comparison is useless due to type limits
175+
176+
lint-improper-ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
177+
.label = not FFI-safe
178+
.note = the type is defined here
179+
180+
lint-improper-ctypes-opaque = opaque types have no C equivalent
181+
182+
lint-improper-ctypes-fnptr-reason = this function pointer has Rust-specific calling convention
183+
lint-improper-ctypes-fnptr-help = consider using an `extern fn(...) -> ...` function pointer instead
184+
185+
lint-improper-ctypes-tuple-reason = tuples have unspecified layout
186+
lint-improper-ctypes-tuple-help = consider using a struct instead
187+
188+
lint-improper-ctypes-str-reason = string slices have no C equivalent
189+
lint-improper-ctypes-str-help = consider using `*const u8` and a length instead
190+
191+
lint-improper-ctypes-dyn = trait objects have no C equivalent
192+
193+
lint-improper-ctypes-slice-reason = slices have no C equivalent
194+
lint-improper-ctypes-slice-help = consider using a raw pointer instead
195+
196+
lint-improper-ctypes-128bit = 128-bit integers don't currently have a known stable ABI
197+
198+
lint-improper-ctypes-char-reason = the `char` type has no C equivalent
199+
lint-improper-ctypes-char-help = consider using `u32` or `libc::wchar_t` instead
200+
201+
lint-improper-ctypes-non-exhaustive = this enum is non-exhaustive
202+
lint-improper-ctypes-non-exhaustive-variant = this enum has non-exhaustive variants
203+
204+
lint-improper-ctypes-enum-repr-reason = enum has no representation hint
205+
lint-improper-ctypes-enum-repr-help =
206+
consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
207+
208+
lint-improper-ctypes-struct-fieldless-reason = this struct has no fields
209+
lint-improper-ctypes-struct-fieldless-help = consider adding a member to this struct
210+
211+
lint-improper-ctypes-union-fieldless-reason = this union has no fields
212+
lint-improper-ctypes-union-fieldless-help = consider adding a member to this union
213+
214+
lint-improper-ctypes-struct-non-exhaustive = this struct is non-exhaustive
215+
lint-improper-ctypes-union-non-exhaustive = this union is non-exhaustive
216+
217+
lint-improper-ctypes-struct-layout-reason = this struct has unspecified layout
218+
lint-improper-ctypes-struct-layout-help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
219+
220+
lint-improper-ctypes-union-layout-reason = this union has unspecified layout
221+
lint-improper-ctypes-union-layout-help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union
222+
223+
lint-improper-ctypes-box = box cannot be represented as a single pointer
224+
225+
lint-improper-ctypes-enum-phantomdata = this enum contains a PhantomData field
226+
227+
lint-improper-ctypes-struct-zst = this struct contains only zero-sized fields
228+
229+
lint-improper-ctypes-array-reason = passing raw arrays by value is not FFI-safe
230+
lint-improper-ctypes-array-help = consider passing a pointer to the array
231+
232+
lint-improper-ctypes-only-phantomdata = composed only of `PhantomData`

compiler/rustc_lint/src/types.rs

Lines changed: 66 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{LateContext, LateLintPass, LintContext};
22
use rustc_ast as ast;
33
use rustc_attr as attr;
44
use rustc_data_structures::fx::FxHashSet;
5-
use rustc_errors::{fluent, Applicability};
5+
use rustc_errors::{fluent, Applicability, DiagnosticMessage};
66
use rustc_hir as hir;
77
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
88
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
@@ -664,7 +664,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
664664
enum FfiResult<'tcx> {
665665
FfiSafe,
666666
FfiPhantom(Ty<'tcx>),
667-
FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> },
667+
FfiUnsafe { ty: Ty<'tcx>, reason: DiagnosticMessage, help: Option<DiagnosticMessage> },
668668
}
669669

670670
pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
@@ -824,8 +824,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
824824
self.emit_ffi_unsafe_type_lint(
825825
ty,
826826
sp,
827-
"passing raw arrays by value is not FFI-safe",
828-
Some("consider passing a pointer to the array"),
827+
fluent::lint::improper_ctypes_array_reason,
828+
Some(fluent::lint::improper_ctypes_array_help),
829829
);
830830
true
831831
} else {
@@ -868,11 +868,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
868868
} else {
869869
// All fields are ZSTs; this means that the type should behave
870870
// like (), which is FFI-unsafe
871-
FfiUnsafe {
872-
ty,
873-
reason: "this struct contains only zero-sized fields".into(),
874-
help: None,
875-
}
871+
FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_struct_zst, help: None }
876872
}
877873
} else {
878874
// We can't completely trust repr(C) markings; make sure the fields are
@@ -886,7 +882,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
886882
FfiPhantom(..) if def.is_enum() => {
887883
return FfiUnsafe {
888884
ty,
889-
reason: "this enum contains a PhantomData field".into(),
885+
reason: fluent::lint::improper_ctypes_enum_phantomdata,
890886
help: None,
891887
};
892888
}
@@ -922,7 +918,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
922918
} else {
923919
return FfiUnsafe {
924920
ty,
925-
reason: "box cannot be represented as a single pointer".to_string(),
921+
reason: fluent::lint::improper_ctypes_box,
926922
help: None,
927923
};
928924
}
@@ -932,17 +928,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
932928
}
933929
match def.adt_kind() {
934930
AdtKind::Struct | AdtKind::Union => {
935-
let kind = if def.is_struct() { "struct" } else { "union" };
936-
937931
if !def.repr().c() && !def.repr().transparent() {
938932
return FfiUnsafe {
939933
ty,
940-
reason: format!("this {} has unspecified layout", kind),
941-
help: Some(format!(
942-
"consider adding a `#[repr(C)]` or \
943-
`#[repr(transparent)]` attribute to this {}",
944-
kind
945-
)),
934+
reason: if def.is_struct() {
935+
fluent::lint::improper_ctypes_struct_layout_reason
936+
} else {
937+
fluent::lint::improper_ctypes_union_layout_reason
938+
},
939+
help: if def.is_struct() {
940+
Some(fluent::lint::improper_ctypes_struct_layout_help)
941+
} else {
942+
Some(fluent::lint::improper_ctypes_union_layout_help)
943+
},
946944
};
947945
}
948946

@@ -951,16 +949,28 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
951949
if is_non_exhaustive && !def.did().is_local() {
952950
return FfiUnsafe {
953951
ty,
954-
reason: format!("this {} is non-exhaustive", kind),
952+
reason: if def.is_struct() {
953+
fluent::lint::improper_ctypes_struct_non_exhaustive
954+
} else {
955+
fluent::lint::improper_ctypes_union_non_exhaustive
956+
},
955957
help: None,
956958
};
957959
}
958960

959961
if def.non_enum_variant().fields.is_empty() {
960962
return FfiUnsafe {
961963
ty,
962-
reason: format!("this {} has no fields", kind),
963-
help: Some(format!("consider adding a member to this {}", kind)),
964+
reason: if def.is_struct() {
965+
fluent::lint::improper_ctypes_struct_fieldless_reason
966+
} else {
967+
fluent::lint::improper_ctypes_union_fieldless_reason
968+
},
969+
help: if def.is_struct() {
970+
Some(fluent::lint::improper_ctypes_struct_fieldless_help)
971+
} else {
972+
Some(fluent::lint::improper_ctypes_union_fieldless_help)
973+
},
964974
};
965975
}
966976

@@ -980,21 +990,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
980990
if repr_nullable_ptr(self.cx, ty, self.mode).is_none() {
981991
return FfiUnsafe {
982992
ty,
983-
reason: "enum has no representation hint".into(),
984-
help: Some(
985-
"consider adding a `#[repr(C)]`, \
986-
`#[repr(transparent)]`, or integer `#[repr(...)]` \
987-
attribute to this enum"
988-
.into(),
989-
),
993+
reason: fluent::lint::improper_ctypes_enum_repr_reason,
994+
help: Some(fluent::lint::improper_ctypes_enum_repr_help),
990995
};
991996
}
992997
}
993998

994999
if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
9951000
return FfiUnsafe {
9961001
ty,
997-
reason: "this enum is non-exhaustive".into(),
1002+
reason: fluent::lint::improper_ctypes_non_exhaustive,
9981003
help: None,
9991004
};
10001005
}
@@ -1005,7 +1010,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10051010
if is_non_exhaustive && !variant.def_id.is_local() {
10061011
return FfiUnsafe {
10071012
ty,
1008-
reason: "this enum has non-exhaustive variants".into(),
1013+
reason: fluent::lint::improper_ctypes_non_exhaustive_variant,
10091014
help: None,
10101015
};
10111016
}
@@ -1023,39 +1028,37 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10231028

10241029
ty::Char => FfiUnsafe {
10251030
ty,
1026-
reason: "the `char` type has no C equivalent".into(),
1027-
help: Some("consider using `u32` or `libc::wchar_t` instead".into()),
1031+
reason: fluent::lint::improper_ctypes_char_reason,
1032+
help: Some(fluent::lint::improper_ctypes_char_help),
10281033
},
10291034

1030-
ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => FfiUnsafe {
1031-
ty,
1032-
reason: "128-bit integers don't currently have a known stable ABI".into(),
1033-
help: None,
1034-
},
1035+
ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
1036+
FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_128bit, help: None }
1037+
}
10351038

10361039
// Primitive types with a stable representation.
10371040
ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
10381041

10391042
ty::Slice(_) => FfiUnsafe {
10401043
ty,
1041-
reason: "slices have no C equivalent".into(),
1042-
help: Some("consider using a raw pointer instead".into()),
1044+
reason: fluent::lint::improper_ctypes_slice_reason,
1045+
help: Some(fluent::lint::improper_ctypes_slice_help),
10431046
},
10441047

10451048
ty::Dynamic(..) => {
1046-
FfiUnsafe { ty, reason: "trait objects have no C equivalent".into(), help: None }
1049+
FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_dyn, help: None }
10471050
}
10481051

10491052
ty::Str => FfiUnsafe {
10501053
ty,
1051-
reason: "string slices have no C equivalent".into(),
1052-
help: Some("consider using `*const u8` and a length instead".into()),
1054+
reason: fluent::lint::improper_ctypes_str_reason,
1055+
help: Some(fluent::lint::improper_ctypes_str_help),
10531056
},
10541057

10551058
ty::Tuple(..) => FfiUnsafe {
10561059
ty,
1057-
reason: "tuples have unspecified layout".into(),
1058-
help: Some("consider using a struct instead".into()),
1060+
reason: fluent::lint::improper_ctypes_tuple_reason,
1061+
help: Some(fluent::lint::improper_ctypes_tuple_help),
10591062
},
10601063

10611064
ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _)
@@ -1086,12 +1089,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10861089
if self.is_internal_abi(sig.abi()) {
10871090
return FfiUnsafe {
10881091
ty,
1089-
reason: "this function pointer has Rust-specific calling convention".into(),
1090-
help: Some(
1091-
"consider using an `extern fn(...) -> ...` \
1092-
function pointer instead"
1093-
.into(),
1094-
),
1092+
reason: fluent::lint::improper_ctypes_fnptr_reason,
1093+
help: Some(fluent::lint::improper_ctypes_fnptr_help),
10951094
};
10961095
}
10971096

@@ -1122,7 +1121,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11221121
// While opaque types are checked for earlier, if a projection in a struct field
11231122
// normalizes to an opaque type, then it will reach this branch.
11241123
ty::Opaque(..) => {
1125-
FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None }
1124+
FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_opaque, help: None }
11261125
}
11271126

11281127
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
@@ -1148,8 +1147,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11481147
&mut self,
11491148
ty: Ty<'tcx>,
11501149
sp: Span,
1151-
note: &str,
1152-
help: Option<&str>,
1150+
note: DiagnosticMessage,
1151+
help: Option<DiagnosticMessage>,
11531152
) {
11541153
let lint = match self.mode {
11551154
CItemKind::Declaration => IMPROPER_CTYPES,
@@ -1161,18 +1160,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11611160
CItemKind::Declaration => "block",
11621161
CItemKind::Definition => "fn",
11631162
};
1164-
let mut diag = lint.build(&format!(
1165-
"`extern` {} uses type `{}`, which is not FFI-safe",
1166-
item_description, ty
1167-
));
1168-
diag.span_label(sp, "not FFI-safe");
1163+
let mut diag = lint.build(fluent::lint::improper_ctypes);
1164+
diag.set_arg("ty", ty);
1165+
diag.set_arg("desc", item_description);
1166+
diag.span_label(sp, fluent::lint::label);
11691167
if let Some(help) = help {
11701168
diag.help(help);
11711169
}
11721170
diag.note(note);
11731171
if let ty::Adt(def, _) = ty.kind() {
11741172
if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
1175-
diag.span_note(sp, "the type is defined here");
1173+
diag.span_note(sp, fluent::lint::note);
11761174
}
11771175
}
11781176
diag.emit();
@@ -1209,7 +1207,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12091207
}
12101208

12111209
if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes { cx: self.cx }).break_value() {
1212-
self.emit_ffi_unsafe_type_lint(ty, sp, "opaque types have no C equivalent", None);
1210+
self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint::improper_ctypes_opaque, None);
12131211
true
12141212
} else {
12151213
false
@@ -1251,13 +1249,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12511249
match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
12521250
FfiResult::FfiSafe => {}
12531251
FfiResult::FfiPhantom(ty) => {
1254-
self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None);
1252+
self.emit_ffi_unsafe_type_lint(
1253+
ty,
1254+
sp,
1255+
fluent::lint::improper_ctypes_only_phantomdata,
1256+
None,
1257+
);
12551258
}
12561259
// If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
12571260
// argument, which after substitution, is `()`, then this branch can be hit.
12581261
FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
12591262
FfiResult::FfiUnsafe { ty, reason, help } => {
1260-
self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref());
1263+
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
12611264
}
12621265
}
12631266
}

0 commit comments

Comments
 (0)