]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_dev/src/setup/intellij.rs
Rollup merge of #86479 - exphp-forks:float-debug-exponential, r=yaahc
[rust.git] / src / tools / clippy / clippy_dev / src / setup / intellij.rs
1 use std::fs;
2 use std::fs::File;
3 use std::io::prelude::*;
4 use std::path::{Path, PathBuf};
5
6 // This module takes an absolute path to a rustc repo and alters the dependencies to point towards
7 // the respective rustc subcrates instead of using extern crate xyz.
8 // This allows IntelliJ to analyze rustc internals and show proper information inside Clippy
9 // code. See https://github.com/rust-lang/rust-clippy/issues/5514 for details
10
11 const RUSTC_PATH_SECTION: &str = "[target.'cfg(NOT_A_PLATFORM)'.dependencies]";
12 const DEPENDENCIES_SECTION: &str = "[dependencies]";
13
14 const CLIPPY_PROJECTS: &[ClippyProjectInfo] = &[
15     ClippyProjectInfo::new("root", "Cargo.toml", "src/driver.rs"),
16     ClippyProjectInfo::new("clippy_lints", "clippy_lints/Cargo.toml", "clippy_lints/src/lib.rs"),
17     ClippyProjectInfo::new("clippy_utils", "clippy_utils/Cargo.toml", "clippy_utils/src/lib.rs"),
18 ];
19
20 /// Used to store clippy project information to later inject the dependency into.
21 struct ClippyProjectInfo {
22     /// Only used to display information to the user
23     name: &'static str,
24     cargo_file: &'static str,
25     lib_rs_file: &'static str,
26 }
27
28 impl ClippyProjectInfo {
29     const fn new(name: &'static str, cargo_file: &'static str, lib_rs_file: &'static str) -> Self {
30         Self {
31             name,
32             cargo_file,
33             lib_rs_file,
34         }
35     }
36 }
37
38 pub fn setup_rustc_src(rustc_path: &str) {
39     let rustc_source_dir = match check_and_get_rustc_dir(rustc_path) {
40         Ok(path) => path,
41         Err(_) => return,
42     };
43
44     for project in CLIPPY_PROJECTS {
45         if inject_deps_into_project(&rustc_source_dir, project).is_err() {
46             return;
47         }
48     }
49
50     println!("info: the source paths can be removed again with `cargo dev remove intellij`");
51 }
52
53 fn check_and_get_rustc_dir(rustc_path: &str) -> Result<PathBuf, ()> {
54     let mut path = PathBuf::from(rustc_path);
55
56     if path.is_relative() {
57         match path.canonicalize() {
58             Ok(absolute_path) => {
59                 println!("info: the rustc path was resolved to: `{}`", absolute_path.display());
60                 path = absolute_path;
61             },
62             Err(err) => {
63                 eprintln!("error: unable to get the absolute path of rustc ({})", err);
64                 return Err(());
65             },
66         };
67     }
68
69     let path = path.join("compiler");
70     println!("info: looking for compiler sources at: {}", path.display());
71
72     if !path.exists() {
73         eprintln!("error: the given path does not exist");
74         return Err(());
75     }
76
77     if !path.is_dir() {
78         eprintln!("error: the given path is not a directory");
79         return Err(());
80     }
81
82     Ok(path)
83 }
84
85 fn inject_deps_into_project(rustc_source_dir: &Path, project: &ClippyProjectInfo) -> Result<(), ()> {
86     let cargo_content = read_project_file(project.cargo_file)?;
87     let lib_content = read_project_file(project.lib_rs_file)?;
88
89     if inject_deps_into_manifest(rustc_source_dir, project.cargo_file, &cargo_content, &lib_content).is_err() {
90         eprintln!(
91             "error: unable to inject dependencies into {} with the Cargo file {}",
92             project.name, project.cargo_file
93         );
94         Err(())
95     } else {
96         Ok(())
97     }
98 }
99
100 /// `clippy_dev` expects to be executed in the root directory of Clippy. This function
101 /// loads the given file or returns an error. Having it in this extra function ensures
102 /// that the error message looks nice.
103 fn read_project_file(file_path: &str) -> Result<String, ()> {
104     let path = Path::new(file_path);
105     if !path.exists() {
106         eprintln!("error: unable to find the file `{}`", file_path);
107         return Err(());
108     }
109
110     match fs::read_to_string(path) {
111         Ok(content) => Ok(content),
112         Err(err) => {
113             eprintln!("error: the file `{}` could not be read ({})", file_path, err);
114             Err(())
115         },
116     }
117 }
118
119 fn inject_deps_into_manifest(
120     rustc_source_dir: &Path,
121     manifest_path: &str,
122     cargo_toml: &str,
123     lib_rs: &str,
124 ) -> std::io::Result<()> {
125     // do not inject deps if we have already done so
126     if cargo_toml.contains(RUSTC_PATH_SECTION) {
127         eprintln!(
128             "warn: dependencies are already setup inside {}, skipping file",
129             manifest_path
130         );
131         return Ok(());
132     }
133
134     let extern_crates = lib_rs
135         .lines()
136         // only take dependencies starting with `rustc_`
137         .filter(|line| line.starts_with("extern crate rustc_"))
138         // we have something like "extern crate foo;", we only care about the "foo"
139         // extern crate rustc_middle;
140         //              ^^^^^^^^^^^^
141         .map(|s| &s[13..(s.len() - 1)]);
142
143     let new_deps = extern_crates.map(|dep| {
144         // format the dependencies that are going to be put inside the Cargo.toml
145         format!(
146             "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
147             dep = dep,
148             source_path = rustc_source_dir.display()
149         )
150     });
151
152     // format a new [dependencies]-block with the new deps we need to inject
153     let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
154     new_deps.for_each(|dep_line| {
155         all_deps.push_str(&dep_line);
156     });
157     all_deps.push_str("\n[dependencies]\n");
158
159     // replace "[dependencies]" with
160     // [dependencies]
161     // dep1 = { path = ... }
162     // dep2 = { path = ... }
163     // etc
164     let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
165
166     // println!("{}", new_manifest);
167     let mut file = File::create(manifest_path)?;
168     file.write_all(new_manifest.as_bytes())?;
169
170     println!("info: successfully setup dependencies inside {}", manifest_path);
171
172     Ok(())
173 }
174
175 pub fn remove_rustc_src() {
176     for project in CLIPPY_PROJECTS {
177         remove_rustc_src_from_project(project);
178     }
179 }
180
181 fn remove_rustc_src_from_project(project: &ClippyProjectInfo) -> bool {
182     let mut cargo_content = if let Ok(content) = read_project_file(project.cargo_file) {
183         content
184     } else {
185         return false;
186     };
187     let section_start = if let Some(section_start) = cargo_content.find(RUSTC_PATH_SECTION) {
188         section_start
189     } else {
190         println!(
191             "info: dependencies could not be found in `{}` for {}, skipping file",
192             project.cargo_file, project.name
193         );
194         return true;
195     };
196
197     let end_point = if let Some(end_point) = cargo_content.find(DEPENDENCIES_SECTION) {
198         end_point
199     } else {
200         eprintln!(
201             "error: the end of the rustc dependencies section could not be found in `{}`",
202             project.cargo_file
203         );
204         return false;
205     };
206
207     cargo_content.replace_range(section_start..end_point, "");
208
209     match File::create(project.cargo_file) {
210         Ok(mut file) => {
211             file.write_all(cargo_content.as_bytes()).unwrap();
212             println!("info: successfully removed dependencies inside {}", project.cargo_file);
213             true
214         },
215         Err(err) => {
216             eprintln!(
217                 "error: unable to open file `{}` to remove rustc dependencies for {} ({})",
218                 project.cargo_file, project.name, err
219             );
220             false
221         },
222     }
223 }