//! Defines database & queries for macro expansion.

use base_db::{salsa, CrateId, FileId, SourceDatabase};
use either::Either;
use limit::Limit;
use mbe::{syntax_node_to_token_tree, ValueResult};
use rustc_hash::FxHashSet;
use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId};
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
use triomphe::Arc;

use crate::{
    attrs::{collect_attrs, AttrId},
    builtin_attr_macro::pseudo_derive_attr_expansion,
    builtin_fn_macro::EagerExpander,
    cfg_process,
    declarative::DeclarativeMacroExpander,
    fixup::{self, SyntaxFixupUndoInfo},
    hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
    proc_macro::ProcMacros,
    span_map::{RealSpanMap, SpanMap, SpanMapRef},
    tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
    CustomProcMacroExpander, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap,
    HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
    MacroFileId,
};

/// Total limit on the number of tokens produced by any macro invocation.
///
/// If an invocation produces more tokens than this limit, it will not be stored in the database and
/// an error will be emitted.
///
/// Actual max for `analysis-stats .` at some point: 30672.
static TOKEN_LIMIT: Limit = Limit::new(1_048_576);

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TokenExpander {
    /// Old-style `macro_rules` or the new macros 2.0
    DeclarativeMacro(Arc<DeclarativeMacroExpander>),
    /// Stuff like `line!` and `file!`.
    BuiltIn(BuiltinFnLikeExpander),
    /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
    BuiltInEager(EagerExpander),
    /// `global_allocator` and such.
    BuiltInAttr(BuiltinAttrExpander),
    /// `derive(Copy)` and such.
    BuiltInDerive(BuiltinDeriveExpander),
    /// The thing we love the most here in rust-analyzer -- procedural macros.
    ProcMacro(CustomProcMacroExpander),
}

#[salsa::query_group(ExpandDatabaseStorage)]
pub trait ExpandDatabase: SourceDatabase {
    /// The proc macros.
    #[salsa::input]
    fn proc_macros(&self) -> Arc<ProcMacros>;

    fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;

    /// Main public API -- parses a hir file, not caring whether it's a real
    /// file or a macro expansion.
    #[salsa::transparent]
    fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
    #[salsa::transparent]
    fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult<Parse<SyntaxNode>>;
    /// Implementation for the macro case.
    // This query is LRU cached
    fn parse_macro_expansion(
        &self,
        macro_file: MacroFileId,
    ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
    #[salsa::transparent]
    #[salsa::invoke(SpanMap::new)]
    fn span_map(&self, file_id: HirFileId) -> SpanMap;

    #[salsa::transparent]
    #[salsa::invoke(crate::span_map::expansion_span_map)]
    fn expansion_span_map(&self, file_id: MacroFileId) -> Arc<ExpansionSpanMap>;
    #[salsa::invoke(crate::span_map::real_span_map)]
    fn real_span_map(&self, file_id: FileId) -> Arc<RealSpanMap>;

    /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
    /// reason why we use salsa at all.
    ///
    /// We encode macro definitions into ids of macro calls, this what allows us
    /// to be incremental.
    #[salsa::interned]
    fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId;
    #[salsa::interned]
    fn intern_syntax_context(&self, ctx: SyntaxContextData) -> SyntaxContextId;

    #[salsa::transparent]
    fn setup_syntax_context_root(&self) -> ();
    #[salsa::transparent]
    #[salsa::invoke(crate::hygiene::dump_syntax_contexts)]
    fn dump_syntax_contexts(&self) -> String;

    /// Lowers syntactic macro call to a token tree representation. That's a firewall
    /// query, only typing in the macro call itself changes the returned
    /// subtree.
    fn macro_arg(
        &self,
        id: MacroCallId,
    ) -> ValueResult<(Arc<tt::Subtree>, SyntaxFixupUndoInfo), Arc<Box<[SyntaxError]>>>;
    /// Fetches the expander for this macro.
    #[salsa::transparent]
    #[salsa::invoke(TokenExpander::macro_expander)]
    fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
    /// Fetches (and compiles) the expander of this decl macro.
    #[salsa::invoke(DeclarativeMacroExpander::expander)]
    fn decl_macro_expander(
        &self,
        def_crate: CrateId,
        id: AstId<ast::Macro>,
    ) -> Arc<DeclarativeMacroExpander>;
    /// Special case of the previous query for procedural macros. We can't LRU
    /// proc macros, since they are not deterministic in general, and
    /// non-determinism breaks salsa in a very, very, very bad way.
    /// @edwin0cheng heroically debugged this once! See #4315 for details
    fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
    /// Retrieves the span to be used for a proc-macro expansions spans.
    /// This is a firewall query as it requires parsing the file, which we don't want proc-macros to
    /// directly depend on as that would cause to frequent invalidations, mainly because of the
    /// parse queries being LRU cached. If they weren't the invalidations would only happen if the
    /// user wrote in the file that defines the proc-macro.
    fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> Span;
    /// Firewall query that returns the errors from the `parse_macro_expansion` query.
    fn parse_macro_expansion_error(
        &self,
        macro_call: MacroCallId,
    ) -> ExpandResult<Box<[SyntaxError]>>;
}

/// This expands the given macro call, but with different arguments. This is
/// used for completion, where we want to see what 'would happen' if we insert a
/// token. The `token_to_map` mapped down into the expansion, with the mapped
/// token returned.
pub fn expand_speculative(
    db: &dyn ExpandDatabase,
    actual_macro_call: MacroCallId,
    speculative_args: &SyntaxNode,
    token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, SyntaxToken)> {
    let loc = db.lookup_intern_macro_call(actual_macro_call);

    // FIXME: This BOGUS here is dangerous once the proc-macro server can call back into the database!
    let span_map = RealSpanMap::absolute(FileId::BOGUS);
    let span_map = SpanMapRef::RealSpanMap(&span_map);

    // Build the subtree and token mapping for the speculative args
    let (mut tt, undo_info) = match loc.kind {
        MacroCallKind::FnLike { .. } => (
            mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site),
            SyntaxFixupUndoInfo::NONE,
        ),
        MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
            mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site),
            SyntaxFixupUndoInfo::NONE,
        ),
        MacroCallKind::Derive { derive_attr_index: index, .. }
        | MacroCallKind::Attr { invoc_attr_index: index, .. } => {
            let censor = if let MacroCallKind::Derive { .. } = loc.kind {
                censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?)
            } else {
                censor_attr_input(index, &ast::Item::cast(speculative_args.clone())?)
            };

            let censor_cfg =
                cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default();
            let mut fixups = fixup::fixup_syntax(span_map, speculative_args, loc.call_site);
            fixups.append.retain(|it, _| match it {
                syntax::NodeOrToken::Token(_) => true,
                it => !censor.contains(it) && !censor_cfg.contains(it),
            });
            fixups.remove.extend(censor);
            fixups.remove.extend(censor_cfg);

            (
                mbe::syntax_node_to_token_tree_modified(
                    speculative_args,
                    span_map,
                    fixups.append,
                    fixups.remove,
                    loc.call_site,
                ),
                fixups.undo_info,
            )
        }
    };

    let attr_arg = match loc.kind {
        MacroCallKind::Attr { invoc_attr_index, .. } => {
            let attr = if loc.def.is_attribute_derive() {
                // for pseudo-derive expansion we actually pass the attribute itself only
                ast::Attr::cast(speculative_args.clone())
            } else {
                // Attributes may have an input token tree, build the subtree and map for this as well
                // then try finding a token id for our token if it is inside this input subtree.
                let item = ast::Item::cast(speculative_args.clone())?;
                collect_attrs(&item)
                    .nth(invoc_attr_index.ast_index())
                    .and_then(|x| Either::left(x.1))
            }?;
            match attr.token_tree() {
                Some(token_tree) => {
                    let mut tree =
                        syntax_node_to_token_tree(token_tree.syntax(), span_map, loc.call_site);
                    tree.delimiter = tt::Delimiter::invisible_spanned(loc.call_site);

                    Some(tree)
                }
                _ => None,
            }
        }
        _ => None,
    };

    // Do the actual expansion, we need to directly expand the proc macro due to the attribute args
    // Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
    let mut speculative_expansion = match loc.def.kind {
        MacroDefKind::ProcMacro(expander, _, ast) => {
            tt.delimiter = tt::Delimiter::invisible_spanned(loc.call_site);
            let span = db.proc_macro_span(ast);
            expander.expand(
                db,
                loc.def.krate,
                loc.krate,
                &tt,
                attr_arg.as_ref(),
                span_with_def_site_ctxt(db, span, actual_macro_call),
                span_with_call_site_ctxt(db, span, actual_macro_call),
                span_with_mixed_site_ctxt(db, span, actual_macro_call),
            )
        }
        MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
            pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, loc.call_site)
        }
        MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand_unhygienic(
            db,
            tt,
            loc.def.krate,
            loc.call_site,
        ),
        MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into),
        MacroDefKind::BuiltInDerive(it, ..) => {
            it.expand(db, actual_macro_call, &tt).map_err(Into::into)
        }
        MacroDefKind::BuiltInEager(it, _) => {
            it.expand(db, actual_macro_call, &tt).map_err(Into::into)
        }
        MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt),
    };

    let expand_to = loc.expand_to();

    fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
    let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);

    let syntax_node = node.syntax_node();
    let token = rev_tmap
        .ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
        .filter_map(|range| syntax_node.covering_element(range).into_token())
        .min_by_key(|t| {
            // prefer tokens of the same kind and text
            // Note the inversion of the score here, as we want to prefer the first token in case
            // of all tokens having the same score
            (t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8
        })?;
    Some((node.syntax_node(), token))
}

fn ast_id_map(db: &dyn ExpandDatabase, file_id: span::HirFileId) -> triomphe::Arc<AstIdMap> {
    triomphe::Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
}

fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
    match file_id.repr() {
        HirFileIdRepr::FileId(file_id) => db.parse(file_id).syntax_node(),
        HirFileIdRepr::MacroFile(macro_file) => {
            db.parse_macro_expansion(macro_file).value.0.syntax_node()
        }
    }
}

fn parse_or_expand_with_err(
    db: &dyn ExpandDatabase,
    file_id: HirFileId,
) -> ExpandResult<Parse<SyntaxNode>> {
    match file_id.repr() {
        HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()),
        HirFileIdRepr::MacroFile(macro_file) => {
            db.parse_macro_expansion(macro_file).map(|(it, _)| it)
        }
    }
}

// FIXME: We should verify that the parsed node is one of the many macro node variants we expect
// instead of having it be untyped
fn parse_macro_expansion(
    db: &dyn ExpandDatabase,
    macro_file: MacroFileId,
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
    let _p = tracing::span!(tracing::Level::INFO, "parse_macro_expansion").entered();
    let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
    let expand_to = loc.expand_to();
    let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc);

    let (parse, rev_token_map) = token_tree_to_syntax_node(
        match &tt {
            CowArc::Arc(it) => it,
            CowArc::Owned(it) => it,
        },
        expand_to,
    );

    ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
}

fn parse_macro_expansion_error(
    db: &dyn ExpandDatabase,
    macro_call_id: MacroCallId,
) -> ExpandResult<Box<[SyntaxError]>> {
    db.parse_macro_expansion(MacroFileId { macro_call_id })
        .map(|it| it.0.errors().into_boxed_slice())
}

pub(crate) fn parse_with_map(
    db: &dyn ExpandDatabase,
    file_id: HirFileId,
) -> (Parse<SyntaxNode>, SpanMap) {
    match file_id.repr() {
        HirFileIdRepr::FileId(file_id) => {
            (db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
        }
        HirFileIdRepr::MacroFile(macro_file) => {
            let (parse, map) = db.parse_macro_expansion(macro_file).value;
            (parse, SpanMap::ExpansionSpanMap(map))
        }
    }
}

// FIXME: for derive attributes, this will return separate copies of the same structures! Though
// they may differ in spans due to differing call sites...
fn macro_arg(
    db: &dyn ExpandDatabase,
    id: MacroCallId,
) -> ValueResult<(Arc<tt::Subtree>, SyntaxFixupUndoInfo), Arc<Box<[SyntaxError]>>> {
    let loc = db.lookup_intern_macro_call(id);

    if let MacroCallLoc {
        def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. },
        kind: MacroCallKind::FnLike { eager: Some(eager), .. },
        ..
    } = &loc
    {
        return ValueResult::ok((eager.arg.clone(), SyntaxFixupUndoInfo::NONE));
    }

    let (parse, map) = parse_with_map(db, loc.kind.file_id());
    let root = parse.syntax_node();

    let (censor, item_node) = match loc.kind {
        MacroCallKind::FnLike { ast_id, .. } => {
            let dummy_tt = |kind| {
                (
                    Arc::new(tt::Subtree {
                        delimiter: tt::Delimiter {
                            open: loc.call_site,
                            close: loc.call_site,
                            kind,
                        },
                        token_trees: Box::default(),
                    }),
                    SyntaxFixupUndoInfo::default(),
                )
            };

            let node = &ast_id.to_ptr(db).to_node(&root);
            let offset = node.syntax().text_range().start();
            let Some(tt) = node.token_tree() else {
                return ValueResult::new(
                    dummy_tt(tt::DelimiterKind::Invisible),
                    Arc::new(Box::new([SyntaxError::new_at_offset(
                        "missing token tree".to_owned(),
                        offset,
                    )])),
                );
            };
            let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
            let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);

            let mismatched_delimiters = !matches!(
                (first, last),
                (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}'])
            );
            if mismatched_delimiters {
                // Don't expand malformed (unbalanced) macro invocations. This is
                // less than ideal, but trying to expand unbalanced  macro calls
                // sometimes produces pathological, deeply nested code which breaks
                // all kinds of things.
                //
                // So instead, we'll return an empty subtree here
                cov_mark::hit!(issue9358_bad_macro_stack_overflow);

                let kind = match first {
                    _ if loc.def.is_proc_macro() => tt::DelimiterKind::Invisible,
                    T!['('] => tt::DelimiterKind::Parenthesis,
                    T!['['] => tt::DelimiterKind::Bracket,
                    T!['{'] => tt::DelimiterKind::Brace,
                    _ => tt::DelimiterKind::Invisible,
                };
                return ValueResult::new(
                    dummy_tt(kind),
                    Arc::new(Box::new([SyntaxError::new_at_offset(
                        "mismatched delimiters".to_owned(),
                        offset,
                    )])),
                );
            }

            let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), loc.call_site);
            if loc.def.is_proc_macro() {
                // proc macros expect their inputs without parentheses, MBEs expect it with them included
                tt.delimiter.kind = tt::DelimiterKind::Invisible;
            }
            let val = (Arc::new(tt), SyntaxFixupUndoInfo::NONE);
            return if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) {
                match parse.errors() {
                    errors if errors.is_empty() => ValueResult::ok(val),
                    errors => ValueResult::new(
                        val,
                        // Box::<[_]>::from(res.errors()), not stable yet
                        Arc::new(errors.to_vec().into_boxed_slice()),
                    ),
                }
            } else {
                ValueResult::ok(val)
            };
        }
        MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
            let node = ast_id.to_ptr(db).to_node(&root);
            (censor_derive_input(derive_attr_index, &node), node.into())
        }
        MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
            let node = ast_id.to_ptr(db).to_node(&root);
            (censor_attr_input(invoc_attr_index, &node), node)
        }
    };

    let (mut tt, undo_info) = {
        let syntax = item_node.syntax();
        let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default();
        let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, loc.call_site);
        fixups.append.retain(|it, _| match it {
            syntax::NodeOrToken::Token(_) => true,
            it => !censor.contains(it) && !censor_cfg.contains(it),
        });
        fixups.remove.extend(censor);
        fixups.remove.extend(censor_cfg);

        (
            mbe::syntax_node_to_token_tree_modified(
                syntax,
                map,
                fixups.append,
                fixups.remove,
                loc.call_site,
            ),
            fixups.undo_info,
        )
    };

    if loc.def.is_proc_macro() {
        // proc macros expect their inputs without parentheses, MBEs expect it with them included
        tt.delimiter.kind = tt::DelimiterKind::Invisible;
    }

    ValueResult::ok((Arc::new(tt), undo_info))
}

// FIXME: Censoring info should be calculated by the caller! Namely by name resolution
/// Derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped
fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet<SyntaxElement> {
    // FIXME: handle `cfg_attr`
    cov_mark::hit!(derive_censoring);
    collect_attrs(node)
        .take(derive_attr_index.ast_index() + 1)
        .filter_map(|(_, attr)| Either::left(attr))
        // FIXME, this resolution should not be done syntactically
        // derive is a proper macro now, no longer builtin
        // But we do not have resolution at this stage, this means
        // we need to know about all macro calls for the given ast item here
        // so we require some kind of mapping...
        .filter(|attr| attr.simple_name().as_deref() == Some("derive"))
        .map(|it| it.syntax().clone().into())
        .collect()
}

/// Attributes expect the invoking attribute to be stripped\
fn censor_attr_input(invoc_attr_index: AttrId, node: &ast::Item) -> FxHashSet<SyntaxElement> {
    // FIXME: handle `cfg_attr`
    cov_mark::hit!(attribute_macro_attr_censoring);
    collect_attrs(node)
        .nth(invoc_attr_index.ast_index())
        .and_then(|(_, attr)| Either::left(attr))
        .map(|attr| attr.syntax().clone().into())
        .into_iter()
        .collect()
}

impl TokenExpander {
    fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
        match id.kind {
            MacroDefKind::Declarative(ast_id) => {
                TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
            }
            MacroDefKind::BuiltIn(expander, _) => TokenExpander::BuiltIn(expander),
            MacroDefKind::BuiltInAttr(expander, _) => TokenExpander::BuiltInAttr(expander),
            MacroDefKind::BuiltInDerive(expander, _) => TokenExpander::BuiltInDerive(expander),
            MacroDefKind::BuiltInEager(expander, ..) => TokenExpander::BuiltInEager(expander),
            MacroDefKind::ProcMacro(expander, ..) => TokenExpander::ProcMacro(expander),
        }
    }
}

enum CowArc<T> {
    Arc(Arc<T>),
    Owned(T),
}

fn macro_expand(
    db: &dyn ExpandDatabase,
    macro_call_id: MacroCallId,
    loc: MacroCallLoc,
) -> ExpandResult<CowArc<tt::Subtree>> {
    let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();

    let ExpandResult { value: tt, err } = match loc.def.kind {
        MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
        _ => {
            let ValueResult { value: (macro_arg, undo_info), err } = db.macro_arg(macro_call_id);
            let format_parse_err = |err: Arc<Box<[SyntaxError]>>| {
                let mut buf = String::new();
                for err in &**err {
                    use std::fmt::Write;
                    _ = write!(buf, "{}, ", err);
                }
                buf.pop();
                buf.pop();
                ExpandError::other(buf)
            };

            let arg = &*macro_arg;
            let res = match loc.def.kind {
                MacroDefKind::Declarative(id) => {
                    db.decl_macro_expander(loc.def.krate, id).expand(db, arg.clone(), macro_call_id)
                }
                MacroDefKind::BuiltIn(it, _) => {
                    it.expand(db, macro_call_id, arg).map_err(Into::into)
                }
                MacroDefKind::BuiltInDerive(it, _) => {
                    it.expand(db, macro_call_id, arg).map_err(Into::into)
                }
                MacroDefKind::BuiltInEager(it, _) => {
                    // This might look a bit odd, but we do not expand the inputs to eager macros here.
                    // Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
                    // That kind of expansion uses the ast id map of an eager macros input though which goes through
                    // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
                    // will end up going through here again, whereas we want to just want to inspect the raw input.
                    // As such we just return the input subtree here.
                    let eager = match &loc.kind {
                        MacroCallKind::FnLike { eager: None, .. } => {
                            return ExpandResult {
                                value: CowArc::Arc(macro_arg.clone()),
                                err: err.map(format_parse_err),
                            };
                        }
                        MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
                        _ => None,
                    };

                    let mut res = it.expand(db, macro_call_id, arg).map_err(Into::into);

                    if let Some(EagerCallInfo { error, .. }) = eager {
                        // FIXME: We should report both errors!
                        res.err = error.clone().or(res.err);
                    }
                    res
                }
                MacroDefKind::BuiltInAttr(it, _) => {
                    let mut res = it.expand(db, macro_call_id, arg);
                    fixup::reverse_fixups(&mut res.value, &undo_info);
                    res
                }
                _ => unreachable!(),
            };
            ExpandResult {
                value: res.value,
                // if the arg had parse errors, show them instead of the expansion errors
                err: err.map(format_parse_err).or(res.err),
            }
        }
    };

    // Skip checking token tree limit for include! macro call
    if !loc.def.is_include() {
        // Set a hard limit for the expanded tt
        if let Err(value) = check_tt_count(&tt) {
            return value.map(|()| {
                CowArc::Owned(tt::Subtree {
                    delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
                    token_trees: Box::new([]),
                })
            });
        }
    }

    ExpandResult { value: CowArc::Owned(tt), err }
}

fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
    let root = db.parse_or_expand(ast.file_id);
    let ast_id_map = &db.ast_id_map(ast.file_id);
    let span_map = &db.span_map(ast.file_id);

    let node = ast_id_map.get(ast.value).to_node(&root);
    let range = ast::HasName::name(&node)
        .map_or_else(|| node.syntax().text_range(), |name| name.syntax().text_range());
    span_map.span_for_range(range)
}

fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
    let loc = db.lookup_intern_macro_call(id);
    let (macro_arg, undo_info) = db.macro_arg(id).value;

    let (expander, ast) = match loc.def.kind {
        MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),
        _ => unreachable!(),
    };

    let attr_arg = match &loc.kind {
        MacroCallKind::Attr { attr_args: Some(attr_args), .. } => Some(&**attr_args),
        _ => None,
    };

    let span = db.proc_macro_span(ast);
    let ExpandResult { value: mut tt, err } = expander.expand(
        db,
        loc.def.krate,
        loc.krate,
        &macro_arg,
        attr_arg,
        span_with_def_site_ctxt(db, span, id),
        span_with_call_site_ctxt(db, span, id),
        span_with_mixed_site_ctxt(db, span, id),
    );

    // Set a hard limit for the expanded tt
    if let Err(value) = check_tt_count(&tt) {
        return value.map(|()| {
            Arc::new(tt::Subtree {
                delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
                token_trees: Box::new([]),
            })
        });
    }

    fixup::reverse_fixups(&mut tt, &undo_info);

    ExpandResult { value: Arc::new(tt), err }
}

fn token_tree_to_syntax_node(
    tt: &tt::Subtree,
    expand_to: ExpandTo,
) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
    let entry_point = match expand_to {
        ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
        ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
        ExpandTo::Pattern => mbe::TopEntryPoint::Pattern,
        ExpandTo::Type => mbe::TopEntryPoint::Type,
        ExpandTo::Expr => mbe::TopEntryPoint::Expr,
    };
    mbe::token_tree_to_syntax_node(tt, entry_point)
}

fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> {
    let count = tt.count();
    if TOKEN_LIMIT.check(count).is_err() {
        Err(ExpandResult {
            value: (),
            err: Some(ExpandError::other(format!(
                "macro invocation exceeds token limit: produced {} tokens, limit is {}",
                count,
                TOKEN_LIMIT.inner(),
            ))),
        })
    } else {
        Ok(())
    }
}

fn setup_syntax_context_root(db: &dyn ExpandDatabase) {
    db.intern_syntax_context(SyntaxContextData::root());
}