rustc_lint/
foreign_modules.rs

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    /// The `clashing_extern_declarations` lint detects when an `extern fn`
32    /// has been declared with the same name but different types.
33    ///
34    /// ### Example
35    ///
36    /// ```rust
37    /// mod m {
38    ///     unsafe extern "C" {
39    ///         fn foo();
40    ///     }
41    /// }
42    ///
43    /// unsafe extern "C" {
44    ///     fn foo(_: u32);
45    /// }
46    /// ```
47    ///
48    /// {{produces}}
49    ///
50    /// ### Explanation
51    ///
52    /// Because two symbols of the same name cannot be resolved to two
53    /// different functions at link time, and one function cannot possibly
54    /// have two types, a clashing extern declaration is almost certainly a
55    /// mistake. Check to make sure that the `extern` definitions are correct
56    /// and equivalent, and possibly consider unifying them in one location.
57    ///
58    /// This lint does not run between crates because a project may have
59    /// dependencies which both rely on the same extern function, but declare
60    /// it in a different (but valid) way. For example, they may both declare
61    /// an opaque type for one or more of the arguments (which would end up
62    /// distinct types), or use types that are valid conversions in the
63    /// language the `extern fn` is defined in. In these cases, the compiler
64    /// can't say that the clashing declaration is incorrect.
65    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    /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls
72    /// contains an entry for key K, it means a symbol with name K has been seen by this lint and
73    /// the symbol should be reported as a clashing declaration.
74    // FIXME: Technically, we could just store a &'tcx str here without issue; however, the
75    // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
76    seen_decls: UnordMap<Symbol, hir::OwnerId>,
77}
78
79/// Differentiate between whether the name for an extern decl came from the link_name attribute or
80/// just from declaration itself. This is important because we don't want to report clashes on
81/// symbol name if they don't actually clash because one or the other links against a symbol with a
82/// different name.
83enum SymbolName {
84    /// The name of the symbol + the span of the annotation which introduced the link name.
85    Link(Symbol, Span),
86    /// No link name, so just the name of the symbol.
87    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    /// Insert a new foreign item into the seen set. If a symbol with the same name already exists
104    /// for the item, return its HirId without updating the set.
105    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            // Avoid updating the map with the new entry when we do find a collision. We want to
111            // make sure we're always pointing to the first definition as the previous declaration.
112            // This lets us avoid emitting "knock-on" diagnostics.
113            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        // Check that the declarations match.
132        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            // Finally, emit the diagnostic.
142            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
175/// Get the name of the symbol that's linked against for a given extern declaration. That is,
176/// the name specified in a #[link_name = ...] attribute if one was specified, else, just the
177/// symbol's name.
178fn 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            // FIXME: Instead of searching through the attributes again to get span
182            // information, we could have codegen_fn_attrs also give span information back for
183            // where the attribute was defined. However, until this is found to be a
184            // bottleneck, this does just fine.
185            (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
194/// We want to ensure that we use spans for both decls that include where the
195/// name was defined, whether that was from the link_name attribute or not.
196fn 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
203/// Checks whether two types are structurally the same enough that the declarations shouldn't
204/// clash. We need this so we don't emit a lint when two modules both declare an extern struct,
205/// with the same members (as the declarations shouldn't clash).
206fn 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        // Sanity-check: must have same ABI, size and alignment.
217        // `extern` blocks cannot be generic, so we'll always get a layout here.
218        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    // Given a transparent newtype, reach through and grab the inner
238    // type unless the newtype makes the type non-null.
239    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                    // continue with `ty`'s non-ZST field,
249                    // otherwise `ty` is a ZST and we can return
250                    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        // We've encountered a cycle. There's no point going any further -- the types are
266        // structurally the same.
267        true
268    } else if a == b {
269        // All nominally-same types are structurally same, too.
270        true
271    } else {
272        // Do a full, depth-first comparison between the two.
273        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                    // Only `repr(C)` types can be compared structurally.
280                    if !(a_def.repr().c() && b_def.repr().c()) {
281                        return false;
282                    }
283                    // If the types differ in their packed-ness, align, or simd-ness they conflict.
284                    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                    // Grab a flattened representation of all fields.
291                    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                    // Perform a structural comparison for each field.
295                    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                    // For arrays, we also check the length.
311                    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                    // For structural sameness, we don't need the region to be same.
327                    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                    // We don't compare regions, but leaving bound regions around ICEs, so
337                    // we erase them.
338                    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                    // Tuples are not `repr(C)` so these cannot be compared structurally.
357                    false
358                }
359                // For these, it's not quite as easy to define structural-sameness quite so easily.
360                // For the purposes of this lint, take the conservative approach and mark them as
361                // not structurally same.
362                (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                // These definitely should have been caught above.
372                (ty::Bool, ty::Bool)
373                | (ty::Char, ty::Char)
374                | (ty::Never, ty::Never)
375                | (ty::Str, ty::Str) => unreachable!(),
376
377                // An Adt and a primitive or pointer type. This can be FFI-safe if non-null
378                // enum layout optimisation is being applied.
379                (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}