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 CURRENT_DIR: LazyLock<Option<PathBuf>> =
60 LazyLock::new(|| std::env::current_dir().ok());
61 static CARGO_HOME: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
62 std::env::var("CARGO_HOME")
63 .map(PathBuf::from)
64 .ok()
65 .or_else(|| std::env::home_dir().map(|p| p.join(".cargo")))
66 });
67 let path = if let Some(rust_src) = path
72 .ancestors()
73 .find(|ancestor| ancestor.ends_with("lib/rustlib/src/rust"))
74 && let Ok(path) = path.strip_prefix(rust_src)
75 {
76 let mut rewritten_path: PathBuf = "/rustc".into();
77 rewritten_path.extend(path);
78 rewritten_path
79 } else if let Ok(path) = path.strip_prefix(&self.sysroot) {
80 let mut rewritten_path: PathBuf = "/toolchain".into();
82 rewritten_path.extend(path);
83 rewritten_path
84 } else if let Some(cargo_home) = &*CARGO_HOME
85 && let Ok(path) = path.strip_prefix(cargo_home)
86 {
87 let mut rewritten_path: PathBuf = "/cargo".into();
88 rewritten_path.extend(path);
89 rewritten_path
90 } else if let Some(current_dir) = &*CURRENT_DIR
91 && let Ok(path) = path.strip_prefix(current_dir)
92 {
93 path.to_path_buf()
94 } else {
95 path
96 };
97 FileName::Local(path)
98 }
99 None => {
100 let virtual_name = name.path(RemapPathScopeComponents::MACRO);
104 let mut components_iter = virtual_name.components();
105 if let Some(
106 [
107 Component::RootDir,
108 Component::Normal(rustc),
109 Component::Normal(hash),
110 ],
111 ) = components_iter.by_ref().array_chunks().next()
112 && rustc.to_str() == Some("rustc")
113 && hash.len() == 40
114 {
115 let path_without_hash = [Component::RootDir, Component::Normal(rustc)]
116 .into_iter()
117 .chain(components_iter)
118 .collect();
119 FileName::Virtual(path_without_hash)
120 } else {
121 FileName::Virtual(virtual_name.into())
122 }
123 }
124 }
125 }
126 _ => FileName::NotReal(format!("{name:?}")),
129 }
130 }
131
132 pub fn translate_span_data(&mut self, span: rustc_span::Span) -> meta::SpanData {
133 if let Some(data) = self.cached_spans.get(&span) {
134 return *data;
135 }
136 let data = self.translate_span_data_uncached(span);
137 self.cached_spans.insert(span, data);
138 data
139 }
140
141 fn translate_span_data_uncached(&mut self, span: rustc_span::Span) -> meta::SpanData {
142 let smap: &rustc_span::source_map::SourceMap = self.tcx.sess.psess.source_map();
143 let source_file = smap.lookup_source_file(span.lo());
144 let file_id = match self.cached_file_ids.get(&source_file.stable_id) {
145 Some(id) => *id,
146 None => {
147 let filename = self.translate_filename(source_file.name.clone());
148 let file_id = match &filename {
149 FileName::NotReal(_) => {
150 unimplemented!();
152 }
153 FileName::Virtual(_) | FileName::Local(_) => self.register_file(filename, span),
154 };
155 self.cached_file_ids.insert(source_file.stable_id, file_id);
156 file_id
157 }
158 };
159
160 let convert_loc = |pos: rustc_span::BytePos| -> Loc {
161 let (line, _col, col_display) = source_file.lookup_file_pos_with_col_display(pos);
162 Loc {
163 line: line as u32,
164 col: col_display as u32,
165 }
166 };
167 let beg = convert_loc(span.lo());
168 let end = convert_loc(span.hi());
169
170 meta::SpanData { file_id, beg, end }
172 }
173
174 pub fn translate_span_from_source_info(
176 &mut self,
177 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
178 source_info: &mir::SourceInfo,
179 ) -> Span {
180 let mut parent_span = None;
182 let mut scope_data = &source_scopes[source_info.scope];
183 while let Some(parent_scope) = scope_data.inlined_parent_scope {
184 scope_data = &source_scopes[parent_scope];
185 parent_span = Some(scope_data.span);
186 }
187
188 if let Some(parent_span) = parent_span {
189 let data = self.translate_span_data(source_info.span);
190 let parent_span = self.translate_span_data(parent_span);
191 Span::new(parent_span, Some(data))
192 } else {
193 self.translate_span(&source_info.span)
194 }
195 }
196
197 pub(crate) fn translate_span(&mut self, span: &rustc_span::Span) -> Span {
198 let macro_call = span
202 .macro_backtrace()
203 .filter(|expn| matches!(expn.kind, rustc_span::ExpnKind::Macro(..)))
204 .last();
205 let data = self.translate_span_data(*span);
206 match macro_call {
207 Some(expn) => {
208 if expn.call_site.source_equal(*span) {
210 Span::new(data, None)
211 } else {
212 Span::new(self.translate_span_data(expn.call_site), Some(data))
213 }
214 }
215 None => Span::new(data, None),
216 }
217 }
218
219 pub(crate) fn def_span(&mut self, def_id: &hax::DefId) -> Span {
220 let span = def_id.def_span(&self.hax_state);
221 self.translate_span(&span)
222 }
223}
224
225impl<'tcx> TranslateCtx<'tcx> {
227 fn path_elem_for_def(
228 &mut self,
229 span: Span,
230 item: &RustcItem<'tcx>,
231 ) -> Result<Option<PathElem>, Error> {
232 let def_id = item.def_id();
233 if let Some(synthetic) = def_id.as_synthetic(&self.hax_state) {
234 return Ok(match synthetic {
235 hax::SyntheticItem::Tuple(n) => Some(PathElem::Builtin(
236 BuiltinPathElem::Tuple(n),
237 Disambiguator::ZERO,
238 )),
239 hax::SyntheticItem::Str => {
240 Some(PathElem::Builtin(BuiltinPathElem::Str, Disambiguator::ZERO))
241 }
242 hax::SyntheticItem::Array
243 | hax::SyntheticItem::Slice
244 | hax::SyntheticItem::FnPtr(_) => None,
245 });
246 }
247 let path_elem = def_id.path_item(&self.hax_state);
248 let disambiguator = Disambiguator::new(path_elem.disambiguator as usize);
251 let path_elem = match path_elem.data {
253 DefPathItem::CrateRoot { name, .. } => {
254 Some(PathElem::Ident(name.to_string(), disambiguator))
255 }
256 DefPathItem::TypeNs(symbol)
259 | DefPathItem::ValueNs(symbol)
260 | DefPathItem::MacroNs(symbol) => {
261 Some(PathElem::Ident(symbol.to_string(), disambiguator))
262 }
263 DefPathItem::Impl => {
264 let impl_elem = match def_id.kind {
268 hax::DefKind::Impl { of_trait: false } => {
270 let full_def = self.hax_def_for_item(item)?;
271 let hax::FullDefKind::InherentImpl(i) = full_def.kind() else {
272 unreachable!()
273 };
274 let item_src =
278 TransItemSource::new(item.clone(), TransItemSourceKind::InherentImpl);
279 let item_id = ItemOrModuleId::Module(item_src.def_id().real_rust_def_id());
280 let mut bt_ctx = ItemTransCtx::new(item_src, item_id, self);
281 bt_ctx.translate_item_generics(
282 span,
283 &full_def,
284 &TransItemSourceKind::InherentImpl,
285 )?;
286 let ty = bt_ctx.translate_ty(span, i.ty())?;
287 ImplElem::Ty(Box::new(Binder {
288 kind: BinderKind::InherentImplBlock,
289 params: bt_ctx.into_generics(),
290 skip_binder: ty,
291 }))
292 }
293 hax::DefKind::Impl { of_trait: true } => {
295 let impl_id = {
296 let item_src = TransItemSource::new(
297 item.clone(),
298 TransItemSourceKind::TraitImpl(TransImplSource::Normal),
299 );
300 self.register_and_enqueue(&None, item_src).expect_item()
301 };
302 ImplElem::Trait(impl_id)
303 }
304 _ => unreachable!(),
305 };
306
307 Some(PathElem::Impl(impl_elem))
308 }
309 DefPathItem::OpaqueTy => None,
311 DefPathItem::Closure => {
315 Some(PathElem::Builtin(BuiltinPathElem::Closure, disambiguator))
316 }
317 DefPathItem::ForeignMod => None,
320 DefPathItem::Ctor => None,
323 DefPathItem::Use => Some(PathElem::Builtin(BuiltinPathElem::Use, disambiguator)),
324 DefPathItem::AnonConst => {
325 Some(PathElem::Builtin(BuiltinPathElem::AnonConst, disambiguator))
326 }
327 DefPathItem::PromotedConst => Some(PathElem::Builtin(
328 BuiltinPathElem::PromotedConst,
329 disambiguator,
330 )),
331 _ => {
332 raise_error!(
333 self,
334 span,
335 "Unexpected DefPathItem for `{def_id:?}`: {path_elem:?}"
336 );
337 }
338 };
339 Ok(path_elem)
340 }
341
342 fn name_for_item(&mut self, item: &RustcItem<'tcx>) -> Result<Name, Error> {
386 if let Some(name) = self.cached_names.get(item) {
387 return Ok(name.clone());
388 }
389 let def_id = item.def_id();
390 trace!("Computing name for `{def_id:?}`");
391
392 let is_builtin = def_id.as_synthetic(&self.hax_state).is_some();
393 let parent_name = if !is_builtin && let Some(parent_id) = def_id.parent(&self.hax_state) {
394 if let RustcItem::Mono(item_ref) = item
395 && let Some(parent_item) = item_ref.typing_parent(&self.hax_state)
396 {
397 self.name_for_item(&RustcItem::Mono(parent_item))?
398 } else {
399 self.name_for_item(&RustcItem::Poly(parent_id.clone()))?
400 }
401 } else {
402 Name { name: Vec::new() }
403 };
404 let span = self.def_span(def_id);
405 let mut name = parent_name;
406 if let Some(path_elem) = self.path_elem_for_def(span, item)? {
407 name.name.push(path_elem);
408 }
409
410 trace!("Computed name for `{def_id:?}`: `{name:?}`");
411 self.cached_names.insert(item.clone(), name.clone());
412 Ok(name)
413 }
414
415 pub fn name_for_src(&mut self, src: &TransItemSource<'tcx>) -> Result<Name, Error> {
418 let mut name = if let Some(parent) = src.parent() {
419 self.name_for_src(&parent)?
420 } else {
421 self.name_for_item(&src.item)?
422 };
423 match &src.kind {
424 TransItemSourceKind::Type
426 | TransItemSourceKind::Fun
427 | TransItemSourceKind::Global
428 | TransItemSourceKind::TraitImpl(TransImplSource::Normal)
429 | TransItemSourceKind::TraitDecl
430 | TransItemSourceKind::InherentImpl
431 | TransItemSourceKind::Module => {}
432
433 TransItemSourceKind::TraitImpl(
434 kind @ (TransImplSource::Callable(..)
435 | TransImplSource::ImplicitDestruct
436 | TransImplSource::TraitAlias),
437 ) => {
438 if let TransImplSource::Callable(..) = kind {
439 let _ = name.name.pop(); }
441 let impl_id = self.register_and_enqueue(&None, src.clone()).expect_item();
442 name.name.push(PathElem::Impl(ImplElem::Trait(impl_id)));
443 }
444 TransItemSourceKind::TraitImpl(TransImplSource::Marker) => {
445 unreachable!("marker impls are only used as vtable item sources")
446 }
447 TransItemSourceKind::CallableMethod(kind) => {
448 let fn_name = kind.method_name().to_string();
449 name.name
450 .push(PathElem::Ident(fn_name, Disambiguator::ZERO));
451 }
452 TransItemSourceKind::DropGlueMethod(..) => {
453 name.name.push(PathElem::Builtin(
454 BuiltinPathElem::DropGlue,
455 Disambiguator::ZERO,
456 ));
457 }
458 TransItemSourceKind::ClosureAsFnCast => {
459 name.name.push(PathElem::Builtin(
460 BuiltinPathElem::ClosureAsFn,
461 Disambiguator::ZERO,
462 ));
463 }
464 TransItemSourceKind::VTable | TransItemSourceKind::VTableInstance(..) => {
465 name.name.push(PathElem::Builtin(
466 BuiltinPathElem::VTable,
467 Disambiguator::ZERO,
468 ));
469 }
470 TransItemSourceKind::VTableMethod(..) => {
471 name.name.push(PathElem::Builtin(
472 BuiltinPathElem::VTableMethod,
473 Disambiguator::ZERO,
474 ));
475 }
476 TransItemSourceKind::VTableDropShim(..) => {
477 name.name.push(PathElem::Builtin(
478 BuiltinPathElem::VTableDropShim,
479 Disambiguator::ZERO,
480 ));
481 }
482 TransItemSourceKind::IntrinsicFallback => {
483 name.name.push(PathElem::Builtin(
484 BuiltinPathElem::IntrinsicFallback,
485 Disambiguator::ZERO,
486 ));
487 }
488 }
489 Ok(name)
490 }
491
492 pub fn translate_name(&mut self, src: &TransItemSource<'tcx>) -> Result<Name, Error> {
494 let mut name = self.name_for_src(src)?;
495 if let RustcItem::Mono(item_ref) = &src.item
498 && !item_ref.generic_args.is_empty()
499 && !matches!(src.kind, TransItemSourceKind::TraitImpl(..))
500 {
501 let trans_id = self.register_no_enqueue(&None, src);
502 let span = self.def_span(&item_ref.def_id);
503 let mut bt_ctx = ItemTransCtx::new(src.clone(), trans_id, self);
504 let binder = bt_ctx.inside_binder(BinderKind::Other, None, |bt_ctx| {
505 bt_ctx.translate_generic_args_without_trait_refs(span, item_ref)
507 })?;
508 if !binder.skip_binder.is_empty() {
509 name.name.push(PathElem::Instantiated(Box::new(binder)));
510 }
511 }
512 Ok(name)
513 }
514
515 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
516 self.options.opacity_for_name(&self.translated, name)
517 }
518}
519
520enum ContractTarget {
521 Parent,
522 Path(String),
523}
524
525impl<'tcx> TranslateCtx<'tcx> {
527 fn resolve_contract_target(
528 &mut self,
529 def_id: &hax::DefId,
530 target: ContractTarget,
531 ) -> Result<MaybeAssocItemId, String> {
532 if !matches!(
533 def_id.kind,
534 hax::DefKind::Fn | hax::DefKind::AssocFn | hax::DefKind::Closure
535 ) {
536 return Err("contract attributes can only be applied to functions".to_string());
537 }
538
539 let parent_id = def_id.parent(&self.hax_state);
540 let target_def_id = match &target {
541 ContractTarget::Parent => parent_id.ok_or_else(|| {
542 "#[charon::contract(..., parent)] is invalid at the crate root".to_string()
543 })?,
544 ContractTarget::Path(target_name) => {
545 let mut siblings = Vec::new();
546 if let Some(parent_id) = &parent_id {
547 let parent_def = self.poly_hax_def(parent_id).map_err(|err| err.msg)?;
548 siblings.extend(
549 parent_def
550 .nameable_children(&self.hax_state)
551 .into_iter()
552 .map(|(_, id)| id),
553 );
554
555 if matches!(
557 parent_def.kind(),
558 hax::FullDefKind::Fn(_)
559 | hax::FullDefKind::AssocFn(_)
560 | hax::FullDefKind::Closure(_)
561 ) && let Some(parent_local_id) =
562 parent_id.as_real_def_id().and_then(|id| id.as_local())
563 && let Some(body_id) =
564 self.tcx.hir_node_by_def_id(parent_local_id).body_id()
565 {
566 use rustc_hir::intravisit;
567
568 struct NestedItems(Vec<rustc_hir::def_id::LocalDefId>);
569 impl<'tcx> intravisit::Visitor<'tcx> for NestedItems {
570 fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
571 self.0.push(id.owner_id.def_id);
572 }
573 }
574
575 let mut nested_items = NestedItems(Vec::new());
576 intravisit::walk_body(&mut nested_items, self.tcx.hir_body(body_id));
577 siblings.extend(
578 nested_items
579 .0
580 .into_iter()
581 .map(|id| id.to_def_id().sinto(&self.hax_state)),
582 );
583 }
584 }
585 let siblings = siblings
586 .into_iter()
587 .filter(|sibling| sibling != def_id)
588 .filter(|sibling| match sibling.path_item(&self.hax_state).data {
589 DefPathItem::ValueNs(name)
590 | DefPathItem::TypeNs(name)
591 | DefPathItem::MacroNs(name) => name.as_str() == target_name.as_str(),
592 _ => false,
593 })
594 .collect_vec();
595 match siblings.as_slice() {
596 [sibling] => sibling.clone(),
597 [] => {
598 let path = NamePattern::parse(target_name)
600 .map_err(|err| format!("invalid item path `{target_name}`: {err}"))?;
601 let targets =
602 super::resolve_path::def_path_def_ids(&self.hax_state, &path, true)
603 .map_err(|err| {
604 format!("failed to resolve item path `{target_name}`: {err}")
605 })?;
606 let [target] = targets.as_slice() else {
607 return Err(format!(
608 "item path `{target_name}` resolved to {} items; expected exactly one",
609 targets.len()
610 ));
611 };
612 target.sinto(&self.hax_state)
613 }
614 _ => {
615 return Err(format!(
616 "found several sibling items named `{target_name}`; expected exactly one"
617 ));
618 }
619 }
620 }
621 };
622
623 let target_def = self.poly_hax_def(&target_def_id).map_err(|err| err.msg)?;
624 if self.options.monomorphize_with_hax && target_def.this().has_non_lt_param {
625 return Err("contracts on generic items are not supported \
626 with `--monomorphize`"
627 .to_string());
628 }
629
630 let associated_item = match target_def.kind() {
631 hax::FullDefKind::AssocFn(f) => Some(f.associated_item()),
632 hax::FullDefKind::AssocConst(c) => Some(c.associated_item()),
633 hax::FullDefKind::AssocTy(t) => Some(t.associated_item()),
634 _ => None,
635 };
636 if let ContractTarget::Path(_) = target
637 && let Some(associated_item) = associated_item
638 && let hax::AssocItemContainer::TraitContainer { trait_ref } =
639 &associated_item.container
640 {
641 let kind = TransItemSourceKind::TraitDecl;
642 let trait_src =
643 TransItemSource::from_item(trait_ref, kind, self.options.monomorphize_with_hax);
644 let trait_id = self.register_and_enqueue(&None, trait_src).expect_item();
645 let item_id = self
646 .translate_assoc_item_id(trait_id, &target_def_id)
647 .map_err(|err| err.msg)?;
648 Ok(MaybeAssocItemId::Assoc(trait_id, item_id))
649 } else {
650 let kind = match target_def.kind() {
651 hax::FullDefKind::Closure(c) => TransItemSourceKind::CallableMethod(
653 super::translate_closures::translate_closure_kind(c.kind()),
654 ),
655 _ => self
656 .base_kind_for_item(&target_def_id)
657 .ok_or_else(|| format!("`{target_def_id:?}` is not a translatable item"))?,
658 };
659 let target_src = TransItemSource::from_item(
660 target_def.this(),
661 kind,
662 self.options.monomorphize_with_hax,
663 );
664 let item_id: ItemId = self.register_and_enqueue(&None, target_src).expect_item();
665 Ok(MaybeAssocItemId::Free(item_id))
666 }
667 }
668
669 fn parse_attr_from_raw(
671 &mut self,
672 def_id: &hax::DefId,
673 raw_attr: RawAttribute,
674 ) -> Result<Attribute, String> {
675 let path = raw_attr.path.split("::").collect_vec();
678 let attr_name = if let &[path_start, attr_name] = path.as_slice()
679 && (path_start == "charon" || path_start == "aeneas" || path_start == "verify")
680 {
681 attr_name
682 } else {
683 return Ok(Attribute::Unknown(raw_attr));
684 };
685
686 match self.parse_special_attr(def_id, attr_name, &raw_attr)? {
687 Some(parsed) => Ok(parsed),
688 None => Err(format!("Unrecognized attribute: `{}`", raw_attr)),
689 }
690 }
691
692 fn parse_special_attr(
694 &mut self,
695 def_id: &hax::DefId,
696 attr_name: &str,
697 raw_attr: &RawAttribute,
698 ) -> Result<Option<Attribute>, String> {
699 let args = raw_attr.args.as_deref();
700 let parsed = match attr_name {
701 "opaque" if args.is_none() => Attribute::Opaque,
703 "exclude" if args.is_none() => Attribute::Exclude,
705 "transparent" if args.is_none() => Attribute::Transparent,
707 "contract" if let Some(args) = args => {
710 use syn::{ext::IdentExt, parse::Parser};
711
712 let parser = |input: syn::parse::ParseStream<'_>| {
713 let mut kind = None;
714 let mut target = None;
715 while !input.is_empty() {
716 let key = input.call(syn::Ident::parse_any)?;
717 let key_name = key.to_string();
718 if key_name == "parent" && !input.peek(syn::Token![=]) {
719 if target.is_some() {
720 return Err(syn::Error::new(
721 key.span(),
722 "duplicate contract target",
723 ));
724 }
725 target = Some(ContractTarget::Parent);
726 } else {
727 input.parse::<syn::Token![=]>()?;
728 let value = input.parse::<syn::LitStr>()?.value();
729 match key_name.as_str() {
730 "kind" => {
731 if kind.is_some() {
732 return Err(syn::Error::new(
733 key.span(),
734 "duplicate contract argument `kind`",
735 ));
736 }
737 kind = Some(value);
738 }
739 "for" => {
740 if target.is_some() {
741 return Err(syn::Error::new(
742 key.span(),
743 "duplicate contract target",
744 ));
745 }
746 target = Some(ContractTarget::Path(value));
747 }
748 "parent" => {
749 return Err(syn::Error::new(
750 key.span(),
751 "contract argument `parent` does not take a value",
752 ));
753 }
754 _ => {
755 return Err(syn::Error::new(
756 key.span(),
757 format!("unknown contract argument `{key}`"),
758 ));
759 }
760 }
761 }
762 if !input.is_empty() {
763 input.parse::<syn::Token![,]>()?;
764 }
765 }
766 let kind =
767 kind.ok_or_else(|| input.error("missing contract argument `kind`"))?;
768 let target = target
769 .ok_or_else(|| input.error("missing contract target `parent` or `for`"))?;
770 Ok((kind, target))
771 };
772 let (kind, target) = parser.parse_str(args).map_err(|err| {
773 format!(
774 "invalid contract syntax: {err}; expected \
775 `#[charon::contract(kind = \"...\", parent)]` or \
776 `#[charon::contract(kind = \"...\", for = \"item path\")]`"
777 )
778 })?;
779 Attribute::IsContract {
780 kind,
781 target: self.resolve_contract_target(def_id, target)?,
782 }
783 }
784 "rename" if let Some(attr) = args => {
786 let Some(attr) = attr
787 .strip_prefix("\"")
788 .and_then(|attr| attr.strip_suffix("\""))
789 else {
790 return Err(format!(
791 "the new name should be between quotes: `rename(\"{attr}\")`."
792 ));
793 };
794
795 if attr.is_empty() {
796 return Err(format!("attribute `rename` should not be empty"));
797 }
798
799 let first_char = attr.chars().nth(0).unwrap();
800 let is_identifier = (first_char.is_alphabetic() || first_char == '_')
801 && attr.chars().all(|c| c.is_alphanumeric() || c == '_');
802 if !is_identifier {
803 return Err(format!(
804 "attribute `rename` should contain a valid identifier"
805 ));
806 }
807
808 Attribute::Rename(attr.to_string())
809 }
810 "variants_prefix" if let Some(attr) = args => {
812 let Some(attr) = attr
813 .strip_prefix("\"")
814 .and_then(|attr| attr.strip_suffix("\""))
815 else {
816 return Err(format!(
817 "the name should be between quotes: `variants_prefix(\"{attr}\")`."
818 ));
819 };
820
821 Attribute::VariantsPrefix(attr.to_string())
822 }
823 "variants_suffix" if let Some(attr) = args => {
825 let Some(attr) = attr
826 .strip_prefix("\"")
827 .and_then(|attr| attr.strip_suffix("\""))
828 else {
829 return Err(format!(
830 "the name should be between quotes: `variants_suffix(\"{attr}\")`."
831 ));
832 };
833
834 Attribute::VariantsSuffix(attr.to_string())
835 }
836 "start_from" => {
838 if matches!(def_id.kind, hax::DefKind::Mod) {
839 return Err("`start_from` on modules has no effect".to_string());
840 }
841 Attribute::Unknown(raw_attr.clone())
842 }
843 "test" if args.is_none() => Attribute::Unknown(raw_attr.clone()),
845 _ => return Ok(None),
846 };
847 Ok(Some(parsed))
848 }
849
850 pub(crate) fn translate_attribute(
853 &mut self,
854 def_id: &hax::DefId,
855 attr: &rustc_hir::Attribute,
856 ) -> Option<Attribute> {
857 use rustc_hir as hir;
858 use rustc_hir::attrs as hir_attrs;
859 match attr {
860 hir::Attribute::Parsed(hir_attrs::AttributeKind::DocComment { comment, .. }) => {
861 (!self.options.no_doc_comments).then(|| Attribute::DocComment(comment.to_string()))
862 }
863 hir::Attribute::Parsed(attr) => self
864 .translate_rustc_attribute_kind(attr)
865 .ok()
866 .map(Attribute::Builtin),
867 hir::Attribute::Unparsed(attr) => {
868 let raw_attr = RawAttribute {
869 path: attr.path.to_string(),
870 args: match &attr.args {
871 hir::AttrArgs::Empty => None,
872 hir::AttrArgs::Delimited(args) => {
873 Some(rustc_ast_pretty::pprust::tts_to_string(&args.tokens))
874 }
875 hir::AttrArgs::Eq { expr, .. } => {
876 self.tcx.sess.source_map().span_to_snippet(expr.span).ok()
877 }
878 },
879 };
880 match self.parse_attr_from_raw(def_id, raw_attr) {
881 Ok(a) => Some(a),
882 Err(msg) => {
883 let span = self.translate_span(&attr.span.sinto(&self.hax_state));
884 register_error!(self, span, "Error parsing attribute: {msg}");
885 None
886 }
887 }
888 }
889 }
890 }
891
892 pub(crate) fn translate_inline(&self, def: &hax::FullDef<'tcx>) -> Option<InlineAttr> {
893 let inline = match def.kind() {
894 hax::FullDefKind::Fn(f) => f.inline(),
895 hax::FullDefKind::AssocFn(f) => f.inline(),
896 hax::FullDefKind::Closure(c) => c.inline(),
897 _ => return None,
898 };
899 match inline {
900 hax::InlineAttr::None => None,
901 hax::InlineAttr::Hint => Some(InlineAttr::Hint),
902 hax::InlineAttr::Never => Some(InlineAttr::Never),
903 hax::InlineAttr::Always => Some(InlineAttr::Always),
904 hax::InlineAttr::Force { .. } => Some(InlineAttr::Always),
905 }
906 }
907
908 pub(crate) fn translate_attr_info(&mut self, def: &hax::FullDef<'tcx>) -> AttrInfo {
909 let public = def.visibility.unwrap_or(false);
911 let inline = self.translate_inline(def);
912 let attributes = def
913 .attributes
914 .iter()
915 .filter_map(|attr| self.translate_attribute(def.def_id(), attr))
916 .collect_vec();
917
918 let rename = {
919 let mut renames = attributes.iter().filter_map(|a| a.as_rename()).cloned();
920 let rename = renames.next();
921 if renames.next().is_some() {
922 let span = self.translate_span(&def.span);
923 register_error!(
924 self,
925 span,
926 "There should be at most one `charon::rename(\"...\")` \
927 or `aeneas::rename(\"...\")` attribute per declaration",
928 );
929 }
930 rename
931 };
932
933 AttrInfo {
934 attributes,
935 inline,
936 public,
937 rename,
938 }
939 }
940}
941
942impl<'tcx> TranslateCtx<'tcx> {
944 pub(crate) fn is_extern_item(&mut self, def: &hax::FullDef<'tcx>) -> bool {
946 def.def_id()
947 .parent(&self.hax_state)
948 .is_some_and(|parent| matches!(parent.kind, hax::DefKind::ForeignMod))
949 }
950
951 pub(crate) fn extern_item_symbol_name(&mut self, def: &hax::FullDef<'tcx>) -> Option<String> {
953 if !self.is_extern_item(def) {
954 return None;
955 }
956 let def_id = def.def_id().as_real_def_id()?;
957 let instance = rustc_middle::ty::Instance::mono(self.tcx, def_id);
958 Some(self.tcx.symbol_name(instance).name.to_string())
959 }
960
961 pub(crate) fn translate_item_meta(
963 &mut self,
964 def: &hax::FullDef<'tcx>,
965 item_src: &TransItemSource<'tcx>,
966 name: Name,
967 name_opacity: ItemOpacity,
968 ) -> ItemMeta {
969 if let Some(item_meta) = self.cached_item_metas.get(item_src) {
970 return item_meta.clone();
971 }
972 let span = def.source_span.as_ref().unwrap_or(&def.span);
973 let span = self.translate_span(span);
974 let is_local = def.def_id().is_local();
975 let (attr_info, lang_item, diagnostic_item) = if !item_src.is_derived_item()
976 || matches!(item_src.kind, TransItemSourceKind::CallableMethod(..))
977 {
978 let attr_info = self.translate_attr_info(def);
979 let lang_item = def
980 .def_id()
981 .as_real_def_id()
982 .and_then(|id| self.tcx.as_lang_item(id))
983 .or_else(|| {
984 self.is_extern_item(def)
987 .then(|| rustc_hir::attrs::find_attr!(&def.attributes, Lang(item) => *item))
988 .flatten()
989 })
990 .map(|lang_item| {
991 self.translate_rustc_lang_item(&lang_item)
992 .expect("all rustc LangItem variants should be translated")
993 });
994 let diagnostic_item = def.diagnostic_item.map(|s| s.to_string());
995 (attr_info, lang_item, diagnostic_item)
996 } else {
997 (AttrInfo::default(), None, None)
998 };
999
1000 let opacity = if attr_info.attributes.iter().any(|attr| attr.is_exclude()) {
1001 ItemOpacity::Invisible.max(name_opacity)
1002 } else if self.is_poly_in_mono(item_src) {
1003 if matches!(item_src.kind, TransItemSourceKind::TraitImpl(..)) {
1004 ItemOpacity::Opaque.max(name_opacity)
1005 } else {
1006 ItemOpacity::Invisible.max(name_opacity)
1007 }
1008 } else if self.is_extern_item(def)
1009 || attr_info.attributes.iter().any(|attr| attr.is_opaque())
1010 {
1011 ItemOpacity::Opaque.max(name_opacity)
1013 } else {
1014 name_opacity
1015 };
1016
1017 let item_meta = ItemMeta {
1018 name,
1019 span,
1020 source_text: def.source_text.clone(),
1021 attr_info,
1022 is_local,
1023 started_from: false,
1025 is_extern: self.is_extern_item(def),
1026 opacity,
1027 lang_item,
1028 diagnostic_item,
1029 has_errors: false,
1030 };
1031 self.cached_item_metas
1032 .insert(item_src.clone(), item_meta.clone());
1033 item_meta
1034 }
1035}