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