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