@@ -26,15 +26,15 @@ use rustc_ast::attr::{self, HasAttrs};
2626use rustc_ast:: tokenstream:: { TokenStream , TokenTree } ;
2727use rustc_ast:: visit:: { FnCtxt , FnKind } ;
2828use rustc_ast_pretty:: pprust:: { self , expr_to_string} ;
29- use rustc_data_structures:: fx:: FxHashSet ;
30- use rustc_errors:: { Applicability , DiagnosticBuilder } ;
29+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
30+ use rustc_errors:: { Applicability , DiagnosticBuilder , DiagnosticStyledString } ;
3131use rustc_feature:: { deprecated_attributes, AttributeGate , AttributeTemplate , AttributeType } ;
3232use rustc_feature:: { GateIssue , Stability } ;
3333use rustc_hir as hir;
3434use rustc_hir:: def:: { DefKind , Res } ;
3535use rustc_hir:: def_id:: DefId ;
36- use rustc_hir:: { GenericParamKind , PatKind } ;
37- use rustc_hir:: { HirIdSet , Node } ;
36+ use rustc_hir:: { ForeignItemKind , GenericParamKind , PatKind } ;
37+ use rustc_hir:: { HirId , HirIdSet , Node } ;
3838use rustc_middle:: lint:: LintDiagnosticBuilder ;
3939use rustc_middle:: ty:: subst:: GenericArgKind ;
4040use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
@@ -48,7 +48,7 @@ use rustc_trait_selection::traits::misc::can_type_implement_copy;
4848
4949use crate :: nonstandard_style:: { method_context, MethodLateContext } ;
5050
51- use log:: debug;
51+ use log:: { debug, trace } ;
5252use std:: fmt:: Write ;
5353
5454// hardwired lints from librustc_middle
@@ -2049,3 +2049,221 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
20492049 }
20502050 }
20512051}
2052+
2053+ declare_lint ! {
2054+ pub CLASHING_EXTERN_DECL ,
2055+ Warn ,
2056+ "detects when an extern fn has been declared with the same name but different types"
2057+ }
2058+
2059+ pub struct ClashingExternDecl {
2060+ seen_decls : FxHashMap < Symbol , HirId > ,
2061+ }
2062+
2063+ /// Differentiate between whether the name for an extern decl came from the link_name attribute or
2064+ /// just from declaration itself. This is important because we don't want to report clashes on
2065+ /// symbol name if they don't actually clash because one or the other links against a symbol with a
2066+ /// different name.
2067+ enum SymbolName {
2068+ /// The name of the symbol + the span of the annotation which introduced the link name.
2069+ Link ( Symbol , Span ) ,
2070+ /// No link name, so just the name of the symbol.
2071+ Normal ( Symbol ) ,
2072+ }
2073+
2074+ impl SymbolName {
2075+ fn get_name ( & self ) -> Symbol {
2076+ match self {
2077+ SymbolName :: Link ( s, _) | SymbolName :: Normal ( s) => * s,
2078+ }
2079+ }
2080+ }
2081+
2082+ impl ClashingExternDecl {
2083+ crate fn new ( ) -> Self {
2084+ ClashingExternDecl { seen_decls : FxHashMap :: default ( ) }
2085+ }
2086+ /// Insert a new foreign item into the seen set. If a symbol with the same name already exists
2087+ /// for the item, return its HirId without updating the set.
2088+ fn insert ( & mut self , tcx : TyCtxt < ' _ > , fi : & hir:: ForeignItem < ' _ > ) -> Option < HirId > {
2089+ let hid = fi. hir_id ;
2090+
2091+ let name =
2092+ & tcx. codegen_fn_attrs ( tcx. hir ( ) . local_def_id ( hid) ) . link_name . unwrap_or ( fi. ident . name ) ;
2093+
2094+ if self . seen_decls . contains_key ( name) {
2095+ // Avoid updating the map with the new entry when we do find a collision. We want to
2096+ // make sure we're always pointing to the first definition as the previous declaration.
2097+ // This lets us avoid emitting "knock-on" diagnostics.
2098+ Some ( * self . seen_decls . get ( name) . unwrap ( ) )
2099+ } else {
2100+ self . seen_decls . insert ( * name, hid)
2101+ }
2102+ }
2103+
2104+ /// Get the name of the symbol that's linked against for a given extern declaration. That is,
2105+ /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the
2106+ /// symbol's name.
2107+ fn name_of_extern_decl ( tcx : TyCtxt < ' _ > , fi : & hir:: ForeignItem < ' _ > ) -> SymbolName {
2108+ let did = tcx. hir ( ) . local_def_id ( fi. hir_id ) ;
2109+ if let Some ( ( overridden_link_name, overridden_link_name_span) ) =
2110+ tcx. codegen_fn_attrs ( did) . link_name . map ( |overridden_link_name| {
2111+ // FIXME: Instead of searching through the attributes again to get span
2112+ // information, we could have codegen_fn_attrs also give span information back for
2113+ // where the attribute was defined. However, until this is found to be a
2114+ // bottleneck, this does just fine.
2115+ (
2116+ overridden_link_name,
2117+ tcx. get_attrs ( did. to_def_id ( ) )
2118+ . iter ( )
2119+ . find ( |at| at. check_name ( sym:: link_name) )
2120+ . unwrap ( )
2121+ . span ,
2122+ )
2123+ } )
2124+ {
2125+ SymbolName :: Link ( overridden_link_name, overridden_link_name_span)
2126+ } else {
2127+ SymbolName :: Normal ( fi. ident . name )
2128+ }
2129+ }
2130+
2131+ /// Checks whether two types are structurally the same enough that the declarations shouldn't
2132+ /// clash. We need this so we don't emit a lint when two modules both declare an extern struct,
2133+ /// with the same members (as the declarations shouldn't clash).
2134+ fn structurally_same_type < ' a , ' tcx > (
2135+ cx : & LateContext < ' a , ' tcx > ,
2136+ a : Ty < ' tcx > ,
2137+ b : Ty < ' tcx > ,
2138+ ) -> bool {
2139+ let tcx = cx. tcx ;
2140+ if a == b || rustc_middle:: ty:: TyS :: same_type ( a, b) {
2141+ // All nominally-same types are structurally same, too.
2142+ true
2143+ } else {
2144+ // Do a full, depth-first comparison between the two.
2145+ use rustc_middle:: ty:: TyKind :: * ;
2146+ let a_kind = & a. kind ;
2147+ let b_kind = & b. kind ;
2148+
2149+ match ( a_kind, b_kind) {
2150+ ( Adt ( ..) , Adt ( ..) ) => {
2151+ // Adts are pretty straightforward: just compare the layouts.
2152+ use rustc_target:: abi:: LayoutOf ;
2153+ let a_layout = cx. layout_of ( a) . unwrap ( ) . layout ;
2154+ let b_layout = cx. layout_of ( b) . unwrap ( ) . layout ;
2155+ a_layout == b_layout
2156+ }
2157+ ( Array ( a_ty, a_const) , Array ( b_ty, b_const) ) => {
2158+ // For arrays, we also check the constness of the type.
2159+ a_const. val == b_const. val
2160+ && Self :: structurally_same_type ( cx, a_const. ty , b_const. ty )
2161+ && Self :: structurally_same_type ( cx, a_ty, b_ty)
2162+ }
2163+ ( Slice ( a_ty) , Slice ( b_ty) ) => Self :: structurally_same_type ( cx, a_ty, b_ty) ,
2164+ ( RawPtr ( a_tymut) , RawPtr ( b_tymut) ) => {
2165+ a_tymut. mutbl == a_tymut. mutbl
2166+ && Self :: structurally_same_type ( cx, & a_tymut. ty , & b_tymut. ty )
2167+ }
2168+ ( Ref ( _a_region, a_ty, a_mut) , Ref ( _b_region, b_ty, b_mut) ) => {
2169+ // For structural sameness, we don't need the region to be same.
2170+ a_mut == b_mut && Self :: structurally_same_type ( cx, a_ty, b_ty)
2171+ }
2172+ ( FnDef ( ..) , FnDef ( ..) ) => {
2173+ let a_sig = a. fn_sig ( tcx) . no_bound_vars ( ) . expect ( "no bound vars" ) ;
2174+ let b_sig = b. fn_sig ( tcx) . no_bound_vars ( ) . expect ( "no bound vars" ) ;
2175+
2176+ ( a_sig. abi , a_sig. unsafety , a_sig. c_variadic )
2177+ == ( b_sig. abi , b_sig. unsafety , b_sig. c_variadic )
2178+ && a_sig. inputs ( ) . iter ( ) . eq_by ( b_sig. inputs ( ) . iter ( ) , |a, b| {
2179+ Self :: structurally_same_type ( cx, a, b)
2180+ } )
2181+ && Self :: structurally_same_type ( cx, a_sig. output ( ) , b_sig. output ( ) )
2182+ }
2183+ ( Tuple ( a_substs) , Tuple ( b_substs) ) => {
2184+ a_substs. types ( ) . eq_by ( b_substs. types ( ) , |a_ty, b_ty| {
2185+ Self :: structurally_same_type ( cx, a_ty, b_ty)
2186+ } )
2187+ }
2188+ // For these, it's not quite as easy to define structural-sameness quite so easily.
2189+ // For the purposes of this lint, take the conservative approach and mark them as
2190+ // not structurally same.
2191+ ( Dynamic ( ..) , Dynamic ( ..) )
2192+ | ( Closure ( ..) , Closure ( ..) )
2193+ | ( Generator ( ..) , Generator ( ..) )
2194+ | ( GeneratorWitness ( ..) , GeneratorWitness ( ..) )
2195+ | ( Projection ( ..) , Projection ( ..) )
2196+ | ( Opaque ( ..) , Opaque ( ..) ) => false ,
2197+ // These definitely should have been caught above.
2198+ ( Bool , Bool ) | ( Char , Char ) | ( Never , Never ) | ( Str , Str ) | ( Error , Error ) => {
2199+ unreachable ! ( )
2200+ }
2201+ _ => false ,
2202+ }
2203+ }
2204+ }
2205+ }
2206+
2207+ impl_lint_pass ! ( ClashingExternDecl => [ CLASHING_EXTERN_DECL ] ) ;
2208+
2209+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for ClashingExternDecl {
2210+ fn check_foreign_item ( & mut self , cx : & LateContext < ' a , ' tcx > , this_fi : & hir:: ForeignItem < ' _ > ) {
2211+ trace ! ( "ClashingExternDecl: check_foreign_item: {:?}" , this_fi) ;
2212+ if let ForeignItemKind :: Fn ( ..) = this_fi. kind {
2213+ let tcx = * & cx. tcx ;
2214+ if let Some ( existing_hid) = self . insert ( tcx, this_fi) {
2215+ let existing_decl_ty = tcx. type_of ( tcx. hir ( ) . local_def_id ( existing_hid) ) ;
2216+ let this_decl_ty = tcx. type_of ( tcx. hir ( ) . local_def_id ( this_fi. hir_id ) ) ;
2217+ debug ! (
2218+ "ClashingExternDecl: Comparing existing {:?}: {:?} to this {:?}: {:?}" ,
2219+ existing_hid, existing_decl_ty, this_fi. hir_id, this_decl_ty
2220+ ) ;
2221+ // Check that the declarations match.
2222+ if !Self :: structurally_same_type ( cx, existing_decl_ty, this_decl_ty) {
2223+ let orig_fi = tcx. hir ( ) . expect_foreign_item ( existing_hid) ;
2224+ let orig = Self :: name_of_extern_decl ( tcx, orig_fi) ;
2225+
2226+ // We want to ensure that we use spans for both decls that include where the
2227+ // name was defined, whether that was from the link_name attribute or not.
2228+ let get_relevant_span =
2229+ |fi : & hir:: ForeignItem < ' _ > | match Self :: name_of_extern_decl ( tcx, fi) {
2230+ SymbolName :: Normal ( _) => fi. span ,
2231+ SymbolName :: Link ( _, annot_span) => fi. span . to ( annot_span) ,
2232+ } ;
2233+ // Finally, emit the diagnostic.
2234+ tcx. struct_span_lint_hir (
2235+ CLASHING_EXTERN_DECL ,
2236+ this_fi. hir_id ,
2237+ get_relevant_span ( this_fi) ,
2238+ |lint| {
2239+ let mut expected_str = DiagnosticStyledString :: new ( ) ;
2240+ expected_str. push ( existing_decl_ty. fn_sig ( tcx) . to_string ( ) , false ) ;
2241+ let mut found_str = DiagnosticStyledString :: new ( ) ;
2242+ found_str. push ( this_decl_ty. fn_sig ( tcx) . to_string ( ) , true ) ;
2243+
2244+ lint. build ( & format ! (
2245+ "`{}` redeclare{} with a different signature" ,
2246+ this_fi. ident. name,
2247+ if orig. get_name( ) == this_fi. ident. name {
2248+ "d" . to_string( )
2249+ } else {
2250+ format!( "s `{}`" , orig. get_name( ) )
2251+ }
2252+ ) )
2253+ . span_label (
2254+ get_relevant_span ( orig_fi) ,
2255+ & format ! ( "`{}` previously declared here" , orig. get_name( ) ) ,
2256+ )
2257+ . span_label (
2258+ get_relevant_span ( this_fi) ,
2259+ "this signature doesn't match the previous declaration" ,
2260+ )
2261+ . note_expected_found ( & "" , expected_str, & "" , found_str)
2262+ . emit ( )
2263+ } ,
2264+ ) ;
2265+ }
2266+ }
2267+ }
2268+ }
2269+ }
0 commit comments