Skip to main content

flux_driver/collector/
extern_specs.rs

1use std::iter;
2
3use flux_middle::ExternSpecMappingErr;
4use flux_rustc_bridge::lowering;
5use flux_syntax::surface;
6use rustc_errors::Diagnostic;
7use rustc_hir as hir;
8use rustc_hir::{
9    BodyId, OwnerId,
10    def_id::{DefId, LocalDefId},
11};
12use rustc_middle::ty::{self, TyCtxt};
13use rustc_span::{ErrorGuaranteed, Span, symbol::kw};
14
15use super::{FluxAttrs, SpecCollector};
16
17type Result<T = ()> = std::result::Result<T, ErrorGuaranteed>;
18
19pub(super) struct ExternSpecCollector<'a, 'sess, 'tcx> {
20    inner: &'a mut SpecCollector<'sess, 'tcx>,
21    /// The block corresponding to the `const _: () = { ... }` annotated with `flux::extern_spec`
22    block: &'tcx hir::Block<'tcx>,
23}
24
25struct ExternImplItem {
26    impl_id: DefId,
27    item_id: DefId,
28}
29
30impl<'a, 'sess, 'tcx> ExternSpecCollector<'a, 'sess, 'tcx> {
31    pub(super) fn collect(inner: &'a mut SpecCollector<'sess, 'tcx>, body_id: BodyId) -> Result {
32        Self::new(inner, body_id)?.run()
33    }
34
35    fn new(inner: &'a mut SpecCollector<'sess, 'tcx>, body_id: BodyId) -> Result<Self> {
36        let body = inner.tcx.hir_body(body_id);
37        if let hir::ExprKind::Block(block, _) = body.value.kind {
38            Ok(Self { inner, block })
39        } else {
40            Err(inner
41                .errors
42                .emit(errors::MalformedExternSpec::new(body.value.span)))
43        }
44    }
45
46    fn run(mut self) -> Result {
47        let item = self.item_at(0)?;
48
49        let attrs = self
50            .inner
51            .parse_attrs_and_report_dups(item.owner_id.def_id)?;
52
53        match &item.kind {
54            hir::ItemKind::Fn { .. } => self.collect_extern_fn(item, attrs),
55            hir::ItemKind::Enum(_, _, enum_def) => {
56                self.collect_extern_enum(item.owner_id, enum_def, attrs)
57            }
58            hir::ItemKind::Struct(_, _, variant) => {
59                self.collect_extern_struct(item.owner_id, variant, attrs)
60            }
61            hir::ItemKind::Trait(_, _, _, _, _, bounds, items) => {
62                self.collect_extern_trait(item.owner_id, bounds, items, attrs)
63            }
64            hir::ItemKind::Impl(impl_) => self.collect_extern_impl(item.owner_id, impl_, attrs),
65            _ => Err(self.malformed()),
66        }
67    }
68
69    fn collect_extern_fn(&mut self, item: &hir::Item, mut attrs: FluxAttrs) -> Result {
70        if attrs.has_attrs() {
71            let (sig, attr_span) = if let Some((sig, span)) = attrs.fn_sig_with_attr_span() {
72                (Some(sig), Some(span))
73            } else {
74                (None, None)
75            };
76            self.inner.check_fn_sig_name(item.owner_id, sig.as_ref())?;
77            let node_id = self.inner.next_node_id();
78            self.inner.insert_item(
79                item.owner_id,
80                surface::Item {
81                    attrs: attrs.into_attr_vec(),
82                    kind: surface::ItemKind::Fn(sig),
83                    node_id,
84                },
85            )?;
86            if let Some(span) = attr_span {
87                self.inner
88                    .specs
89                    .set_spec_attr_span(item.owner_id.def_id.to_def_id(), span);
90            }
91        }
92
93        let extern_id = self.extract_extern_id_from_fn(item)?;
94        self.insert_extern_id(item.owner_id.def_id, extern_id)?;
95        self.check_generics(item.owner_id, extern_id)?;
96
97        Ok(())
98    }
99
100    fn collect_extern_struct(
101        &mut self,
102        struct_id: OwnerId,
103        variant: &hir::VariantData,
104        attrs: FluxAttrs,
105    ) -> Result {
106        let dummy_struct = self.item_at(1)?;
107        self.inner.specs.insert_dummy(dummy_struct.owner_id.def_id);
108
109        let extern_id = self.extract_extern_id_from_struct(dummy_struct).unwrap();
110        self.insert_extern_id(struct_id.def_id, extern_id)?;
111        self.check_generics(struct_id, extern_id)?;
112
113        if let Some(ctor_id) = variant.ctor_def_id() {
114            self.inner.specs.insert_dummy(ctor_id);
115        }
116
117        self.inner.collect_struct_def(struct_id, attrs, variant)?;
118
119        Ok(())
120    }
121
122    fn collect_extern_enum(
123        &mut self,
124        enum_id: OwnerId,
125        enum_def: &hir::EnumDef,
126        attrs: FluxAttrs,
127    ) -> Result {
128        let dummy_struct = self.item_at(1)?;
129        self.inner.specs.insert_dummy(dummy_struct.owner_id.def_id);
130
131        let extern_id = self.extract_extern_id_from_struct(dummy_struct).unwrap();
132        self.insert_extern_id(enum_id.def_id, extern_id)?;
133        self.check_generics(enum_id, extern_id)?;
134
135        self.inner.collect_enum_def(enum_id, attrs, enum_def)?;
136
137        // Add stuff about Ctor
138        // Get the AdtDef for the enum
139        let extern_enum_def = self.tcx().adt_def(extern_id);
140
141        // Collect all constructor DefIds from variants
142        let extern_variants = extern_enum_def.variants();
143        let enum_variants = enum_def.variants;
144        let extern_len = extern_variants.len();
145        let enum_len = enum_variants.len();
146        if extern_len != enum_len {
147            let reason = format!("expected {extern_len:?} variants but only have {enum_len:?}");
148            return Err(self.invalid_enum_extern_spec(reason));
149        }
150        for (extern_variant, variant) in extern_enum_def.variants().iter().zip(enum_def.variants) {
151            if let Some(extern_ctor) = extern_variant.ctor_def_id()
152                && let Some(ctor) = variant.data.ctor_def_id()
153                && self.tcx().def_kind(extern_ctor) == self.tcx().def_kind(ctor)
154            {
155                self.insert_extern_id(ctor, extern_ctor)?;
156            } else {
157                let reason = format!(
158                    "extern variant `{}` incompatible with specified `{}`",
159                    extern_variant.ident(self.tcx()),
160                    rustc_hir_pretty::id_to_string(&self.tcx(), variant.hir_id)
161                );
162                return Err(self.invalid_enum_extern_spec(reason));
163            }
164        }
165        Ok(())
166    }
167
168    fn collect_extern_impl(
169        &mut self,
170        impl_id: OwnerId,
171        impl_: &hir::Impl,
172        attrs: FluxAttrs,
173    ) -> Result {
174        self.inner.collect_impl(impl_id, attrs)?;
175
176        let dummy_item = self.item_at(1)?;
177        self.inner.specs.insert_dummy(dummy_item.owner_id.def_id);
178
179        // If this is a trait impl compute the impl_id from the trait_ref
180        let mut impl_of_trait = None;
181        let mut local_trait_self_ty = None;
182        if let hir::ItemKind::Impl(dummy_impl) = &dummy_item.kind {
183            let (extern_id, self_ty) =
184                self.extract_extern_id_from_impl(dummy_item.owner_id, dummy_impl)?;
185            impl_of_trait = Some(extern_id);
186            local_trait_self_ty = Some(self_ty);
187
188            self.inner
189                .specs
190                .insert_dummy(self.item_at(2)?.owner_id.def_id);
191        }
192
193        let mut extern_impl_id = impl_of_trait;
194        for item_id in impl_.items {
195            let item = self.tcx().hir_impl_item(*item_id);
196            let extern_item = if let hir::ImplItemKind::Fn { .. } = item.kind {
197                let attrs = self
198                    .inner
199                    .parse_attrs_and_report_dups(item_id.owner_id.def_id)?;
200                self.collect_extern_impl_fn(impl_of_trait, item, attrs)?
201            } else {
202                continue;
203            };
204
205            if *extern_impl_id.get_or_insert(extern_item.impl_id) != extern_item.impl_id {
206                return Err(self.invalid_impl_block());
207            }
208        }
209
210        if let Some(extern_impl_id) = extern_impl_id {
211            self.check_generics(impl_id, extern_impl_id)?;
212            // For trait impls, check that the self type matches the external definition
213            if let Some(local_self_ty) = local_trait_self_ty {
214                self.check_extern_impl_self_ty(impl_id, local_self_ty, extern_impl_id)?;
215            }
216            self.insert_extern_id(impl_id.def_id, extern_impl_id)?;
217        }
218
219        Ok(())
220    }
221
222    fn collect_extern_impl_fn(
223        &mut self,
224        impl_of_trait: Option<DefId>,
225        item: &hir::ImplItem,
226        mut attrs: FluxAttrs,
227    ) -> Result<ExternImplItem> {
228        if attrs.has_attrs() {
229            let (sig, attr_span) = if let Some((sig, span)) = attrs.fn_sig_with_attr_span() {
230                (Some(sig), Some(span))
231            } else {
232                (None, None)
233            };
234            self.inner.check_fn_sig_name(item.owner_id, sig.as_ref())?;
235            let node_id = self.inner.next_node_id();
236            self.inner.insert_impl_item(
237                item.owner_id,
238                surface::ImplItemFn { attrs: attrs.into_attr_vec(), sig, node_id },
239            )?;
240            if let Some(span) = attr_span {
241                self.inner
242                    .specs
243                    .set_spec_attr_span(item.owner_id.def_id.to_def_id(), span);
244            }
245        }
246
247        let extern_impl_item = self.extract_extern_id_from_impl_fn(impl_of_trait, item)?;
248        self.insert_extern_id(item.owner_id.def_id, extern_impl_item.item_id)?;
249        self.check_generics(item.owner_id, extern_impl_item.item_id)?;
250
251        Ok(extern_impl_item)
252    }
253
254    fn collect_extern_trait(
255        &mut self,
256        trait_id: OwnerId,
257        bounds: &hir::GenericBounds,
258        items: &[hir::TraitItemId],
259        attrs: FluxAttrs,
260    ) -> Result {
261        self.inner.collect_trait(trait_id, attrs)?;
262
263        let extern_trait_id = self.extract_extern_id_from_trait(bounds)?;
264        self.insert_extern_id(trait_id.def_id, extern_trait_id)?;
265        self.check_generics(trait_id, extern_trait_id)?;
266
267        for item_id in items {
268            let item = self.tcx().hir_trait_item(*item_id);
269            if let hir::TraitItemKind::Fn { .. } = item.kind {
270                let attrs = self
271                    .inner
272                    .parse_attrs_and_report_dups(item.owner_id.def_id)?;
273                self.collect_extern_trait_fn(extern_trait_id, item, attrs)?;
274            } else {
275                continue;
276            }
277        }
278
279        Ok(())
280    }
281
282    fn collect_extern_trait_fn(
283        &mut self,
284        extern_trait_id: DefId,
285        item: &hir::TraitItem,
286        mut attrs: FluxAttrs,
287    ) -> Result {
288        let item_id = item.owner_id;
289        if attrs.has_attrs() {
290            let (sig, attr_span) = if let Some((sig, span)) = attrs.fn_sig_with_attr_span() {
291                (Some(sig), Some(span))
292            } else {
293                (None, None)
294            };
295            self.inner.check_fn_sig_name(item.owner_id, sig.as_ref())?;
296            let node_id = self.inner.next_node_id();
297            self.inner.insert_trait_item(
298                item.owner_id,
299                surface::TraitItemFn { attrs: attrs.into_attr_vec(), sig, node_id },
300            )?;
301            if let Some(span) = attr_span {
302                self.inner
303                    .specs
304                    .set_spec_attr_span(item.owner_id.def_id.to_def_id(), span);
305            }
306        }
307
308        let extern_fn_id = self.extract_extern_id_from_trait_fn(extern_trait_id, item)?;
309        self.insert_extern_id(item.owner_id.def_id, extern_fn_id)?;
310        self.check_generics(item_id, extern_fn_id)?;
311
312        Ok(())
313    }
314
315    fn extract_extern_id_from_struct(&self, item: &hir::Item) -> Result<DefId> {
316        if let hir::ItemKind::Struct(_, _, data) = item.kind
317            && let Some(extern_field) = data.fields().last()
318            && let ty = self.tcx().type_of(extern_field.def_id)
319            && let Some(adt_def) = ty.skip_binder().ty_adt_def()
320        {
321            Ok(adt_def.did())
322        } else {
323            Err(self.malformed())
324        }
325    }
326
327    fn extract_extern_id_from_fn(&self, item: &hir::Item) -> Result<DefId> {
328        if let hir::ItemKind::Fn { body, .. } = item.kind {
329            self.extract_callee_from_body(body)
330        } else {
331            Err(self.malformed())
332        }
333    }
334
335    fn extract_extern_id_from_impl_fn(
336        &self,
337        impl_of_trait: Option<DefId>,
338        item: &hir::ImplItem,
339    ) -> Result<ExternImplItem> {
340        if let hir::ImplItemKind::Fn(_, body_id) = item.kind {
341            let callee_id = self.extract_callee_from_body(body_id)?;
342            if let Some(extern_impl_id) = impl_of_trait {
343                let map = self.tcx().impl_item_implementor_ids(extern_impl_id);
344                if let Some(extern_item_id) = map.get(&callee_id) {
345                    Ok(ExternImplItem { impl_id: extern_impl_id, item_id: *extern_item_id })
346                } else {
347                    Err(self.item_not_in_trait_impl(item.owner_id, callee_id, extern_impl_id))
348                }
349            } else {
350                let opt_extern_impl_id = self.tcx().impl_of_assoc(callee_id);
351                if let Some(extern_impl_id) = opt_extern_impl_id {
352                    debug_assert!(!self.tcx().impl_is_of_trait(extern_impl_id));
353                    Ok(ExternImplItem { impl_id: extern_impl_id, item_id: callee_id })
354                } else {
355                    Err(self.invalid_item_in_inherent_impl(item.owner_id, callee_id))
356                }
357            }
358        } else {
359            Err(self.malformed())
360        }
361    }
362
363    fn extract_extern_id_from_trait(&self, bounds: &hir::GenericBounds) -> Result<DefId> {
364        if let Some(bound) = bounds.first()
365            && let Some(trait_ref) = bound.trait_ref()
366            && let Some(trait_id) = trait_ref.trait_def_id()
367        {
368            Ok(trait_id)
369        } else {
370            Err(self.malformed())
371        }
372    }
373
374    fn extract_extern_id_from_trait_fn(
375        &self,
376        trait_id: DefId,
377        item: &hir::TraitItem,
378    ) -> Result<DefId> {
379        if let hir::TraitItemKind::Fn(_, trait_fn) = item.kind
380            && let hir::TraitFn::Provided(body_id) = trait_fn
381        {
382            let callee_id = self.extract_callee_from_body(body_id)?;
383            if let Some(callee_trait_id) = self.tcx().trait_of_assoc(callee_id)
384                && trait_id == callee_trait_id
385            {
386                Ok(callee_id)
387            } else {
388                // I can't figure out how to trigger this via code generated with the extern spec
389                // macro that also type checks but leaving it here as a precaution.
390                Err(self.item_not_in_trait(item.owner_id, callee_id, trait_id))
391            }
392        } else {
393            Err(self.malformed())
394        }
395    }
396
397    fn extract_extern_id_from_impl(
398        &self,
399        impl_id: OwnerId,
400        impl_: &hir::Impl,
401    ) -> Result<(DefId, ty::Ty<'tcx>)> {
402        if let Some(item_id) = impl_.items.first()
403            && let hir::ImplItemKind::Fn { .. } = self.tcx().hir_impl_item(*item_id).kind
404            && let Some((clause, _)) = self
405                .tcx()
406                .predicates_of(item_id.owner_id.def_id)
407                .predicates
408                .first()
409            && let Some(poly_trait_pred) = clause.as_trait_clause()
410            && let Some(trait_pred) = poly_trait_pred.no_bound_vars()
411        {
412            let trait_ref = trait_pred.trait_ref;
413            let local_self_ty = trait_ref.self_ty();
414            lowering::resolve_trait_ref_impl_id(self.tcx(), impl_id.to_def_id(), trait_ref)
415                .map(|(impl_id, _)| (impl_id, local_self_ty))
416                .ok_or_else(|| self.cannot_resolve_trait_impl())
417        } else {
418            Err(self.malformed())
419        }
420    }
421
422    fn extract_callee_from_body(&self, body_id: hir::BodyId) -> Result<DefId> {
423        let owner = self.tcx().hir_body_owner_def_id(body_id);
424        let typeck = self.tcx().typeck(owner);
425        if let hir::ExprKind::Block(b, _) = self.tcx().hir_body(body_id).value.kind
426            && let Some(e) = b.expr
427        {
428            // Peel through an optional `unsafe { ... }` block
429            let call_expr =
430                if let hir::ExprKind::Block(inner_b, _) = e.kind { inner_b.expr } else { Some(e) };
431            if let Some(e) = call_expr
432                && let hir::ExprKind::Call(callee, _) = e.kind
433                && let rustc_middle::ty::FnDef(callee_id, _) =
434                    typeck.node_type(callee.hir_id).kind()
435            {
436                return Ok(*callee_id);
437            }
438        }
439        Err(self.malformed())
440    }
441
442    /// Returns the item inside the const block at position `i` starting from the end.
443    #[track_caller]
444    fn item_at(&self, i: usize) -> Result<&'tcx hir::Item<'tcx>> {
445        let stmts = self.block.stmts;
446        let index = stmts
447            .len()
448            .checked_sub(i + 1)
449            .ok_or_else(|| self.malformed())?;
450        let st = stmts.get(index).ok_or_else(|| self.malformed())?;
451        if let hir::StmtKind::Item(item_id) = st.kind {
452            Ok(self.tcx().hir_item(item_id))
453        } else {
454            Err(self.malformed())
455        }
456    }
457
458    fn insert_extern_id(&mut self, local_id: LocalDefId, extern_id: DefId) -> Result {
459        self.inner
460            .specs
461            .insert_extern_spec_id_mapping(local_id, extern_id)
462            .map_err(|err| {
463                match err {
464                    ExternSpecMappingErr::IsLocal(extern_id_local) => {
465                        self.emit(errors::ExternSpecForLocalDef {
466                            span: ident_or_def_span(self.tcx(), local_id),
467                            local_def_span: ident_or_def_span(self.tcx(), extern_id_local),
468                            name: self.tcx().def_path_str(extern_id),
469                        })
470                    }
471                    ExternSpecMappingErr::Dup(previous_extern_spec) => {
472                        self.emit(errors::DupExternSpec {
473                            span: ident_or_def_span(self.tcx(), local_id),
474                            previous_span: ident_or_def_span(self.tcx(), previous_extern_spec),
475                            name: self.tcx().def_path_str(extern_id),
476                        })
477                    }
478                }
479            })
480    }
481
482    fn check_generics(&mut self, local_id: OwnerId, extern_id: DefId) -> Result {
483        let tcx = self.tcx();
484        let local_params = &tcx.generics_of(local_id).own_params;
485        let extern_params = &tcx.generics_of(extern_id).own_params;
486
487        let mismatch = 'mismatch: {
488            if local_params.len() != extern_params.len() {
489                break 'mismatch true;
490            }
491            for (local_param, extern_param) in iter::zip(local_params, extern_params) {
492                if !cmp_generic_param_def(local_param, extern_param) {
493                    break 'mismatch true;
494                }
495                // We skip the self parameter because its id is the same as the trait's id, which
496                // has already been inserted.
497                if local_param.name != kw::SelfUpper {
498                    #[expect(clippy::disallowed_methods)]
499                    self.insert_extern_id(local_param.def_id.expect_local(), extern_param.def_id)?;
500                }
501            }
502            false
503        };
504        if mismatch {
505            let local_hir_generics = tcx.hir_get_generics(local_id.def_id).unwrap();
506            let span = local_hir_generics.span;
507            Err(self.emit(errors::MismatchedGenerics {
508                span,
509                extern_def: tcx.def_span(extern_id),
510                def_descr: tcx.def_descr(extern_id),
511            }))
512        } else {
513            Ok(())
514        }
515    }
516
517    fn check_extern_impl_self_ty(
518        &mut self,
519        local_impl_id: OwnerId,
520        local_self_ty: ty::Ty<'tcx>,
521        extern_impl_id: DefId,
522    ) -> Result {
523        let tcx = self.tcx();
524
525        // Get the self type from the external impl
526        let extern_self_ty = tcx.type_of(extern_impl_id).instantiate_identity();
527
528        // Compare self types. `local_self_ty` is the user-written self type from the extern
529        // spec's trait_ref (not the `__FluxExternImplStruct` wrapper that the macro retargets
530        // the impl onto). After `check_generics` has verified param names and indices match,
531        // raw Ty equality is sound for the cases we want to catch (concrete type mismatches
532        // like `Range<usize>` vs `Range<A>`).
533        if local_self_ty != extern_self_ty {
534            // Emit on the user's impl block (compiletest matches `//~ ERROR` against the
535            // primary span line). The dummy `__FluxExternImplStruct` wrapper's self_ty has
536            // a macro-generated span that doesn't land on user source.
537            let span = tcx.def_span(local_impl_id);
538            Err(self.emit(errors::MismatchedImplSelfTy {
539                span,
540                local_self_ty: local_self_ty.to_string(),
541                extern_self_ty: extern_self_ty.to_string(),
542                extern_impl_span: tcx.def_span(extern_impl_id),
543            }))
544        } else {
545            Ok(())
546        }
547    }
548
549    #[track_caller]
550    fn malformed(&self) -> ErrorGuaranteed {
551        self.emit(errors::MalformedExternSpec::new(self.block.span))
552    }
553
554    #[track_caller]
555    fn invalid_enum_extern_spec(&self, reason: String) -> ErrorGuaranteed {
556        self.emit(errors::InvalidEnumExternSpec::new(self.block.span, reason))
557    }
558
559    #[track_caller]
560    fn item_not_in_trait_impl(
561        &self,
562        local_id: OwnerId,
563        extern_id: DefId,
564        extern_impl_id: DefId,
565    ) -> ErrorGuaranteed {
566        let tcx = self.tcx();
567        self.emit(errors::ItemNotInTraitImpl {
568            span: ident_or_def_span(tcx, local_id),
569            name: tcx.def_path_str(extern_id),
570            extern_impl_span: tcx.def_span(extern_impl_id),
571        })
572    }
573
574    fn invalid_item_in_inherent_impl(
575        &self,
576        local_id: OwnerId,
577        extern_id: DefId,
578    ) -> ErrorGuaranteed {
579        let tcx = self.tcx();
580        self.emit(errors::InvalidItemInInherentImpl {
581            span: ident_or_def_span(tcx, local_id),
582            name: tcx.def_path_str(extern_id),
583            extern_item_span: tcx.def_span(extern_id),
584        })
585    }
586
587    #[track_caller]
588    fn invalid_impl_block(&self) -> ErrorGuaranteed {
589        self.emit(errors::InvalidImplBlock { span: self.block.span })
590    }
591
592    #[track_caller]
593    fn cannot_resolve_trait_impl(&self) -> ErrorGuaranteed {
594        self.emit(errors::CannotResolveTraitImpl { span: self.block.span })
595    }
596
597    #[track_caller]
598    fn item_not_in_trait(
599        &self,
600        local_id: OwnerId,
601        extern_id: DefId,
602        extern_trait_id: DefId,
603    ) -> ErrorGuaranteed {
604        let tcx = self.tcx();
605        self.emit(errors::ItemNotInTrait {
606            span: ident_or_def_span(tcx, local_id),
607            name: tcx.def_path_str(extern_id),
608            extern_trait_span: tcx.def_span(extern_trait_id),
609        })
610    }
611
612    #[track_caller]
613    fn emit<'b>(&'b self, err: impl Diagnostic<'b>) -> ErrorGuaranteed {
614        self.inner.errors.emit(err)
615    }
616
617    fn tcx(&self) -> TyCtxt<'tcx> {
618        self.inner.tcx
619    }
620}
621
622fn cmp_generic_param_def(a: &ty::GenericParamDef, b: &ty::GenericParamDef) -> bool {
623    if a.name != b.name {
624        return false;
625    }
626    if a.index != b.index {
627        return false;
628    }
629    matches!(
630        (&a.kind, &b.kind),
631        (ty::GenericParamDefKind::Lifetime, ty::GenericParamDefKind::Lifetime)
632            | (ty::GenericParamDefKind::Type { .. }, ty::GenericParamDefKind::Type { .. })
633            | (ty::GenericParamDefKind::Const { .. }, ty::GenericParamDefKind::Const { .. })
634    )
635}
636
637fn ident_or_def_span(tcx: TyCtxt, def_id: impl Into<DefId>) -> Span {
638    let def_id = def_id.into();
639    tcx.def_ident_span(def_id)
640        .unwrap_or_else(|| tcx.def_span(def_id))
641}
642
643mod errors {
644    use flux_errors::E0999;
645    use flux_macros::Diagnostic;
646    use rustc_span::Span;
647
648    #[derive(Diagnostic)]
649    #[diag(driver_malformed_extern_spec, code = E0999)]
650    pub(super) struct MalformedExternSpec {
651        #[primary_span]
652        span: Span,
653    }
654
655    impl MalformedExternSpec {
656        pub(super) fn new(span: Span) -> Self {
657            Self { span }
658        }
659    }
660
661    #[derive(Diagnostic)]
662    #[diag(driver_invalid_enum_extern_spec, code = E0999)]
663    pub(super) struct InvalidEnumExternSpec {
664        #[primary_span]
665        span: Span,
666        reason: String,
667    }
668
669    impl InvalidEnumExternSpec {
670        pub(super) fn new(span: Span, reason: String) -> Self {
671            Self { span, reason }
672        }
673    }
674
675    #[derive(Diagnostic)]
676    #[diag(driver_cannot_resolve_trait_impl, code = E0999)]
677    #[note]
678    pub(super) struct CannotResolveTraitImpl {
679        #[primary_span]
680        pub span: Span,
681    }
682
683    #[derive(Diagnostic)]
684    #[diag(driver_invalid_impl_block, code = E0999)]
685    pub(super) struct InvalidImplBlock {
686        #[primary_span]
687        #[label]
688        pub span: Span,
689    }
690
691    #[derive(Diagnostic)]
692    #[diag(driver_item_not_in_trait_impl, code = E0999)]
693    pub(super) struct ItemNotInTraitImpl {
694        #[primary_span]
695        #[label]
696        pub span: Span,
697        pub name: String,
698        #[note]
699        pub extern_impl_span: Span,
700    }
701
702    #[derive(Diagnostic)]
703    #[diag(driver_invalid_item_in_inherent_impl, code = E0999)]
704    pub(super) struct InvalidItemInInherentImpl {
705        #[primary_span]
706        #[label]
707        pub span: Span,
708        pub name: String,
709        #[note]
710        pub extern_item_span: Span,
711    }
712
713    #[derive(Diagnostic)]
714    #[diag(driver_item_not_in_trait, code = E0999)]
715    pub(super) struct ItemNotInTrait {
716        #[primary_span]
717        #[label]
718        pub span: Span,
719        pub name: String,
720        #[note]
721        pub extern_trait_span: Span,
722    }
723
724    #[derive(Diagnostic)]
725    #[diag(driver_extern_spec_for_local_def, code = E0999)]
726    pub(super) struct ExternSpecForLocalDef {
727        #[primary_span]
728        pub span: Span,
729        #[help]
730        pub local_def_span: Span,
731        pub name: String,
732    }
733
734    #[derive(Diagnostic)]
735    #[diag(driver_dup_extern_spec, code = E0999)]
736    pub(super) struct DupExternSpec {
737        #[primary_span]
738        #[label]
739        pub span: Span,
740        #[note]
741        pub previous_span: Span,
742        pub name: String,
743    }
744
745    #[derive(Diagnostic)]
746    #[diag(driver_mismatched_generics, code = E0999)]
747    #[note]
748    pub(super) struct MismatchedGenerics {
749        #[primary_span]
750        #[label]
751        pub span: Span,
752        #[label(driver_extern_def_label)]
753        pub extern_def: Span,
754        pub def_descr: &'static str,
755    }
756
757    #[derive(Diagnostic)]
758    #[diag(driver_mismatched_impl_self_ty, code = E0999)]
759    #[note]
760    pub(super) struct MismatchedImplSelfTy {
761        #[primary_span]
762        #[label]
763        pub span: Span,
764        pub local_self_ty: String,
765        pub extern_self_ty: String,
766        #[label(driver_extern_impl_label)]
767        pub extern_impl_span: Span,
768    }
769}