Skip to content

Commit 25322b1

Browse files
committed
ImproperCTypes: splitting definitions lint into two
First retconned commit in this change to impact rustc users: The improper_ctypes_definitions has been split into improper_c_fn_definitions and improper_c_callbacks, with the former lint name being turned into a lint group, so that users aren't forced to immediately change their code. Deprecating this old name will be left as an exercise to whichever team is in charge of breaking changes. Another lint group has been created to deal with all `improper_c*` lints at once.
1 parent cb9b68f commit 25322b1

29 files changed

+132
-90
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dere
336336
.method_def = method calls to `{$method_name}` require a reference
337337
.suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit
338338
339-
lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
339+
lint_improper_ctypes = {$desc} uses type `{$ty}`, which is not FFI-safe
340340
.label = not FFI-safe
341341
.note = the type is defined here
342342

compiler/rustc_lint/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ fn register_builtins(store: &mut LintStore) {
335335
REFINING_IMPL_TRAIT_INTERNAL
336336
);
337337

338+
add_lint_group!("improper_c_boundaries", IMPROPER_CTYPES_DEFINITIONS, IMPROPER_CTYPES);
339+
338340
add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024);
339341

340342
add_lint_group!(

compiler/rustc_lint/src/types.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use tracing::debug;
1111
use {rustc_ast as ast, rustc_hir as hir};
1212

1313
mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations
14-
pub(crate) use improper_ctypes::ImproperCTypesLint;
14+
pub(crate) use improper_ctypes::{
15+
IMPROPER_CTYPES, IMPROPER_CTYPES_DEFINITIONS, ImproperCTypesLint,
16+
};
1517

1618
use crate::lints::{
1719
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,19 @@ use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
2525
declare_lint! {
2626
/// The `improper_ctypes` lint detects incorrect use of types in foreign
2727
/// modules.
28+
/// (In other words, declarations of items defined in foreign code.)
29+
/// This also includes all [`extern` function] pointers.
30+
///
31+
/// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
2832
///
2933
/// ### Example
3034
///
3135
/// ```rust
3236
/// unsafe extern "C" {
3337
/// static STATIC: String;
38+
/// fn some_func(a:String);
3439
/// }
40+
/// extern "C" fn register_callback(a: i32, call: extern "C" fn(char)) { /* ... */ }
3541
/// ```
3642
///
3743
/// {{produces}}
@@ -44,14 +50,15 @@ declare_lint! {
4450
/// detects a probable mistake in a definition. The lint usually should
4551
/// provide a description of the issue, along with possibly a hint on how
4652
/// to resolve it.
47-
IMPROPER_CTYPES,
53+
pub(crate) IMPROPER_CTYPES,
4854
Warn,
4955
"proper use of libc types in foreign modules"
5056
}
5157

5258
declare_lint! {
5359
/// The `improper_ctypes_definitions` lint detects incorrect use of
5460
/// [`extern` function] definitions.
61+
/// (In other words, functions to be used by foreign code.)
5562
///
5663
/// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
5764
///
@@ -71,7 +78,7 @@ declare_lint! {
7178
/// lint is an alert that these types should not be used. The lint usually
7279
/// should provide a description of the issue, along with possibly a hint
7380
/// on how to resolve it.
74-
IMPROPER_CTYPES_DEFINITIONS,
81+
pub(crate) IMPROPER_CTYPES_DEFINITIONS,
7582
Warn,
7683
"proper use of libc types in foreign item definitions"
7784
}
@@ -134,7 +141,7 @@ declare_lint! {
134141
declare_lint_pass!(ImproperCTypesLint => [
135142
IMPROPER_CTYPES,
136143
IMPROPER_CTYPES_DEFINITIONS,
137-
USES_POWER_ALIGNMENT
144+
USES_POWER_ALIGNMENT,
138145
]);
139146

140147
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
@@ -254,13 +261,19 @@ fn check_struct_for_power_alignment<'tcx>(
254261
}
255262
}
256263

257-
/// Annotates whether we are in the context of an item *defined* in rust
258-
/// and exposed to an FFI boundary,
259-
/// or the context of an item from elsewhere, whose interface is re-*declared* in rust.
260-
#[derive(Clone, Copy)]
264+
/// Annotates the nature of the "original item" being checked, and its relation
265+
/// to FFI boundaries.
266+
/// Mainly, whether is is something defined in rust and exported through the FFI boundary,
267+
/// or something rust imports through the same boundary.
268+
/// Callbacks are ultimately treated as imported items, in terms of denying/warning/ignoring FFI-unsafety
269+
#[derive(Clone, Copy, Debug)]
261270
enum CItemKind {
262-
Declaration,
263-
Definition,
271+
/// Imported items in an `extern "C"` block (function declarations, static variables) -> IMPROPER_CTYPES
272+
ImportedExtern,
273+
/// `extern "C"` function definitions, to be used elsewhere -> IMPROPER_CTYPES_DEFINITIONS,
274+
ExportedFunction,
275+
/// `extern "C"` function pointers -> also IMPROPER_CTYPES,
276+
Callback,
264277
}
265278

266279
/// Annotates whether we are in the context of a function's argument types or return type.
@@ -582,10 +595,13 @@ impl VisitorState {
582595
/// Get the proper visitor state for a given function's arguments or return type.
583596
fn entry_point_from_fnmode(fn_mode: CItemKind, fn_pos: FnPos) -> Self {
584597
let p_flags = match (fn_mode, fn_pos) {
585-
(CItemKind::Definition, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION,
586-
(CItemKind::Declaration, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION,
587-
(CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION,
588-
(CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION,
598+
(CItemKind::ExportedFunction, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DEFINITION,
599+
(CItemKind::ImportedExtern, FnPos::Ret) => RootUseFlags::RETURN_TY_IN_DECLARATION,
600+
(CItemKind::ExportedFunction, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION,
601+
(CItemKind::ImportedExtern, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION,
602+
// we could also deal with CItemKind::Callback,
603+
// but we bake an assumption from this function's call sites here.
604+
_ => bug!("cannot be called with CItemKind::{:?}", fn_mode),
589605
};
590606
Self::entry_point(p_flags)
591607
}
@@ -689,7 +705,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
689705
// - otherwise, treat the box itself correctly, and follow pointee safety logic
690706
// as described in the other `indirection_type` match branch.
691707
if state.is_in_defined_function()
692-
|| (state.is_in_fnptr() && matches!(self.base_fn_mode, CItemKind::Definition))
708+
|| (state.is_in_fnptr()
709+
&& matches!(self.base_fn_mode, CItemKind::ExportedFunction))
693710
{
694711
if inner_ty.is_sized(tcx, self.cx.typing_env()) {
695712
return FfiSafe;
@@ -1177,7 +1194,7 @@ impl<'tcx> ImproperCTypesLint {
11771194
// FIXME(ctypes): make a check_for_fnptr
11781195
let ffi_res = visitor.check_type(bridge_state, fn_ptr_ty);
11791196

1180-
self.process_ffi_result(cx, span, ffi_res, fn_mode);
1197+
self.process_ffi_result(cx, span, ffi_res, CItemKind::Callback);
11811198
}
11821199
}
11831200

@@ -1223,9 +1240,9 @@ impl<'tcx> ImproperCTypesLint {
12231240

12241241
fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
12251242
let ty = cx.tcx.type_of(id).instantiate_identity();
1226-
let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
1243+
let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::ImportedExtern);
12271244
let ffi_res = visitor.check_type(VisitorState::static_var(), ty);
1228-
self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
1245+
self.process_ffi_result(cx, span, ffi_res, CItemKind::ImportedExtern);
12291246
}
12301247

12311248
/// Check if a function's argument types and result type are "ffi-safe".
@@ -1326,12 +1343,16 @@ impl<'tcx> ImproperCTypesLint {
13261343
fn_mode: CItemKind,
13271344
) {
13281345
let lint = match fn_mode {
1329-
CItemKind::Declaration => IMPROPER_CTYPES,
1330-
CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
1346+
CItemKind::ImportedExtern => IMPROPER_CTYPES,
1347+
CItemKind::ExportedFunction => IMPROPER_CTYPES_DEFINITIONS,
1348+
// Internally, we treat this differently, but at the end of the day
1349+
// their linting needs to be enabled/disabled alongside that of "FFI-imported" items.
1350+
CItemKind::Callback => IMPROPER_CTYPES,
13311351
};
13321352
let desc = match fn_mode {
1333-
CItemKind::Declaration => "block",
1334-
CItemKind::Definition => "fn",
1353+
CItemKind::ImportedExtern => "`extern` block",
1354+
CItemKind::ExportedFunction => "`extern` fn",
1355+
CItemKind::Callback => "`extern` callback",
13351356
};
13361357
for reason in reasons.iter_mut() {
13371358
reason.span_note = if let ty::Adt(def, _) = reason.ty.kind()
@@ -1347,13 +1368,20 @@ impl<'tcx> ImproperCTypesLint {
13471368
}
13481369
}
13491370

1350-
/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
1351-
/// `extern "C" { }` blocks):
1371+
/// IMPROPER_CTYPES checks items that are part of a header to a non-rust library
1372+
/// Namely, functions and static variables in `extern "<abi>" { }`,
1373+
/// if `<abi>` is external (e.g. "C").
1374+
/// it also checks for function pointers marked with an external ABI.
1375+
/// (fields of type `extern "<abi>" fn`, where e.g. `<abi>` is `C`)
1376+
/// These pointers are searched in all other items which contain types
1377+
/// (e.g.functions, struct definitions, etc)
13521378
///
1353-
/// - `extern "<abi>" fn` definitions are checked in the same way as the
1354-
/// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
1355-
/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
1356-
/// checked for extern fn-ptrs with external ABIs.
1379+
/// `IMPROPER_CTYPES_DEFINITIONS` checks rust-defined functions that are marked
1380+
/// to be used from the other side of a FFI boundary.
1381+
/// In other words, `extern "<abi>" fn` definitions and trait-method declarations.
1382+
/// This only matters if `<abi>` is external (e.g. `C`).
1383+
///
1384+
/// maybe later: specialised lints for pointees
13571385
impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
13581386
fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
13591387
let abi = cx.tcx.hir_get_foreign_abi(it.hir_id());
@@ -1364,11 +1392,16 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
13641392
// "the element rendered unsafe" because their unsafety doesn't affect
13651393
// their surroundings, and their type is often declared inline
13661394
if !abi.is_rustic_abi() {
1367-
self.check_foreign_fn(cx, CItemKind::Declaration, it.owner_id.def_id, sig.decl);
1395+
self.check_foreign_fn(
1396+
cx,
1397+
CItemKind::ImportedExtern,
1398+
it.owner_id.def_id,
1399+
sig.decl,
1400+
);
13681401
} else {
13691402
self.check_fn_for_external_abi_fnptr(
13701403
cx,
1371-
CItemKind::Declaration,
1404+
CItemKind::ImportedExtern,
13721405
it.owner_id.def_id,
13731406
sig.decl,
13741407
);
@@ -1391,7 +1424,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
13911424
VisitorState::static_var(),
13921425
ty,
13931426
cx.tcx.type_of(item.owner_id).instantiate_identity(),
1394-
CItemKind::Definition,
1427+
CItemKind::ExportedFunction, // TODO: for some reason, this is the value that reproduces old behaviour
13951428
);
13961429
}
13971430
// See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks
@@ -1425,7 +1458,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
14251458
VisitorState::static_var(),
14261459
field.ty,
14271460
cx.tcx.type_of(field.def_id).instantiate_identity(),
1428-
CItemKind::Definition,
1461+
CItemKind::ImportedExtern,
14291462
);
14301463
}
14311464

@@ -1450,9 +1483,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
14501483
// "the element rendered unsafe" because their unsafety doesn't affect
14511484
// their surroundings, and their type is often declared inline
14521485
if !abi.is_rustic_abi() {
1453-
self.check_foreign_fn(cx, CItemKind::Definition, id, decl);
1486+
self.check_foreign_fn(cx, CItemKind::ExportedFunction, id, decl);
14541487
} else {
1455-
self.check_fn_for_external_abi_fnptr(cx, CItemKind::Definition, id, decl);
1488+
self.check_fn_for_external_abi_fnptr(cx, CItemKind::ExportedFunction, id, decl);
14561489
}
14571490
}
14581491
}

src/tools/lint-docs/src/groups.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
3030
"unknown-or-malformed-diagnostic-attributes",
3131
"detects unknown or malformed diagnostic attributes",
3232
),
33+
("improper-c-boundaries", "Lints for points where rust code interacts with non-rust code"),
3334
];
3435

3536
type LintGroups = BTreeMap<String, BTreeSet<String>>;

tests/assembly-llvm/naked-functions/wasm32.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ extern "C" fn fn_i64_i64(num: i64) -> i64 {
9999
// wasm32-unknown: .functype fn_i128_i128 (i32, i64, i64) -> ()
100100
// wasm32-wasip1: .functype fn_i128_i128 (i32, i64, i64) -> ()
101101
// wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> ()
102-
#[allow(improper_ctypes_definitions)]
103102
#[no_mangle]
104103
#[unsafe(naked)]
105104
extern "C" fn fn_i128_i128(num: i128) -> i128 {

tests/ui/abi/compatibility.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@
6363
#![feature(no_core, rustc_attrs, lang_items)]
6464
#![feature(unsized_fn_params, transparent_unions)]
6565
#![no_core]
66-
#![allow(unused, improper_ctypes_definitions, internal_features)]
66+
#![allow(unused, internal_features)]
67+
#![allow(improper_ctypes_definitions, improper_ctypes)]
6768

6869
// FIXME: some targets are broken in various ways.
6970
// Hence there are `cfg` throughout this test to disable parts of it on those targets.

tests/ui/abi/extern/extern-pass-empty.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ run-pass
2-
#![allow(improper_ctypes)] // FIXME: this test is inherently not FFI-safe.
2+
#![allow(improper_ctypes)]
3+
// FIXME: this test is inherently not FFI-safe.
34

45
// Test a foreign function that accepts empty struct.
56

tests/ui/abi/foreign/foreign-fn-with-byval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ run-pass
2-
#![allow(improper_ctypes, improper_ctypes_definitions)]
2+
#![allow(improper_ctypes)]
33

44
#[derive(Copy, Clone)]
55
pub struct S {

tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//@ check-pass
2-
#![allow(improper_ctypes_definitions)]
2+
#![allow(improper_ctypes_definitions, improper_ctypes)]
33
#![feature(unsized_fn_params)]
44
#![crate_type = "lib"]
55

0 commit comments

Comments
 (0)