aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-03-11 10:51:07 +0000
committerGitHub <[email protected]>2020-03-11 10:51:07 +0000
commitc48dcf74118b6e0df747f036a9b66701037f3fc7 (patch)
treebc047c31d43d2e16428c56bf7cdf305f4a30fa66
parent7b323b45a15809a20052f13d5a8f073aaa274a86 (diff)
parent6ea7c319154f9ec10721f4041afc9d07d6b2476b (diff)
Merge #3549
3549: Implement env! macro r=matklad a=edwin0cheng This PR implements `env!` macro by adding following things: 1. Added `additional_outdirs` settings in vscode. (naming to be bikeshed) 2. Added `ExternSourceId` which is a wrapping for SourceRootId but only used in extern sources. It is because `OUT_DIR` is not belonged to any crate and we have to access it behind an `AstDatabase`. 3. This PR does not implement the `OUT_DIR` parsing from `cargo check`. I don't have general design about this, @kiljacken could we reuse some cargo watch code for that ? ~~Block on [#3536]~~ PS: After this PR , we (kind of) completed the `include!(concat!(env!('OUT_DIR'), "foo.rs")` macro call combo. [Exodia Obliterate!](https://www.youtube.com/watch?v=RfqNH3FoGi0) Co-authored-by: Edwin Cheng <[email protected]>
-rw-r--r--crates/ra_db/src/fixture.rs12
-rw-r--r--crates/ra_db/src/input.rs43
-rw-r--r--crates/ra_db/src/lib.rs18
-rw-r--r--crates/ra_hir_def/src/test_db.rs10
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs182
-rw-r--r--crates/ra_hir_expand/src/test_db.rs9
-rw-r--r--crates/ra_hir_ty/src/test_db.rs7
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs20
-rw-r--r--crates/ra_ide/src/lib.rs1
-rw-r--r--crates/ra_ide/src/mock_analysis.rs2
-rw-r--r--crates/ra_ide/src/parent_module.rs1
-rw-r--r--crates/ra_ide_db/src/lib.rs7
-rw-r--r--crates/ra_project_model/src/lib.rs25
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/world.rs38
-rw-r--r--editors/code/package.json5
-rw-r--r--editors/code/src/client.ts1
-rw-r--r--editors/code/src/config.ts1
20 files changed, 320 insertions, 72 deletions
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 7f43c2971..3dc86ca2d 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -61,7 +61,14 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
61 }; 61 };
62 62
63 let mut crate_graph = CrateGraph::default(); 63 let mut crate_graph = CrateGraph::default();
64 crate_graph.add_crate_root(file_id, meta.edition, meta.krate, meta.cfg, meta.env); 64 crate_graph.add_crate_root(
65 file_id,
66 meta.edition,
67 meta.krate,
68 meta.cfg,
69 meta.env,
70 Default::default(),
71 );
65 crate_graph 72 crate_graph
66 } else { 73 } else {
67 let mut crate_graph = CrateGraph::default(); 74 let mut crate_graph = CrateGraph::default();
@@ -71,6 +78,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
71 None, 78 None,
72 CfgOptions::default(), 79 CfgOptions::default(),
73 Env::default(), 80 Env::default(),
81 Default::default(),
74 ); 82 );
75 crate_graph 83 crate_graph
76 }; 84 };
@@ -119,6 +127,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
119 Some(krate.clone()), 127 Some(krate.clone()),
120 meta.cfg, 128 meta.cfg,
121 meta.env, 129 meta.env,
130 Default::default(),
122 ); 131 );
123 let prev = crates.insert(krate.clone(), crate_id); 132 let prev = crates.insert(krate.clone(), crate_id);
124 assert!(prev.is_none()); 133 assert!(prev.is_none());
@@ -155,6 +164,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
155 None, 164 None,
156 CfgOptions::default(), 165 CfgOptions::default(),
157 Env::default(), 166 Env::default(),
167 Default::default(),
158 ); 168 );
159 } else { 169 } else {
160 for (from, to) in crate_deps { 170 for (from, to) in crate_deps {
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 1a1c64202..06d40db96 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -113,6 +113,7 @@ pub struct CrateData {
113 pub display_name: Option<String>, 113 pub display_name: Option<String>,
114 pub cfg_options: CfgOptions, 114 pub cfg_options: CfgOptions,
115 pub env: Env, 115 pub env: Env,
116 pub extern_source: ExternSource,
116 pub dependencies: Vec<Dependency>, 117 pub dependencies: Vec<Dependency>,
117} 118}
118 119
@@ -122,11 +123,22 @@ pub enum Edition {
122 Edition2015, 123 Edition2015,
123} 124}
124 125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
127pub struct ExternSourceId(pub u32);
128
125#[derive(Default, Debug, Clone, PartialEq, Eq)] 129#[derive(Default, Debug, Clone, PartialEq, Eq)]
126pub struct Env { 130pub struct Env {
127 entries: FxHashMap<String, String>, 131 entries: FxHashMap<String, String>,
128} 132}
129 133
134// FIXME: Redesign vfs for solve the following limitation ?
135// Note: Some env variables (e.g. OUT_DIR) are located outside of the
136// crate. We store a map to allow remap it to ExternSourceId
137#[derive(Default, Debug, Clone, PartialEq, Eq)]
138pub struct ExternSource {
139 extern_paths: FxHashMap<String, ExternSourceId>,
140}
141
130#[derive(Debug, Clone, PartialEq, Eq)] 142#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct Dependency { 143pub struct Dependency {
132 pub crate_id: CrateId, 144 pub crate_id: CrateId,
@@ -141,6 +153,7 @@ impl CrateGraph {
141 display_name: Option<String>, 153 display_name: Option<String>,
142 cfg_options: CfgOptions, 154 cfg_options: CfgOptions,
143 env: Env, 155 env: Env,
156 extern_source: ExternSource,
144 ) -> CrateId { 157 ) -> CrateId {
145 let data = CrateData { 158 let data = CrateData {
146 root_file_id: file_id, 159 root_file_id: file_id,
@@ -148,6 +161,7 @@ impl CrateGraph {
148 display_name, 161 display_name,
149 cfg_options, 162 cfg_options,
150 env, 163 env,
164 extern_source,
151 dependencies: Vec::new(), 165 dependencies: Vec::new(),
152 }; 166 };
153 let crate_id = CrateId(self.arena.len() as u32); 167 let crate_id = CrateId(self.arena.len() as u32);
@@ -271,6 +285,27 @@ impl Env {
271 } 285 }
272} 286}
273 287
288impl ExternSource {
289 pub fn extern_path(&self, path: &str) -> Option<(ExternSourceId, RelativePathBuf)> {
290 self.extern_paths.iter().find_map(|(root_path, id)| {
291 if path.starts_with(root_path) {
292 let mut rel_path = &path[root_path.len()..];
293 if rel_path.starts_with("/") {
294 rel_path = &rel_path[1..];
295 }
296 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
297 Some((id.clone(), rel_path))
298 } else {
299 None
300 }
301 })
302 }
303
304 pub fn set_extern_path(&mut self, root_path: &str, root: ExternSourceId) {
305 self.extern_paths.insert(root_path.to_owned(), root);
306 }
307}
308
274#[derive(Debug)] 309#[derive(Debug)]
275pub struct ParseEditionError { 310pub struct ParseEditionError {
276 invalid_input: String, 311 invalid_input: String,
@@ -300,6 +335,7 @@ mod tests {
300 None, 335 None,
301 CfgOptions::default(), 336 CfgOptions::default(),
302 Env::default(), 337 Env::default(),
338 Default::default(),
303 ); 339 );
304 let crate2 = graph.add_crate_root( 340 let crate2 = graph.add_crate_root(
305 FileId(2u32), 341 FileId(2u32),
@@ -307,6 +343,7 @@ mod tests {
307 None, 343 None,
308 CfgOptions::default(), 344 CfgOptions::default(),
309 Env::default(), 345 Env::default(),
346 Default::default(),
310 ); 347 );
311 let crate3 = graph.add_crate_root( 348 let crate3 = graph.add_crate_root(
312 FileId(3u32), 349 FileId(3u32),
@@ -314,6 +351,7 @@ mod tests {
314 None, 351 None,
315 CfgOptions::default(), 352 CfgOptions::default(),
316 Env::default(), 353 Env::default(),
354 Default::default(),
317 ); 355 );
318 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 356 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
319 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 357 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -329,6 +367,7 @@ mod tests {
329 None, 367 None,
330 CfgOptions::default(), 368 CfgOptions::default(),
331 Env::default(), 369 Env::default(),
370 Default::default(),
332 ); 371 );
333 let crate2 = graph.add_crate_root( 372 let crate2 = graph.add_crate_root(
334 FileId(2u32), 373 FileId(2u32),
@@ -336,6 +375,7 @@ mod tests {
336 None, 375 None,
337 CfgOptions::default(), 376 CfgOptions::default(),
338 Env::default(), 377 Env::default(),
378 Default::default(),
339 ); 379 );
340 let crate3 = graph.add_crate_root( 380 let crate3 = graph.add_crate_root(
341 FileId(3u32), 381 FileId(3u32),
@@ -343,6 +383,7 @@ mod tests {
343 None, 383 None,
344 CfgOptions::default(), 384 CfgOptions::default(),
345 Env::default(), 385 Env::default(),
386 Default::default(),
346 ); 387 );
347 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 388 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
348 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 389 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -357,6 +398,7 @@ mod tests {
357 None, 398 None,
358 CfgOptions::default(), 399 CfgOptions::default(),
359 Env::default(), 400 Env::default(),
401 Default::default(),
360 ); 402 );
361 let crate2 = graph.add_crate_root( 403 let crate2 = graph.add_crate_root(
362 FileId(2u32), 404 FileId(2u32),
@@ -364,6 +406,7 @@ mod tests {
364 None, 406 None,
365 CfgOptions::default(), 407 CfgOptions::default(),
366 Env::default(), 408 Env::default(),
409 Default::default(),
367 ); 410 );
368 assert!(graph 411 assert!(graph
369 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) 412 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index fb002d717..d500d5e85 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -11,7 +11,8 @@ use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit};
11pub use crate::{ 11pub use crate::{
12 cancellation::Canceled, 12 cancellation::Canceled,
13 input::{ 13 input::{
14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, SourceRoot, SourceRootId, 14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId,
15 FileId, SourceRoot, SourceRootId,
15 }, 16 },
16}; 17};
17pub use relative_path::{RelativePath, RelativePathBuf}; 18pub use relative_path::{RelativePath, RelativePathBuf};
@@ -87,6 +88,12 @@ pub trait FileLoader {
87 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) 88 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath)
88 -> Option<FileId>; 89 -> Option<FileId>;
89 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 90 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>;
91
92 fn resolve_extern_path(
93 &self,
94 extern_id: ExternSourceId,
95 relative_path: &RelativePath,
96 ) -> Option<FileId>;
90} 97}
91 98
92/// Database which stores all significant input facts: source code and project 99/// Database which stores all significant input facts: source code and project
@@ -164,4 +171,13 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
164 let source_root = self.0.file_source_root(file_id); 171 let source_root = self.0.file_source_root(file_id);
165 self.0.source_root_crates(source_root) 172 self.0.source_root_crates(source_root)
166 } 173 }
174
175 fn resolve_extern_path(
176 &self,
177 extern_id: ExternSourceId,
178 relative_path: &RelativePath,
179 ) -> Option<FileId> {
180 let source_root = self.0.source_root(SourceRootId(extern_id.0));
181 source_root.file_by_relative_path(&relative_path)
182 }
167} 183}
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index 1568820e9..0756916a8 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -6,7 +6,7 @@ use std::{
6}; 6};
7 7
8use crate::db::DefDatabase; 8use crate::db::DefDatabase;
9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 9use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
10 10
11#[salsa::database( 11#[salsa::database(
12 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
@@ -52,6 +52,14 @@ impl FileLoader for TestDB {
52 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 52 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
53 FileLoaderDelegate(self).relevant_crates(file_id) 53 FileLoaderDelegate(self).relevant_crates(file_id)
54 } 54 }
55
56 fn resolve_extern_path(
57 &self,
58 extern_id: ExternSourceId,
59 relative_path: &RelativePath,
60 ) -> Option<FileId> {
61 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
62 }
55} 63}
56 64
57impl TestDB { 65impl TestDB {
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 3f60b1cca..a90007f26 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -90,15 +90,15 @@ register_builtin! {
90 (line, Line) => line_expand, 90 (line, Line) => line_expand,
91 (stringify, Stringify) => stringify_expand, 91 (stringify, Stringify) => stringify_expand,
92 (format_args, FormatArgs) => format_args_expand, 92 (format_args, FormatArgs) => format_args_expand,
93 (env, Env) => env_expand,
94 (option_env, OptionEnv) => option_env_expand,
95 // format_args_nl only differs in that it adds a newline in the end, 93 // format_args_nl only differs in that it adds a newline in the end,
96 // so we use the same stub expansion for now 94 // so we use the same stub expansion for now
97 (format_args_nl, FormatArgsNl) => format_args_expand, 95 (format_args_nl, FormatArgsNl) => format_args_expand,
98 96
99 EAGER: 97 EAGER:
100 (concat, Concat) => concat_expand, 98 (concat, Concat) => concat_expand,
101 (include, Include) => include_expand 99 (include, Include) => include_expand,
100 (env, Env) => env_expand,
101 (option_env, OptionEnv) => option_env_expand
102} 102}
103 103
104fn line_expand( 104fn line_expand(
@@ -137,31 +137,6 @@ fn stringify_expand(
137 Ok(expanded) 137 Ok(expanded)
138} 138}
139 139
140fn env_expand(
141 _db: &dyn AstDatabase,
142 _id: LazyMacroId,
143 _tt: &tt::Subtree,
144) -> Result<tt::Subtree, mbe::ExpandError> {
145 // dummy implementation for type-checking purposes
146 // we cannot use an empty string here, because for
147 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
148 // `include!("foo.rs"), which maybe infinite loop
149 let expanded = quote! { "__RA_UNIMPLEMENTATED__" };
150
151 Ok(expanded)
152}
153
154fn option_env_expand(
155 _db: &dyn AstDatabase,
156 _id: LazyMacroId,
157 _tt: &tt::Subtree,
158) -> Result<tt::Subtree, mbe::ExpandError> {
159 // dummy implementation for type-checking purposes
160 let expanded = quote! { std::option::Option::None::<&str> };
161
162 Ok(expanded)
163}
164
165fn column_expand( 140fn column_expand(
166 _db: &dyn AstDatabase, 141 _db: &dyn AstDatabase,
167 _id: LazyMacroId, 142 _id: LazyMacroId,
@@ -278,30 +253,37 @@ fn concat_expand(
278 253
279fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { 254fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
280 let call_site = call_id.as_file().original_file(db); 255 let call_site = call_id.as_file().original_file(db);
281 let path = RelativePath::new(&path);
282 256
283 let res = db.resolve_relative_path(call_site, &path)?; 257 // Handle trivial case
284 // Prevent include itself 258 if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) {
285 if res == call_site { 259 // Prevent include itself
286 return None; 260 return if res == call_site { None } else { Some(res) };
287 } 261 }
288 Some(res) 262
263 // Extern paths ?
264 let krate = db.relevant_crates(call_site).get(0)?.clone();
265 let (extern_source_id, relative_file) =
266 db.crate_graph()[krate].extern_source.extern_path(path)?;
267
268 db.resolve_extern_path(extern_source_id, &relative_file)
289} 269}
290 270
291fn include_expand( 271fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
292 db: &dyn AstDatabase, 272 tt.token_trees
293 arg_id: EagerMacroId,
294 tt: &tt::Subtree,
295) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
296 let path = tt
297 .token_trees
298 .get(0) 273 .get(0)
299 .and_then(|tt| match tt { 274 .and_then(|tt| match tt {
300 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it), 275 tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it),
301 _ => None, 276 _ => None,
302 }) 277 })
303 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 278 .ok_or_else(|| mbe::ExpandError::ConversionError)
279}
304 280
281fn include_expand(
282 db: &dyn AstDatabase,
283 arg_id: EagerMacroId,
284 tt: &tt::Subtree,
285) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
286 let path = parse_string(tt)?;
305 let file_id = 287 let file_id =
306 relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?; 288 relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?;
307 289
@@ -314,12 +296,58 @@ fn include_expand(
314 Ok((res, FragmentKind::Items)) 296 Ok((res, FragmentKind::Items))
315} 297}
316 298
299fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
300 let call_id: MacroCallId = arg_id.into();
301 let original_file = call_id.as_file().original_file(db);
302
303 let krate = db.relevant_crates(original_file).get(0)?.clone();
304 db.crate_graph()[krate].env.get(key)
305}
306
307fn env_expand(
308 db: &dyn AstDatabase,
309 arg_id: EagerMacroId,
310 tt: &tt::Subtree,
311) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
312 let key = parse_string(tt)?;
313
314 // FIXME:
315 // If the environment variable is not defined int rustc, then a compilation error will be emitted.
316 // We might do the same if we fully support all other stuffs.
317 // But for now on, we should return some dummy string for better type infer purpose.
318 // However, we cannot use an empty string here, because for
319 // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
320 // `include!("foo.rs"), which might go to infinite loop
321 let s = get_env_inner(db, arg_id, &key).unwrap_or("__RA_UNIMPLEMENTATED__".to_string());
322 let expanded = quote! { #s };
323
324 Ok((expanded, FragmentKind::Expr))
325}
326
327fn option_env_expand(
328 db: &dyn AstDatabase,
329 arg_id: EagerMacroId,
330 tt: &tt::Subtree,
331) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
332 let key = parse_string(tt)?;
333 let expanded = match get_env_inner(db, arg_id, &key) {
334 None => quote! { std::option::Option::None::<&str> },
335 Some(s) => quote! { std::option::Some(#s) },
336 };
337
338 Ok((expanded, FragmentKind::Expr))
339}
340
317#[cfg(test)] 341#[cfg(test)]
318mod tests { 342mod tests {
319 use super::*; 343 use super::*;
320 use crate::{name::AsName, test_db::TestDB, AstNode, MacroCallId, MacroCallKind, MacroCallLoc}; 344 use crate::{
345 name::AsName, test_db::TestDB, AstNode, EagerCallLoc, MacroCallId, MacroCallKind,
346 MacroCallLoc,
347 };
321 use ra_db::{fixture::WithFixture, SourceDatabase}; 348 use ra_db::{fixture::WithFixture, SourceDatabase};
322 use ra_syntax::ast::NameOwner; 349 use ra_syntax::ast::NameOwner;
350 use std::sync::Arc;
323 351
324 fn expand_builtin_macro(ra_fixture: &str) -> String { 352 fn expand_builtin_macro(ra_fixture: &str) -> String {
325 let (db, file_id) = TestDB::with_single_file(&ra_fixture); 353 let (db, file_id) = TestDB::with_single_file(&ra_fixture);
@@ -330,27 +358,61 @@ mod tests {
330 let ast_id_map = db.ast_id_map(file_id.into()); 358 let ast_id_map = db.ast_id_map(file_id.into());
331 359
332 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 360 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
333 let expander = expander.left().unwrap();
334 361
335 // the first one should be a macro_rules 362 let file_id = match expander {
336 let def = MacroDefId { 363 Either::Left(expander) => {
337 krate: Some(CrateId(0)), 364 // the first one should be a macro_rules
338 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 365 let def = MacroDefId {
339 kind: MacroDefKind::BuiltIn(expander), 366 krate: Some(CrateId(0)),
340 }; 367 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
368 kind: MacroDefKind::BuiltIn(expander),
369 };
341 370
342 let loc = MacroCallLoc { 371 let loc = MacroCallLoc {
343 def, 372 def,
344 kind: MacroCallKind::FnLike(AstId::new( 373 kind: MacroCallKind::FnLike(AstId::new(
345 file_id.into(), 374 file_id.into(),
346 ast_id_map.ast_id(&macro_calls[1]), 375 ast_id_map.ast_id(&macro_calls[1]),
347 )), 376 )),
348 }; 377 };
378
379 let id: MacroCallId = db.intern_macro(loc).into();
380 id.as_file()
381 }
382 Either::Right(expander) => {
383 // the first one should be a macro_rules
384 let def = MacroDefId {
385 krate: Some(CrateId(0)),
386 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
387 kind: MacroDefKind::BuiltInEager(expander),
388 };
349 389
350 let id: MacroCallId = db.intern_macro(loc).into(); 390 let args = macro_calls[1].token_tree().unwrap();
351 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 391 let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0;
392
393 let arg_id = db.intern_eager_expansion({
394 EagerCallLoc {
395 def,
396 fragment: FragmentKind::Expr,
397 subtree: Arc::new(parsed_args.clone()),
398 file_id: file_id.into(),
399 }
400 });
401
402 let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap();
403 let eager = EagerCallLoc {
404 def,
405 fragment,
406 subtree: Arc::new(subtree),
407 file_id: file_id.into(),
408 };
409
410 let id: MacroCallId = db.intern_eager_expansion(eager.into()).into();
411 id.as_file()
412 }
413 };
352 414
353 parsed.text().to_string() 415 db.parse_or_expand(file_id).unwrap().to_string()
354 } 416 }
355 417
356 #[test] 418 #[test]
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index 918736e2a..c1fb762de 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -5,7 +5,7 @@ use std::{
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 8use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
9 9
10#[salsa::database( 10#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 11 ra_db::SourceDatabaseExtStorage,
@@ -51,4 +51,11 @@ impl FileLoader for TestDB {
51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
52 FileLoaderDelegate(self).relevant_crates(file_id) 52 FileLoaderDelegate(self).relevant_crates(file_id)
53 } 53 }
54 fn resolve_extern_path(
55 &self,
56 anchor: ExternSourceId,
57 relative_path: &RelativePath,
58 ) -> Option<FileId> {
59 FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path)
60 }
54} 61}
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index c794f7b84..0be2fea4b 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -67,6 +67,13 @@ impl FileLoader for TestDB {
67 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 67 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
68 FileLoaderDelegate(self).relevant_crates(file_id) 68 FileLoaderDelegate(self).relevant_crates(file_id)
69 } 69 }
70 fn resolve_extern_path(
71 &self,
72 extern_id: ra_db::ExternSourceId,
73 relative_path: &RelativePath,
74 ) -> Option<FileId> {
75 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
76 }
70} 77}
71 78
72impl TestDB { 79impl TestDB {
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index ffa78b046..32457bbf7 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -550,6 +550,26 @@ fn main() {
550} 550}
551 551
552#[test] 552#[test]
553fn infer_builtin_macros_env() {
554 assert_snapshot!(
555 infer(r#"
556//- /main.rs env:foo=bar
557#[rustc_builtin_macro]
558macro_rules! env {() => {}}
559
560fn main() {
561 let x = env!("foo");
562}
563"#),
564 @r###"
565 ![0; 5) '"bar"': &str
566 [88; 116) '{ ...o"); }': ()
567 [98; 99) 'x': &str
568 "###
569 );
570}
571
572#[test]
553fn infer_derive_clone_simple() { 573fn infer_derive_clone_simple() {
554 let (db, pos) = TestDB::with_position( 574 let (db, pos) = TestDB::with_position(
555 r#" 575 r#"
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 9f45003d3..015fae195 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -212,6 +212,7 @@ impl Analysis {
212 None, 212 None,
213 cfg_options, 213 cfg_options,
214 Env::default(), 214 Env::default(),
215 Default::default(),
215 ); 216 );
216 change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); 217 change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text));
217 change.set_crate_graph(crate_graph); 218 change.set_crate_graph(crate_graph);
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 90f84b052..25816cf6f 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -102,6 +102,7 @@ impl MockAnalysis {
102 None, 102 None,
103 cfg_options, 103 cfg_options,
104 Env::default(), 104 Env::default(),
105 Default::default(),
105 )); 106 ));
106 } else if path.ends_with("/lib.rs") { 107 } else if path.ends_with("/lib.rs") {
107 let crate_name = path.parent().unwrap().file_name().unwrap(); 108 let crate_name = path.parent().unwrap().file_name().unwrap();
@@ -111,6 +112,7 @@ impl MockAnalysis {
111 Some(crate_name.to_owned()), 112 Some(crate_name.to_owned()),
112 cfg_options, 113 cfg_options,
113 Env::default(), 114 Env::default(),
115 Default::default(),
114 ); 116 );
115 if let Some(root_crate) = root_crate { 117 if let Some(root_crate) = root_crate {
116 crate_graph 118 crate_graph
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index b73cefd97..76d130b9b 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -136,6 +136,7 @@ mod tests {
136 None, 136 None,
137 CfgOptions::default(), 137 CfgOptions::default(),
138 Env::default(), 138 Env::default(),
139 Default::default(),
139 ); 140 );
140 let mut change = AnalysisChange::new(); 141 let mut change = AnalysisChange::new();
141 change.set_crate_graph(crate_graph); 142 change.set_crate_graph(crate_graph);
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 6bcccc848..fc1b19def 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -55,6 +55,13 @@ impl FileLoader for RootDatabase {
55 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 55 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
56 FileLoaderDelegate(self).relevant_crates(file_id) 56 FileLoaderDelegate(self).relevant_crates(file_id)
57 } 57 }
58 fn resolve_extern_path(
59 &self,
60 extern_id: ra_db::ExternSourceId,
61 relative_path: &RelativePath,
62 ) -> Option<FileId> {
63 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
64 }
58} 65}
59 66
60impl salsa::Database for RootDatabase { 67impl salsa::Database for RootDatabase {
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 37845ca56..a6274709d 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
14 14
15use anyhow::{bail, Context, Result}; 15use anyhow::{bail, Context, Result};
16use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
17use ra_db::{CrateGraph, CrateName, Edition, Env, FileId}; 17use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
18use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
19use serde_json::from_reader; 19use serde_json::from_reader;
20 20
@@ -162,6 +162,7 @@ impl ProjectWorkspace {
162 pub fn to_crate_graph( 162 pub fn to_crate_graph(
163 &self, 163 &self,
164 default_cfg_options: &CfgOptions, 164 default_cfg_options: &CfgOptions,
165 outdirs: &FxHashMap<String, (ExternSourceId, String)>,
165 load: &mut dyn FnMut(&Path) -> Option<FileId>, 166 load: &mut dyn FnMut(&Path) -> Option<FileId>,
166 ) -> CrateGraph { 167 ) -> CrateGraph {
167 let mut crate_graph = CrateGraph::default(); 168 let mut crate_graph = CrateGraph::default();
@@ -185,6 +186,8 @@ impl ProjectWorkspace {
185 } 186 }
186 opts 187 opts
187 }; 188 };
189
190 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
188 crates.insert( 191 crates.insert(
189 crate_id, 192 crate_id,
190 crate_graph.add_crate_root( 193 crate_graph.add_crate_root(
@@ -194,6 +197,7 @@ impl ProjectWorkspace {
194 None, 197 None,
195 cfg_options, 198 cfg_options,
196 Env::default(), 199 Env::default(),
200 Default::default(),
197 ), 201 ),
198 ); 202 );
199 } 203 }
@@ -231,12 +235,20 @@ impl ProjectWorkspace {
231 opts 235 opts
232 }; 236 };
233 237
238 let mut env = Env::default();
239 let mut extern_source = ExternSource::default();
240 if let Some((id, path)) = outdirs.get(krate.name(&sysroot)) {
241 env.set("OUT_DIR", path.clone());
242 extern_source.set_extern_path(&path, *id);
243 }
244
234 let crate_id = crate_graph.add_crate_root( 245 let crate_id = crate_graph.add_crate_root(
235 file_id, 246 file_id,
236 Edition::Edition2018, 247 Edition::Edition2018,
237 Some(krate.name(&sysroot).to_string()), 248 Some(krate.name(&sysroot).to_string()),
238 cfg_options, 249 cfg_options,
239 Env::default(), 250 env,
251 extern_source,
240 ); 252 );
241 sysroot_crates.insert(krate, crate_id); 253 sysroot_crates.insert(krate, crate_id);
242 } 254 }
@@ -275,12 +287,19 @@ impl ProjectWorkspace {
275 opts.insert_features(pkg.features(&cargo).iter().map(Into::into)); 287 opts.insert_features(pkg.features(&cargo).iter().map(Into::into));
276 opts 288 opts
277 }; 289 };
290 let mut env = Env::default();
291 let mut extern_source = ExternSource::default();
292 if let Some((id, path)) = outdirs.get(pkg.name(&cargo)) {
293 env.set("OUT_DIR", path.clone());
294 extern_source.set_extern_path(&path, *id);
295 }
278 let crate_id = crate_graph.add_crate_root( 296 let crate_id = crate_graph.add_crate_root(
279 file_id, 297 file_id,
280 edition, 298 edition,
281 Some(pkg.name(&cargo).to_string()), 299 Some(pkg.name(&cargo).to_string()),
282 cfg_options, 300 cfg_options,
283 Env::default(), 301 env,
302 extern_source,
284 ); 303 );
285 if tgt.kind(&cargo) == TargetKind::Lib { 304 if tgt.kind(&cargo) == TargetKind::Lib {
286 lib_tgt = Some(crate_id); 305 lib_tgt = Some(crate_id);
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 5df29a383..2ce69c9b3 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -52,7 +52,10 @@ pub(crate) fn load_cargo(
52 opts 52 opts
53 }; 53 };
54 54
55 let crate_graph = ws.to_crate_graph(&default_cfg_options, &mut |path: &Path| { 55 // FIXME: outdirs?
56 let outdirs = FxHashMap::default();
57
58 let crate_graph = ws.to_crate_graph(&default_cfg_options, &outdirs, &mut |path: &Path| {
56 let vfs_file = vfs.load(path); 59 let vfs_file = vfs.load(path);
57 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 60 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
58 vfs_file.map(vfs_file_to_id) 61 vfs_file.map(vfs_file_to_id)
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3314269ec..a8bf29ddf 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -44,6 +44,9 @@ pub struct ServerConfig {
44 /// Fine grained feature flags to disable specific features. 44 /// Fine grained feature flags to disable specific features.
45 pub feature_flags: FxHashMap<String, bool>, 45 pub feature_flags: FxHashMap<String, bool>,
46 46
47 /// Fine grained controls for additional `OUT_DIR` env variables
48 pub additional_out_dirs: FxHashMap<String, String>,
49
47 pub rustfmt_args: Vec<String>, 50 pub rustfmt_args: Vec<String>,
48 51
49 /// Cargo feature configurations. 52 /// Cargo feature configurations.
@@ -64,6 +67,7 @@ impl Default for ServerConfig {
64 cargo_watch_all_targets: true, 67 cargo_watch_all_targets: true,
65 with_sysroot: true, 68 with_sysroot: true,
66 feature_flags: FxHashMap::default(), 69 feature_flags: FxHashMap::default(),
70 additional_out_dirs: FxHashMap::default(),
67 cargo_features: Default::default(), 71 cargo_features: Default::default(),
68 rustfmt_args: Vec::new(), 72 rustfmt_args: Vec::new(),
69 } 73 }
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f9de712a0..4f7aac754 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -196,6 +196,7 @@ pub fn main_loop(
196 Watch(!config.use_client_watching), 196 Watch(!config.use_client_watching),
197 options, 197 options,
198 feature_flags, 198 feature_flags,
199 config.additional_out_dirs,
199 ) 200 )
200 }; 201 };
201 202
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 1ddc3c1a5..9ef368529 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -26,6 +26,8 @@ use crate::{
26 vfs_glob::{Glob, RustPackageFilterBuilder}, 26 vfs_glob::{Glob, RustPackageFilterBuilder},
27 LspError, Result, 27 LspError, Result,
28}; 28};
29use ra_db::ExternSourceId;
30use rustc_hash::{FxHashMap, FxHashSet};
29 31
30#[derive(Debug, Clone)] 32#[derive(Debug, Clone)]
31pub struct Options { 33pub struct Options {
@@ -78,6 +80,7 @@ impl WorldState {
78 watch: Watch, 80 watch: Watch,
79 options: Options, 81 options: Options,
80 feature_flags: FeatureFlags, 82 feature_flags: FeatureFlags,
83 additional_out_dirs: FxHashMap<String, String>,
81 ) -> WorldState { 84 ) -> WorldState {
82 let mut change = AnalysisChange::new(); 85 let mut change = AnalysisChange::new();
83 86
@@ -99,6 +102,19 @@ impl WorldState {
99 RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter()) 102 RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter())
100 })); 103 }));
101 } 104 }
105
106 let extern_dirs: FxHashSet<_> =
107 additional_out_dirs.iter().map(|(_, path)| (PathBuf::from(path))).collect();
108 let mut extern_source_roots = FxHashMap::default();
109
110 roots.extend(additional_out_dirs.iter().map(|(_, path)| {
111 let mut filter = RustPackageFilterBuilder::default().set_member(false);
112 for glob in exclude_globs.iter() {
113 filter = filter.exclude(glob.clone());
114 }
115 RootEntry::new(PathBuf::from(&path), filter.into_vfs_filter())
116 }));
117
102 let (task_sender, task_receiver) = unbounded(); 118 let (task_sender, task_receiver) = unbounded();
103 let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); 119 let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
104 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch); 120 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
@@ -108,6 +124,11 @@ impl WorldState {
108 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); 124 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
109 change.add_root(SourceRootId(r.0), is_local); 125 change.add_root(SourceRootId(r.0), is_local);
110 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); 126 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
127
128 // FIXME: add path2root in vfs to simpily this logic
129 if extern_dirs.contains(&vfs_root_path) {
130 extern_source_roots.insert(vfs_root_path, ExternSourceId(r.0));
131 }
111 } 132 }
112 133
113 // FIXME: Read default cfgs from config 134 // FIXME: Read default cfgs from config
@@ -125,11 +146,20 @@ impl WorldState {
125 vfs_file.map(|f| FileId(f.0)) 146 vfs_file.map(|f| FileId(f.0))
126 }; 147 };
127 148
128 workspaces.iter().map(|ws| ws.to_crate_graph(&default_cfg_options, &mut load)).for_each( 149 let mut outdirs = FxHashMap::default();
129 |graph| { 150 for (name, path) in additional_out_dirs {
151 let path = PathBuf::from(&path);
152 if let Some(id) = extern_source_roots.get(&path) {
153 outdirs.insert(name, (id.clone(), path.to_string_lossy().replace("\\", "/")));
154 }
155 }
156
157 workspaces
158 .iter()
159 .map(|ws| ws.to_crate_graph(&default_cfg_options, &outdirs, &mut load))
160 .for_each(|graph| {
130 crate_graph.extend(graph); 161 crate_graph.extend(graph);
131 }, 162 });
132 );
133 change.set_crate_graph(crate_graph); 163 change.set_crate_graph(crate_graph);
134 164
135 // FIXME: Figure out the multi-workspace situation 165 // FIXME: Figure out the multi-workspace situation
diff --git a/editors/code/package.json b/editors/code/package.json
index 512885454..1fe8e9f8a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -224,6 +224,11 @@
224 "default": true, 224 "default": true,
225 "description": "Whether to ask for permission before downloading any files from the Internet" 225 "description": "Whether to ask for permission before downloading any files from the Internet"
226 }, 226 },
227 "rust-analyzer.additionalOutDirs": {
228 "type": "object",
229 "default": {},
230 "markdownDescription": "Fine grained controls for OUT_DIR `env!(\"OUT_DIR\")` variable. e.g. `{\"foo\":\"/path/to/foo\"}`, "
231 },
227 "rust-analyzer.serverPath": { 232 "rust-analyzer.serverPath": {
228 "type": [ 233 "type": [
229 "null", 234 "null",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 540f7c9ea..6ce3b9235 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -37,6 +37,7 @@ export async function createClient(config: Config, serverPath: string): Promise<
37 excludeGlobs: config.excludeGlobs, 37 excludeGlobs: config.excludeGlobs,
38 useClientWatching: config.useClientWatching, 38 useClientWatching: config.useClientWatching,
39 featureFlags: config.featureFlags, 39 featureFlags: config.featureFlags,
40 additionalOutDirs: config.additionalOutDirs,
40 withSysroot: config.withSysroot, 41 withSysroot: config.withSysroot,
41 cargoFeatures: config.cargoFeatures, 42 cargoFeatures: config.cargoFeatures,
42 rustfmtArgs: config.rustfmtArgs, 43 rustfmtArgs: config.rustfmtArgs,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index b72206d3c..3ade7e900 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -154,6 +154,7 @@ export class Config {
154 get excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; } 154 get excludeGlobs() { return this.cfg.get("excludeGlobs") as string[]; }
155 get useClientWatching() { return this.cfg.get("useClientWatching") as boolean; } 155 get useClientWatching() { return this.cfg.get("useClientWatching") as boolean; }
156 get featureFlags() { return this.cfg.get("featureFlags") as Record<string, boolean>; } 156 get featureFlags() { return this.cfg.get("featureFlags") as Record<string, boolean>; }
157 get additionalOutDirs() { return this.cfg.get("additionalOutDirs") as Record<string, string>; }
157 get rustfmtArgs() { return this.cfg.get("rustfmtArgs") as string[]; } 158 get rustfmtArgs() { return this.cfg.get("rustfmtArgs") as string[]; }
158 159
159 get cargoWatchOptions(): CargoWatchOptions { 160 get cargoWatchOptions(): CargoWatchOptions {