1use rustc_abi::FIRST_VARIANT;
2use rustc_data_structures::stack::ensure_sufficient_stack;
3use rustc_data_structures::unord::{UnordMap, UnordSet};
4use rustc_hir as hir;
5use rustc_hir::def::DefKind;
6use rustc_middle::query::Providers;
7use rustc_middle::ty::{self, AdtDef, Instance, Ty, TyCtxt};
8use rustc_session::declare_lint;
9use rustc_span::{Span, Symbol, sym};
10use tracing::{debug, instrument};
11
12use crate::lints::{BuiltinClashingExtern, BuiltinClashingExternSub};
13use crate::{LintVec, types};
14
15pub(crate) fn provide(providers: &mut Providers) {
16 *providers = Providers { clashing_extern_declarations, ..*providers };
17}
18
19pub(crate) fn get_lints() -> LintVec {
20 vec![CLASHING_EXTERN_DECLARATIONS]
21}
22
23fn clashing_extern_declarations(tcx: TyCtxt<'_>, (): ()) {
24 let mut lint = ClashingExternDeclarations::new();
25 for id in tcx.hir_crate_items(()).foreign_items() {
26 lint.check_foreign_item(tcx, id);
27 }
28}
29
30declare_lint! {
31 pub CLASHING_EXTERN_DECLARATIONS,
66 Warn,
67 "detects when an extern fn has been declared with the same name but different types"
68}
69
70struct ClashingExternDeclarations {
71 seen_decls: UnordMap<Symbol, hir::OwnerId>,
77}
78
79enum SymbolName {
84 Link(Symbol, Span),
86 Normal(Symbol),
88}
89
90impl SymbolName {
91 fn get_name(&self) -> Symbol {
92 match self {
93 SymbolName::Link(s, _) | SymbolName::Normal(s) => *s,
94 }
95 }
96}
97
98impl ClashingExternDeclarations {
99 pub(crate) fn new() -> Self {
100 ClashingExternDeclarations { seen_decls: Default::default() }
101 }
102
103 fn insert(&mut self, tcx: TyCtxt<'_>, fi: hir::ForeignItemId) -> Option<hir::OwnerId> {
106 let did = fi.owner_id.to_def_id();
107 let instance = Instance::new(did, ty::List::identity_for_item(tcx, did));
108 let name = Symbol::intern(tcx.symbol_name(instance).name);
109 if let Some(&existing_id) = self.seen_decls.get(&name) {
110 Some(existing_id)
114 } else {
115 self.seen_decls.insert(name, fi.owner_id)
116 }
117 }
118
119 #[instrument(level = "trace", skip(self, tcx))]
120 fn check_foreign_item<'tcx>(&mut self, tcx: TyCtxt<'tcx>, this_fi: hir::ForeignItemId) {
121 let DefKind::Fn = tcx.def_kind(this_fi.owner_id) else { return };
122 let Some(existing_did) = self.insert(tcx, this_fi) else { return };
123
124 let existing_decl_ty = tcx.type_of(existing_did).skip_binder();
125 let this_decl_ty = tcx.type_of(this_fi.owner_id).instantiate_identity();
126 debug!(
127 "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
128 existing_did, existing_decl_ty, this_fi.owner_id, this_decl_ty
129 );
130
131 if !structurally_same_type(
133 tcx,
134 ty::TypingEnv::non_body_analysis(tcx, this_fi.owner_id),
135 existing_decl_ty,
136 this_decl_ty,
137 types::CItemKind::Declaration,
138 ) {
139 let orig = name_of_extern_decl(tcx, existing_did);
140
141 let this = tcx.item_name(this_fi.owner_id.to_def_id());
143 let orig = orig.get_name();
144 let previous_decl_label = get_relevant_span(tcx, existing_did);
145 let mismatch_label = get_relevant_span(tcx, this_fi.owner_id);
146 let sub =
147 BuiltinClashingExternSub { tcx, expected: existing_decl_ty, found: this_decl_ty };
148 let decorator = if orig == this {
149 BuiltinClashingExtern::SameName {
150 this,
151 orig,
152 previous_decl_label,
153 mismatch_label,
154 sub,
155 }
156 } else {
157 BuiltinClashingExtern::DiffName {
158 this,
159 orig,
160 previous_decl_label,
161 mismatch_label,
162 sub,
163 }
164 };
165 tcx.emit_node_span_lint(
166 CLASHING_EXTERN_DECLARATIONS,
167 this_fi.hir_id(),
168 mismatch_label,
169 decorator,
170 );
171 }
172 }
173}
174
175fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
179 if let Some((overridden_link_name, overridden_link_name_span)) =
180 tcx.codegen_fn_attrs(fi).link_name.map(|overridden_link_name| {
181 (overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span())
186 })
187 {
188 SymbolName::Link(overridden_link_name, overridden_link_name_span)
189 } else {
190 SymbolName::Normal(tcx.item_name(fi.to_def_id()))
191 }
192}
193
194fn get_relevant_span(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> Span {
197 match name_of_extern_decl(tcx, fi) {
198 SymbolName::Normal(_) => tcx.def_span(fi),
199 SymbolName::Link(_, annot_span) => annot_span,
200 }
201}
202
203fn structurally_same_type<'tcx>(
207 tcx: TyCtxt<'tcx>,
208 typing_env: ty::TypingEnv<'tcx>,
209 a: Ty<'tcx>,
210 b: Ty<'tcx>,
211 ckind: types::CItemKind,
212) -> bool {
213 let mut seen_types = UnordSet::default();
214 let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b, ckind);
215 if cfg!(debug_assertions) && result {
216 let a_layout = tcx.layout_of(typing_env.as_query_input(a)).unwrap();
219 let b_layout = tcx.layout_of(typing_env.as_query_input(b)).unwrap();
220 assert_eq!(a_layout.backend_repr, b_layout.backend_repr);
221 assert_eq!(a_layout.size, b_layout.size);
222 assert_eq!(a_layout.align, b_layout.align);
223 }
224 result
225}
226
227fn structurally_same_type_impl<'tcx>(
228 seen_types: &mut UnordSet<(Ty<'tcx>, Ty<'tcx>)>,
229 tcx: TyCtxt<'tcx>,
230 typing_env: ty::TypingEnv<'tcx>,
231 a: Ty<'tcx>,
232 b: Ty<'tcx>,
233 ckind: types::CItemKind,
234) -> bool {
235 debug!("structurally_same_type_impl(tcx, a = {:?}, b = {:?})", a, b);
236
237 let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> {
240 loop {
241 if let ty::Adt(def, args) = *ty.kind() {
242 let is_transparent = def.repr().transparent();
243 let is_non_null = types::nonnull_optimization_guaranteed(tcx, def);
244 debug!(?ty, is_transparent, is_non_null);
245 if is_transparent && !is_non_null {
246 debug_assert_eq!(def.variants().len(), 1);
247 let v = &def.variant(FIRST_VARIANT);
248 if let Some(field) = types::transparent_newtype_field(tcx, v) {
251 ty = field.ty(tcx, args);
252 continue;
253 }
254 }
255 }
256 debug!("non_transparent_ty -> {:?}", ty);
257 return ty;
258 }
259 };
260
261 let a = non_transparent_ty(a);
262 let b = non_transparent_ty(b);
263
264 if !seen_types.insert((a, b)) {
265 true
268 } else if a == b {
269 true
271 } else {
272 let is_primitive_or_pointer =
274 |ty: Ty<'tcx>| ty.is_primitive() || matches!(ty.kind(), ty::RawPtr(..) | ty::Ref(..));
275
276 ensure_sufficient_stack(|| {
277 match (a.kind(), b.kind()) {
278 (&ty::Adt(a_def, a_gen_args), &ty::Adt(b_def, b_gen_args)) => {
279 if !(a_def.repr().c() && b_def.repr().c()) {
281 return false;
282 }
283 let repr_characteristica =
285 |def: AdtDef<'tcx>| (def.repr().pack, def.repr().align, def.repr().simd());
286 if repr_characteristica(a_def) != repr_characteristica(b_def) {
287 return false;
288 }
289
290 let a_fields = a_def.variants().iter().flat_map(|v| v.fields.iter());
292 let b_fields = b_def.variants().iter().flat_map(|v| v.fields.iter());
293
294 a_fields.eq_by(
296 b_fields,
297 |&ty::FieldDef { did: a_did, .. }, &ty::FieldDef { did: b_did, .. }| {
298 structurally_same_type_impl(
299 seen_types,
300 tcx,
301 typing_env,
302 tcx.type_of(a_did).instantiate(tcx, a_gen_args),
303 tcx.type_of(b_did).instantiate(tcx, b_gen_args),
304 ckind,
305 )
306 },
307 )
308 }
309 (ty::Array(a_ty, a_len), ty::Array(b_ty, b_len)) => {
310 a_len == b_len
312 && structurally_same_type_impl(
313 seen_types, tcx, typing_env, *a_ty, *b_ty, ckind,
314 )
315 }
316 (ty::Slice(a_ty), ty::Slice(b_ty)) => {
317 structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty, ckind)
318 }
319 (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => {
320 a_mutbl == b_mutbl
321 && structurally_same_type_impl(
322 seen_types, tcx, typing_env, *a_ty, *b_ty, ckind,
323 )
324 }
325 (ty::Ref(_a_region, a_ty, a_mut), ty::Ref(_b_region, b_ty, b_mut)) => {
326 a_mut == b_mut
328 && structurally_same_type_impl(
329 seen_types, tcx, typing_env, *a_ty, *b_ty, ckind,
330 )
331 }
332 (ty::FnDef(..), ty::FnDef(..)) => {
333 let a_poly_sig = a.fn_sig(tcx);
334 let b_poly_sig = b.fn_sig(tcx);
335
336 let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig);
339 let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig);
340
341 (a_sig.abi, a_sig.safety, a_sig.c_variadic)
342 == (b_sig.abi, b_sig.safety, b_sig.c_variadic)
343 && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
344 structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b, ckind)
345 })
346 && structurally_same_type_impl(
347 seen_types,
348 tcx,
349 typing_env,
350 a_sig.output(),
351 b_sig.output(),
352 ckind,
353 )
354 }
355 (ty::Tuple(..), ty::Tuple(..)) => {
356 false
358 }
359 (ty::Dynamic(..), ty::Dynamic(..))
363 | (ty::Error(..), ty::Error(..))
364 | (ty::Closure(..), ty::Closure(..))
365 | (ty::Coroutine(..), ty::Coroutine(..))
366 | (ty::CoroutineWitness(..), ty::CoroutineWitness(..))
367 | (ty::Alias(ty::Projection, ..), ty::Alias(ty::Projection, ..))
368 | (ty::Alias(ty::Inherent, ..), ty::Alias(ty::Inherent, ..))
369 | (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => false,
370
371 (ty::Bool, ty::Bool)
373 | (ty::Char, ty::Char)
374 | (ty::Never, ty::Never)
375 | (ty::Str, ty::Str) => unreachable!(),
376
377 (ty::Adt(..) | ty::Pat(..), _) if is_primitive_or_pointer(b) => {
380 if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) {
381 a_inner == b
382 } else {
383 false
384 }
385 }
386 (_, ty::Adt(..) | ty::Pat(..)) if is_primitive_or_pointer(a) => {
387 if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) {
388 b_inner == a
389 } else {
390 false
391 }
392 }
393
394 _ => false,
395 }
396 })
397 }
398}