@@ -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
@@ -2049,3 +2049,221 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
2049
2049
}
2050
2050
}
2051
2051
}
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