]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/bin/main.rs
7ee35d52b3c3f1f25a4ceebf8710b938898c2ce4
[rust.git] / crates / rust-analyzer / src / bin / main.rs
1 //! Driver for rust-analyzer.
2 //!
3 //! Based on cli flags, either spawns an LSP server, or runs a batch analysis
4 mod flags;
5 mod logger;
6 mod rustc_wrapper;
7
8 use std::{convert::TryFrom, env, fs, path::Path, process};
9
10 use lsp_server::Connection;
11 use project_model::ProjectManifest;
12 use rust_analyzer::{
13     cli::{self, AnalysisStatsCmd},
14     config::Config,
15     from_json,
16     lsp_ext::supports_utf8,
17     Result,
18 };
19 use vfs::AbsPathBuf;
20
21 #[cfg(all(feature = "mimalloc"))]
22 #[global_allocator]
23 static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
24
25 #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
26 #[global_allocator]
27 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
28
29 fn main() {
30     if std::env::var("RA_RUSTC_WRAPPER").is_ok() {
31         let mut args = std::env::args_os();
32         let _me = args.next().unwrap();
33         let rustc = args.next().unwrap();
34         let code = match rustc_wrapper::run_rustc_skipping_cargo_checking(rustc, args.collect()) {
35             Ok(rustc_wrapper::ExitCode(code)) => code.unwrap_or(102),
36             Err(err) => {
37                 eprintln!("{}", err);
38                 101
39             }
40         };
41         process::exit(code);
42     }
43
44     if let Err(err) = try_main() {
45         log::error!("Unexpected error: {}", err);
46         eprintln!("{}", err);
47         process::exit(101);
48     }
49 }
50
51 fn try_main() -> Result<()> {
52     let flags = flags::RustAnalyzer::from_env()?;
53
54     #[cfg(debug_assertions)]
55     if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
56         #[allow(unused_mut)]
57         let mut d = 4;
58         while d == 4 {
59             d = 4;
60         }
61     }
62
63     setup_logging(flags.log_file.as_deref(), flags.no_log_buffering)?;
64     let verbosity = flags.verbosity();
65
66     match flags.subcommand {
67         flags::RustAnalyzerCmd::LspServer(cmd) => {
68             if cmd.print_config_schema {
69                 println!("{:#}", Config::json_schema());
70                 return Ok(());
71             }
72             if cmd.version {
73                 println!("rust-analyzer {}", env!("REV"));
74                 return Ok(());
75             }
76             if cmd.help {
77                 println!("{}", flags::RustAnalyzer::HELP);
78                 return Ok(());
79             }
80             run_server()?
81         }
82         flags::RustAnalyzerCmd::ProcMacro(_) => proc_macro_srv::cli::run()?,
83         flags::RustAnalyzerCmd::Parse(cmd) => cli::parse(cmd.no_dump)?,
84         flags::RustAnalyzerCmd::Symbols(_) => cli::symbols()?,
85         flags::RustAnalyzerCmd::Highlight(cmd) => cli::highlight(cmd.rainbow)?,
86         flags::RustAnalyzerCmd::AnalysisStats(cmd) => AnalysisStatsCmd {
87             randomize: cmd.randomize,
88             parallel: cmd.parallel,
89             memory_usage: cmd.memory_usage,
90             only: cmd.only,
91             with_deps: cmd.with_deps,
92             no_sysroot: cmd.no_sysroot,
93             path: cmd.path,
94             load_output_dirs: cmd.load_output_dirs,
95             with_proc_macro: cmd.with_proc_macro,
96             skip_inference: cmd.skip_inference,
97         }
98         .run(verbosity)?,
99
100         flags::RustAnalyzerCmd::Diagnostics(cmd) => {
101             cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
102         }
103         flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?,
104         flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?,
105     }
106     Ok(())
107 }
108
109 fn setup_logging(log_file: Option<&Path>, no_buffering: bool) -> Result<()> {
110     env::set_var("RUST_BACKTRACE", "short");
111
112     let log_file = match log_file {
113         Some(path) => {
114             if let Some(parent) = path.parent() {
115                 let _ = fs::create_dir_all(parent);
116             }
117             Some(fs::File::create(path)?)
118         }
119         None => None,
120     };
121     let filter = env::var("RA_LOG").ok();
122     logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();
123
124     tracing_setup::setup_tracing()?;
125
126     profile::init();
127
128     Ok(())
129 }
130
131 mod tracing_setup {
132     use tracing::subscriber;
133     use tracing_subscriber::layer::SubscriberExt;
134     use tracing_subscriber::EnvFilter;
135     use tracing_subscriber::Registry;
136     use tracing_tree::HierarchicalLayer;
137
138     pub(crate) fn setup_tracing() -> super::Result<()> {
139         let filter = EnvFilter::from_env("CHALK_DEBUG");
140         let layer = HierarchicalLayer::default()
141             .with_indent_lines(true)
142             .with_ansi(false)
143             .with_indent_amount(2)
144             .with_writer(std::io::stderr);
145         let subscriber = Registry::default().with(filter).with(layer);
146         subscriber::set_global_default(subscriber)?;
147         Ok(())
148     }
149 }
150
151 fn run_server() -> Result<()> {
152     log::info!("server version {} will start", env!("REV"));
153
154     let (connection, io_threads) = Connection::stdio();
155
156     let (initialize_id, initialize_params) = connection.initialize_start()?;
157     log::info!("InitializeParams: {}", initialize_params);
158     let initialize_params =
159         from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
160
161     let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);
162
163     let initialize_result = lsp_types::InitializeResult {
164         capabilities: server_capabilities,
165         server_info: Some(lsp_types::ServerInfo {
166             name: String::from("rust-analyzer"),
167             version: Some(String::from(env!("REV"))),
168         }),
169         offset_encoding: if supports_utf8(&initialize_params.capabilities) {
170             Some("utf-8".to_string())
171         } else {
172             None
173         },
174     };
175
176     let initialize_result = serde_json::to_value(initialize_result).unwrap();
177
178     connection.initialize_finish(initialize_id, initialize_result)?;
179
180     if let Some(client_info) = initialize_params.client_info {
181         log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
182     }
183
184     let config = {
185         let root_path = match initialize_params
186             .root_uri
187             .and_then(|it| it.to_file_path().ok())
188             .and_then(|it| AbsPathBuf::try_from(it).ok())
189         {
190             Some(it) => it,
191             None => {
192                 let cwd = env::current_dir()?;
193                 AbsPathBuf::assert(cwd)
194             }
195         };
196
197         let mut config = Config::new(root_path, initialize_params.capabilities);
198         if let Some(json) = initialize_params.initialization_options {
199             config.update(json);
200         }
201
202         if config.linked_projects().is_empty() {
203             let workspace_roots = initialize_params
204                 .workspace_folders
205                 .map(|workspaces| {
206                     workspaces
207                         .into_iter()
208                         .filter_map(|it| it.uri.to_file_path().ok())
209                         .filter_map(|it| AbsPathBuf::try_from(it).ok())
210                         .collect::<Vec<_>>()
211                 })
212                 .filter(|workspaces| !workspaces.is_empty())
213                 .unwrap_or_else(|| vec![config.root_path.clone()]);
214
215             let discovered = ProjectManifest::discover_all(&workspace_roots);
216             log::info!("discovered projects: {:?}", discovered);
217             if discovered.is_empty() && config.detached_files().is_empty() {
218                 log::error!("failed to find any projects in {:?}", workspace_roots);
219             }
220
221             config.discovered_projects = Some(discovered);
222         }
223
224         config
225     };
226
227     rust_analyzer::main_loop(config, connection)?;
228
229     io_threads.join()?;
230     log::info!("server did shut down");
231     Ok(())
232 }