From 7b490903809ce5c03c83869357a68e88f8cc0799 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Matthias=20Kr=C3=BCger?= Date: Wed, 27 May 2020 14:08:31 +0200 Subject: [PATCH] clippy_dev: add ra_setup This takes an absolute path to a rustc repo and adds path-dependencies that point towards the respective rustc subcrates into the Cargo.tomls of the clippy and clippy_lints crate. This allows rustc-analyzer to show proper type annotations etc on rustc-internals inside the clippy repo. Usage: cargo dev ra-setup /absolute/path/to/rust/ cc https://github.com/rust-analyzer/rust-analyzer/issues/3517 cc https://github.com/rust-lang/rust-clippy/issues/5514 --- clippy_dev/src/lib.rs | 3 +- clippy_dev/src/main.rs | 16 ++++++- clippy_dev/src/ra_setup.rs | 90 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 clippy_dev/src/ra_setup.rs diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 6fdd282c684..5baa31d5cde 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -11,6 +11,7 @@ pub mod fmt; pub mod new_lint; +pub mod ra_setup; pub mod stderr_length_check; pub mod update_lints; @@ -400,7 +401,7 @@ fn test_replace_region_no_changes() { changed: false, new_lines: "123\n456\n789".to_string(), }; - let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || vec![]); + let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new); assert_eq!(expected, result); } diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d99235f7c07..281037ae37c 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, stderr_length_check, update_lints}; +use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints}; fn main() { let matches = App::new("Clippy developer tooling") @@ -87,6 +87,19 @@ fn main() { SubCommand::with_name("limit_stderr_length") .about("Ensures that stderr files do not grow longer than a certain amount of lines."), ) + .subcommand( + SubCommand::with_name("ra-setup") + .about("Alter dependencies so rust-analyzer can find rustc internals") + .arg( + Arg::with_name("rustc-repo-path") + .long("repo-path") + .short("r") + .help("The path to a rustc repo that will be used for setting the dependencies") + .takes_value(true) + .value_name("path") + .required(true), + ), + ) .get_matches(); match matches.subcommand() { @@ -115,6 +128,7 @@ fn main() { ("limit_stderr_length", _) => { stderr_length_check::check(); }, + ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), _ => {}, } } diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs new file mode 100644 index 00000000000..8617445c8a6 --- /dev/null +++ b/clippy_dev/src/ra_setup.rs @@ -0,0 +1,90 @@ +#![allow(clippy::filter_map)] + +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; + +// This module takes an absolute path to a rustc repo and alters the dependencies to point towards +// the respective rustc subcrates instead of using extern crate xyz. +// This allows rust analyzer to analyze rustc internals and show proper information inside clippy +// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details + +pub fn run(rustc_path: Option<&str>) { + // we can unwrap here because the arg is required here + let rustc_path = PathBuf::from(rustc_path.unwrap()); + assert!(rustc_path.is_dir(), "path is not a directory"); + let rustc_source_basedir = rustc_path.join("src"); + assert!( + rustc_source_basedir.is_dir(), + "are you sure the path leads to a rustc repo?" + ); + + let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml"); + let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "Cargo.toml", + &clippy_root_manifest, + &clippy_root_lib_rs, + ) + .expect("Failed to inject deps into ./Cargo.toml"); + + let clippy_lints_manifest = + fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml"); + let clippy_lints_lib_rs = + fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs"); + inject_deps_into_manifest( + &rustc_source_basedir, + "clippy_lints/Cargo.toml", + &clippy_lints_manifest, + &clippy_lints_lib_rs, + ) + .expect("Failed to inject deps into ./clippy_lints/Cargo.toml"); +} + +fn inject_deps_into_manifest( + rustc_source_dir: &PathBuf, + manifest_path: &str, + cargo_toml: &str, + lib_rs: &str, +) -> std::io::Result<()> { + let extern_crates = lib_rs + .lines() + // get the deps + .filter(|line| line.starts_with("extern crate")) + // we have something like "extern crate foo;", we only care about the "foo" + // ↓ ↓ + // extern crate rustc_middle; + .map(|s| &s[13..(s.len() - 1)]); + + let new_deps = extern_crates.map(|dep| { + // format the dependencies that are going to be put inside the Cargo.toml + format!( + "{dep} = {{ path = \"{source_path}/lib{dep}\" }}\n", + dep = dep, + source_path = rustc_source_dir.display() + ) + }); + + // format a new [dependencies]-block with the new deps we need to inject + let mut all_deps = String::from("[dependencies]\n"); + new_deps.for_each(|dep_line| { + all_deps.push_str(&dep_line); + }); + + // replace "[dependencies]" with + // [dependencies] + // dep1 = { path = ... } + // dep2 = { path = ... } + // etc + let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1); + + // println!("{}", new_manifest); + let mut file = File::create(manifest_path)?; + file.write_all(new_manifest.as_bytes())?; + + println!("Dependency paths injected: {}", manifest_path); + + Ok(()) +} -- 2.44.0