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