//! A reference index over the didbot workspace, and the queries it answers. //! //! `git grep` is a text search over a language that has modules, `use` //! re-exports, trait impls, macros and `cfg`. It cannot tell a definition from //! a mention in a comment, and it cannot follow a re-export. This builds the //! index a compiler-accurate answer needs instead: the item inventory from //! rustdoc's JSON, the reference edges from `rust-analyzer scip`, joined into //! one SQLite file. //! //! scripts/xray.sh produces both inputs and calls this. mod layout; mod query; mod rustdoc; mod scip_index; mod store; use anyhow::Result; use clap::{Parser, Subcommand}; use std::path::PathBuf; #[derive(Parser)] #[command(name = "xray", about = "A reference index over a Rust workspace.")] struct Cli { /// The index to read or write. #[arg(long, default_value = ".xray/index.db", global = true)] db: PathBuf, #[command(subcommand)] command: Command, } #[derive(Subcommand)] enum Command { /// Loads a rustdoc JSON directory and a SCIP index into the index. Build { /// The workspace root the two dumps were taken in. #[arg(long, default_value = ".")] workspace: PathBuf, /// The `rust-analyzer scip` output. #[arg(long)] scip: PathBuf, /// A directory `cargo doc --output-format json` wrote. Repeatable: /// the lib and bin runs go to directories of their own. #[arg(long, required = true)] docs: Vec, /// The revision the sources were at. An index is only as current as /// this. #[arg(long, default_value = "unknown")] rev: String, }, /// What the index was built from. Info, /// Public items nothing outside their own crate, and no test, reaches. DeadPub { /// Only this crate. #[arg(long = "crate")] krate: Option, /// Include `pub` items that no public path reaches, which cannot be /// used from another crate whatever they are marked. #[arg(long)] all: bool, }, /// Every reference to an item, with file and line. Refs { path: String }, /// Declared workspace dependencies and the references crossing them. CrateEdges, /// Every item in a file, with its span. FileItems { file: String }, } fn main() -> Result<()> { let cli = Cli::parse(); match cli.command { Command::Build { workspace, scip, docs, rev, } => { let stats = store::build(&cli.db, &workspace, &scip, &docs, &rev)?; println!( "{} items, {} references, {} definitions rustdoc does not know", stats.items, stats.refs, stats.unresolved ); } Command::Info => query::info(&query::open(&cli.db)?)?, Command::DeadPub { krate, all } => { query::dead_pub(&query::open(&cli.db)?, krate.as_deref(), all)? } Command::Refs { path } => query::refs(&query::open(&cli.db)?, &path)?, Command::CrateEdges => query::crate_edges(&query::open(&cli.db)?)?, Command::FileItems { file } => query::file_items(&query::open(&cli.db)?, &file)?, } Ok(()) }