cargo-equip 0.19.0

A Cargo subcommand to bundle your code into one `.rs` file for competitive programming.
Documentation
use crate::{ra_proc_macro::ProcMacroExpander, shell::Shell};
use anyhow::{anyhow, bail, Context as _};
use camino::{Utf8Path, Utf8PathBuf};
use fixedbitset::FixedBitSet;
use if_chain::if_chain;
use itertools::Itertools as _;
use maplit::btreemap;
use proc_macro2::{LineColumn, Span, TokenStream, TokenTree};
use quote::{quote, ToTokens};
use std::{
    borrow::Cow,
    collections::{BTreeMap, BTreeSet, VecDeque},
    env, mem,
    ops::Range,
    str,
};
use syn::{
    parse::{ParseStream, Parser as _},
    parse_quote,
    punctuated::{Pair, Punctuated},
    spanned::Spanned,
    visit::{self, Visit},
    Arm, AttrStyle, Attribute, BareFnArg, ConstParam, Expr, ExprArray, ExprAssign, ExprAssignOp,
    ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast,
    ExprClosure, ExprContinue, ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet,
    ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange,
    ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType,
    ExprUnary, ExprUnsafe, ExprWhile, ExprYield, Field, FieldPat, FieldValue, ForeignItemFn,
    ForeignItemMacro, ForeignItemStatic, ForeignItemType, Ident, ImplItemConst, ImplItemMacro,
    ImplItemMethod, ImplItemType, Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn,
    ItemForeignMod, ItemImpl, ItemMacro, ItemMacro2, ItemMod, ItemStatic, ItemStruct, ItemTrait,
    ItemTraitAlias, ItemType, ItemUnion, ItemUse, LifetimeDef, Lit, LitStr, Local, Macro, Meta,
    MetaList, MetaNameValue, NestedMeta, PatBox, PatIdent, PatLit, PatMacro, PatOr, PatPath,
    PatRange, PatReference, PatRest, PatSlice, PatStruct, PatTuple, PatTupleStruct, PatType,
    PatWild, PathSegment, Receiver, Token, TraitItemConst, TraitItemMacro, TraitItemMethod,
    TraitItemType, TypeParam, UseGroup, UseName, UsePath, UseRename, UseTree, Variadic, Variant,
    VisRestricted,
};

pub(crate) fn find_skip_attribute(code: &str) -> anyhow::Result<bool> {
    let syn::File { attrs, .. } = syn::parse_file(code)
        .map_err(|e| anyhow!("{:?}", e))
        .with_context(|| "could not parse the code")?;

    Ok(attrs
        .iter()
        .flat_map(Attribute::parse_meta)
        .flat_map(|meta| match meta {
            Meta::List(meta_list) => Some(meta_list),
            _ => None,
        })
        .filter(|MetaList { path, .. }| path.is_ident("cfg_attr"))
        .any(|MetaList { nested, .. }| {
            matches!(
                *nested.iter().collect::<Vec<_>>(),
                [pred, attr]
                if matches!(
                    cfg_expr::Expression::parse(&pred.to_token_stream().to_string()),
                    Ok(expr)
                    if expr.eval(|pred| match pred {
                        cfg_expr::Predicate::Flag("cargo_equip") => Some(true),
                        _ => None,
                    }) == Some(true)
                ) && *attr == parse_quote!(cargo_equip::skip)
            )
        }))
}

pub(crate) fn indent_code(code: &str, n: usize) -> String {
    let is_safe_to_indent = code.parse::<TokenStream>().map_or(false, |token_stream| {
        !token_stream.into_iter().any(|tt| {
            matches!(
                tt, TokenTree::Literal(lit)
                if lit.span().start().line != lit.span().end().line
            )
        })
    });

    if is_safe_to_indent {
        code.lines()
            .map(|line| match line {
                "" => "\n".to_owned(),
                line => format!("{}{}\n", "    ".repeat(n), line),
            })
            .join("")
    } else {
        code.to_owned()
    }
}

fn replace_ranges(code: &str, replacements: BTreeMap<(LineColumn, LineColumn), String>) -> String {
    if replacements.is_empty() {
        return code.to_owned();
    }
    let replacements = replacements.into_iter().collect::<Vec<_>>();
    let mut replacements = &*replacements;
    let mut skip_until = None;
    let mut ret = "".to_owned();
    let mut lines = code.trim_end().split('\n').enumerate().peekable();
    while let Some((i, s)) = lines.next() {
        for (j, c) in s.chars().enumerate() {
            if_chain! {
                if let Some(((start, end), replacement)) = replacements.get(0);
                if (i, j) == (start.line - 1, start.column);
                then {
                    ret += replacement;
                    if start == end {
                        ret.push(c);
                    } else {
                        skip_until = Some(*end);
                    }
                    replacements = &replacements[1..];
                } else {
                    if !matches!(skip_until, Some(LineColumn { line, column }) if (i, j) < (line - 1, column)) {
                        ret.push(c);
                        skip_until = None;
                    }
                }
            }
        }
        while let Some(((start, end), replacement)) = replacements.get(0) {
            if i == start.line - 1 {
                ret += replacement;
                if start < end {
                    skip_until = Some(*end);
                }
                replacements = &replacements[1..];
            } else {
                break;
            }
        }
        if lines.peek().is_some() || code.ends_with('\n') {
            ret += "\n";
        }
    }

    debug_assert!(syn::parse_file(code).is_ok());

    ret
}

pub(crate) fn insert_prelude_for_main_crate(
    code: &str,
    cargo_equip_mod_name: &Ident,
) -> syn::Result<String> {
    let file = &syn::parse_file(code)?;
    let mut replacements = btreemap!();
    Visitor {
        replacements: &mut replacements,
        cargo_equip_mod_name,
    }
    .visit_file(file);
    return Ok(replace_ranges(code, replacements));

    struct Visitor<'a> {
        replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
        cargo_equip_mod_name: &'a Ident,
    }

    impl Visitor<'_> {
        fn visit_items(&mut self, items: &[Item], crate_root: bool) {
            if let Some(first) = items.first() {
                let pos = first.span().start();
                self.replacements.insert(
                    (pos, pos),
                    format!(
                        "pub use {}{}::prelude::*;\n\n",
                        if crate_root { "" } else { "crate::" },
                        self.cargo_equip_mod_name,
                    ),
                );
            }
            for item in items {
                if let Item::Mod(item) = item {
                    self.visit_item_mod(item);
                }
            }
        }
    }

    impl Visit<'_> for Visitor<'_> {
        fn visit_file(&mut self, i: &syn::File) {
            self.visit_items(&i.items, true);
        }

        fn visit_item_mod(&mut self, i: &ItemMod) {
            if let Some((_, items)) = &i.content {
                self.visit_items(items, false);
            }
        }
    }
}

pub(crate) fn allow_unused_imports_for_seemingly_proc_macros(
    code: &str,
    mut seemingly_proc_macro: impl FnMut(&str, &str) -> bool,
) -> syn::Result<String> {
    let file = &syn::parse_file(code)?;
    let mut replacements = btreemap!();
    Visitor {
        replacements: &mut replacements,
        seemingly_proc_macro: &mut seemingly_proc_macro,
    }
    .visit_file(file);
    return Ok(if replacements.is_empty() {
        code.to_owned()
    } else {
        replace_ranges(code, replacements)
    });

    struct Visitor<'a, P> {
        replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
        seemingly_proc_macro: P,
    }

    impl<P: FnMut(&str, &str) -> bool> Visit<'_> for Visitor<'_, P> {
        fn visit_item_use(&mut self, i: &ItemUse) {
            if let UseTree::Path(UsePath { ident, tree, .. }) = &i.tree {
                match &**tree {
                    UseTree::Name(name)
                        if (self.seemingly_proc_macro)(
                            &ident.to_string(),
                            &name.ident.to_string(),
                        ) =>
                    {
                        self.replacements.insert(
                            (i.span().start(), i.span().start()),
                            "#[allow(unused_imports)]\n".to_owned(),
                        );
                    }
                    UseTree::Group(UseGroup { items, .. }) => {
                        for pair in items.pairs() {
                            let item = pair.value();
                            if let UseTree::Name(name) = item {
                                if (self.seemingly_proc_macro)(
                                    &ident.to_string(),
                                    &name.ident.to_string(),
                                ) {
                                    self.replacements.insert(
                                        (pair.span().start(), pair.span().start()),
                                        "/*".to_owned(),
                                    );
                                    self.replacements.insert(
                                        (pair.span().end(), pair.span().end()),
                                        "*/".to_owned(),
                                    );
                                    self.replacements.insert(
                                        (i.span().end(), i.span().end()),
                                        format!(
                                            "\n#[allow(unused_imports)]\n{}use {}::{};",
                                            (i.vis.to_token_stream().to_string() + " ").trim(),
                                            ident,
                                            name.ident,
                                        ),
                                    );
                                }
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}

fn set_span(mask: &mut [FixedBitSet], span: Span, p: bool) {
    let i1 = span.start().line - 1;
    if span.start().line == span.end().line {
        let l = span.start().column;
        let r = span.end().column;
        mask[i1].set_range(l..r, p);
    } else {
        let i2 = span.end().line - 1;
        let l = span.start().column;
        mask[i1].insert_range(l..);
        for mask in &mut mask[i1 + 1..i2] {
            mask.set_range(.., p);
        }
        let r = span.end().column;
        mask[i2].set_range(..r, p);
    }
}

pub(crate) fn parse_file(code: &str) -> anyhow::Result<syn::File> {
    syn::parse_file(code)
        .map_err(|e| anyhow!("{}", e))
        .with_context(|| "broke the code during modification")
}

pub(crate) fn process_bin<'cm>(
    cargo_equip_mod_name: &Ident,
    src_path: &Utf8Path,
    proc_macro_expander: Option<&mut ProcMacroExpander<'_>>,
    translate_extern_crate_name: impl FnMut(&str) -> Option<String>,
    is_lib_to_bundle: impl FnMut(&str) -> bool,
    context: impl FnOnce() -> (String, &'cm str),
) -> anyhow::Result<String> {
    let mut edit = CodeEdit::new(cargo_equip_mod_name, src_path, context)?;
    if let Some(proc_macro_expander) = proc_macro_expander {
        edit.expand_proc_macros(proc_macro_expander)?;
    }
    edit.translate_extern_crate_paths(translate_extern_crate_name)?;
    edit.process_extern_crate_in_bin(is_lib_to_bundle)?;
    edit.finish()
}

pub(crate) struct CodeEdit<'opt> {
    cargo_equip_mod_name: &'opt Ident,
    has_local_inner_macros_attr: bool,
    string: String,
    file: syn::File,
    replacements: BTreeMap<(LineColumn, LineColumn), String>,
}

impl<'opt> CodeEdit<'opt> {
    pub(crate) fn new<'cm>(
        cargo_equip_mod_name: &'opt Ident,
        src_path: &Utf8Path,
        err_context: impl FnOnce() -> (String, &'cm str),
    ) -> anyhow::Result<Self> {
        return (|| {
            Self::from_code(cargo_equip_mod_name, &expand_mods(src_path, 0)?)
                .map_err(anyhow::Error::from)
        })()
        .with_context(|| {
            let (crate_name, package_id) = err_context();
            format!("could not expand `{}` from `{}`", crate_name, package_id)
        });

        fn expand_mods(src_path: &Utf8Path, depth: usize) -> anyhow::Result<String> {
            let content = cargo_util::paths::read(src_path.as_ref())?;

            let syn::File { items, .. } = syn::parse_file(&content)
                .map_err(|e| anyhow!("{:?}", e))
                .with_context(|| format!("could not parse `{}`", src_path))?;

            let replacements = items
                .into_iter()
                .flat_map(|item| match item {
                    Item::Mod(ItemMod {
                        attrs,
                        ident,
                        content: None,
                        semi,
                        ..
                    }) => Some((attrs, ident, semi)),
                    _ => None,
                })
                .map(|(attrs, ident, semi)| {
                    let paths = if let Some(path) = attrs
                        .iter()
                        .flat_map(Attribute::parse_meta)
                        .flat_map(|meta| match meta {
                            Meta::NameValue(name_value) => Some(name_value),
                            _ => None,
                        })
                        .filter(|MetaNameValue { path, .. }| {
                            matches!(path.get_ident(), Some(i) if i == "path")
                        })
                        .find_map(|MetaNameValue { lit, .. }| match lit {
                            Lit::Str(s) => Some(s.value()),
                            _ => None,
                        }) {
                        vec![src_path.with_file_name("").join(path)]
                    } else if depth == 0 || src_path.file_name() == Some("mod.rs") {
                        vec![
                            src_path
                                .with_file_name(&ident.to_string())
                                .with_extension("rs"),
                            src_path.with_file_name(&ident.to_string()).join("mod.rs"),
                        ]
                    } else {
                        vec![
                            src_path
                                .with_extension("")
                                .with_file_name(&ident.to_string())
                                .with_extension("rs"),
                            src_path
                                .with_extension("")
                                .with_file_name(&ident.to_string())
                                .join("mod.rs"),
                        ]
                    };

                    if let Some(path) = paths.iter().find(|p| p.exists()) {
                        let start = semi.span().start();
                        let end = semi.span().end();
                        let content = expand_mods(path, depth + 1)?;
                        let content = indent_code(&content, depth + 1);
                        let content = format!(" {{\n{}{}}}", content, "    ".repeat(depth + 1));
                        Ok(((start, end), content))
                    } else {
                        bail!("one of {:?} does not exist", paths);
                    }
                })
                .collect::<anyhow::Result<_>>()?;

            Ok(replace_ranges(&content, replacements))
        }
    }

    fn from_code(cargo_equip_mod_name: &'opt Ident, string: &str) -> syn::Result<Self> {
        let file = syn::parse_file(string)?;
        return Ok(Self {
            cargo_equip_mod_name,
            has_local_inner_macros_attr: check_local_inner_macros(&file),
            string: string.to_owned(),
            file,
            replacements: btreemap!(),
        });

        fn check_local_inner_macros(file: &syn::File) -> bool {
            let mut out = false;
            Visitor { out: &mut out }.visit_file(file);
            return out;

            struct Visitor<'a> {
                out: &'a mut bool,
            }

            impl Visit<'_> for Visitor<'_> {
                fn visit_item_macro(&mut self, i: &ItemMacro) {
                    *self.out |= i
                        .attrs
                        .iter()
                        .flat_map(Attribute::parse_meta)
                        .flat_map(|meta| match meta {
                            Meta::List(MetaList { path, nested, .. }) => Some((path, nested)),
                            _ => None,
                        })
                        .any(|(path, nested)| {
                            path.is_ident("macro_export")
                                && nested.iter().any(|meta| {
                                    matches!(
                                        meta,
                                        NestedMeta::Meta(Meta::Path(path))
                                        if path.is_ident("local_inner_macros")
                                    )
                                })
                        });
                }
            }
        }
    }

    pub(crate) fn has_local_inner_macros_attr(&self) -> bool {
        self.has_local_inner_macros_attr
    }

    pub(crate) fn finish(mut self) -> anyhow::Result<String> {
        self.apply()?;
        Ok(self.string)
    }

    fn apply(&mut self) -> anyhow::Result<()> {
        if !self.replacements.is_empty() {
            self.force_apply()?;
        }
        Ok(())
    }

    fn force_apply(&mut self) -> anyhow::Result<()> {
        self.string = replace_ranges(&self.string, mem::take(&mut self.replacements));
        self.file =
            syn::parse_file(&self.string).with_context(|| "broke the code during modification")?;
        Ok(())
    }

    fn process_extern_crate_in_bin(
        &mut self,
        is_lib_to_bundle: impl FnMut(&str) -> bool,
    ) -> anyhow::Result<()> {
        self.apply()?;
        Visitor {
            replacements: &mut self.replacements,
            cargo_equip_mod_name: self.cargo_equip_mod_name,
            is_lib_to_bundle,
        }
        .visit_file(&self.file);
        return Ok(());

        struct Visitor<'a, F> {
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
            cargo_equip_mod_name: &'a Ident,
            is_lib_to_bundle: F,
        }

        impl<F: FnMut(&str) -> bool> Visit<'_> for Visitor<'_, F> {
            fn visit_item_extern_crate(&mut self, item_use: &ItemExternCrate) {
                let ItemExternCrate {
                    attrs,
                    vis,
                    ident,
                    rename,
                    ..
                } = item_use;

                if (self.is_lib_to_bundle)(&ident.to_string()) {
                    let is_macro_use = attrs
                        .iter()
                        .flat_map(Attribute::parse_meta)
                        .any(|m| m.path().is_ident("macro_use"));
                    let vis = vis.to_token_stream();

                    let mut insertion = "".to_owned();

                    if let Some((_, rename)) = rename {
                        if rename != "_" {
                            insertion = format!(
                                "{} use crate::{}::crates::{} as {};",
                                vis, self.cargo_equip_mod_name, ident, rename
                            );
                        }
                    } else {
                        insertion = format!(
                            "{} use crate::{}::crates::{};",
                            vis, self.cargo_equip_mod_name, ident,
                        );
                    }

                    if is_macro_use {
                        insertion += &format!(
                            "{} use crate::{}::macros::{}::*;",
                            vis, self.cargo_equip_mod_name, ident,
                        );
                    }

                    let insertion = insertion.trim_start();

                    let pos = item_use.span().start();
                    self.replacements.insert((pos, pos), "/*".to_owned());
                    let pos = item_use.span().end();
                    self.replacements
                        .insert((pos, pos), "*/".to_owned() + insertion);
                }
            }
        }
    }

    pub(crate) fn process_extern_crates_in_lib(
        &mut self,
        convert_extern_crate_name: impl FnMut(&str) -> Option<String>,
        shell: &mut Shell,
    ) -> anyhow::Result<()> {
        self.apply()?;

        for item in &self.file.items {
            if let Item::ExternCrate(ItemExternCrate {
                vis,
                ident,
                rename: Some((_, rename)),
                ..
            }) = item
            {
                shell.warn(format!(
                    "declaring `extern crate .. as ..` in a root module is not recommended: \
                     `{} extern crate {} as {}`",
                    vis.to_token_stream(),
                    ident,
                    rename,
                ))?;
            }
        }

        Visitor {
            replacements: &mut self.replacements,
            cargo_equip_mod_name: self.cargo_equip_mod_name,
            convert_extern_crate_name,
        }
        .visit_file(&self.file);
        return Ok(());

        struct Visitor<'a, F> {
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
            cargo_equip_mod_name: &'a Ident,
            convert_extern_crate_name: F,
        }

        impl<F: FnMut(&str) -> Option<String>> Visit<'_> for Visitor<'_, F> {
            fn visit_item_extern_crate(&mut self, item_use: &ItemExternCrate) {
                let ItemExternCrate {
                    attrs,
                    vis,
                    ident,
                    rename,
                    semi_token,
                    ..
                } = item_use;

                if let Some(to) = (self.convert_extern_crate_name)(&ident.to_string()) {
                    let to = Ident::new(&to, Span::call_site());
                    let Self {
                        cargo_equip_mod_name,
                        ..
                    } = self;
                    self.replacements.insert(
                        (item_use.span().start(), semi_token.span().end()),
                        if let Some((_, rename)) = rename {
                            quote!(
                                #(#attrs)* #vis use crate::#cargo_equip_mod_name::crates::#to as #rename;
                            )
                            .to_string()
                        } else {
                            quote!(
                                #(#attrs)* #vis use crate::#cargo_equip_mod_name::crates::#to as #ident;
                            )
                            .to_string()
                        },
                    );
                }
            }
        }
    }

    pub(crate) fn expand_proc_macros(
        &mut self,
        expander: &mut ProcMacroExpander<'_>,
    ) -> anyhow::Result<()> {
        self.apply()?;

        loop {
            self.force_apply()?;

            let code_lines = &self.string.split('\n').collect::<Vec<_>>();

            let mut output = Ok(None);
            AttributeMacroVisitor {
                expander,
                output: &mut output,
            }
            .visit_file(&self.file);

            if let Some((span, expansion)) = output? {
                let end = to_index(code_lines, span.end());
                let start = to_index(code_lines, span.start());
                self.string
                    .insert_str(end, &format!("*/{}", minify_group(expansion)));
                self.string.insert_str(start, "/*");

                continue;
            }

            let mut output = Ok(None);
            DeriveMacroVisitor {
                expander,
                output: &mut output,
            }
            .visit_file(&self.file);

            if let Some((expansion, item_span, macro_path_span, comma_span)) = output? {
                let insert_at = to_index(code_lines, item_span.end());
                let comma_end = comma_span.map(|comma_end| to_index(code_lines, comma_end));
                let path_range = to_range(code_lines, macro_path_span);

                self.string.insert_str(insert_at, &minify_group(expansion));
                let end = if let Some(comma_end) = comma_end {
                    comma_end
                } else {
                    path_range.end
                };
                self.string.insert_str(end, "*/");
                self.string.insert_str(path_range.start, "/*");

                continue;
            }

            let mut output = Ok(None);
            FunctionLikeMacroVisitor {
                expander,
                output: &mut output,
            }
            .visit_file(&self.file);

            if let Some((span, expansion)) = output? {
                let i1 = to_index(code_lines, span.end());
                let i2 = to_index(code_lines, span.start());
                self.string
                    .insert_str(i1, &format!("*/{}", minify_group(expansion)));
                self.string.insert_str(i2, "/*");
                continue;
            }

            return Ok(());
        }

        struct AttributeMacroVisitor<'a, 'msg> {
            expander: &'a mut ProcMacroExpander<'msg>,
            output: &'a mut anyhow::Result<Option<(Span, proc_macro2::Group)>>,
        }

        impl AttributeMacroVisitor<'_, '_> {
            fn visit_item_with_attrs<'a, T: ToTokens + Clone + 'a>(
                &mut self,
                i: &'a T,
                attrs: &[Attribute],
                remove_attr: fn(&mut T, usize) -> Attribute,
                visit: fn(&mut Self, &'a T),
            ) {
                if !matches!(self.output, Ok(None)) {
                    return;
                }

                if let Some(result) = attrs
                    .iter()
                    .enumerate()
                    .filter(|(_, Attribute { style, .. })| *style == AttrStyle::Outer)
                    .find_map(|(nth, attr)| {
                        let Self { expander, .. } = self;
                        let macro_name = attr.path.get_ident()?.to_string();
                        expander
                            .attempt_expand_attr(
                                &macro_name,
                                || {
                                    let i = &mut i.clone();
                                    remove_attr(i, nth);
                                    i.to_token_stream()
                                },
                                || {
                                    proc_macro2::Group::new(
                                        proc_macro2::Delimiter::None,
                                        syn::parse2::<proc_macro2::Group>(attr.tokens.clone())
                                            .map(|attr| attr.stream())
                                            .unwrap_or_default(),
                                    )
                                },
                            )
                            .transpose()
                    })
                {
                    *self.output = match result {
                        Ok(expansion) => Ok(Some((i.span(), expansion))),
                        Err(err) => Err(err),
                    };
                } else {
                    visit(self, i);
                }
            }
        }

        macro_rules! impl_visits {
            ($(fn $method:ident(&mut self, _: &'_ $ty:path) { _(_, _, _, $visit:path) })*) => {
                $(
                    fn $method(&mut self, i: &'_ $ty) {
                        self.visit_item_with_attrs(i, &i.attrs, |i, nth| i.attrs.remove(nth), $visit)
                    }
                )*
            };
        }

        impl Visit<'_> for AttributeMacroVisitor<'_, '_> {
            impl_visits! {
                fn visit_item_const       (&mut self, _: &'_ ItemConst      ) { _(_, _, _, visit::visit_item_const       ) }
                fn visit_item_enum        (&mut self, _: &'_ ItemEnum       ) { _(_, _, _, visit::visit_item_enum        ) }
                fn visit_item_extern_crate(&mut self, _: &'_ ItemExternCrate) { _(_, _, _, visit::visit_item_extern_crate) }
                fn visit_item_fn          (&mut self, _: &'_ ItemFn         ) { _(_, _, _, visit::visit_item_fn          ) }
                fn visit_item_foreign_mod (&mut self, _: &'_ ItemForeignMod ) { _(_, _, _, visit::visit_item_foreign_mod ) }
                fn visit_item_impl        (&mut self, _: &'_ ItemImpl       ) { _(_, _, _, visit::visit_item_impl        ) }
                fn visit_item_macro       (&mut self, _: &'_ ItemMacro      ) { _(_, _, _, visit::visit_item_macro       ) }
                fn visit_item_macro2      (&mut self, _: &'_ ItemMacro2     ) { _(_, _, _, visit::visit_item_macro2      ) }
                fn visit_item_mod         (&mut self, _: &'_ ItemMod        ) { _(_, _, _, visit::visit_item_mod         ) }
                fn visit_item_static      (&mut self, _: &'_ ItemStatic     ) { _(_, _, _, visit::visit_item_static      ) }
                fn visit_item_struct      (&mut self, _: &'_ ItemStruct     ) { _(_, _, _, visit::visit_item_struct      ) }
                fn visit_item_trait       (&mut self, _: &'_ ItemTrait      ) { _(_, _, _, visit::visit_item_trait       ) }
                fn visit_item_trait_alias (&mut self, _: &'_ ItemTraitAlias ) { _(_, _, _, visit::visit_item_trait_alias ) }
                fn visit_item_type        (&mut self, _: &'_ ItemType       ) { _(_, _, _, visit::visit_item_type        ) }
                fn visit_item_union       (&mut self, _: &'_ ItemUnion      ) { _(_, _, _, visit::visit_item_union       ) }
                fn visit_item_use         (&mut self, _: &'_ ItemUse        ) { _(_, _, _, visit::visit_item_use         ) }
            }
        }

        #[allow(clippy::type_complexity)]
        struct DeriveMacroVisitor<'a, 'msg> {
            expander: &'a mut ProcMacroExpander<'msg>,
            output: &'a mut anyhow::Result<
                Option<(proc_macro2::Group, Span, Span, Option<LineColumn>)>,
            >,
        }

        impl DeriveMacroVisitor<'_, '_> {
            fn visit_struct_enum_union(&mut self, i: impl ToTokens, attrs: &[Attribute]) {
                if !matches!(self.output, Ok(None)) {
                    return;
                }

                if let Some(result) = attrs
                    .iter()
                    .flat_map(Attribute::parse_meta)
                    .flat_map(|meta| match meta {
                        Meta::List(list_meta) => Some(list_meta),
                        _ => None,
                    })
                    .filter(|MetaList { path, .. }| path.is_ident("derive"))
                    .flat_map(|MetaList { nested, .. }| nested.into_pairs())
                    .flat_map(|pair| {
                        fn get_ident(nested_meta: &NestedMeta) -> Option<String> {
                            if let NestedMeta::Meta(Meta::Path(path)) = nested_meta {
                                path.get_ident().map(ToString::to_string)
                            } else {
                                None
                            }
                        }

                        match pair {
                            Pair::Punctuated(m, p) => {
                                Some((get_ident(&m)?, m.span(), Some(p.span().end())))
                            }
                            Pair::End(m) => Some((get_ident(&m)?, m.span(), None)),
                        }
                    })
                    .find_map(|(macro_name, path_span, comma_end)| {
                        let Self { expander, .. } = self;
                        expander
                            .attempt_expand_custom_derive(&macro_name, || i.to_token_stream())
                            .transpose()
                            .map(move |expansion| {
                                expansion.map(move |expansion| (expansion, path_span, comma_end))
                            })
                    })
                {
                    *self.output = match result {
                        Ok((expansion, path_span, comma_end)) => {
                            Ok(Some((expansion, i.span(), path_span, comma_end)))
                        }
                        Err(err) => Err(err),
                    };
                }
            }
        }

        impl Visit<'_> for DeriveMacroVisitor<'_, '_> {
            fn visit_item_struct(&mut self, i: &'_ ItemStruct) {
                self.visit_struct_enum_union(i, &i.attrs);
            }

            fn visit_item_enum(&mut self, i: &'_ ItemEnum) {
                self.visit_struct_enum_union(i, &i.attrs);
            }

            fn visit_item_union(&mut self, i: &'_ ItemUnion) {
                self.visit_struct_enum_union(i, &i.attrs);
            }
        }

        struct FunctionLikeMacroVisitor<'a, 'msg> {
            expander: &'a mut ProcMacroExpander<'msg>,
            output: &'a mut anyhow::Result<Option<(Span, proc_macro2::Group)>>,
        }

        impl Visit<'_> for FunctionLikeMacroVisitor<'_, '_> {
            fn visit_item_macro(&mut self, i: &'_ ItemMacro) {
                if i.ident.is_none() {
                    self.visit_macro(&i.mac);
                }
            }

            fn visit_macro(&mut self, i: &'_ Macro) {
                if !matches!(self.output, Ok(None)) {
                    return;
                }

                if let Some(macro_name) = i.path.get_ident() {
                    let Self { expander, .. } = self;
                    let expansion = expander
                        .attempt_expand_func_like(&macro_name.to_string(), || i.tokens.clone());

                    *self.output = match expansion {
                        Ok(Some(expansion)) => Ok(Some((i.span(), expansion))),
                        Ok(None) => Ok(None),
                        Err(err) => Err(err),
                    };
                }
            }
        }

        fn to_range(lines: &[&str], span: Span) -> Range<usize> {
            to_index(lines, span.start())..to_index(lines, span.end())
        }

        fn to_index(lines: &[&str], loc: LineColumn) -> usize {
            lines[..loc.line - 1]
                .iter()
                .map(|s| s.len() + 1)
                .sum::<usize>()
                + lines[loc.line - 1]
                    .char_indices()
                    .nth(loc.column)
                    .map(|(i, _)| i)
                    .unwrap_or_else(|| lines[loc.line - 1].len())
        }

        fn minify_group(group: proc_macro2::Group) -> String {
            rustminify::minify_tokens(TokenTree::from(group).into())
        }
    }

    pub(crate) fn expand_includes(&mut self, out_dir: &Utf8Path) -> anyhow::Result<()> {
        self.apply()?;
        Visitor {
            out_dir,
            replacements: &mut self.replacements,
        }
        .visit_file(&self.file);
        return Ok(());

        struct Visitor<'a> {
            out_dir: &'a Utf8Path,
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
        }

        impl Visitor<'_> {
            fn resolve(&self, expr: &Expr) -> Option<String> {
                if let Expr::Macro(ExprMacro {
                    mac: Macro { path, tokens, .. },
                    ..
                }) = expr
                {
                    if [parse_quote!(::core::concat), parse_quote!(::std::concat)].contains(path) {
                        (|parse_stream: ParseStream<'_>| {
                            Punctuated::<Expr, Token![,]>::parse_separated_nonempty(parse_stream)
                        })
                        .parse2(tokens.clone())
                        .ok()?
                        .iter()
                        .map(|expr| self.resolve(expr))
                        .collect()
                    } else if [parse_quote!(::core::env), parse_quote!(::std::env)].contains(path) {
                        let name = syn::parse2::<LitStr>(tokens.clone()).ok()?.value();
                        if name == "OUT_DIR" {
                            Some(self.out_dir.as_str().to_owned())
                        } else {
                            env::var(name).ok()
                        }
                    } else {
                        None
                    }
                } else if let Expr::Lit(ExprLit {
                    lit: Lit::Str(lit_str),
                    ..
                }) = expr
                {
                    Some(lit_str.value())
                } else {
                    None
                }
            }
        }

        impl Visit<'_> for Visitor<'_> {
            fn visit_item_macro(&mut self, i: &ItemMacro) {
                if i.ident.is_none()
                    && [parse_quote!(::core::include), parse_quote!(::std::include)]
                        .contains(&i.mac.path)
                {
                    if let Ok(expr) = syn::parse2(i.mac.tokens.clone()) {
                        if let Some(path) = self.resolve(&expr) {
                            let path = Utf8PathBuf::from(path);
                            if path.is_absolute() {
                                if let Ok(content) = cargo_util::paths::read(path.as_ref()) {
                                    self.replacements
                                        .insert((i.span().start(), i.span().end()), content);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    pub(crate) fn translate_extern_crate_paths(
        &mut self,
        translate_extern_crate_name: impl FnMut(&str) -> Option<String>,
    ) -> anyhow::Result<()> {
        self.apply()?;
        Visitor {
            replacements: &mut self.replacements,
            cargo_equip_mod_name: self.cargo_equip_mod_name,
            translate_extern_crate_name,
        }
        .visit_file(&self.file);
        return Ok(());

        struct Visitor<'a, F> {
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
            cargo_equip_mod_name: &'a Ident,
            translate_extern_crate_name: F,
        }

        impl<F: FnMut(&str) -> Option<String>> Visitor<'_, F> {
            fn attempt_translate(&mut self, leading_colon: Span, extern_crate_name: &Ident) {
                if let Some(pseudo_extern_crate_name) =
                    (self.translate_extern_crate_name)(&extern_crate_name.to_string())
                {
                    self.replacements.insert(
                        (leading_colon.start(), leading_colon.end()),
                        format!("/*::*/crate::{}::crates::", self.cargo_equip_mod_name),
                    );

                    if extern_crate_name != &*pseudo_extern_crate_name {
                        let span = extern_crate_name.span();
                        self.replacements.insert(
                            (span.start(), span.end()),
                            format!("/*{}*/{}", extern_crate_name, pseudo_extern_crate_name),
                        );
                    }
                }
            }
        }

        impl<F: FnMut(&str) -> Option<String>> Visit<'_> for Visitor<'_, F> {
            fn visit_item_use(&mut self, i: &'_ ItemUse) {
                if let Some(leading_colon) = i.leading_colon {
                    for extern_crate_name in extract_first_segments(&i.tree) {
                        self.attempt_translate(leading_colon.span(), extern_crate_name);
                    }
                }

                fn extract_first_segments(tree: &UseTree) -> Vec<&Ident> {
                    match tree {
                        UseTree::Path(UsePath { ident, .. })
                        | UseTree::Name(UseName { ident })
                        | UseTree::Rename(UseRename { ident, .. }) => {
                            vec![ident]
                        }
                        UseTree::Glob(_) => vec![],
                        UseTree::Group(UseGroup { items, .. }) => {
                            items.iter().flat_map(extract_first_segments).collect()
                        }
                    }
                }
            }

            fn visit_path(&mut self, i: &'_ syn::Path) {
                if let Some(leading_colon) = i.leading_colon {
                    let PathSegment { ident, .. } = i
                        .segments
                        .last()
                        .expect("`syn::Path::segments` is considered not to be empty");
                    self.attempt_translate(leading_colon.span(), ident);
                }
            }
        }
    }

    pub(crate) fn translate_crate_path(&mut self, extern_crate_name: &str) -> anyhow::Result<()> {
        self.apply()?;
        Visitor {
            extern_crate_name,
            cargo_equip_mod_name: self.cargo_equip_mod_name,
            replacements: &mut self.replacements,
        }
        .visit_file(&self.file);
        return Ok(());

        struct Visitor<'a> {
            extern_crate_name: &'a str,
            cargo_equip_mod_name: &'a Ident,
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
        }

        impl Visitor<'_> {
            fn insert(&mut self, crate_token: &Ident) {
                let pos = crate_token.span().end();
                self.replacements.insert(
                    (pos, pos),
                    format!(
                        "::{}::crates::{}",
                        self.cargo_equip_mod_name, self.extern_crate_name,
                    ),
                );
            }
        }

        impl Visit<'_> for Visitor<'_> {
            fn visit_path(&mut self, path: &'_ syn::Path) {
                if let (None, Some(first)) = (path.leading_colon, path.segments.first()) {
                    if first.ident == "crate" && first.arguments.is_empty() {
                        self.insert(&first.ident);
                    }
                }
            }

            fn visit_item_use(&mut self, item_use: &'_ ItemUse) {
                if item_use.leading_colon.is_none() {
                    self.visit_use_tree(&item_use.tree);
                }
            }

            fn visit_use_tree(&mut self, use_tree: &UseTree) {
                match &use_tree {
                    UseTree::Path(UsePath { ident, .. })
                    | UseTree::Name(UseName { ident })
                    | UseTree::Rename(UseRename { ident, .. })
                        if ident == "crate" =>
                    {
                        self.insert(ident);
                    }
                    UseTree::Group(UseGroup { items, .. }) => {
                        for item in items {
                            self.visit_use_tree(item);
                        }
                    }
                    _ => {}
                }
            }

            fn visit_vis_restricted(&mut self, vis_restricted: &VisRestricted) {
                if vis_restricted.in_token.is_some() {
                    self.visit_path(&vis_restricted.path);
                } else if let Some(ident) = vis_restricted.path.get_ident() {
                    if ident == "crate" {
                        let pos = vis_restricted.path.span().start();
                        self.replacements.insert((pos, pos), "in ".to_owned());

                        self.insert(ident);
                    }
                }
            }
        }
    }

    pub(crate) fn modify_declarative_macros(
        &mut self,
        pseudo_extern_crate_name: &str,
    ) -> anyhow::Result<String> {
        self.apply()?;

        let mut macro_names = btreemap!();

        for item_macro in collect_item_macros(&self.file) {
            if let ItemMacro {
                attrs,
                ident: Some(ident),
                mac: Macro { tokens, .. },
                ..
            } = item_macro
            {
                replace_dollar_crates(
                    tokens.clone(),
                    self.cargo_equip_mod_name,
                    pseudo_extern_crate_name,
                    &mut self.replacements,
                );
                if attrs
                    .iter()
                    .flat_map(Attribute::parse_meta)
                    .any(|m| m.path().is_ident("macro_export"))
                {
                    let rename = format!(
                        "{}_macro_def_{}_{}",
                        self.cargo_equip_mod_name, pseudo_extern_crate_name, ident,
                    );
                    self.replacements.insert(
                        (ident.span().start(), ident.span().end()),
                        format!("/*{}*/{}", ident, rename),
                    );
                    let pos = item_macro.span().end();
                    self.replacements.insert(
                        (pos, pos),
                        format!(
                            "\nmacro_rules!{}{{($($tt:tt)*)=>(crate::{}!{{$($tt)*}})}}",
                            ident, rename,
                        ),
                    );
                    macro_names.insert(rename, ident);
                }
            }
        }

        if !macro_names.is_empty() {
            if let Some(first) = self.file.items.first() {
                let pos = first.span().start();
                self.replacements.entry((pos, pos)).or_default().insert_str(
                    0,
                    &format!(
                        "pub use crate::{}::macros::{}::*;",
                        self.cargo_equip_mod_name, pseudo_extern_crate_name,
                    ),
                );
            }
        }

        let macro_mod_content = if macro_names.is_empty() {
            "".to_owned()
        } else {
            format!(
                "pub use crate::{}{}{};\n",
                if macro_names.len() > 1 { "{" } else { "" },
                macro_names
                    .iter()
                    .map(|(rename, name)| if *name == rename {
                        name.to_string()
                    } else {
                        format!("{} as {}", rename, name)
                    })
                    .format(", "),
                if macro_names.len() > 1 { "}" } else { "" },
            )
        };

        return Ok(macro_mod_content);

        fn collect_item_macros(file: &syn::File) -> Vec<&ItemMacro> {
            let mut acc = vec![];
            Visitor { acc: &mut acc }.visit_file(file);
            return acc;

            struct Visitor<'a, 'b> {
                acc: &'b mut Vec<&'a ItemMacro>,
            }

            impl<'a, 'b> Visit<'a> for Visitor<'a, 'b> {
                fn visit_item_macro(&mut self, i: &'a ItemMacro) {
                    self.acc.push(i);
                }
            }
        }

        fn replace_dollar_crates(
            token_stream: TokenStream,
            cargo_equip_mod_name: &Ident,
            pseudo_extern_crate_name: &str,
            acc: &mut BTreeMap<(LineColumn, LineColumn), String>,
        ) {
            let mut token_stream = token_stream.into_iter().peekable();

            if let Some(proc_macro2::TokenTree::Group(group)) = token_stream.peek() {
                replace_dollar_crates(
                    group.stream(),
                    cargo_equip_mod_name,
                    pseudo_extern_crate_name,
                    acc,
                );
            }

            for (tt1, tt2) in token_stream.tuple_windows() {
                if let proc_macro2::TokenTree::Group(group) = &tt2 {
                    replace_dollar_crates(
                        group.stream(),
                        cargo_equip_mod_name,
                        pseudo_extern_crate_name,
                        acc,
                    );
                }

                if matches!(
                    (&tt1, &tt2),
                    (proc_macro2::TokenTree::Punct(p), proc_macro2::TokenTree::Ident(i))
                    if p.as_char() == '$' && i == "crate"
                ) {
                    let pos = tt2.span().end();
                    acc.insert(
                        (pos, pos),
                        format!(
                            "::{}::crates::{}",
                            cargo_equip_mod_name, pseudo_extern_crate_name,
                        ),
                    );
                }
            }
        }
    }

    pub(crate) fn resolve_pseudo_prelude(
        &mut self,
        pseudo_extern_crate_name: &str,
        libs_with_local_inner_macros: &BTreeSet<&str>,
        extern_crate_name_translation: &BTreeMap<String, String>,
    ) -> anyhow::Result<String> {
        if extern_crate_name_translation.is_empty() && libs_with_local_inner_macros.is_empty() {
            return Ok("".to_owned());
        }

        self.apply()?;

        let syn::File { attrs, items, .. } = &self.file;

        let external_local_inner_macros = {
            let macros = libs_with_local_inner_macros
                .iter()
                .map(|name| format!("{}::*", name))
                .join(", ");
            match libs_with_local_inner_macros.len() {
                0 => None,
                1 => Some(macros),
                _ => Some(format!("{{{}}}", macros)),
            }
        };

        let pseudo_extern_crates = {
            let uses = extern_crate_name_translation
                .iter()
                .map(|(extern_crate_name, pseudo_extern_crate_name)| {
                    if extern_crate_name == pseudo_extern_crate_name {
                        extern_crate_name.clone()
                    } else {
                        format!("{} as {}", pseudo_extern_crate_name, extern_crate_name)
                    }
                })
                .join(", ");
            match extern_crate_name_translation.len() {
                0 => None,
                1 => Some(uses),
                _ => Some(format!("{{{}}}", uses)),
            }
        };

        let mut prelude = "".to_owned();
        if let Some(external_local_inner_macros) = &external_local_inner_macros {
            prelude += &format!(
                "pub(in crate::{0}) use crate::{0}::macros::{1};",
                self.cargo_equip_mod_name, external_local_inner_macros,
            );
        }
        if let Some(pseudo_extern_crates) = &pseudo_extern_crates {
            prelude += &format!(
                "pub(in crate::{0}) use crate::{0}::crates::{1};",
                self.cargo_equip_mod_name, pseudo_extern_crates,
            );
        }

        self.replacements.insert(
            {
                let pos = if let Some(item) = items.first() {
                    item.span().start()
                } else if let Some(attr) = attrs.last() {
                    attr.span().end()
                } else {
                    LineColumn { line: 0, column: 0 }
                };
                (pos, pos)
            },
            {
                format!(
                    "use crate::{}::preludes::{}::*;",
                    self.cargo_equip_mod_name, pseudo_extern_crate_name,
                )
            },
        );

        let mut queue = items
            .iter()
            .flat_map(|item| match item {
                Item::Mod(item_mod) => Some((1, item_mod)),
                _ => None,
            })
            .collect::<VecDeque<_>>();

        while let Some((depth, ItemMod { attrs, content, .. })) = queue.pop_front() {
            let (_, items) = content.as_ref().expect("should be expanded");
            let pos = if let Some(item) = items.first() {
                item.span().start()
            } else if let Some(attr) = attrs.last() {
                attr.span().end()
            } else {
                LineColumn { line: 0, column: 0 }
            };
            self.replacements.insert(
                (pos, pos),
                format!(
                    "use crate::{}::preludes::{}::*;",
                    self.cargo_equip_mod_name, pseudo_extern_crate_name,
                ),
            );
            for item in items {
                if let Item::Mod(item_mod) = item {
                    queue.push_back((depth + 1, item_mod));
                }
            }
        }
        Ok(prelude)
    }

    pub(crate) fn resolve_cfgs(&mut self, features: &[String]) -> anyhow::Result<()> {
        self.apply()?;
        Visitor {
            replacements: &mut self.replacements,
            features,
        }
        .visit_file(&self.file);
        return Ok(());

        struct Visitor<'a> {
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
            features: &'a [String],
        }

        impl Visitor<'_> {
            fn proceed<'a, T: ToTokens>(
                &mut self,
                i: &'a T,
                attrs: fn(&T) -> &[Attribute],
                visit: fn(&mut Self, &'a T),
            ) {
                let sufficiencies = attrs(i)
                    .iter()
                    .flat_map(|a| a.parse_meta().map(|m| (a.span(), m)))
                    .flat_map(|(span, meta)| match meta {
                        Meta::List(meta_list) => Some((span, meta_list)),
                        _ => None,
                    })
                    .filter(|(_, MetaList { path, .. })| path.is_ident("cfg"))
                    .flat_map(|(span, MetaList { nested, .. })| {
                        let expr =
                            cfg_expr::Expression::parse(&nested.to_token_stream().to_string())
                                .ok()?;
                        Some((span, expr))
                    })
                    .map(|(span, expr)| {
                        let sufficiency = expr.eval(|pred| match pred {
                            cfg_expr::Predicate::Test | cfg_expr::Predicate::ProcMacro => {
                                Some(false)
                            }
                            cfg_expr::Predicate::Flag("cargo_equip") => Some(true),
                            cfg_expr::Predicate::Feature(feature) => {
                                Some(self.features.contains(&(*feature).to_owned()))
                            }
                            _ => None,
                        });
                        (span, sufficiency)
                    })
                    .collect::<Vec<_>>();

                if sufficiencies.iter().any(|&(_, p)| p == Some(false)) {
                    self.replacements
                        .insert((i.span().start(), i.span().end()), "".to_owned());
                } else {
                    for (span, p) in sufficiencies {
                        if p == Some(true) {
                            self.replacements
                                .insert((span.start(), span.end()), "".to_owned());
                        }
                    }
                    visit(self, i);
                }
            }
        }

        macro_rules! impl_visits {
            ($(fn $method:ident(&mut self, _: &'_ $ty:path) { _(_, _, $visit:path) })*) => {
                $(
                    fn $method(&mut self, i: &'_ $ty) {
                        self.proceed(i, |$ty { attrs, .. }| attrs, $visit);
                    }
                )*
            };
        }

        impl Visit<'_> for Visitor<'_> {
            impl_visits! {
                fn visit_arm                (&mut self, _: &'_ Arm              ) { _(_, _, visit::visit_arm                ) }
                fn visit_bare_fn_arg        (&mut self, _: &'_ BareFnArg        ) { _(_, _, visit::visit_bare_fn_arg        ) }
                fn visit_const_param        (&mut self, _: &'_ ConstParam       ) { _(_, _, visit::visit_const_param        ) }
                fn visit_expr_array         (&mut self, _: &'_ ExprArray        ) { _(_, _, visit::visit_expr_array         ) }
                fn visit_expr_assign        (&mut self, _: &'_ ExprAssign       ) { _(_, _, visit::visit_expr_assign        ) }
                fn visit_expr_assign_op     (&mut self, _: &'_ ExprAssignOp     ) { _(_, _, visit::visit_expr_assign_op     ) }
                fn visit_expr_async         (&mut self, _: &'_ ExprAsync        ) { _(_, _, visit::visit_expr_async         ) }
                fn visit_expr_await         (&mut self, _: &'_ ExprAwait        ) { _(_, _, visit::visit_expr_await         ) }
                fn visit_expr_binary        (&mut self, _: &'_ ExprBinary       ) { _(_, _, visit::visit_expr_binary        ) }
                fn visit_expr_block         (&mut self, _: &'_ ExprBlock        ) { _(_, _, visit::visit_expr_block         ) }
                fn visit_expr_box           (&mut self, _: &'_ ExprBox          ) { _(_, _, visit::visit_expr_box           ) }
                fn visit_expr_break         (&mut self, _: &'_ ExprBreak        ) { _(_, _, visit::visit_expr_break         ) }
                fn visit_expr_call          (&mut self, _: &'_ ExprCall         ) { _(_, _, visit::visit_expr_call          ) }
                fn visit_expr_cast          (&mut self, _: &'_ ExprCast         ) { _(_, _, visit::visit_expr_cast          ) }
                fn visit_expr_closure       (&mut self, _: &'_ ExprClosure      ) { _(_, _, visit::visit_expr_closure       ) }
                fn visit_expr_continue      (&mut self, _: &'_ ExprContinue     ) { _(_, _, visit::visit_expr_continue      ) }
                fn visit_expr_field         (&mut self, _: &'_ ExprField        ) { _(_, _, visit::visit_expr_field         ) }
                fn visit_expr_for_loop      (&mut self, _: &'_ ExprForLoop      ) { _(_, _, visit::visit_expr_for_loop      ) }
                fn visit_expr_group         (&mut self, _: &'_ ExprGroup        ) { _(_, _, visit::visit_expr_group         ) }
                fn visit_expr_if            (&mut self, _: &'_ ExprIf           ) { _(_, _, visit::visit_expr_if            ) }
                fn visit_expr_index         (&mut self, _: &'_ ExprIndex        ) { _(_, _, visit::visit_expr_index         ) }
                fn visit_expr_let           (&mut self, _: &'_ ExprLet          ) { _(_, _, visit::visit_expr_let           ) }
                fn visit_expr_lit           (&mut self, _: &'_ ExprLit          ) { _(_, _, visit::visit_expr_lit           ) }
                fn visit_expr_loop          (&mut self, _: &'_ ExprLoop         ) { _(_, _, visit::visit_expr_loop          ) }
                fn visit_expr_macro         (&mut self, _: &'_ ExprMacro        ) { _(_, _, visit::visit_expr_macro         ) }
                fn visit_expr_match         (&mut self, _: &'_ ExprMatch        ) { _(_, _, visit::visit_expr_match         ) }
                fn visit_expr_method_call   (&mut self, _: &'_ ExprMethodCall   ) { _(_, _, visit::visit_expr_method_call   ) }
                fn visit_expr_paren         (&mut self, _: &'_ ExprParen        ) { _(_, _, visit::visit_expr_paren         ) }
                fn visit_expr_path          (&mut self, _: &'_ ExprPath         ) { _(_, _, visit::visit_expr_path          ) }
                fn visit_expr_range         (&mut self, _: &'_ ExprRange        ) { _(_, _, visit::visit_expr_range         ) }
                fn visit_expr_reference     (&mut self, _: &'_ ExprReference    ) { _(_, _, visit::visit_expr_reference     ) }
                fn visit_expr_repeat        (&mut self, _: &'_ ExprRepeat       ) { _(_, _, visit::visit_expr_repeat        ) }
                fn visit_expr_return        (&mut self, _: &'_ ExprReturn       ) { _(_, _, visit::visit_expr_return        ) }
                fn visit_expr_struct        (&mut self, _: &'_ ExprStruct       ) { _(_, _, visit::visit_expr_struct        ) }
                fn visit_expr_try           (&mut self, _: &'_ ExprTry          ) { _(_, _, visit::visit_expr_try           ) }
                fn visit_expr_try_block     (&mut self, _: &'_ ExprTryBlock     ) { _(_, _, visit::visit_expr_try_block     ) }
                fn visit_expr_tuple         (&mut self, _: &'_ ExprTuple        ) { _(_, _, visit::visit_expr_tuple         ) }
                fn visit_expr_type          (&mut self, _: &'_ ExprType         ) { _(_, _, visit::visit_expr_type          ) }
                fn visit_expr_unary         (&mut self, _: &'_ ExprUnary        ) { _(_, _, visit::visit_expr_unary         ) }
                fn visit_expr_unsafe        (&mut self, _: &'_ ExprUnsafe       ) { _(_, _, visit::visit_expr_unsafe        ) }
                fn visit_expr_while         (&mut self, _: &'_ ExprWhile        ) { _(_, _, visit::visit_expr_while         ) }
                fn visit_expr_yield         (&mut self, _: &'_ ExprYield        ) { _(_, _, visit::visit_expr_yield         ) }
                fn visit_field              (&mut self, _: &'_ Field            ) { _(_, _, visit::visit_field              ) }
                fn visit_field_pat          (&mut self, _: &'_ FieldPat         ) { _(_, _, visit::visit_field_pat          ) }
                fn visit_field_value        (&mut self, _: &'_ FieldValue       ) { _(_, _, visit::visit_field_value        ) }
                fn visit_file               (&mut self, _: &'_ syn::File        ) { _(_, _, visit::visit_file               ) }
                fn visit_foreign_item_fn    (&mut self, _: &'_ ForeignItemFn    ) { _(_, _, visit::visit_foreign_item_fn    ) }
                fn visit_foreign_item_macro (&mut self, _: &'_ ForeignItemMacro ) { _(_, _, visit::visit_foreign_item_macro ) }
                fn visit_foreign_item_static(&mut self, _: &'_ ForeignItemStatic) { _(_, _, visit::visit_foreign_item_static) }
                fn visit_foreign_item_type  (&mut self, _: &'_ ForeignItemType  ) { _(_, _, visit::visit_foreign_item_type  ) }
                fn visit_impl_item_const    (&mut self, _: &'_ ImplItemConst    ) { _(_, _, visit::visit_impl_item_const    ) }
                fn visit_impl_item_macro    (&mut self, _: &'_ ImplItemMacro    ) { _(_, _, visit::visit_impl_item_macro    ) }
                fn visit_impl_item_method   (&mut self, _: &'_ ImplItemMethod   ) { _(_, _, visit::visit_impl_item_method   ) }
                fn visit_impl_item_type     (&mut self, _: &'_ ImplItemType     ) { _(_, _, visit::visit_impl_item_type     ) }
                fn visit_item_const         (&mut self, _: &'_ ItemConst        ) { _(_, _, visit::visit_item_const         ) }
                fn visit_item_enum          (&mut self, _: &'_ ItemEnum         ) { _(_, _, visit::visit_item_enum          ) }
                fn visit_item_extern_crate  (&mut self, _: &'_ ItemExternCrate  ) { _(_, _, visit::visit_item_extern_crate  ) }
                fn visit_item_fn            (&mut self, _: &'_ ItemFn           ) { _(_, _, visit::visit_item_fn            ) }
                fn visit_item_foreign_mod   (&mut self, _: &'_ ItemForeignMod   ) { _(_, _, visit::visit_item_foreign_mod   ) }
                fn visit_item_impl          (&mut self, _: &'_ ItemImpl         ) { _(_, _, visit::visit_item_impl          ) }
                fn visit_item_macro         (&mut self, _: &'_ ItemMacro        ) { _(_, _, visit::visit_item_macro         ) }
                fn visit_item_macro2        (&mut self, _: &'_ ItemMacro2       ) { _(_, _, visit::visit_item_macro2        ) }
                fn visit_item_mod           (&mut self, _: &'_ ItemMod          ) { _(_, _, visit::visit_item_mod           ) }
                fn visit_item_static        (&mut self, _: &'_ ItemStatic       ) { _(_, _, visit::visit_item_static        ) }
                fn visit_item_struct        (&mut self, _: &'_ ItemStruct       ) { _(_, _, visit::visit_item_struct        ) }
                fn visit_item_trait         (&mut self, _: &'_ ItemTrait        ) { _(_, _, visit::visit_item_trait         ) }
                fn visit_item_trait_alias   (&mut self, _: &'_ ItemTraitAlias   ) { _(_, _, visit::visit_item_trait_alias   ) }
                fn visit_item_type          (&mut self, _: &'_ ItemType         ) { _(_, _, visit::visit_item_type          ) }
                fn visit_item_union         (&mut self, _: &'_ ItemUnion        ) { _(_, _, visit::visit_item_union         ) }
                fn visit_item_use           (&mut self, _: &'_ ItemUse          ) { _(_, _, visit::visit_item_use           ) }
                fn visit_lifetime_def       (&mut self, _: &'_ LifetimeDef      ) { _(_, _, visit::visit_lifetime_def       ) }
                fn visit_local              (&mut self, _: &'_ Local            ) { _(_, _, visit::visit_local              ) }
                fn visit_pat_box            (&mut self, _: &'_ PatBox           ) { _(_, _, visit::visit_pat_box            ) }
                fn visit_pat_ident          (&mut self, _: &'_ PatIdent         ) { _(_, _, visit::visit_pat_ident          ) }
                fn visit_pat_lit            (&mut self, _: &'_ PatLit           ) { _(_, _, visit::visit_pat_lit            ) }
                fn visit_pat_macro          (&mut self, _: &'_ PatMacro         ) { _(_, _, visit::visit_pat_macro          ) }
                fn visit_pat_or             (&mut self, _: &'_ PatOr            ) { _(_, _, visit::visit_pat_or             ) }
                fn visit_pat_path           (&mut self, _: &'_ PatPath          ) { _(_, _, visit::visit_pat_path           ) }
                fn visit_pat_range          (&mut self, _: &'_ PatRange         ) { _(_, _, visit::visit_pat_range          ) }
                fn visit_pat_reference      (&mut self, _: &'_ PatReference     ) { _(_, _, visit::visit_pat_reference      ) }
                fn visit_pat_rest           (&mut self, _: &'_ PatRest          ) { _(_, _, visit::visit_pat_rest           ) }
                fn visit_pat_slice          (&mut self, _: &'_ PatSlice         ) { _(_, _, visit::visit_pat_slice          ) }
                fn visit_pat_struct         (&mut self, _: &'_ PatStruct        ) { _(_, _, visit::visit_pat_struct         ) }
                fn visit_pat_tuple          (&mut self, _: &'_ PatTuple         ) { _(_, _, visit::visit_pat_tuple          ) }
                fn visit_pat_tuple_struct   (&mut self, _: &'_ PatTupleStruct   ) { _(_, _, visit::visit_pat_tuple_struct   ) }
                fn visit_pat_type           (&mut self, _: &'_ PatType          ) { _(_, _, visit::visit_pat_type           ) }
                fn visit_pat_wild           (&mut self, _: &'_ PatWild          ) { _(_, _, visit::visit_pat_wild           ) }
                fn visit_receiver           (&mut self, _: &'_ Receiver         ) { _(_, _, visit::visit_receiver           ) }
                fn visit_trait_item_const   (&mut self, _: &'_ TraitItemConst   ) { _(_, _, visit::visit_trait_item_const   ) }
                fn visit_trait_item_macro   (&mut self, _: &'_ TraitItemMacro   ) { _(_, _, visit::visit_trait_item_macro   ) }
                fn visit_trait_item_method  (&mut self, _: &'_ TraitItemMethod  ) { _(_, _, visit::visit_trait_item_method  ) }
                fn visit_trait_item_type    (&mut self, _: &'_ TraitItemType    ) { _(_, _, visit::visit_trait_item_type    ) }
                fn visit_type_param         (&mut self, _: &'_ TypeParam        ) { _(_, _, visit::visit_type_param         ) }
                fn visit_variadic           (&mut self, _: &'_ Variadic         ) { _(_, _, visit::visit_variadic           ) }
                fn visit_variant            (&mut self, _: &'_ Variant          ) { _(_, _, visit::visit_variant            ) }
            }
        }
    }

    pub(crate) fn allow_missing_docs(&mut self) {
        Visitor {
            replacements: &mut self.replacements,
        }
        .visit_file(&self.file);

        struct Visitor<'a> {
            replacements: &'a mut BTreeMap<(LineColumn, LineColumn), String>,
        }

        impl Visit<'_> for Visitor<'_> {
            fn visit_attribute(&mut self, i: &Attribute) {
                if let Ok(Meta::List(MetaList { path, nested, .. })) = i.parse_meta() {
                    if ["warn", "deny", "forbid"]
                        .iter()
                        .any(|lint| path.is_ident(lint))
                    {
                        for meta in nested {
                            if let NestedMeta::Meta(Meta::Path(path)) = meta {
                                if ["missing_docs", "missing_crate_level_docs"]
                                    .iter()
                                    .any(|lint| path.is_ident(lint))
                                {
                                    let pos = path.span().start();
                                    self.replacements.insert((pos, pos), "/*".to_owned());
                                    let pos = path.span().end();
                                    self.replacements.insert((pos, pos), "*/".to_owned());
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    pub(crate) fn erase_docs(&mut self) -> anyhow::Result<()> {
        return self.erase(
            |mask, token_stream| syn::parse2(token_stream).map(|f| Visitor(mask).visit_file(&f)),
            || "broke the code during erasing doc comments",
        );

        struct Visitor<'a>(&'a mut [FixedBitSet]);

        impl Visit<'_> for Visitor<'_> {
            fn visit_attribute(&mut self, attr: &'_ Attribute) {
                if matches!(attr.parse_meta(), Ok(m) if m.path().is_ident("doc")) {
                    set_span(self.0, attr.span(), true);
                }
            }
        }
    }

    pub(crate) fn erase_comments(&mut self) -> anyhow::Result<()> {
        return self.erase(
            |mask, token_stream| {
                for mask in &mut *mask {
                    mask.insert_range(..);
                }
                visit_token_stream(mask, token_stream);
                Ok(())
            },
            || "broke the code during erasing comments",
        );

        fn visit_token_stream(mask: &mut [FixedBitSet], token_stream: TokenStream) {
            for tt in token_stream {
                if let TokenTree::Group(group) = tt {
                    set_span(mask, group.span_open(), false);
                    visit_token_stream(mask, group.stream());
                    set_span(mask, group.span_close(), false);
                } else {
                    set_span(mask, tt.span(), false);
                }
            }
        }
    }

    fn erase(
        &mut self,
        visit_file: fn(&mut [FixedBitSet], TokenStream) -> syn::Result<()>,
        err_msg: fn() -> &'static str,
    ) -> anyhow::Result<()> {
        self.apply()?;

        let code = &if self.string.contains("\r\n") {
            Cow::from(self.string.replace("\r\n", "\n"))
        } else {
            Cow::from(&self.string)
        };

        let code = if code.starts_with("#!") {
            let (_, code) = code.split_at(code.find('\n').unwrap_or(code.len()));
            code
        } else {
            code
        };

        let token_stream = code
            .parse::<TokenStream>()
            .map_err(|e| anyhow!("{}", e))
            .with_context(|| "broke the code during modification")?;

        let mut erase = code
            .lines()
            .map(|l| FixedBitSet::with_capacity(l.chars().count()))
            .collect::<Vec<_>>();

        visit_file(&mut erase, token_stream)
            .map_err(|e| anyhow!("{:?}", e))
            .with_context(err_msg)?;

        let mut acc = "".to_owned();
        for (line, erase) in code.lines().zip_eq(erase) {
            for (j, ch) in line.chars().enumerate() {
                acc.push(if erase[j] { ' ' } else { ch });
            }
            acc += "\n";
        }
        self.string = acc.trim_start().to_owned();
        self.apply()
    }
}

#[cfg(test)]
mod tests {
    use crate::rust::CodeEdit;
    use pretty_assertions::assert_eq;
    use proc_macro2::Span;
    use syn::Ident;

    thread_local! {
        static DUMMY_MOD_NAME: Ident = Ident::new("__", Span::call_site());
    }

    #[test]
    fn erase_docs() -> anyhow::Result<()> {
        fn test(input: &str, expected: &str) -> anyhow::Result<()> {
            DUMMY_MOD_NAME.with(|dummy_mod_name| {
                let mut edit = CodeEdit::from_code(dummy_mod_name, input)?;
                edit.erase_docs()?;
                assert_eq!(expected, edit.finish()?);
                Ok(())
            })
        }

        test(
            r#"//! aaaaa
//! bbbbb

fn main() {}

/// ccccc
struct Foo;
"#,
            r#"fn main() {}

         
struct Foo;
"#,
        )?;

        test(
            r#"//! モジュールのドキュメント

/// アイテムのドキュメント
fn foo() {}
"#,
            r#"fn foo() {}
"#,
        )
    }

    #[test]
    fn erase_comments() -> anyhow::Result<()> {
        fn test(input: &str, expected: &str) -> anyhow::Result<()> {
            DUMMY_MOD_NAME.with(|dummy_mod_name| {
                let mut edit = CodeEdit::from_code(dummy_mod_name, input)?;
                edit.erase_comments()?;
                assert_eq!(expected, edit.finish()?);
                Ok(())
            })
        }

        test(
            r#"// aaaaa
// bbbbb
fn main() {
    // ccccc
    /*ddddd*/println!("Hi!");/*eeeee*/
    // fffff
}
// ggggg
"#,
            r#"fn main() {
            
             println!("Hi!");         
            
}
        
"#,
        )?;

        test(
            r#"/* aaaaa */ type A = (i64, i64, i64); // bbbbb
"#,
            r#"type A = (i64, i64, i64);         
"#,
        )?;

        test(
            r#"// あああ
/*いいい*/fn foo() {
    let _ = 1 + 1; // ううううう
}
"#,
            r#"fn foo() {
    let _ = 1 + 1;         
}
"#,
        )
    }
}