Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//! Pretty printing of macros output.

use base_db::CrateId;
use rustc_hash::FxHashMap;
use syntax::NodeOrToken;
use syntax::{ast::make, SyntaxNode};

use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap};

/// Inserts whitespace and replaces `$crate` in macro expansions.
#[expect(deprecated)]
pub fn prettify_macro_expansion(
    db: &dyn ExpandDatabase,
    syn: SyntaxNode,
    span_map: &ExpansionSpanMap,
    target_crate_id: CrateId,
) -> SyntaxNode {
    // Because `syntax_bridge::prettify_macro_expansion::prettify_macro_expansion()` clones subtree for `syn`,
    // that means it will be offsetted to the beginning.
    let span_offset = syn.text_range().start();
    let crate_graph = db.crate_graph();
    let target_crate = &crate_graph[target_crate_id];
    let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
    syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| {
        let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx;
        let replacement =
            syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| {
                let ctx_data = db.lookup_intern_syntax_context(ctx);
                let macro_call_id =
                    ctx_data.outer_expn.expect("`$crate` cannot come from `SyntaxContextId::ROOT`");
                let macro_call = db.lookup_intern_macro_call(macro_call_id);
                let macro_def_crate = macro_call.def.krate;
                // First, if this is the same crate as the macro, nothing will work but `crate`.
                // If not, if the target trait has the macro's crate as a dependency, using the dependency name
                // will work in inserted code and match the user's expectation.
                // If not, the crate's display name is what the dependency name is likely to be once such dependency
                // is inserted, and also understandable to the user.
                // Lastly, if nothing else found, resort to leaving `$crate`.
                if target_crate_id == macro_def_crate {
                    make::tokens::crate_kw()
                } else if let Some(dep) =
                    target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
                {
                    make::tokens::ident(&dep.name)
                } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
                    make::tokens::ident(crate_name.crate_name())
                } else {
                    return dollar_crate.clone();
                }
            });
        if replacement.text() == "$crate" {
            // The parent may have many children, and looking for the token may yield incorrect results.
            return dollar_crate.clone();
        }
        // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens.
        let parent = replacement.parent().unwrap().clone_subtree().clone_for_update();
        parent
            .children_with_tokens()
            .filter_map(NodeOrToken::into_token)
            .find(|it| it.kind() == replacement.kind())
            .unwrap()
    })
}
ed /// outside `hir-ty`. /// /// It is guaranteed that: /// - the yielded types don't contain inference variables (but may contain `TyKind::Error`). /// - a type won't be yielded more than once; in other words, the returned iterator will stop if it /// detects a cycle in the deref chain. pub fn autoderef( db: &dyn HirDatabase, env: Arc<TraitEnvironment>, ty: Canonical<Ty>, ) -> impl Iterator<Item = Ty> { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); let mut autoderef = Autoderef::new(&mut table, ty, false); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be // resolved, just replace with fallback type. let resolved = autoderef.table.resolve_completely(ty); // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. // // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more // performant. if v.contains(&resolved) { break; } v.push(resolved); } v.into_iter() } #[derive(Debug)] pub(crate) struct Autoderef<'a, 'db> { pub(crate) table: &'a mut InferenceTable<'db>, ty: Ty, at_start: bool, steps: Vec<(AutoderefKind, Ty)>, explicit: bool, } impl<'a, 'db> Autoderef<'a, 'db> { pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self { let ty = table.resolve_ty_shallow(&ty); Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit } } pub(crate) fn step_count(&self) -> usize { self.steps.len() } pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { &self.steps } pub(crate) fn final_ty(&self) -> Ty { self.ty.clone() } } impl Iterator for Autoderef<'_, '_> { type Item = (Ty, usize); fn next(&mut self) -> Option<Self::Item> { if self.at_start { self.at_start = false; return Some((self.ty.clone(), 0)); } if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() { return None; } let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?; self.steps.push((kind, self.ty.clone())); self.ty = new_ty; Some((self.ty.clone(), self.step_count())) } } pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, explicit: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) } } pub(crate) fn builtin_deref<'ty>( table: &mut InferenceTable<'_>, ty: &'ty Ty, explicit: bool, ) -> Option<&'ty Ty> { match ty.kind(Interner) { TyKind::Ref(.., ty) => Some(ty), TyKind::Raw(.., ty) if explicit => Some(ty), &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => { if crate::lang_items::is_box(table.db, adt) { substs.at(Interner, 0).ty(Interner) } else { None } } _ => None, } } pub(crate) fn deref_by_trait( table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, ty: Ty, ) -> Option<Ty> { let _p = profile::span("deref_by_trait"); if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { // don't try to deref unknown variables return None; } let deref_trait = db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?; let projection = { let b = TyBuilder::subst_for_def(db, deref_trait, None); if b.remaining() != 1 { // the Target type + Deref trait should only have one generic parameter, // namely Deref's Self type return None; } let deref_subst = b.push(ty).build(); TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build() }; // Check that the type implements Deref at all let trait_ref = projection.trait_ref(db); let implements_goal: Goal = trait_ref.cast(Interner); table.try_obligation(implements_goal.clone())?; table.register_obligation(implements_goal); let result = table.normalize_projection_ty(projection); Some(table.resolve_ty_shallow(&result)) }