aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock13
-rw-r--r--crates/ra_cargo_watch/src/conv.rs14
-rw-r--r--crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap52
-rw-r--r--crates/ra_cargo_watch/src/lib.rs67
-rw-r--r--crates/ra_hir/src/code_model.rs24
-rw-r--r--crates/ra_hir/src/lib.rs12
-rw-r--r--crates/ra_hir_def/src/body/lower.rs12
-rw-r--r--crates/ra_hir_def/src/path.rs2
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs16
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs7
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs27
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs13
12 files changed, 136 insertions, 123 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3f4d9204c..f8d26192d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -845,14 +845,9 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
845 845
846[[package]] 846[[package]]
847name = "proc-macro-hack" 847name = "proc-macro-hack"
848version = "0.5.12" 848version = "0.5.14"
849source = "registry+https://github.com/rust-lang/crates.io-index" 849source = "registry+https://github.com/rust-lang/crates.io-index"
850checksum = "f918f2b601f93baa836c1c2945faef682ba5b6d4828ecb45eeb7cc3c71b811b4" 850checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420"
851dependencies = [
852 "proc-macro2",
853 "quote",
854 "syn",
855]
856 851
857[[package]] 852[[package]]
858name = "proc-macro2" 853name = "proc-macro2"
@@ -1480,9 +1475,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1480 1475
1481[[package]] 1476[[package]]
1482name = "syn" 1477name = "syn"
1483version = "1.0.16" 1478version = "1.0.17"
1484source = "registry+https://github.com/rust-lang/crates.io-index" 1479source = "registry+https://github.com/rust-lang/crates.io-index"
1485checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" 1480checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
1486dependencies = [ 1481dependencies = [
1487 "proc-macro2", 1482 "proc-macro2",
1488 "quote", 1483 "quote",
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs
index c6f8ca329..817543deb 100644
--- a/crates/ra_cargo_watch/src/conv.rs
+++ b/crates/ra_cargo_watch/src/conv.rs
@@ -1,7 +1,8 @@
1//! This module provides the functionality needed to convert diagnostics from 1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use cargo_metadata::diagnostic::{ 3use cargo_metadata::diagnostic::{
4 Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, 4 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
5 DiagnosticSpanMacroExpansion,
5}; 6};
6use lsp_types::{ 7use lsp_types::{
7 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, 8 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag,
@@ -136,10 +137,13 @@ fn map_rust_child_diagnostic(
136 137
137 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new(); 138 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new();
138 for &span in &spans { 139 for &span in &spans {
139 if let Some(suggested_replacement) = &span.suggested_replacement { 140 match (&span.suggestion_applicability, &span.suggested_replacement) {
140 let location = map_span_to_location(span, workspace_root); 141 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => {
141 let edit = TextEdit::new(location.range, suggested_replacement.clone()); 142 let location = map_span_to_location(span, workspace_root);
142 edit_map.entry(location.uri).or_default().push(edit); 143 let edit = TextEdit::new(location.range, suggested_replacement.clone());
144 edit_map.entry(location.uri).or_default().push(edit);
145 }
146 _ => {}
143 } 147 }
144 } 148 }
145 149
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
index 9e8f4eff4..a59fa84fa 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
@@ -58,44 +58,26 @@ expression: diag
58 }, 58 },
59 message: "lint level defined here", 59 message: "lint level defined here",
60 }, 60 },
61 DiagnosticRelatedInformation {
62 location: Location {
63 uri: "file:///test/compiler/mir/tagset.rs",
64 range: Range {
65 start: Position {
66 line: 41,
67 character: 23,
68 },
69 end: Position {
70 line: 41,
71 character: 28,
72 },
73 },
74 },
75 message: "consider passing by value instead",
76 },
61 ], 77 ],
62 ), 78 ),
63 tags: None, 79 tags: None,
64 }, 80 },
65 fixes: [ 81 fixes: [],
66 CodeAction {
67 title: "consider passing by value instead",
68 kind: Some(
69 "quickfix",
70 ),
71 diagnostics: None,
72 edit: Some(
73 WorkspaceEdit {
74 changes: Some(
75 {
76 "file:///test/compiler/mir/tagset.rs": [
77 TextEdit {
78 range: Range {
79 start: Position {
80 line: 41,
81 character: 23,
82 },
83 end: Position {
84 line: 41,
85 character: 28,
86 },
87 },
88 new_text: "self",
89 },
90 ],
91 },
92 ),
93 document_changes: None,
94 },
95 ),
96 command: None,
97 is_preferred: None,
98 },
99 ],
100 }, 82 },
101] 83]
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
index bffe5eb00..7c525c430 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -8,9 +8,10 @@ use lsp_types::{
8 WorkDoneProgressEnd, WorkDoneProgressReport, 8 WorkDoneProgressEnd, WorkDoneProgressReport,
9}; 9};
10use std::{ 10use std::{
11 error, fmt,
11 io::{BufRead, BufReader}, 12 io::{BufRead, BufReader},
12 path::{Path, PathBuf}, 13 path::{Path, PathBuf},
13 process::{Child, Command, Stdio}, 14 process::{Command, Stdio},
14 thread::JoinHandle, 15 thread::JoinHandle,
15 time::Instant, 16 time::Instant,
16}; 17};
@@ -70,10 +71,10 @@ impl std::ops::Drop for CheckWatcher {
70 fn drop(&mut self) { 71 fn drop(&mut self) {
71 if let Some(handle) = self.handle.take() { 72 if let Some(handle) = self.handle.take() {
72 // Take the sender out of the option 73 // Take the sender out of the option
73 let recv = self.cmd_send.take(); 74 let cmd_send = self.cmd_send.take();
74 75
75 // Dropping the sender finishes the thread loop 76 // Dropping the sender finishes the thread loop
76 drop(recv); 77 drop(cmd_send);
77 78
78 // Join the thread, it should finish shortly. We don't really care 79 // Join the thread, it should finish shortly. We don't really care
79 // whether it panicked, so it is safe to ignore the result 80 // whether it panicked, so it is safe to ignore the result
@@ -246,11 +247,21 @@ enum CheckEvent {
246 End, 247 End,
247} 248}
248 249
250#[derive(Debug)]
251pub struct CargoError(String);
252
253impl fmt::Display for CargoError {
254 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255 write!(f, "Cargo failed: {}", self.0)
256 }
257}
258impl error::Error for CargoError {}
259
249pub fn run_cargo( 260pub fn run_cargo(
250 args: &[String], 261 args: &[String],
251 current_dir: Option<&Path>, 262 current_dir: Option<&Path>,
252 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, 263 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
253) -> Child { 264) -> Result<(), CargoError> {
254 let mut command = Command::new("cargo"); 265 let mut command = Command::new("cargo");
255 if let Some(current_dir) = current_dir { 266 if let Some(current_dir) = current_dir {
256 command.current_dir(current_dir); 267 command.current_dir(current_dir);
@@ -273,6 +284,8 @@ pub fn run_cargo(
273 // simply skip a line if it doesn't parse, which just ignores any 284 // simply skip a line if it doesn't parse, which just ignores any
274 // erroneus output. 285 // erroneus output.
275 let stdout = BufReader::new(child.stdout.take().unwrap()); 286 let stdout = BufReader::new(child.stdout.take().unwrap());
287 let mut read_at_least_one_message = false;
288
276 for line in stdout.lines() { 289 for line in stdout.lines() {
277 let line = match line { 290 let line = match line {
278 Ok(line) => line, 291 Ok(line) => line,
@@ -291,12 +304,31 @@ pub fn run_cargo(
291 } 304 }
292 }; 305 };
293 306
307 read_at_least_one_message = true;
308
294 if !on_message(message) { 309 if !on_message(message) {
295 break; 310 break;
296 } 311 }
297 } 312 }
298 313
299 child 314 // It is okay to ignore the result, as it only errors if the process is already dead
315 let _ = child.kill();
316
317 let err_msg = match child.wait() {
318 Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
319 // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
320 // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
321 format!(
322 "the command produced no valid metadata (exit code: {:?}): cargo {}",
323 exit_code,
324 args.join(" ")
325 )
326 }
327 Err(err) => format!("io error: {:?}", err),
328 Ok(_) => return Ok(()),
329 };
330
331 Err(CargoError(err_msg))
300} 332}
301 333
302impl WatchThread { 334impl WatchThread {
@@ -325,7 +357,7 @@ impl WatchThread {
325 // which will break out of the loop, and continue the shutdown 357 // which will break out of the loop, and continue the shutdown
326 let _ = message_send.send(CheckEvent::Begin); 358 let _ = message_send.send(CheckEvent::Begin);
327 359
328 let mut child = run_cargo(&args, Some(&workspace_root), &mut |message| { 360 let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
329 // Skip certain kinds of messages to only spend time on what's useful 361 // Skip certain kinds of messages to only spend time on what's useful
330 match &message { 362 match &message {
331 Message::CompilerArtifact(artifact) if artifact.fresh => return true, 363 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
@@ -334,26 +366,19 @@ impl WatchThread {
334 _ => {} 366 _ => {}
335 } 367 }
336 368
337 match message_send.send(CheckEvent::Msg(message)) { 369 // if the send channel was closed, we want to shutdown
338 Ok(()) => {} 370 message_send.send(CheckEvent::Msg(message)).is_ok()
339 Err(_err) => {
340 // The send channel was closed, so we want to shutdown
341 return false;
342 }
343 };
344
345 true
346 }); 371 });
347 372
373 if let Err(err) = res {
374 // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
375 // to display user-caused misconfiguration errors instead of just logging them here
376 log::error!("Cargo watcher failed {:?}", err);
377 }
378
348 // We can ignore any error here, as we are already in the progress 379 // We can ignore any error here, as we are already in the progress
349 // of shutting down. 380 // of shutting down.
350 let _ = message_send.send(CheckEvent::End); 381 let _ = message_send.send(CheckEvent::End);
351
352 // It is okay to ignore the result, as it only errors if the process is already dead
353 let _ = child.kill();
354
355 // Again, we don't care about the exit status so just ignore the result
356 let _ = child.wait();
357 })) 382 }))
358 } else { 383 } else {
359 None 384 None
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 45e31095c..4b150ef06 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1042,30 +1042,18 @@ impl Type {
1042 } 1042 }
1043 1043
1044 pub fn is_bool(&self) -> bool { 1044 pub fn is_bool(&self) -> bool {
1045 match &self.ty.value { 1045 matches!(self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. }))
1046 Ty::Apply(a_ty) => match a_ty.ctor {
1047 TypeCtor::Bool => true,
1048 _ => false,
1049 },
1050 _ => false,
1051 }
1052 } 1046 }
1053 1047
1054 pub fn is_mutable_reference(&self) -> bool { 1048 pub fn is_mutable_reference(&self) -> bool {
1055 match &self.ty.value { 1049 matches!(
1056 Ty::Apply(a_ty) => match a_ty.ctor { 1050 self.ty.value,
1057 TypeCtor::Ref(Mutability::Mut) => true, 1051 Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(Mutability::Mut), .. })
1058 _ => false, 1052 )
1059 },
1060 _ => false,
1061 }
1062 } 1053 }
1063 1054
1064 pub fn is_unknown(&self) -> bool { 1055 pub fn is_unknown(&self) -> bool {
1065 match &self.ty.value { 1056 matches!(self.ty.value, Ty::Unknown)
1066 Ty::Unknown => true,
1067 _ => false,
1068 }
1069 } 1057 }
1070 1058
1071 /// Checks that particular type `ty` implements `std::future::Future`. 1059 /// Checks that particular type `ty` implements `std::future::Future`.
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 9f59d590c..713d45f48 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -4,6 +4,18 @@
4//! The principal difference between HIR and syntax trees is that HIR is bound 4//! The principal difference between HIR and syntax trees is that HIR is bound
5//! to a particular crate instance. That is, it has cfg flags and features 5//! to a particular crate instance. That is, it has cfg flags and features
6//! applied. So, the relation between syntax and HIR is many-to-one. 6//! applied. So, the relation between syntax and HIR is many-to-one.
7//!
8//! HIR is the public API of the all of the compiler logic above syntax trees.
9//! It is written in "OO" style. Each type is self contained (as in, it knows it's
10//! parents and full context). It should be "clean code".
11//!
12//! `ra_hir_*` crates are the implementation of the compiler logic.
13//! They are written in "ECS" style, with relatively little abstractions.
14//! Many types are not self-contained, and explicitly use local indexes, arenas, etc.
15//!
16//! `ra_hir` is what insulates the "we don't know how to actually write an incremental compiler"
17//! from the ide with completions, hovers, etc. It is a (soft, internal) boundary:
18//! https://www.tedinski.com/2018/02/06/system-boundaries.html.
7 19
8#![recursion_limit = "512"] 20#![recursion_limit = "512"]
9 21
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index e8c58ed32..3cf0c66ea 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -453,7 +453,7 @@ impl ExprCollector<'_> {
453 } 453 }
454 } 454 }
455 ast::Expr::MacroCall(e) => { 455 ast::Expr::MacroCall(e) => {
456 if let Some(name) = is_macro_rules(&e) { 456 if let Some(name) = e.is_macro_rules().map(|it| it.as_name()) {
457 let mac = MacroDefId { 457 let mac = MacroDefId {
458 krate: Some(self.expander.module.krate), 458 krate: Some(self.expander.module.krate),
459 ast_id: Some(self.expander.ast_id(&e)), 459 ast_id: Some(self.expander.ast_id(&e)),
@@ -697,16 +697,6 @@ impl ExprCollector<'_> {
697 } 697 }
698} 698}
699 699
700fn is_macro_rules(m: &ast::MacroCall) -> Option<Name> {
701 let name = m.path()?.segment()?.name_ref()?.as_name();
702
703 if name == name![macro_rules] {
704 Some(m.name()?.as_name())
705 } else {
706 None
707 }
708}
709
710impl From<ast::BinOp> for BinaryOp { 700impl From<ast::BinOp> for BinaryOp {
711 fn from(ast_op: ast::BinOp) -> Self { 701 fn from(ast_op: ast::BinOp) -> Self {
712 match ast_op { 702 match ast_op {
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 246032c13..904080341 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -95,7 +95,7 @@ pub struct Path {
95 /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`. 95 /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`.
96 type_anchor: Option<Box<TypeRef>>, 96 type_anchor: Option<Box<TypeRef>>,
97 mod_path: ModPath, 97 mod_path: ModPath,
98 /// Invariant: the same len as self.path.segments 98 /// Invariant: the same len as `self.mod_path.segments`
99 generic_args: Vec<Option<Arc<GenericArgs>>>, 99 generic_args: Vec<Option<Arc<GenericArgs>>>,
100} 100}
101 101
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index 69c059ac8..533c6ccfb 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -95,14 +95,14 @@ impl Ty {
95 // Types like slice can have inherent impls in several crates, (core and alloc). 95 // Types like slice can have inherent impls in several crates, (core and alloc).
96 // The corresponding impls are marked with lang items, so we can use them to find the required crates. 96 // The corresponding impls are marked with lang items, so we can use them to find the required crates.
97 macro_rules! lang_item_crate { 97 macro_rules! lang_item_crate {
98 ($($name:expr),+ $(,)?) => {{ 98 ($($name:expr),+ $(,)?) => {{
99 let mut v = ArrayVec::<[LangItemTarget; 2]>::new(); 99 let mut v = ArrayVec::<[LangItemTarget; 2]>::new();
100 $( 100 $(
101 v.extend(db.lang_item(cur_crate, $name.into())); 101 v.extend(db.lang_item(cur_crate, $name.into()));
102 )+ 102 )+
103 v 103 v
104 }}; 104 }};
105 } 105 }
106 106
107 let lang_item_targets = match self { 107 let lang_item_targets = match self {
108 Ty::Apply(a_ty) => match a_ty.ctor { 108 Ty::Apply(a_ty) => match a_ty.ctor {
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index 884359ee3..0f46f93c1 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -362,6 +362,13 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
362 ast::TypeAliasDef(it) => { decl(it) }, 362 ast::TypeAliasDef(it) => { decl(it) },
363 ast::ConstDef(it) => { decl(it) }, 363 ast::ConstDef(it) => { decl(it) },
364 ast::StaticDef(it) => { decl(it) }, 364 ast::StaticDef(it) => { decl(it) },
365 ast::MacroCall(it) => {
366 if it.is_macro_rules().is_some() {
367 decl(it)
368 } else {
369 None
370 }
371 },
365 _ => None, 372 _ => None,
366 } 373 }
367 } 374 }
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index c2857dbfc..c7f9bd873 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -6,7 +6,7 @@ use std::{
6}; 6};
7 7
8use anyhow::{Context, Result}; 8use anyhow::{Context, Result};
9use cargo_metadata::{CargoOpt, Message, MetadataCommand, PackageId}; 9use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
10use ra_arena::{Arena, Idx}; 10use ra_arena::{Arena, Idx};
11use ra_cargo_watch::run_cargo; 11use ra_cargo_watch::run_cargo;
12use ra_db::Edition; 12use ra_db::Edition;
@@ -254,7 +254,7 @@ pub fn load_out_dirs(
254 "check".to_string(), 254 "check".to_string(),
255 "--message-format=json".to_string(), 255 "--message-format=json".to_string(),
256 "--manifest-path".to_string(), 256 "--manifest-path".to_string(),
257 format!("{}", cargo_toml.display()), 257 cargo_toml.display().to_string(),
258 ]; 258 ];
259 259
260 if cargo_features.all_features { 260 if cargo_features.all_features {
@@ -263,19 +263,15 @@ pub fn load_out_dirs(
263 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 263 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
264 // https://github.com/oli-obk/cargo_metadata/issues/79 264 // https://github.com/oli-obk/cargo_metadata/issues/79
265 args.push("--no-default-features".to_string()); 265 args.push("--no-default-features".to_string());
266 } else if !cargo_features.features.is_empty() { 266 } else {
267 for feature in &cargo_features.features { 267 args.extend(cargo_features.features.iter().cloned());
268 args.push(feature.clone());
269 }
270 } 268 }
271 269
272 let mut res = FxHashMap::default(); 270 let mut acc = FxHashMap::default();
273 let mut child = run_cargo(&args, cargo_toml.parent(), &mut |message| { 271 let res = run_cargo(&args, cargo_toml.parent(), &mut |message| {
274 match message { 272 match message {
275 Message::BuildScriptExecuted(message) => { 273 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
276 let package_id = message.package_id; 274 acc.insert(package_id, out_dir);
277 let out_dir = message.out_dir;
278 res.insert(package_id, out_dir);
279 } 275 }
280 276
281 Message::CompilerArtifact(_) => (), 277 Message::CompilerArtifact(_) => (),
@@ -285,6 +281,9 @@ pub fn load_out_dirs(
285 true 281 true
286 }); 282 });
287 283
288 let _ = child.wait(); 284 if let Err(err) = res {
289 res 285 log::error!("Failed to load outdirs: {:?}", err);
286 }
287
288 acc
290} 289}
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index c3ae8f90e..392731dac 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -4,7 +4,7 @@
4use itertools::Itertools; 4use itertools::Itertools;
5 5
6use crate::{ 6use crate::{
7 ast::{self, child_opt, children, AstNode, AttrInput, SyntaxNode}, 7 ast::{self, child_opt, children, AstNode, AttrInput, NameOwner, SyntaxNode},
8 SmolStr, SyntaxElement, 8 SmolStr, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, T, 10 SyntaxToken, T,
@@ -514,3 +514,14 @@ impl ast::Visibility {
514 self.syntax().children_with_tokens().any(|it| it.kind() == T![super]) 514 self.syntax().children_with_tokens().any(|it| it.kind() == T![super])
515 } 515 }
516} 516}
517
518impl ast::MacroCall {
519 pub fn is_macro_rules(&self) -> Option<ast::Name> {
520 let name_ref = self.path()?.segment()?.name_ref()?;
521 if name_ref.text() == "macro_rules" {
522 self.name()
523 } else {
524 None
525 }
526 }
527}