@@ -79,6 +79,37 @@ declare_lint! {
7979 "proper use of libc types in foreign item definitions"
8080}
8181
82+ declare_lint ! {
83+ /// The `improper_c_var_definitions` lint detects incorrect use of
84+ /// [`no_mangle`] and [`export_name`] static variable definitions.
85+ /// (In other words, static variables accessible by name by foreign code.)
86+ ///
87+ /// [`no_mangle`]: https://doc.rust-lang.org/stable/reference/abi.html#the-no_mangle-attribute
88+ /// [`export_name`]: https://doc.rust-lang.org/stable/reference/abi.html#the-export_name-attribute
89+ ///
90+ /// ### Example
91+ ///
92+ /// ```rust
93+ /// # #[unsafe(no_mangle)]
94+ /// # #[used]
95+ /// static mut PLUGIN_ABI_MIN_VERSION: &'static str = "0.0.5";
96+ /// ```
97+ ///
98+ /// {{produces}}
99+ ///
100+ /// ### Explanation
101+ ///
102+ /// The compiler has several checks to verify that types used in
103+ /// static variables exposed to foreign code are safe and follow
104+ /// certain rules to ensure proper compatibility with the foreign interfaces.
105+ /// This lint is issued when it detects a probable mistake in a definition.
106+ /// The lint usually should provide a description of the issue,
107+ /// along with possibly a hint on how to resolve it.
108+ pub ( crate ) IMPROPER_C_VAR_DEFINITIONS ,
109+ Warn ,
110+ "proper use of libc types in foreign-reachable static variable definitions"
111+ }
112+
82113declare_lint ! {
83114 /// The `improper_c_callbacks` lint detects incorrect use of
84115 /// [`extern` function] pointers.
@@ -165,6 +196,7 @@ declare_lint! {
165196declare_lint_pass ! ( ImproperCTypesLint => [
166197 IMPROPER_CTYPES ,
167198 IMPROPER_C_FN_DEFINITIONS ,
199+ IMPROPER_C_VAR_DEFINITIONS ,
168200 IMPROPER_C_CALLBACKS ,
169201 USES_POWER_ALIGNMENT ,
170202] ) ;
@@ -316,6 +348,8 @@ enum CItemKind {
316348 ImportedExtern ,
317349 /// `extern "C"` function definitions, to be used elsewhere -> IMPROPER_C_FN_DEFINITIONS,
318350 ExportedFunction ,
351+ /// `no_mangle`/`export_name` static variables, assumed to be used from across an FFI boundary,
352+ ExportedStatic ,
319353 /// `extern "C"` function pointers -> IMPROPER_C_CALLBACKS,
320354 Callback ,
321355}
@@ -691,6 +725,8 @@ struct VisitorState {
691725impl PersistentStateFlags {
692726 // The values that can be set.
693727 const STATIC_TY : Self = Self :: STATIC ;
728+ const EXPORTED_STATIC_TY : Self =
729+ Self :: from_bits ( Self :: STATIC . bits ( ) | Self :: DEFINED . bits ( ) ) . unwrap ( ) ;
694730 const ARGUMENT_TY_IN_DEFINITION : Self =
695731 Self :: from_bits ( Self :: FUNC . bits ( ) | Self :: DEFINED . bits ( ) ) . unwrap ( ) ;
696732 const RETURN_TY_IN_DEFINITION : Self =
@@ -792,6 +828,12 @@ impl VisitorState {
792828 Self :: entry_point ( PersistentStateFlags :: STATIC_TY )
793829 }
794830
831+ /// Get the proper visitor state for a locally-defined static variable's type
832+ #[ inline]
833+ fn static_var_def ( ) -> Self {
834+ Self :: entry_point ( PersistentStateFlags :: EXPORTED_STATIC_TY )
835+ }
836+
795837 /// Whether the type is used as the type of a static variable.
796838 fn is_direct_in_static ( & self ) -> bool {
797839 let ret = self . persistent_flags . contains ( PersistentStateFlags :: STATIC ) ;
@@ -1716,6 +1758,14 @@ impl<'tcx> ImproperCTypesLint {
17161758 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ImportedExtern ) ;
17171759 }
17181760
1761+ /// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type.
1762+ fn check_exported_static ( & self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
1763+ let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
1764+ let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
1765+ let ffi_res = visitor. check_type ( VisitorState :: static_var_def ( ) , ty) ;
1766+ self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ExportedStatic ) ;
1767+ }
1768+
17191769 /// Check if a function's argument types and result type are "ffi-safe".
17201770 fn check_foreign_fn (
17211771 & mut self ,
@@ -1816,11 +1866,13 @@ impl<'tcx> ImproperCTypesLint {
18161866 let lint = match fn_mode {
18171867 CItemKind :: ImportedExtern => IMPROPER_CTYPES ,
18181868 CItemKind :: ExportedFunction => IMPROPER_C_FN_DEFINITIONS ,
1869+ CItemKind :: ExportedStatic => IMPROPER_C_VAR_DEFINITIONS ,
18191870 CItemKind :: Callback => IMPROPER_C_CALLBACKS ,
18201871 } ;
18211872 let desc = match fn_mode {
18221873 CItemKind :: ImportedExtern => "`extern` block" ,
18231874 CItemKind :: ExportedFunction => "`extern` fn" ,
1875+ CItemKind :: ExportedStatic => "foreign-code-reachable static" ,
18241876 CItemKind :: Callback => "`extern` callback" ,
18251877 } ;
18261878 for reason in reasons. iter_mut ( ) {
@@ -1888,6 +1940,25 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
18881940 ty,
18891941 cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ,
18901942 ) ;
1943+
1944+ // FIXME: cx.tcx.has_attr no worky
1945+ // if matches!(item.kind, hir::ItemKind::Static(..))
1946+ // && (cx.tcx.has_attr(item.owner_id, sym::no_mangle)
1947+ // || cx.tcx.has_attr(item.owner_id, sym::export_name))
1948+ if matches ! ( item. kind, hir:: ItemKind :: Static ( ..) ) {
1949+ let is_exported_static = cx. tcx . get_all_attrs ( item. owner_id ) . iter ( ) . any ( |x| {
1950+ matches ! (
1951+ x,
1952+ hir:: Attribute :: Parsed (
1953+ hir:: attrs:: AttributeKind :: NoMangle ( _)
1954+ | hir:: attrs:: AttributeKind :: ExportName { .. }
1955+ )
1956+ )
1957+ } ) ;
1958+ if is_exported_static {
1959+ self . check_exported_static ( cx, item. owner_id , ty. span ) ;
1960+ }
1961+ }
18911962 }
18921963 // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks
18931964 hir:: ItemKind :: Fn { .. } => { }
0 commit comments