1use itertools::Itertools;
3use rustc_middle::mir;
4use rustc_span::RemapPathScopeComponents;
5use std::{
6 cmp::Ord,
7 path::{Component, PathBuf},
8 sync::LazyLock,
9};
10
11use super::translate_crate::RustcItem;
12use super::translate_ctx::*;
13use crate::hax;
14use crate::hax::{DefPathItem, SInto};
15use charon_lib::{ast::*, name_matcher::NamePattern};
16
17impl<'tcx> TranslateCtx<'tcx> {
19 fn register_file(&mut self, filename: FileName, span: rustc_span::Span) -> FileId {
22 match self.file_to_id.get(&filename) {
24 Some(id) => *id,
25 None => {
26 let source_file = self.tcx.sess.source_map().lookup_source_file(span.lo());
27 let crate_name = self.tcx.crate_name(source_file.cnum).to_string();
28 let id = self.translated.files.push_with(|id| File {
29 id,
30 name: filename.clone(),
31 crate_name,
32 contents: source_file.src.as_deref().cloned(),
33 });
34 self.file_to_id.insert(filename, id);
35 id
36 }
37 }
38 }
39
40 pub fn translate_filename(&mut self, name: rustc_span::FileName) -> meta::FileName {
41 match name {
42 rustc_span::FileName::Real(name) => {
43 match name.local_path() {
44 Some(path) => {
45 let path: PathBuf = {
48 let mut normalized = PathBuf::new();
49 for comp in path.components() {
50 for segment in comp.as_os_str().to_string_lossy().split("\\") {
51 normalized.push(segment);
52 }
53 }
54 normalized
55 };
56 static CARGO_HOME: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
60 std::env::var("CARGO_HOME")
61 .map(PathBuf::from)
62 .ok()
63 .or_else(|| std::env::home_dir().map(|p| p.join(".cargo")))
64 });
65 let path = if let Some(rust_src) = path
70 .ancestors()
71 .find(|ancestor| ancestor.ends_with("lib/rustlib/src/rust"))
72 && let Ok(path) = path.strip_prefix(rust_src)
73 {
74 let mut rewritten_path: PathBuf = "/rustc".into();
75 rewritten_path.extend(path);
76 rewritten_path
77 } else if let Ok(path) = path.strip_prefix(&self.sysroot) {
78 let mut rewritten_path: PathBuf = "/toolchain".into();
80 rewritten_path.extend(path);
81 rewritten_path
82 } else if let Some(cargo_home) = &*CARGO_HOME
83 && let Ok(path) = path.strip_prefix(cargo_home)
84 {
85 let mut rewritten_path: PathBuf = "/cargo".into();
86 rewritten_path.extend(path);
87 rewritten_path
88 } else if let Ok(current_dir) = std::env::current_dir()
89 && let Ok(path) = path.strip_prefix(current_dir)
90 {
91 path.to_path_buf()
92 } else {
93 path
94 };
95 FileName::Local(path)
96 }
97 None => {
98 let virtual_name = name.path(RemapPathScopeComponents::MACRO);
102 let mut components_iter = virtual_name.components();
103 if let Some(
104 [
105 Component::RootDir,
106 Component::Normal(rustc),
107 Component::Normal(hash),
108 ],
109 ) = components_iter.by_ref().array_chunks().next()
110 && rustc.to_str() == Some("rustc")
111 && hash.len() == 40
112 {
113 let path_without_hash = [Component::RootDir, Component::Normal(rustc)]
114 .into_iter()
115 .chain(components_iter)
116 .collect();
117 FileName::Virtual(path_without_hash)
118 } else {
119 FileName::Virtual(virtual_name.into())
120 }
121 }
122 }
123 }
124 _ => FileName::NotReal(format!("{name:?}")),
127 }
128 }
129
130 pub fn translate_span_data(&mut self, span: rustc_span::Span) -> meta::SpanData {
131 let smap: &rustc_span::source_map::SourceMap = self.tcx.sess.psess.source_map();
132 let filename = smap.span_to_filename(span);
133 let filename = self.translate_filename(filename);
134 let file_id = match &filename {
135 FileName::NotReal(_) => {
136 unimplemented!();
138 }
139 FileName::Virtual(_) | FileName::Local(_) => self.register_file(filename, span),
140 };
141
142 let convert_loc = |pos: rustc_span::BytePos| -> Loc {
143 let loc = smap.lookup_char_pos(pos);
144 Loc {
145 line: loc.line,
146 col: loc.col_display,
147 }
148 };
149 let beg = convert_loc(span.lo());
150 let end = convert_loc(span.hi());
151
152 meta::SpanData { file_id, beg, end }
154 }
155
156 pub fn translate_span_from_source_info(
158 &mut self,
159 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
160 source_info: &mir::SourceInfo,
161 ) -> Span {
162 let data = self.translate_span_data(source_info.span);
164
165 let mut parent_span = None;
167 let mut scope_data = &source_scopes[source_info.scope];
168 while let Some(parent_scope) = scope_data.inlined_parent_scope {
169 scope_data = &source_scopes[parent_scope];
170 parent_span = Some(scope_data.span);
171 }
172
173 if let Some(parent_span) = parent_span {
174 let parent_span = self.translate_span_data(parent_span);
175 Span {
176 data: parent_span,
177 generated_from_span: Some(data),
178 }
179 } else {
180 Span {
181 data,
182 generated_from_span: None,
183 }
184 }
185 }
186
187 pub(crate) fn translate_span(&mut self, span: &rustc_span::Span) -> Span {
188 Span {
189 data: self.translate_span_data(*span),
190 generated_from_span: None,
191 }
192 }
193
194 pub(crate) fn def_span(&mut self, def_id: &hax::DefId) -> Span {
195 let span = def_id.def_span(&self.hax_state);
196 self.translate_span(&span)
197 }
198}
199
200impl<'tcx> TranslateCtx<'tcx> {
202 fn path_elem_for_def(
203 &mut self,
204 span: Span,
205 item: &RustcItem,
206 ) -> Result<Option<PathElem>, Error> {
207 let def_id = item.def_id();
208 let path_elem = def_id.path_item(&self.hax_state);
209 let disambiguator = Disambiguator::new(path_elem.disambiguator as usize);
212 let path_elem = match path_elem.data {
214 DefPathItem::CrateRoot { name, .. } => {
215 Some(PathElem::Ident(name.to_string(), disambiguator))
216 }
217 DefPathItem::TypeNs(symbol)
220 | DefPathItem::ValueNs(symbol)
221 | DefPathItem::MacroNs(symbol) => {
222 Some(PathElem::Ident(symbol.to_string(), disambiguator))
223 }
224 DefPathItem::Impl => {
225 let full_def = self.hax_def_for_item(item)?;
226 let impl_elem = match full_def.kind() {
230 hax::FullDefKind::InherentImpl { ty, .. } => {
232 let item_src =
236 TransItemSource::new(item.clone(), TransItemSourceKind::InherentImpl);
237 let mut bt_ctx = ItemTransCtx::new(item_src, None, self);
238 bt_ctx.translate_item_generics(
239 span,
240 &full_def,
241 &TransItemSourceKind::InherentImpl,
242 )?;
243 let ty = bt_ctx.translate_ty(span, ty)?;
244 ImplElem::Ty(Box::new(Binder {
245 kind: BinderKind::InherentImplBlock,
246 params: bt_ctx.into_generics(),
247 skip_binder: ty,
248 }))
249 }
250 hax::FullDefKind::TraitImpl { .. } => {
252 let impl_id = {
253 let item_src = TransItemSource::new(
254 item.clone(),
255 TransItemSourceKind::TraitImpl(TransImplSource::Normal),
256 );
257 self.register_and_enqueue(&None, item_src).unwrap()
258 };
259 ImplElem::Trait(impl_id)
260 }
261 _ => unreachable!(),
262 };
263
264 Some(PathElem::Impl(impl_elem))
265 }
266 DefPathItem::OpaqueTy => None,
268 DefPathItem::Closure => Some(PathElem::Ident("closure".to_string(), disambiguator)),
272 DefPathItem::ForeignMod => None,
275 DefPathItem::Ctor => None,
278 DefPathItem::Use => Some(PathElem::Ident("{use}".to_string(), disambiguator)),
279 DefPathItem::AnonConst => Some(PathElem::Ident("{const}".to_string(), disambiguator)),
280 DefPathItem::PromotedConst => Some(PathElem::Ident(
281 "{promoted_const}".to_string(),
282 disambiguator,
283 )),
284 _ => {
285 raise_error!(
286 self,
287 span,
288 "Unexpected DefPathItem for `{def_id:?}`: {path_elem:?}"
289 );
290 }
291 };
292 Ok(path_elem)
293 }
294
295 fn name_for_item(&mut self, item: &RustcItem) -> Result<Name, Error> {
339 if let Some(name) = self.cached_names.get(item) {
340 return Ok(name.clone());
341 }
342 let def_id = item.def_id();
343 trace!("Computing name for `{def_id:?}`");
344
345 let parent_name = if let Some(parent_id) = def_id.parent(&self.hax_state) {
346 let def = self.hax_def_for_item(item)?;
347 if matches!(item, RustcItem::Mono(..))
348 && let Some(parent_item) = def.typing_parent(&self.hax_state)
349 {
350 self.name_for_item(&RustcItem::Mono(parent_item.clone()))?
351 } else {
352 self.name_for_item(&RustcItem::Poly(parent_id.clone()))?
353 }
354 } else {
355 Name { name: Vec::new() }
356 };
357 let span = self.def_span(def_id);
358 let mut name = parent_name;
359 if let Some(path_elem) = self.path_elem_for_def(span, item)? {
360 name.name.push(path_elem);
361 }
362
363 trace!("Computed name for `{def_id:?}`: `{name:?}`");
364 self.cached_names.insert(item.clone(), name.clone());
365 Ok(name)
366 }
367
368 pub fn name_for_src(&mut self, src: &TransItemSource) -> Result<Name, Error> {
371 let mut name = if let Some(parent) = src.parent() {
372 self.name_for_src(&parent)?
373 } else {
374 self.name_for_item(&src.item)?
375 };
376 match &src.kind {
377 TransItemSourceKind::Type
379 | TransItemSourceKind::Fun
380 | TransItemSourceKind::Global
381 | TransItemSourceKind::TraitImpl(TransImplSource::Normal)
382 | TransItemSourceKind::TraitDecl
383 | TransItemSourceKind::InherentImpl
384 | TransItemSourceKind::Module => {}
385
386 TransItemSourceKind::TraitImpl(
387 kind @ (TransImplSource::Callable(..)
388 | TransImplSource::ImplicitDestruct
389 | TransImplSource::TraitAlias),
390 ) => {
391 if let TransImplSource::Callable(..) = kind {
392 let _ = name.name.pop(); }
394 let impl_id = self.register_and_enqueue(&None, src.clone()).unwrap();
395 name.name.push(PathElem::Impl(ImplElem::Trait(impl_id)));
396 }
397 TransItemSourceKind::CallableMethod(kind) => {
398 let fn_name = kind.method_name().to_string();
399 name.name
400 .push(PathElem::Ident(fn_name, Disambiguator::ZERO));
401 }
402 TransItemSourceKind::DropGlueMethod(..) => {
403 name.name.push(PathElem::Ident(
404 "drop_glue".to_string(),
405 Disambiguator::ZERO,
406 ));
407 }
408 TransItemSourceKind::ClosureAsFnCast => {
409 name.name
410 .push(PathElem::Ident("as_fn".into(), Disambiguator::ZERO));
411 }
412 TransItemSourceKind::VTable
413 | TransItemSourceKind::VTableInstance(..)
414 | TransItemSourceKind::VTableInstanceInitializer(..) => {
415 name.name
416 .push(PathElem::Ident("{vtable}".into(), Disambiguator::ZERO));
417 }
418 TransItemSourceKind::VTableMethod => {
419 name.name.push(PathElem::Ident(
420 "{vtable_method}".into(),
421 Disambiguator::ZERO,
422 ));
423 }
424 TransItemSourceKind::VTableDropShim => {
425 name.name.push(PathElem::Ident(
426 "{vtable_drop_shim}".into(),
427 Disambiguator::ZERO,
428 ));
429 }
430 }
431 Ok(name)
432 }
433
434 pub fn translate_name(&mut self, src: &TransItemSource) -> Result<Name, Error> {
436 let mut name = self.name_for_src(src)?;
437 if let RustcItem::Mono(item_ref) = &src.item
440 && !item_ref.generic_args.is_empty()
441 && !matches!(src.kind, TransItemSourceKind::TraitImpl(..))
442 {
443 let trans_id = self.register_no_enqueue(&None, src).unwrap();
444 let span = self.def_span(&item_ref.def_id);
445 let mut bt_ctx = ItemTransCtx::new(src.clone(), trans_id, self);
446 let binder = bt_ctx.inside_binder(BinderKind::Other, |bt_ctx| {
447 bt_ctx.translate_generic_args(span, &item_ref.generic_args, &[])
449 })?;
450 if !binder.skip_binder.is_empty() {
451 name.name.push(PathElem::Instantiated(Box::new(binder)));
452 }
453 }
454 Ok(name)
455 }
456
457 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
458 self.options.opacity_for_name(&self.translated, name)
459 }
460}
461
462enum ContractTarget {
463 Parent,
464 Path(String),
465}
466
467impl<'tcx> TranslateCtx<'tcx> {
469 fn resolve_contract_target(
470 &mut self,
471 def_id: &hax::DefId,
472 target: ContractTarget,
473 ) -> Result<MaybeAssocItemId, String> {
474 if !matches!(
475 def_id.kind,
476 hax::DefKind::Fn | hax::DefKind::AssocFn | hax::DefKind::Closure
477 ) {
478 return Err("contract attributes can only be applied to functions".to_string());
479 }
480
481 let parent_id = def_id.parent(&self.hax_state);
482 let target_def_id = match &target {
483 ContractTarget::Parent => parent_id.ok_or_else(|| {
484 "#[charon::contract(..., parent)] is invalid at the crate root".to_string()
485 })?,
486 ContractTarget::Path(target_name) => {
487 let mut siblings = Vec::new();
488 if let Some(parent_id) = &parent_id {
489 let parent_def = self.poly_hax_def(parent_id).map_err(|err| err.msg)?;
490 siblings.extend(
491 parent_def
492 .nameable_children(&self.hax_state)
493 .into_iter()
494 .map(|(_, id)| id),
495 );
496
497 if matches!(
499 parent_def.kind(),
500 hax::FullDefKind::Fn { .. }
501 | hax::FullDefKind::AssocFn { .. }
502 | hax::FullDefKind::Closure { .. }
503 ) && let Some(parent_local_id) =
504 parent_id.as_real_def_id().and_then(|id| id.as_local())
505 && let Some(body_id) =
506 self.tcx.hir_node_by_def_id(parent_local_id).body_id()
507 {
508 use rustc_hir::intravisit;
509
510 struct NestedItems(Vec<rustc_hir::def_id::LocalDefId>);
511 impl<'tcx> intravisit::Visitor<'tcx> for NestedItems {
512 fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
513 self.0.push(id.owner_id.def_id);
514 }
515 }
516
517 let mut nested_items = NestedItems(Vec::new());
518 intravisit::walk_body(&mut nested_items, self.tcx.hir_body(body_id));
519 siblings.extend(
520 nested_items
521 .0
522 .into_iter()
523 .map(|id| id.to_def_id().sinto(&self.hax_state)),
524 );
525 }
526 }
527 let siblings = siblings
528 .into_iter()
529 .filter(|sibling| sibling != def_id)
530 .filter(|sibling| match sibling.path_item(&self.hax_state).data {
531 DefPathItem::ValueNs(name)
532 | DefPathItem::TypeNs(name)
533 | DefPathItem::MacroNs(name) => name.as_str() == target_name.as_str(),
534 _ => false,
535 })
536 .collect_vec();
537 match siblings.as_slice() {
538 [sibling] => sibling.clone(),
539 [] => {
540 let path = NamePattern::parse(target_name)
542 .map_err(|err| format!("invalid item path `{target_name}`: {err}"))?;
543 let targets =
544 super::resolve_path::def_path_def_ids(&self.hax_state, &path, true)
545 .map_err(|err| {
546 format!("failed to resolve item path `{target_name}`: {err}")
547 })?;
548 let [target] = targets.as_slice() else {
549 return Err(format!(
550 "item path `{target_name}` resolved to {} items; expected exactly one",
551 targets.len()
552 ));
553 };
554 target.sinto(&self.hax_state)
555 }
556 _ => {
557 return Err(format!(
558 "found several sibling items named `{target_name}`; expected exactly one"
559 ));
560 }
561 }
562 }
563 };
564
565 let target_def = self.poly_hax_def(&target_def_id).map_err(|err| err.msg)?;
566 if self.options.monomorphize_with_hax && target_def.this().has_non_lt_param {
567 return Err("contracts on generic items are not supported \
568 with `--monomorphize`"
569 .to_string());
570 }
571
572 if let ContractTarget::Path(_) = target
573 && let hax::FullDefKind::AssocFn {
574 associated_item, ..
575 }
576 | hax::FullDefKind::AssocConst {
577 associated_item, ..
578 }
579 | hax::FullDefKind::AssocTy {
580 associated_item, ..
581 } = target_def.kind()
582 && let hax::AssocItemContainer::TraitContainer { trait_ref } =
583 &associated_item.container
584 {
585 let kind = TransItemSourceKind::TraitDecl;
586 let trait_src =
587 TransItemSource::from_item(trait_ref, kind, self.options.monomorphize_with_hax);
588 let trait_id = self.register_and_enqueue(&None, trait_src).unwrap();
589 let item_id = self
590 .translate_assoc_item_id(trait_id, &target_def_id)
591 .map_err(|err| err.msg)?;
592 Ok(MaybeAssocItemId::Assoc(trait_id, item_id))
593 } else {
594 let kind = match target_def.kind() {
595 hax::FullDefKind::Closure { args, .. } => TransItemSourceKind::CallableMethod(
597 super::translate_closures::translate_closure_kind(&args.kind),
598 ),
599 _ => self
600 .base_kind_for_item(&target_def_id)
601 .ok_or_else(|| format!("`{target_def_id:?}` is not a translatable item"))?,
602 };
603 let target_src = TransItemSource::from_item(
604 target_def.this(),
605 kind,
606 self.options.monomorphize_with_hax,
607 );
608 let item_id: ItemId = self.register_and_enqueue(&None, target_src).unwrap();
609 Ok(MaybeAssocItemId::Free(item_id))
610 }
611 }
612
613 fn parse_attr_from_raw(
615 &mut self,
616 def_id: &hax::DefId,
617 raw_attr: RawAttribute,
618 ) -> Result<Attribute, String> {
619 let path = raw_attr.path.split("::").collect_vec();
622 let attr_name = if let &[path_start, attr_name] = path.as_slice()
623 && (path_start == "charon" || path_start == "aeneas" || path_start == "verify")
624 {
625 attr_name
626 } else {
627 return Ok(Attribute::Unknown(raw_attr));
628 };
629
630 match self.parse_special_attr(def_id, attr_name, &raw_attr)? {
631 Some(parsed) => Ok(parsed),
632 None => Err(format!("Unrecognized attribute: `{}`", raw_attr)),
633 }
634 }
635
636 fn parse_special_attr(
638 &mut self,
639 def_id: &hax::DefId,
640 attr_name: &str,
641 raw_attr: &RawAttribute,
642 ) -> Result<Option<Attribute>, String> {
643 let args = raw_attr.args.as_deref();
644 let parsed = match attr_name {
645 "opaque" if args.is_none() => Attribute::Opaque,
647 "exclude" if args.is_none() => Attribute::Exclude,
649 "transparent" if args.is_none() => Attribute::Transparent,
651 "contract" if let Some(args) = args => {
654 use syn::{ext::IdentExt, parse::Parser};
655
656 let parser = |input: syn::parse::ParseStream<'_>| {
657 let mut kind = None;
658 let mut target = None;
659 while !input.is_empty() {
660 let key = input.call(syn::Ident::parse_any)?;
661 let key_name = key.to_string();
662 if key_name == "parent" && !input.peek(syn::Token![=]) {
663 if target.is_some() {
664 return Err(syn::Error::new(
665 key.span(),
666 "duplicate contract target",
667 ));
668 }
669 target = Some(ContractTarget::Parent);
670 } else {
671 input.parse::<syn::Token![=]>()?;
672 let value = input.parse::<syn::LitStr>()?.value();
673 match key_name.as_str() {
674 "kind" => {
675 if kind.is_some() {
676 return Err(syn::Error::new(
677 key.span(),
678 "duplicate contract argument `kind`",
679 ));
680 }
681 kind = Some(value);
682 }
683 "for" => {
684 if target.is_some() {
685 return Err(syn::Error::new(
686 key.span(),
687 "duplicate contract target",
688 ));
689 }
690 target = Some(ContractTarget::Path(value));
691 }
692 "parent" => {
693 return Err(syn::Error::new(
694 key.span(),
695 "contract argument `parent` does not take a value",
696 ));
697 }
698 _ => {
699 return Err(syn::Error::new(
700 key.span(),
701 format!("unknown contract argument `{key}`"),
702 ));
703 }
704 }
705 }
706 if !input.is_empty() {
707 input.parse::<syn::Token![,]>()?;
708 }
709 }
710 let kind =
711 kind.ok_or_else(|| input.error("missing contract argument `kind`"))?;
712 let target = target
713 .ok_or_else(|| input.error("missing contract target `parent` or `for`"))?;
714 Ok((kind, target))
715 };
716 let (kind, target) = parser.parse_str(args).map_err(|err| {
717 format!(
718 "invalid contract syntax: {err}; expected \
719 `#[charon::contract(kind = \"...\", parent)]` or \
720 `#[charon::contract(kind = \"...\", for = \"item path\")]`"
721 )
722 })?;
723 Attribute::IsContract {
724 kind,
725 target: self.resolve_contract_target(def_id, target)?,
726 }
727 }
728 "rename" if let Some(attr) = args => {
730 let Some(attr) = attr
731 .strip_prefix("\"")
732 .and_then(|attr| attr.strip_suffix("\""))
733 else {
734 return Err(format!(
735 "the new name should be between quotes: `rename(\"{attr}\")`."
736 ));
737 };
738
739 if attr.is_empty() {
740 return Err(format!("attribute `rename` should not be empty"));
741 }
742
743 let first_char = attr.chars().nth(0).unwrap();
744 let is_identifier = (first_char.is_alphabetic() || first_char == '_')
745 && attr.chars().all(|c| c.is_alphanumeric() || c == '_');
746 if !is_identifier {
747 return Err(format!(
748 "attribute `rename` should contain a valid identifier"
749 ));
750 }
751
752 Attribute::Rename(attr.to_string())
753 }
754 "variants_prefix" if let Some(attr) = args => {
756 let Some(attr) = attr
757 .strip_prefix("\"")
758 .and_then(|attr| attr.strip_suffix("\""))
759 else {
760 return Err(format!(
761 "the name should be between quotes: `variants_prefix(\"{attr}\")`."
762 ));
763 };
764
765 Attribute::VariantsPrefix(attr.to_string())
766 }
767 "variants_suffix" if let Some(attr) = args => {
769 let Some(attr) = attr
770 .strip_prefix("\"")
771 .and_then(|attr| attr.strip_suffix("\""))
772 else {
773 return Err(format!(
774 "the name should be between quotes: `variants_suffix(\"{attr}\")`."
775 ));
776 };
777
778 Attribute::VariantsSuffix(attr.to_string())
779 }
780 "start_from" => {
782 if matches!(def_id.kind, hax::DefKind::Mod) {
783 return Err("`start_from` on modules has no effect".to_string());
784 }
785 Attribute::Unknown(raw_attr.clone())
786 }
787 "test" if args.is_none() => Attribute::Unknown(raw_attr.clone()),
789 _ => return Ok(None),
790 };
791 Ok(Some(parsed))
792 }
793
794 pub(crate) fn translate_attribute(
797 &mut self,
798 def_id: &hax::DefId,
799 attr: &rustc_hir::Attribute,
800 ) -> Option<Attribute> {
801 use rustc_hir as hir;
802 use rustc_hir::attrs as hir_attrs;
803 match attr {
804 hir::Attribute::Parsed(hir_attrs::AttributeKind::DocComment { comment, .. }) => {
805 Some(Attribute::DocComment(comment.to_string()))
806 }
807 hir::Attribute::Parsed(attr) => self
808 .translate_rustc_attribute_kind(attr)
809 .ok()
810 .map(Attribute::Builtin),
811 hir::Attribute::Unparsed(attr) => {
812 let raw_attr = RawAttribute {
813 path: attr.path.to_string(),
814 args: match &attr.args {
815 hir::AttrArgs::Empty => None,
816 hir::AttrArgs::Delimited(args) => {
817 Some(rustc_ast_pretty::pprust::tts_to_string(&args.tokens))
818 }
819 hir::AttrArgs::Eq { expr, .. } => {
820 self.tcx.sess.source_map().span_to_snippet(expr.span).ok()
821 }
822 },
823 };
824 match self.parse_attr_from_raw(def_id, raw_attr) {
825 Ok(a) => Some(a),
826 Err(msg) => {
827 let span = self.translate_span(&attr.span.sinto(&self.hax_state));
828 register_error!(self, span, "Error parsing attribute: {msg}");
829 None
830 }
831 }
832 }
833 }
834 }
835
836 pub(crate) fn translate_inline(&self, def: &hax::FullDef<'tcx>) -> Option<InlineAttr> {
837 match def.kind() {
838 hax::FullDefKind::Fn { inline, .. }
839 | hax::FullDefKind::AssocFn { inline, .. }
840 | hax::FullDefKind::Closure { inline, .. } => match inline {
841 hax::InlineAttr::None => None,
842 hax::InlineAttr::Hint => Some(InlineAttr::Hint),
843 hax::InlineAttr::Never => Some(InlineAttr::Never),
844 hax::InlineAttr::Always => Some(InlineAttr::Always),
845 hax::InlineAttr::Force { .. } => Some(InlineAttr::Always),
846 },
847 _ => None,
848 }
849 }
850
851 pub(crate) fn translate_attr_info(&mut self, def: &hax::FullDef<'tcx>) -> AttrInfo {
852 let public = def.visibility.unwrap_or(false);
854 let inline = self.translate_inline(def);
855 let attributes = def
856 .attributes
857 .iter()
858 .filter_map(|attr| self.translate_attribute(def.def_id(), attr))
859 .collect_vec();
860
861 let rename = {
862 let mut renames = attributes.iter().filter_map(|a| a.as_rename()).cloned();
863 let rename = renames.next();
864 if renames.next().is_some() {
865 let span = self.translate_span(&def.span);
866 register_error!(
867 self,
868 span,
869 "There should be at most one `charon::rename(\"...\")` \
870 or `aeneas::rename(\"...\")` attribute per declaration",
871 );
872 }
873 rename
874 };
875
876 AttrInfo {
877 attributes,
878 inline,
879 public,
880 rename,
881 }
882 }
883}
884
885impl<'tcx> TranslateCtx<'tcx> {
887 pub(crate) fn is_extern_item(&mut self, def: &hax::FullDef<'tcx>) -> bool {
889 def.def_id()
890 .parent(&self.hax_state)
891 .is_some_and(|parent| matches!(parent.kind, hax::DefKind::ForeignMod))
892 }
893
894 pub(crate) fn extern_item_symbol_name(&mut self, def: &hax::FullDef<'tcx>) -> Option<String> {
896 if !self.is_extern_item(def) {
897 return None;
898 }
899 let path_item = def.def_id().path_item(&self.hax_state);
900 match path_item.data {
901 hax::DefPathItem::ValueNs(name) | hax::DefPathItem::TypeNs(name) => {
902 Some(name.to_string())
903 }
904 _ => None,
905 }
906 }
907
908 pub(crate) fn translate_item_meta(
910 &mut self,
911 def: &hax::FullDef<'tcx>,
912 item_src: &TransItemSource,
913 name: Name,
914 name_opacity: ItemOpacity,
915 ) -> ItemMeta {
916 if let Some(item_meta) = self.cached_item_metas.get(item_src) {
917 return item_meta.clone();
918 }
919 let span = def.source_span.as_ref().unwrap_or(&def.span);
920 let span = self.translate_span(span);
921 let is_local = def.def_id().is_local();
922 let (attr_info, lang_item, diagnostic_item) = if !item_src.is_derived_item()
923 || matches!(item_src.kind, TransItemSourceKind::CallableMethod(..))
924 {
925 let attr_info = self.translate_attr_info(def);
926 let lang_item = def
927 .def_id()
928 .as_real_def_id()
929 .and_then(|id| self.tcx.as_lang_item(id))
930 .map(|lang_item| {
931 self.translate_rustc_lang_item(&lang_item)
932 .expect("all rustc LangItem variants should be translated")
933 });
934 let diagnostic_item = def.diagnostic_item.map(|s| s.to_string());
935 (attr_info, lang_item, diagnostic_item)
936 } else {
937 (AttrInfo::default(), None, None)
938 };
939
940 let opacity = if attr_info.attributes.iter().any(|attr| attr.is_exclude()) {
941 ItemOpacity::Invisible.max(name_opacity)
942 } else if self.is_extern_item(def)
943 || attr_info.attributes.iter().any(|attr| attr.is_opaque())
944 {
945 ItemOpacity::Opaque.max(name_opacity)
947 } else {
948 name_opacity
949 };
950
951 let item_meta = ItemMeta {
952 name,
953 span,
954 source_text: def.source_text.clone(),
955 attr_info,
956 is_local,
957 opacity,
958 lang_item,
959 diagnostic_item,
960 };
961 self.cached_item_metas
962 .insert(item_src.clone(), item_meta.clone());
963 item_meta
964 }
965}