1use itertools::Itertools;
3use rustc_middle::mir;
4use std::cmp::Ord;
5use std::path::{Component, PathBuf};
6
7use super::translate_crate::RustcItem;
8use super::translate_ctx::*;
9use super::translate_generics::BindingLevel;
10use charon_lib::ast::*;
11use hax::{DefPathItem, SInto};
12
13impl<'tcx, 'ctx> TranslateCtx<'tcx> {
15 fn register_file(&mut self, filename: FileName, span: rustc_span::Span) -> FileId {
18 match self.file_to_id.get(&filename) {
20 Some(id) => *id,
21 None => {
22 let source_file = self.tcx.sess.source_map().lookup_source_file(span.lo());
23 let crate_name = self.tcx.crate_name(source_file.cnum).to_string();
24 let file = File {
25 name: filename.clone(),
26 crate_name,
27 contents: source_file.src.as_deref().cloned(),
28 };
29 let id = self.translated.files.push(file);
30 self.file_to_id.insert(filename, id);
31 id
32 }
33 }
34 }
35
36 pub fn translate_filename(&mut self, name: rustc_span::FileName) -> meta::FileName {
37 match name {
38 rustc_span::FileName::Real(name) => {
39 use rustc_span::RealFileName;
40 match name {
41 RealFileName::LocalPath(path) => {
42 let path = if let Ok(path) = path.strip_prefix(&self.sysroot) {
43 if let Ok(path) = path.strip_prefix("lib/rustlib/src/rust") {
48 let mut rewritten_path: PathBuf = "/rustc".into();
49 rewritten_path.extend(path);
50 rewritten_path
51 } else {
52 let mut rewritten_path: PathBuf = "/toolchain".into();
54 rewritten_path.extend(path);
55 rewritten_path
56 }
57 } else {
58 let cargo_home = std::env::var("CARGO_HOME")
62 .map(PathBuf::from)
63 .ok()
64 .or_else(|| std::env::home_dir().map(|p| p.join(".cargo")));
65 if let Some(cargo_home) = cargo_home
66 && let Ok(path) = path.strip_prefix(cargo_home)
67 {
68 let mut rewritten_path: PathBuf = "/cargo".into();
70 rewritten_path.extend(path);
71 rewritten_path
72 } else {
73 path.clone()
74 }
75 };
76 FileName::Local(path)
77 }
78 RealFileName::Remapped { virtual_name, .. } => {
79 let mut components_iter = virtual_name.components();
83 if let Some(
84 [
85 Component::RootDir,
86 Component::Normal(rustc),
87 Component::Normal(hash),
88 ],
89 ) = components_iter.by_ref().array_chunks().next()
90 && rustc.to_str() == Some("rustc")
91 && hash.len() == 40
92 {
93 let path_without_hash = [Component::RootDir, Component::Normal(rustc)]
94 .into_iter()
95 .chain(components_iter)
96 .collect();
97 FileName::Virtual(path_without_hash)
98 } else {
99 FileName::Virtual(virtual_name.clone())
100 }
101 }
102 }
103 }
104 _ => FileName::NotReal(format!("{name:?}")),
107 }
108 }
109
110 pub fn translate_span_data(&mut self, span: rustc_span::Span) -> meta::SpanData {
111 let smap: &rustc_span::source_map::SourceMap = self.tcx.sess.psess.source_map();
112 let filename = smap.span_to_filename(span);
113 let filename = self.translate_filename(filename);
114 let span = span;
115 let file_id = match &filename {
116 FileName::NotReal(_) => {
117 unimplemented!();
119 }
120 FileName::Virtual(_) | FileName::Local(_) => self.register_file(filename, span),
121 };
122
123 let convert_loc = |pos: rustc_span::BytePos| -> Loc {
124 let loc = smap.lookup_char_pos(pos);
125 Loc {
126 line: loc.line,
127 col: loc.col_display,
128 }
129 };
130 let beg = convert_loc(span.lo());
131 let end = convert_loc(span.hi());
132
133 meta::SpanData { file_id, beg, end }
135 }
136
137 pub fn translate_span_from_source_info(
139 &mut self,
140 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
141 source_info: &mir::SourceInfo,
142 ) -> Span {
143 let data = self.translate_span_data(source_info.span);
145
146 let mut parent_span = None;
148 let mut scope_data = &source_scopes[source_info.scope];
149 while let Some(parent_scope) = scope_data.inlined_parent_scope {
150 scope_data = &source_scopes[parent_scope];
151 parent_span = Some(scope_data.span);
152 }
153
154 if let Some(parent_span) = parent_span {
155 let parent_span = self.translate_span_data(parent_span);
156 Span {
157 data: parent_span,
158 generated_from_span: Some(data),
159 }
160 } else {
161 Span {
162 data,
163 generated_from_span: None,
164 }
165 }
166 }
167
168 pub(crate) fn translate_span(&mut self, span: &rustc_span::Span) -> Span {
169 Span {
170 data: self.translate_span_data(*span),
171 generated_from_span: None,
172 }
173 }
174
175 pub(crate) fn def_span(&mut self, def_id: &hax::DefId) -> Span {
176 let span = def_id.def_span(&self.hax_state);
177 self.translate_span(&span)
178 }
179}
180
181impl<'tcx, 'ctx> TranslateCtx<'tcx> {
183 fn path_elem_for_def(
184 &mut self,
185 span: Span,
186 item: &RustcItem,
187 ) -> Result<Option<PathElem>, Error> {
188 let def_id = item.def_id();
189 let path_elem = def_id.path_item(&self.hax_state);
190 let disambiguator = Disambiguator::new(path_elem.disambiguator as usize);
193 let path_elem = match path_elem.data {
195 DefPathItem::CrateRoot { name, .. } => {
196 error_assert!(self, span, path_elem.disambiguator == 0);
198 Some(PathElem::Ident(name.to_string(), disambiguator))
199 }
200 DefPathItem::TypeNs(symbol)
203 | DefPathItem::ValueNs(symbol)
204 | DefPathItem::MacroNs(symbol) => {
205 Some(PathElem::Ident(symbol.to_string(), disambiguator))
206 }
207 DefPathItem::Impl => {
208 let full_def = self.hax_def_for_item(item)?;
209 let impl_elem = match full_def.kind() {
213 hax::FullDefKind::InherentImpl { ty, .. } => {
215 let item_src =
219 TransItemSource::new(item.clone(), TransItemSourceKind::InherentImpl);
220 let mut bt_ctx = ItemTransCtx::new(item_src, None, self);
221 bt_ctx.translate_item_generics(
222 span,
223 &full_def,
224 &TransItemSourceKind::InherentImpl,
225 )?;
226 let ty = bt_ctx.translate_ty(span, &ty)?;
227 ImplElem::Ty(Binder {
228 kind: BinderKind::InherentImplBlock,
229 params: bt_ctx.into_generics(),
230 skip_binder: ty,
231 })
232 }
233 hax::FullDefKind::TraitImpl { .. } => {
235 let impl_id = {
236 let item_src = TransItemSource::new(
237 item.clone(),
238 TransItemSourceKind::TraitImpl(TraitImplSource::Normal),
239 );
240 self.register_and_enqueue(&None, item_src).unwrap()
241 };
242 ImplElem::Trait(impl_id)
243 }
244 _ => unreachable!(),
245 };
246
247 Some(PathElem::Impl(impl_elem))
248 }
249 DefPathItem::OpaqueTy => None,
251 DefPathItem::Closure => Some(PathElem::Ident("closure".to_string(), disambiguator)),
255 DefPathItem::ForeignMod => None,
258 DefPathItem::Ctor => None,
261 DefPathItem::Use => Some(PathElem::Ident("{use}".to_string(), disambiguator)),
262 DefPathItem::AnonConst => Some(PathElem::Ident("{const}".to_string(), disambiguator)),
263 DefPathItem::PromotedConst => Some(PathElem::Ident(
264 "{promoted_const}".to_string(),
265 disambiguator,
266 )),
267 _ => {
268 raise_error!(
269 self,
270 span,
271 "Unexpected DefPathItem for `{def_id:?}`: {path_elem:?}"
272 );
273 }
274 };
275 Ok(path_elem)
276 }
277
278 fn name_for_item(&mut self, item: &RustcItem) -> Result<Name, Error> {
322 if let Some(name) = self.cached_names.get(item) {
323 return Ok(name.clone());
324 }
325 let def_id = item.def_id();
326 trace!("Computing name for `{def_id:?}`");
327
328 let parent_name = if let Some(parent_id) = def_id.parent(&self.hax_state) {
329 let def = self.hax_def_for_item(item)?;
330 if matches!(item, RustcItem::Mono(..))
331 && let Some(parent_item) = def.typing_parent(&self.hax_state)
332 {
333 self.name_for_item(&RustcItem::Mono(parent_item.clone()))?
334 } else {
335 self.name_for_item(&RustcItem::Poly(parent_id.clone()))?
336 }
337 } else {
338 Name { name: Vec::new() }
339 };
340 let span = self.def_span(def_id);
341 let mut name = parent_name;
342 if let Some(path_elem) = self.path_elem_for_def(span, item)? {
343 name.name.push(path_elem);
344 }
345
346 trace!("Computed name for `{def_id:?}`: `{name:?}`");
347 self.cached_names.insert(item.clone(), name.clone());
348 Ok(name)
349 }
350
351 pub fn name_for_src(&mut self, src: &TransItemSource) -> Result<Name, Error> {
354 let mut name = if let Some(parent) = src.parent() {
355 self.name_for_src(&parent)?
356 } else {
357 self.name_for_item(&src.item)?
358 };
359 match &src.kind {
360 TransItemSourceKind::Type
362 | TransItemSourceKind::Fun
363 | TransItemSourceKind::Global
364 | TransItemSourceKind::TraitImpl(TraitImplSource::Normal)
365 | TransItemSourceKind::TraitDecl
366 | TransItemSourceKind::InherentImpl
367 | TransItemSourceKind::Module => {}
368
369 TransItemSourceKind::TraitImpl(
370 kind @ (TraitImplSource::Closure(..)
371 | TraitImplSource::ImplicitDestruct
372 | TraitImplSource::TraitAlias),
373 ) => {
374 if let TraitImplSource::Closure(..) = kind {
375 let _ = name.name.pop(); }
377 let impl_id = self.register_and_enqueue(&None, src.clone()).unwrap();
378 name.name.push(PathElem::Impl(ImplElem::Trait(impl_id)));
379 }
380 TransItemSourceKind::DefaultedMethod(_, method_name) => {
381 name.name.push(PathElem::Ident(
382 method_name.to_string(),
383 Disambiguator::ZERO,
384 ));
385 }
386 TransItemSourceKind::ClosureMethod(kind) => {
387 let fn_name = kind.method_name().to_string();
388 name.name
389 .push(PathElem::Ident(fn_name, Disambiguator::ZERO));
390 }
391 TransItemSourceKind::DropInPlaceMethod(..) => {
392 name.name.push(PathElem::Ident(
393 "drop_in_place".to_string(),
394 Disambiguator::ZERO,
395 ));
396 }
397 TransItemSourceKind::ClosureAsFnCast => {
398 name.name
399 .push(PathElem::Ident("as_fn".into(), Disambiguator::ZERO));
400 }
401 TransItemSourceKind::VTable
402 | TransItemSourceKind::VTableInstance(..)
403 | TransItemSourceKind::VTableInstanceInitializer(..) => {
404 name.name
405 .push(PathElem::Ident("{vtable}".into(), Disambiguator::ZERO));
406 }
407 TransItemSourceKind::VTableMethod => {
408 name.name.push(PathElem::Ident(
409 "{vtable_method}".into(),
410 Disambiguator::ZERO,
411 ));
412 }
413 TransItemSourceKind::VTableDropShim => {
414 name.name.push(PathElem::Ident(
415 "{vtable_drop_shim}".into(),
416 Disambiguator::ZERO,
417 ));
418 }
419 }
420 Ok(name)
421 }
422
423 pub fn translate_name(&mut self, src: &TransItemSource) -> Result<Name, Error> {
425 let mut name = self.name_for_src(src)?;
426 if let RustcItem::Mono(item_ref) = &src.item
428 && !item_ref.generic_args.is_empty()
429 {
430 let trans_id = self.register_no_enqueue(&None, src).unwrap();
431 let span = self.def_span(&item_ref.def_id);
432 let mut bt_ctx = ItemTransCtx::new(src.clone(), trans_id, self);
433 bt_ctx.binding_levels.push(BindingLevel::new(true));
434 let args = bt_ctx.translate_generic_args(
435 span,
436 &item_ref.generic_args,
437 &item_ref.impl_exprs,
438 )?;
439 name.name.push(PathElem::Instantiated(Box::new(Binder {
440 params: GenericParams::empty(),
441 skip_binder: args,
442 kind: BinderKind::Other,
443 })));
444 }
445 Ok(name)
446 }
447
448 pub(crate) fn translate_trait_item_name(
450 &mut self,
451 def_id: &hax::DefId,
452 ) -> Result<TraitItemName, Error> {
453 let def = self.poly_hax_def(def_id)?;
454 let assoc = match def.kind() {
455 hax::FullDefKind::AssocTy {
456 associated_item, ..
457 }
458 | hax::FullDefKind::AssocConst {
459 associated_item, ..
460 }
461 | hax::FullDefKind::AssocFn {
462 associated_item, ..
463 } => associated_item,
464 _ => panic!("Unexpected def for associated item: {def:?}"),
465 };
466 Ok(TraitItemName(
467 assoc
468 .name
469 .as_ref()
470 .map(|n| n.to_string().into())
471 .unwrap_or_default(),
472 ))
473 }
474
475 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
476 self.options.opacity_for_name(&self.translated, name)
477 }
478}
479
480impl<'tcx, 'ctx> TranslateCtx<'tcx> {
482 fn parse_attr_from_raw(
484 &mut self,
485 def_id: &hax::DefId,
486 raw_attr: RawAttribute,
487 ) -> Result<Attribute, String> {
488 let path = raw_attr.path.split("::").collect_vec();
491 let attr_name = if let &[path_start, attr_name] = path.as_slice()
492 && (path_start == "charon" || path_start == "aeneas" || path_start == "verify")
493 {
494 attr_name
495 } else {
496 return Ok(Attribute::Unknown(raw_attr));
497 };
498
499 match self.parse_special_attr(def_id, attr_name, &raw_attr)? {
500 Some(parsed) => Ok(parsed),
501 None => Err(format!(
502 "Unrecognized attribute: `{}`",
503 raw_attr.to_string()
504 )),
505 }
506 }
507
508 fn parse_special_attr(
510 &mut self,
511 def_id: &hax::DefId,
512 attr_name: &str,
513 raw_attr: &RawAttribute,
514 ) -> Result<Option<Attribute>, String> {
515 let args = raw_attr.args.as_deref();
516 let parsed = match attr_name {
517 "opaque" if args.is_none() => Attribute::Opaque,
519 "exclude" if args.is_none() => Attribute::Exclude,
521 "rename" if let Some(attr) = args => {
523 let Some(attr) = attr
524 .strip_prefix("\"")
525 .and_then(|attr| attr.strip_suffix("\""))
526 else {
527 return Err(format!(
528 "the new name should be between quotes: `rename(\"{attr}\")`."
529 ));
530 };
531
532 if attr.is_empty() {
533 return Err(format!("attribute `rename` should not be empty"));
534 }
535
536 let first_char = attr.chars().nth(0).unwrap();
537 let is_identifier = (first_char.is_alphabetic() || first_char == '_')
538 && attr.chars().all(|c| c.is_alphanumeric() || c == '_');
539 if !is_identifier {
540 return Err(format!(
541 "attribute `rename` should contain a valid identifier"
542 ));
543 }
544
545 Attribute::Rename(attr.to_string())
546 }
547 "variants_prefix" if let Some(attr) = args => {
549 let Some(attr) = attr
550 .strip_prefix("\"")
551 .and_then(|attr| attr.strip_suffix("\""))
552 else {
553 return Err(format!(
554 "the name should be between quotes: `variants_prefix(\"{attr}\")`."
555 ));
556 };
557
558 Attribute::VariantsPrefix(attr.to_string())
559 }
560 "variants_suffix" if let Some(attr) = args => {
562 let Some(attr) = attr
563 .strip_prefix("\"")
564 .and_then(|attr| attr.strip_suffix("\""))
565 else {
566 return Err(format!(
567 "the name should be between quotes: `variants_suffix(\"{attr}\")`."
568 ));
569 };
570
571 Attribute::VariantsSuffix(attr.to_string())
572 }
573 "start_from" => {
575 if matches!(def_id.kind, hax::DefKind::Mod) {
576 return Err(format!("`start_from` on modules has no effect"));
577 }
578 Attribute::Unknown(raw_attr.clone())
579 }
580 _ => return Ok(None),
581 };
582 Ok(Some(parsed))
583 }
584
585 pub(crate) fn translate_attribute(
588 &mut self,
589 def_id: &hax::DefId,
590 attr: &rustc_hir::Attribute,
591 ) -> Option<Attribute> {
592 use rustc_hir as hir;
593 use rustc_hir::attrs as hir_attrs;
594 match attr {
595 hir::Attribute::Parsed(hir_attrs::AttributeKind::DocComment { comment, .. }) => {
596 Some(Attribute::DocComment(comment.to_string()))
597 }
598 hir::Attribute::Parsed(_) => None,
599 hir::Attribute::Unparsed(attr) => {
600 let raw_attr = RawAttribute {
601 path: attr.path.to_string(),
602 args: match &attr.args {
603 hir::AttrArgs::Empty => None,
604 hir::AttrArgs::Delimited(args) => {
605 Some(rustc_ast_pretty::pprust::tts_to_string(&args.tokens))
606 }
607 hir::AttrArgs::Eq { expr, .. } => {
608 self.tcx.sess.source_map().span_to_snippet(expr.span).ok()
609 }
610 },
611 };
612 match self.parse_attr_from_raw(def_id, raw_attr) {
613 Ok(a) => Some(a),
614 Err(msg) => {
615 let span = self.translate_span(&attr.span.sinto(&self.hax_state));
616 register_error!(self, span, "Error parsing attribute: {msg}");
617 None
618 }
619 }
620 }
621 }
622 }
623
624 pub(crate) fn translate_inline(&self, def: &hax::FullDef) -> Option<InlineAttr> {
625 match def.kind() {
626 hax::FullDefKind::Fn { inline, .. }
627 | hax::FullDefKind::AssocFn { inline, .. }
628 | hax::FullDefKind::Closure { inline, .. } => match inline {
629 hax::InlineAttr::None => None,
630 hax::InlineAttr::Hint => Some(InlineAttr::Hint),
631 hax::InlineAttr::Never => Some(InlineAttr::Never),
632 hax::InlineAttr::Always => Some(InlineAttr::Always),
633 hax::InlineAttr::Force { .. } => Some(InlineAttr::Always),
634 },
635 _ => None,
636 }
637 }
638
639 pub(crate) fn translate_attr_info(&mut self, def: &hax::FullDef) -> AttrInfo {
640 let public = def.visibility.unwrap_or(false);
642 let inline = self.translate_inline(def);
643 let attributes = def
644 .attributes
645 .iter()
646 .filter_map(|attr| self.translate_attribute(def.def_id(), &attr))
647 .collect_vec();
648
649 let rename = {
650 let mut renames = attributes.iter().filter_map(|a| a.as_rename()).cloned();
651 let rename = renames.next();
652 if renames.next().is_some() {
653 let span = self.translate_span(&def.span);
654 register_error!(
655 self,
656 span,
657 "There should be at most one `charon::rename(\"...\")` \
658 or `aeneas::rename(\"...\")` attribute per declaration",
659 );
660 }
661 rename
662 };
663
664 AttrInfo {
665 attributes,
666 inline,
667 public,
668 rename,
669 }
670 }
671}
672
673impl<'tcx, 'ctx> TranslateCtx<'tcx> {
675 pub(crate) fn is_extern_item(&mut self, def: &hax::FullDef) -> bool {
677 def.def_id()
678 .parent(&self.hax_state)
679 .is_some_and(|parent| matches!(parent.kind, hax::DefKind::ForeignMod { .. }))
680 }
681
682 pub(crate) fn translate_item_meta(
684 &mut self,
685 def: &hax::FullDef,
686 item_src: &TransItemSource,
687 name: Name,
688 name_opacity: ItemOpacity,
689 ) -> ItemMeta {
690 if let Some(item_meta) = self.cached_item_metas.get(&item_src) {
691 return item_meta.clone();
692 }
693 let span = def.source_span.as_ref().unwrap_or(&def.span);
694 let span = self.translate_span(span);
695 let is_local = def.def_id().is_local();
696 let (attr_info, lang_item) = if !item_src.is_derived_item()
697 || matches!(item_src.kind, TransItemSourceKind::ClosureMethod(..))
698 {
699 let attr_info = self.translate_attr_info(def);
700 let lang_item = def
701 .lang_item
702 .clone()
703 .or_else(|| def.diagnostic_item.clone())
704 .map(|s| s.to_string());
705 (attr_info, lang_item)
706 } else {
707 (AttrInfo::default(), None)
708 };
709
710 let opacity = if attr_info.attributes.iter().any(|attr| attr.is_exclude()) {
711 ItemOpacity::Invisible.max(name_opacity)
712 } else if self.is_extern_item(def)
713 || attr_info.attributes.iter().any(|attr| attr.is_opaque())
714 {
715 ItemOpacity::Opaque.max(name_opacity)
717 } else {
718 name_opacity
719 };
720
721 let item_meta = ItemMeta {
722 name,
723 span,
724 source_text: def.source_text.clone(),
725 attr_info,
726 is_local,
727 opacity,
728 lang_item,
729 };
730 self.cached_item_metas
731 .insert(item_src.clone(), item_meta.clone());
732 item_meta
733 }
734}