@@ -57,27 +57,32 @@ declare_lint! {
5757
5858declare_lint ! {
5959 /// The `improper_ctypes_definitions` lint detects incorrect use of
60- /// [`extern` function] definitions.
61- /// (In other words, functions to be used by foreign code.)
60+ /// [`extern` function] definitions and [`no_mangle`] / [`export_name`] static variable definitions .
61+ /// (In other words, functions and global variables to be used by foreign code.)
6262 ///
6363 /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
64+ /// [`no_mangle`]: https://doc.rust-lang.org/stable/reference/abi.html#the-no_mangle-attribute
65+ /// [`export_name`]: https://doc.rust-lang.org/stable/reference/abi.html#the-export_name-attribute
6466 ///
6567 /// ### Example
6668 ///
6769 /// ```rust
6870 /// # #![allow(unused)]
6971 /// pub extern "C" fn str_type(p: &str) { }
72+ /// # #[used]
73+ /// # #[unsafe(no_mangle)]
74+ /// static PLUGIN_ABI_MIN_VERSION: &'static str = "0.0.5";
7075 /// ```
7176 ///
7277 /// {{produces}}
7378 ///
7479 /// ### Explanation
7580 ///
76- /// There are many parameter and return types that may be specified in an
77- /// `extern` function that are not compatible with the given ABI. This
78- /// lint is an alert that these types should not be used. The lint usually
79- /// should provide a description of the issue, along with possibly a hint
80- /// on how to resolve it.
81+ /// There are many types that may be specified at interfaces exposed to foreign code,
82+ /// but are not follow the rules to ensure proper ABI compatibility.
83+ /// This lint is issued when a mistake is detected.
84+ /// The lint usually should provide a description of the issue,
85+ /// along with possibly a hint on how to resolve it.
8186 pub ( crate ) IMPROPER_CTYPES_DEFINITIONS ,
8287 Warn ,
8388 "proper use of libc types in foreign item definitions"
@@ -282,6 +287,9 @@ enum CItemKind {
282287 ExportedFunction ,
283288 /// `extern "C"` function pointers -> also IMPROPER_CTYPES,
284289 Callback ,
290+ /// `no_mangle`/`export_name` static variables, assumed to be used from across an FFI boundary,
291+ /// -> also IMPROPER_CTYPES_DEFINITIONS
292+ ExportedStatic ,
285293}
286294
287295/// Annotates whether we are in the context of a function's argument types or return type.
@@ -694,6 +702,8 @@ struct VisitorState {
694702impl RootUseFlags {
695703 // The values that can be set.
696704 const STATIC_TY : Self = Self :: STATIC ;
705+ const EXPORTED_STATIC_TY : Self =
706+ Self :: from_bits ( Self :: STATIC . bits ( ) | Self :: DEFINED . bits ( ) ) . unwrap ( ) ;
697707 const ARGUMENT_TY_IN_DEFINITION : Self =
698708 Self :: from_bits ( Self :: FUNC . bits ( ) | Self :: DEFINED . bits ( ) ) . unwrap ( ) ;
699709 const RETURN_TY_IN_DEFINITION : Self =
@@ -734,6 +744,9 @@ impl VisitorState {
734744 ( CItemKind :: ExportedFunction , FnPos :: Arg ) => RootUseFlags :: ARGUMENT_TY_IN_DEFINITION ,
735745 ( CItemKind :: ImportedExtern , FnPos :: Arg ) => RootUseFlags :: ARGUMENT_TY_IN_DECLARATION ,
736746 ( CItemKind :: Callback , FnPos :: Arg ) => RootUseFlags :: ARGUMENT_TY_IN_FNPTR ,
747+ ( CItemKind :: ExportedStatic , _) => bug ! (
748+ "VisitorState::entry_point_from_fnmode() should not be used for static variables!"
749+ ) ,
737750 } ;
738751 Self :: entry_point ( p_flags)
739752 }
@@ -743,6 +756,11 @@ impl VisitorState {
743756 Self :: entry_point ( RootUseFlags :: STATIC_TY )
744757 }
745758
759+ /// Get the proper visitor state for a locally-defined static variable's type
760+ fn static_var_def ( ) -> Self {
761+ Self :: entry_point ( RootUseFlags :: EXPORTED_STATIC_TY )
762+ }
763+
746764 /// Whether the type is used as the type of a static variable.
747765 fn is_direct_in_static ( & self ) -> bool {
748766 let ret = self . root_use_flags . contains ( RootUseFlags :: STATIC ) ;
@@ -1632,6 +1650,15 @@ impl<'tcx> ImproperCTypesLint {
16321650 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ImportedExtern ) ;
16331651 }
16341652
1653+ /// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type.
1654+ fn check_exported_static ( & self , cx : & LateContext < ' tcx > , id : hir:: HirId , span : Span ) {
1655+ let ty = cx. tcx . type_of ( id. owner ) . instantiate_identity ( ) ;
1656+ let mod_id = cx. tcx . parent_module ( id) . to_def_id ( ) ;
1657+ let mut visitor = ImproperCTypesVisitor :: new ( cx, mod_id) ;
1658+ let ffi_res = visitor. check_type ( VisitorState :: static_var_def ( ) , ty) ;
1659+ self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ExportedStatic ) ;
1660+ }
1661+
16351662 /// Check if a function's argument types and result type are "ffi-safe".
16361663 fn check_foreign_fn (
16371664 & mut self ,
@@ -1738,10 +1765,13 @@ impl<'tcx> ImproperCTypesLint {
17381765 // Internally, we treat this differently, but at the end of the day
17391766 // their linting needs to be enabled/disabled alongside that of "FFI-imported" items.
17401767 CItemKind :: Callback => IMPROPER_CTYPES ,
1768+ // Same thing with static variables, which are "FFI-exported"
1769+ CItemKind :: ExportedStatic => IMPROPER_CTYPES_DEFINITIONS ,
17411770 } ;
17421771 let desc = match fn_mode {
17431772 CItemKind :: ImportedExtern => "`extern` block" ,
17441773 CItemKind :: ExportedFunction => "`extern` fn" ,
1774+ CItemKind :: ExportedStatic => "foreign-code-reachable static" ,
17451775 CItemKind :: Callback => "`extern` callback" ,
17461776 } ;
17471777 for reason in reasons. iter_mut ( ) {
@@ -1785,7 +1815,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
17851815 let mir_sig = cx. tcx . fn_sig ( it. owner_id . def_id ) . instantiate_identity ( ) ;
17861816 let mod_id = cx. tcx . parent_module_from_def_id ( it. owner_id . def_id ) . to_def_id ( ) ;
17871817 if !abi. is_rustic_abi ( ) {
1788- self . check_foreign_fn ( cx, CItemKind :: ImportedExtern , mir_sig, sig. decl , mod_id, 0 ) ;
1818+ self . check_foreign_fn (
1819+ cx,
1820+ CItemKind :: ImportedExtern ,
1821+ mir_sig,
1822+ sig. decl ,
1823+ mod_id,
1824+ 0 ,
1825+ ) ;
17891826 }
17901827 }
17911828 hir:: ForeignItemKind :: Static ( ty, _, _) if !abi. is_rustic_abi ( ) => {
@@ -1805,6 +1842,25 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18051842 ty,
18061843 cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ,
18071844 ) ;
1845+
1846+ // FIXME: cx.tcx.has_attr no worky
1847+ // if matches!(item.kind, hir::ItemKind::Static(..))
1848+ // && (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
1849+ // || cx.tcx.has_attr(item.owner_id, sym::export_name))
1850+ if matches ! ( item. kind, hir:: ItemKind :: Static ( ..) ) {
1851+ let is_exported_static = cx. tcx . get_all_attrs ( item. owner_id ) . iter ( ) . any ( |x| {
1852+ matches ! (
1853+ x,
1854+ hir:: Attribute :: Parsed (
1855+ hir:: attrs:: AttributeKind :: NoMangle ( _)
1856+ | hir:: attrs:: AttributeKind :: ExportName { .. }
1857+ )
1858+ )
1859+ } ) ;
1860+ if is_exported_static {
1861+ self . check_exported_static ( cx, item. hir_id ( ) , ty. span ) ;
1862+ }
1863+ }
18081864 }
18091865 // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks
18101866 hir:: ItemKind :: Fn { .. } => { }
@@ -1887,7 +1943,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18871943 if !sig. header . abi . is_rustic_abi ( ) {
18881944 let mir_sig = cx. tcx . fn_sig ( local_id) . instantiate_identity ( ) ;
18891945 let mod_id = cx. tcx . parent_module_from_def_id ( local_id) . to_def_id ( ) ;
1890- self . check_foreign_fn ( cx, CItemKind :: ExportedFunction , mir_sig, sig. decl , mod_id, 0 ) ;
1946+ self . check_foreign_fn (
1947+ cx,
1948+ CItemKind :: ExportedFunction ,
1949+ mir_sig,
1950+ sig. decl ,
1951+ mod_id,
1952+ 0 ,
1953+ ) ;
18911954 }
18921955 }
18931956 hir:: TraitItemKind :: Type ( _, ty_maybe) => {
0 commit comments