]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / rust-analyzer / src / cli / load_cargo.rs
1 //! Loads a Cargo project into a static instance of analysis, without support
2 //! for incorporating changes.
3 use std::{path::Path, sync::Arc};
4
5 use anyhow::Result;
6 use crossbeam_channel::{unbounded, Receiver};
7 use hir::db::DefDatabase;
8 use ide::{AnalysisHost, Change};
9 use ide_db::{base_db::CrateGraph, FxHashMap};
10 use proc_macro_api::ProcMacroServer;
11 use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
12 use vfs::{loader::Handle, AbsPath, AbsPathBuf};
13
14 use crate::reload::{load_proc_macro, ProjectFolders, SourceRootConfig};
15
16 // Note: Since this type is used by external tools that use rust-analyzer as a library
17 // what otherwise would be `pub(crate)` has to be `pub` here instead.
18 pub struct LoadCargoConfig {
19 pub load_out_dirs_from_check: bool,
20 pub with_proc_macro: bool,
21 pub prefill_caches: bool,
22 }
23
24 // Note: Since this function is used by external tools that use rust-analyzer as a library
25 // what otherwise would be `pub(crate)` has to be `pub` here instead.
26 pub fn load_workspace_at(
27 root: &Path,
28 cargo_config: &CargoConfig,
29 load_config: &LoadCargoConfig,
30 progress: &dyn Fn(String),
31 ) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
32 let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
33 let root = ProjectManifest::discover_single(&root)?;
34 let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
35
36 if load_config.load_out_dirs_from_check {
37 let build_scripts = workspace.run_build_scripts(cargo_config, progress)?;
38 workspace.set_build_scripts(build_scripts)
39 }
40
41 load_workspace(workspace, &cargo_config.extra_env, load_config)
42 }
43
44 // Note: Since this function is used by external tools that use rust-analyzer as a library
45 // what otherwise would be `pub(crate)` has to be `pub` here instead.
46 //
47 // The reason both, `load_workspace_at` and `load_workspace` are `pub` is that some of
48 // these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides.
49 pub fn load_workspace(
50 ws: ProjectWorkspace,
51 extra_env: &FxHashMap<String, String>,
52 load_config: &LoadCargoConfig,
53 ) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
54 let (sender, receiver) = unbounded();
55 let mut vfs = vfs::Vfs::default();
56 let mut loader = {
57 let loader =
58 vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
59 Box::new(loader)
60 };
61
62 let proc_macro_client = if load_config.with_proc_macro {
63 let mut path = AbsPathBuf::assert(std::env::current_exe()?);
64 let mut args = vec!["proc-macro"];
65
66 if let ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } =
67 &ws
68 {
69 if let Some(sysroot) = sysroot.as_ref() {
70 let standalone_server_name =
71 format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
72 let server_path = sysroot.root().join("libexec").join(&standalone_server_name);
73 if std::fs::metadata(&server_path).is_ok() {
74 path = server_path;
75 args = vec![];
76 }
77 }
78 }
79
80 ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|e| e.to_string())
81 } else {
82 Err("proc macro server disabled".to_owned())
83 };
84
85 let crate_graph = ws.to_crate_graph(
86 &mut |_, path: &AbsPath| {
87 load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[])
88 },
89 &mut |path: &AbsPath| {
90 let contents = loader.load_sync(path);
91 let path = vfs::VfsPath::from(path.to_path_buf());
92 vfs.set_file_contents(path.clone(), contents);
93 vfs.file_id(&path)
94 },
95 extra_env,
96 );
97
98 let project_folders = ProjectFolders::new(&[ws], &[]);
99 loader.set_config(vfs::loader::Config {
100 load: project_folders.load,
101 watch: vec![],
102 version: 0,
103 });
104
105 tracing::debug!("crate graph: {:?}", crate_graph);
106 let host =
107 load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
108
109 if load_config.prefill_caches {
110 host.analysis().parallel_prime_caches(1, |_| {})?;
111 }
112 Ok((host, vfs, proc_macro_client.ok()))
113 }
114
115 fn load_crate_graph(
116 crate_graph: CrateGraph,
117 source_root_config: SourceRootConfig,
118 vfs: &mut vfs::Vfs,
119 receiver: &Receiver<vfs::loader::Message>,
120 ) -> AnalysisHost {
121 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
122 let mut host = AnalysisHost::new(lru_cap);
123 let mut analysis_change = Change::new();
124
125 host.raw_database_mut().set_enable_proc_attr_macros(true);
126
127 // wait until Vfs has loaded all roots
128 for task in receiver {
129 match task {
130 vfs::loader::Message::Progress { n_done, n_total, config_version: _ } => {
131 if n_done == n_total {
132 break;
133 }
134 }
135 vfs::loader::Message::Loaded { files } => {
136 for (path, contents) in files {
137 vfs.set_file_contents(path.into(), contents);
138 }
139 }
140 }
141 }
142 let changes = vfs.take_changes();
143 for file in changes {
144 if file.exists() {
145 let contents = vfs.file_contents(file.file_id).to_vec();
146 if let Ok(text) = String::from_utf8(contents) {
147 analysis_change.change_file(file.file_id, Some(Arc::new(text)))
148 }
149 }
150 }
151 let source_roots = source_root_config.partition(vfs);
152 analysis_change.set_roots(source_roots);
153
154 analysis_change.set_crate_graph(crate_graph);
155
156 host.apply_change(analysis_change);
157 host
158 }
159
160 #[cfg(test)]
161 mod tests {
162 use super::*;
163
164 use hir::Crate;
165
166 #[test]
167 fn test_loading_rust_analyzer() {
168 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
169 let cargo_config = CargoConfig::default();
170 let load_cargo_config = LoadCargoConfig {
171 load_out_dirs_from_check: false,
172 with_proc_macro: false,
173 prefill_caches: false,
174 };
175 let (host, _vfs, _proc_macro) =
176 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap();
177
178 let n_crates = Crate::all(host.raw_database()).len();
179 // RA has quite a few crates, but the exact count doesn't matter
180 assert!(n_crates > 20);
181 }
182 }