about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2023-03-02 11:23:44 +0000
committerDavid Wood <david.wood@huawei.com>2023-07-03 13:40:20 +0100
commitc75b080d7d878740c49539413d1b000906d420d4 (patch)
tree411ba13166fc08c3dee5c426ae20fb7e31169044
parenteddfce53c10d281cde6d283f36a12c43606b3f8c (diff)
downloadrust-c75b080d7d878740c49539413d1b000906d420d4.tar.gz
rust-c75b080d7d878740c49539413d1b000906d420d4.zip
lint/ctypes: multiple external fn-ptrs in ty
Extend previous commit's support for checking for external fn-ptrs in
internal fn types to report errors for multiple found fn-ptrs.

Signed-off-by: David Wood <david.wood@huawei.com>
-rw-r--r--compiler/rustc_lint/src/types.rs63
-rw-r--r--tests/ui/lint/lint-ctypes-94223.rs4
-rw-r--r--tests/ui/lint/lint-ctypes-94223.stderr20
3 files changed, 68 insertions, 19 deletions
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index fefd431971e..eeeac96798d 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1384,35 +1384,40 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     /// Argument types and the result type are checked for functions with external ABIs.
     /// For functions with internal ABIs, argument types and the result type are walked to find
     /// fn-ptr types that have external ABIs, as these still need checked.
-    fn check_maybe_foreign_fn(&mut self, abi: SpecAbi, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
+    fn check_maybe_foreign_fn(
+        &mut self,
+        abi: SpecAbi,
+        def_id: LocalDefId,
+        decl: &'tcx hir::FnDecl<'_>,
+    ) {
         let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
         let sig = self.cx.tcx.erase_late_bound_regions(sig);
 
         let is_internal_abi = self.is_internal_abi(abi);
         let check_ty = |this: &mut ImproperCTypesVisitor<'a, 'tcx>,
-                        span: Span,
+                        hir_ty: &'tcx hir::Ty<'_>,
                         ty: Ty<'tcx>,
                         is_return_type: bool| {
             // If this function has an external ABI, then its arguments and return type should be
             // checked..
             if !is_internal_abi {
-                this.check_type_for_ffi_and_report_errors(span, ty, false, is_return_type);
+                this.check_type_for_ffi_and_report_errors(hir_ty.span, ty, false, is_return_type);
                 return;
             }
 
             // ..but if this function has an internal ABI, then search the argument or return type
             // for any fn-ptr types with external ABI, which should be checked..
-            if let Some(fn_ptr_ty) = this.find_fn_ptr_ty_with_external_abi(ty) {
+            for (fn_ptr_ty, span) in this.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
                 this.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, is_return_type);
             }
         };
 
         for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
-            check_ty(self, input_hir.span, *input_ty, false);
+            check_ty(self, input_hir, *input_ty, false);
         }
 
         if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
-            check_ty(self, ret_hir.span, sig.output(), true);
+            check_ty(self, ret_hir, sig.output(), true);
         }
     }
 
@@ -1431,30 +1436,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     /// Find any fn-ptr types with external ABIs in `ty`.
     ///
     /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
-    fn find_fn_ptr_ty_with_external_abi(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
-        struct FnPtrFinder<'parent, 'a, 'tcx>(&'parent ImproperCTypesVisitor<'a, 'tcx>);
+    fn find_fn_ptr_ty_with_external_abi(
+        &self,
+        hir_ty: &hir::Ty<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Vec<(Ty<'tcx>, Span)> {
+        struct FnPtrFinder<'parent, 'a, 'tcx> {
+            visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
+            spans: Vec<Span>,
+            tys: Vec<Ty<'tcx>>,
+        }
+
+        impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
+            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
+                debug!(?ty);
+                if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
+                    && !self.visitor.is_internal_abi(*abi)
+                {
+                    self.spans.push(ty.span);
+                }
+
+                hir::intravisit::walk_ty(self, ty)
+            }
+        }
+
         impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
             type BreakTy = Ty<'tcx>;
 
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-                if let ty::FnPtr(sig) = ty.kind() && !self.0.is_internal_abi(sig.abi()) {
-                    ControlFlow::Break(ty)
-                } else {
-                    ty.super_visit_with(self)
+                if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) {
+                    self.tys.push(ty);
                 }
+
+                ty.super_visit_with(self)
             }
         }
 
-        self.cx
-            .tcx
-            .normalize_erasing_regions(self.cx.param_env, ty)
-            .visit_with(&mut FnPtrFinder(&*self))
-            .break_value()
+        let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
+        self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty).visit_with(&mut visitor);
+        hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
+
+        iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
-    fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) {
+    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
         let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
         let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
 
diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs
index ca7f3b73f66..98ccbd23a9e 100644
--- a/tests/ui/lint/lint-ctypes-94223.rs
+++ b/tests/ui/lint/lint-ctypes-94223.rs
@@ -3,3 +3,7 @@
 
 pub fn bad(f: extern "C" fn([u8])) {}
 //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
+
+pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {}
+//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
+//~^^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr
index f8afed34ea4..e05d6197cb4 100644
--- a/tests/ui/lint/lint-ctypes-94223.stderr
+++ b/tests/ui/lint/lint-ctypes-94223.stderr
@@ -12,5 +12,23 @@ note: the lint level is defined here
 LL | #![deny(improper_ctypes_definitions)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to previous error
+error: `extern` fn uses type `[u8]`, which is not FFI-safe
+  --> $DIR/lint-ctypes-94223.rs:7:28
+   |
+LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {}
+   |                            ^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = help: consider using a raw pointer instead
+   = note: slices have no C equivalent
+
+error: `extern` fn uses type `[u8]`, which is not FFI-safe
+  --> $DIR/lint-ctypes-94223.rs:7:49
+   |
+LL | pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {}
+   |                                                 ^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = help: consider using a raw pointer instead
+   = note: slices have no C equivalent
+
+error: aborting due to 3 previous errors