aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/semantics.rs8
-rw-r--r--crates/ra_hir/src/source_analyzer.rs99
-rw-r--r--crates/ra_hir_def/src/body.rs4
-rw-r--r--crates/ra_hir_def/src/body/lower.rs16
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs2
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs5
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs2
-rw-r--r--crates/ra_hir_ty/src/infer.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/coerce.rs4
-rw-r--r--crates/ra_hir_ty/src/infer/unify.rs42
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs49
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs198
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs19
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs40
-rw-r--r--crates/ra_ide/src/diagnostics.rs72
-rw-r--r--crates/ra_ide/src/marks.rs1
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs3
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs12
-rw-r--r--crates/ra_proc_macro/src/lib.rs12
-rw-r--r--crates/ra_proc_macro/src/process.rs19
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/src/cli.rs (renamed from crates/ra_proc_macro_srv/src/main.rs)5
-rw-r--r--crates/ra_proc_macro_srv/src/dylib.rs77
-rw-r--r--crates/ra_proc_macro_srv/src/lib.rs6
-rw-r--r--crates/ra_project_model/src/lib.rs231
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/args.rs24
-rw-r--r--crates/rust-analyzer/src/bin/main.rs22
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs3
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs3
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs9
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs18
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/main_loop.rs60
-rw-r--r--crates/rust-analyzer/src/world.rs8
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs91
36 files changed, 884 insertions, 299 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 2707e422d..0b477f0e9 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -20,6 +20,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
20 20
21use crate::{ 21use crate::{
22 db::HirDatabase, 22 db::HirDatabase,
23 diagnostics::Diagnostic,
23 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 24 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
24 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 25 source_analyzer::{resolve_hir_path, SourceAnalyzer},
25 AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name, 26 AssocItem, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, Name,
@@ -126,6 +127,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
126 original_range(self.db, node.as_ref()) 127 original_range(self.db, node.as_ref())
127 } 128 }
128 129
130 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
131 let src = diagnostics.source();
132 let root = self.db.parse_or_expand(src.file_id).unwrap();
133 let node = src.value.to_node(&root);
134 original_range(self.db, src.with_value(&node))
135 }
136
129 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { 137 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
130 let node = self.find_file(node); 138 let node = self.find_file(node);
131 node.ancestors_with_macros(self.db).map(|it| it.value) 139 node.ancestors_with_macros(self.db).map(|it| it.value)
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 58ae6ce41..23af400b8 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -23,7 +23,7 @@ use hir_ty::{
23}; 23};
24use ra_syntax::{ 24use ra_syntax::{
25 ast::{self, AstNode}, 25 ast::{self, AstNode},
26 SyntaxNode, SyntaxNodePtr, TextUnit, 26 SyntaxNode, TextRange, TextUnit,
27}; 27};
28 28
29use crate::{ 29use crate::{
@@ -56,7 +56,7 @@ impl SourceAnalyzer {
56 let scopes = db.expr_scopes(def); 56 let scopes = db.expr_scopes(def);
57 let scope = match offset { 57 let scope = match offset {
58 None => scope_for(&scopes, &source_map, node), 58 None => scope_for(&scopes, &source_map, node),
59 Some(offset) => scope_for_offset(&scopes, &source_map, node.with_value(offset)), 59 Some(offset) => scope_for_offset(db, &scopes, &source_map, node.with_value(offset)),
60 }; 60 };
61 let resolver = resolver_for_scope(db.upcast(), def, scope); 61 let resolver = resolver_for_scope(db.upcast(), def, scope);
62 SourceAnalyzer { 62 SourceAnalyzer {
@@ -304,6 +304,7 @@ fn scope_for(
304} 304}
305 305
306fn scope_for_offset( 306fn scope_for_offset(
307 db: &dyn HirDatabase,
307 scopes: &ExprScopes, 308 scopes: &ExprScopes,
308 source_map: &BodySourceMap, 309 source_map: &BodySourceMap,
309 offset: InFile<TextUnit>, 310 offset: InFile<TextUnit>,
@@ -317,21 +318,63 @@ fn scope_for_offset(
317 if source.file_id != offset.file_id { 318 if source.file_id != offset.file_id {
318 return None; 319 return None;
319 } 320 }
320 let syntax_node_ptr = source.value.syntax_node_ptr(); 321 let root = source.file_syntax(db.upcast());
321 Some((syntax_node_ptr, scope)) 322 let node = source.value.to_node(&root);
323 Some((node.syntax().text_range(), scope))
322 }) 324 })
323 // find containing scope 325 // find containing scope
324 .min_by_key(|(ptr, _scope)| { 326 .min_by_key(|(expr_range, _scope)| {
325 ( 327 (
326 !(ptr.range().start() <= offset.value && offset.value <= ptr.range().end()), 328 !(expr_range.start() <= offset.value && offset.value <= expr_range.end()),
327 ptr.range().len(), 329 expr_range.len(),
328 ) 330 )
329 }) 331 })
330 .map(|(ptr, scope)| { 332 .map(|(expr_range, scope)| {
331 adjust(scopes, source_map, ptr, offset.file_id, offset.value).unwrap_or(*scope) 333 adjust(db, scopes, source_map, expr_range, offset.file_id, offset.value)
334 .unwrap_or(*scope)
332 }) 335 })
333} 336}
334 337
338// XXX: during completion, cursor might be outside of any particular
339// expression. Try to figure out the correct scope...
340fn adjust(
341 db: &dyn HirDatabase,
342 scopes: &ExprScopes,
343 source_map: &BodySourceMap,
344 expr_range: TextRange,
345 file_id: HirFileId,
346 offset: TextUnit,
347) -> Option<ScopeId> {
348 let child_scopes = scopes
349 .scope_by_expr()
350 .iter()
351 .filter_map(|(id, scope)| {
352 let source = source_map.expr_syntax(*id).ok()?;
353 // FIXME: correctly handle macro expansion
354 if source.file_id != file_id {
355 return None;
356 }
357 let root = source.file_syntax(db.upcast());
358 let node = source.value.to_node(&root);
359 Some((node.syntax().text_range(), scope))
360 })
361 .filter(|(range, _)| {
362 range.start() <= offset && range.is_subrange(&expr_range) && *range != expr_range
363 });
364
365 child_scopes
366 .max_by(|(r1, _), (r2, _)| {
367 if r2.is_subrange(&r1) {
368 std::cmp::Ordering::Greater
369 } else if r1.is_subrange(&r2) {
370 std::cmp::Ordering::Less
371 } else {
372 r1.start().cmp(&r2.start())
373 }
374 })
375 .map(|(_ptr, scope)| *scope)
376}
377
335pub(crate) fn resolve_hir_path( 378pub(crate) fn resolve_hir_path(
336 db: &dyn HirDatabase, 379 db: &dyn HirDatabase,
337 resolver: &Resolver, 380 resolver: &Resolver,
@@ -376,41 +419,3 @@ pub(crate) fn resolve_hir_path(
376 .map(|def| PathResolution::Macro(def.into())) 419 .map(|def| PathResolution::Macro(def.into()))
377 }) 420 })
378} 421}
379
380// XXX: during completion, cursor might be outside of any particular
381// expression. Try to figure out the correct scope...
382fn adjust(
383 scopes: &ExprScopes,
384 source_map: &BodySourceMap,
385 ptr: SyntaxNodePtr,
386 file_id: HirFileId,
387 offset: TextUnit,
388) -> Option<ScopeId> {
389 let r = ptr.range();
390 let child_scopes = scopes
391 .scope_by_expr()
392 .iter()
393 .filter_map(|(id, scope)| {
394 let source = source_map.expr_syntax(*id).ok()?;
395 // FIXME: correctly handle macro expansion
396 if source.file_id != file_id {
397 return None;
398 }
399 let syntax_node_ptr = source.value.syntax_node_ptr();
400 Some((syntax_node_ptr, scope))
401 })
402 .map(|(ptr, scope)| (ptr.range(), scope))
403 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
404
405 child_scopes
406 .max_by(|(r1, _), (r2, _)| {
407 if r2.is_subrange(&r1) {
408 std::cmp::Ordering::Greater
409 } else if r1.is_subrange(&r2) {
410 std::cmp::Ordering::Less
411 } else {
412 r1.start().cmp(&r2.start())
413 }
414 })
415 .map(|(_ptr, scope)| *scope)
416}
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index eafaf48c1..3b169440a 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -210,7 +210,7 @@ pub struct BodySourceMap {
210 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, 210 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
211 pat_map: FxHashMap<PatSource, PatId>, 211 pat_map: FxHashMap<PatSource, PatId>,
212 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, 212 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
213 field_map: FxHashMap<(ExprId, usize), AstPtr<ast::RecordField>>, 213 field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordField>>>,
214 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, 214 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
215} 215}
216 216
@@ -303,7 +303,7 @@ impl BodySourceMap {
303 self.pat_map.get(&src).cloned() 303 self.pat_map.get(&src).cloned()
304 } 304 }
305 305
306 pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> { 306 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> {
307 self.field_map[&(expr, field)].clone() 307 self.field_map[&(expr, field)].clone()
308 } 308 }
309} 309}
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 79abe55ce..82a52804d 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -320,7 +320,8 @@ impl ExprCollector<'_> {
320 320
321 let res = self.alloc_expr(record_lit, syntax_ptr); 321 let res = self.alloc_expr(record_lit, syntax_ptr);
322 for (i, ptr) in field_ptrs.into_iter().enumerate() { 322 for (i, ptr) in field_ptrs.into_iter().enumerate() {
323 self.source_map.field_map.insert((res, i), ptr); 323 let src = self.expander.to_source(ptr);
324 self.source_map.field_map.insert((res, i), src);
324 } 325 }
325 res 326 res
326 } 327 }
@@ -650,6 +651,7 @@ impl ExprCollector<'_> {
650 ast::Pat::SlicePat(p) => { 651 ast::Pat::SlicePat(p) => {
651 let SlicePatComponents { prefix, slice, suffix } = p.components(); 652 let SlicePatComponents { prefix, slice, suffix } = p.components();
652 653
654 // FIXME properly handle `DotDotPat`
653 Pat::Slice { 655 Pat::Slice {
654 prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(), 656 prefix: prefix.into_iter().map(|p| self.collect_pat(p)).collect(),
655 slice: slice.map(|p| self.collect_pat(p)), 657 slice: slice.map(|p| self.collect_pat(p)),
@@ -666,9 +668,15 @@ impl ExprCollector<'_> {
666 Pat::Missing 668 Pat::Missing
667 } 669 }
668 } 670 }
669 ast::Pat::DotDotPat(_) => unreachable!( 671 ast::Pat::DotDotPat(_) => {
670 "`DotDotPat` requires special handling and should not be mapped to a Pat." 672 // `DotDotPat` requires special handling and should not be mapped
671 ), 673 // to a Pat. Here we are using `Pat::Missing` as a fallback for
674 // when `DotDotPat` is mapped to `Pat`, which can easily happen
675 // when the source code being analyzed has a malformed pattern
676 // which includes `..` in a place where it isn't valid.
677
678 Pat::Missing
679 }
672 // FIXME: implement 680 // FIXME: implement
673 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, 681 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
674 }; 682 };
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index cfa0f2f76..510c5e064 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -20,7 +20,7 @@ impl Diagnostic for UnresolvedModule {
20 "unresolved module".to_string() 20 "unresolved module".to_string()
21 } 21 }
22 fn source(&self) -> InFile<SyntaxNodePtr> { 22 fn source(&self) -> InFile<SyntaxNodePtr> {
23 InFile { file_id: self.file, value: self.decl.clone().into() } 23 InFile::new(self.file, self.decl.clone().into())
24 } 24 }
25 fn as_any(&self) -> &(dyn Any + Send + 'static) { 25 fn as_any(&self) -> &(dyn Any + Send + 'static) {
26 self 26 self
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 108c1e38c..99209c6e8 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -16,16 +16,13 @@
16 16
17use std::{any::Any, fmt}; 17use std::{any::Any, fmt};
18 18
19use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; 19use ra_syntax::{SyntaxNode, SyntaxNodePtr};
20 20
21use crate::{db::AstDatabase, InFile}; 21use crate::{db::AstDatabase, InFile};
22 22
23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { 23pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
24 fn message(&self) -> String; 24 fn message(&self) -> String;
25 fn source(&self) -> InFile<SyntaxNodePtr>; 25 fn source(&self) -> InFile<SyntaxNodePtr>;
26 fn highlight_range(&self) -> TextRange {
27 self.source().value.range()
28 }
29 fn as_any(&self) -> &(dyn Any + Send + 'static); 26 fn as_any(&self) -> &(dyn Any + Send + 'static);
30} 27}
31 28
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 927896d6f..c8fd54861 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -21,7 +21,7 @@ impl Diagnostic for NoSuchField {
21 } 21 }
22 22
23 fn source(&self) -> InFile<SyntaxNodePtr> { 23 fn source(&self) -> InFile<SyntaxNodePtr> {
24 InFile { file_id: self.file, value: self.field.clone().into() } 24 InFile::new(self.file, self.field.clone().into())
25 } 25 }
26 26
27 fn as_any(&self) -> &(dyn Any + Send + 'static) { 27 fn as_any(&self) -> &(dyn Any + Send + 'static) {
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 246b0e9be..b6d9b3438 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -682,10 +682,10 @@ mod diagnostics {
682 ) { 682 ) {
683 match self { 683 match self {
684 InferenceDiagnostic::NoSuchField { expr, field } => { 684 InferenceDiagnostic::NoSuchField { expr, field } => {
685 let file = owner.lookup(db.upcast()).source(db.upcast()).file_id; 685 let source = owner.lookup(db.upcast()).source(db.upcast());
686 let (_, source_map) = db.body_with_source_map(owner.into()); 686 let (_, source_map) = db.body_with_source_map(owner.into());
687 let field = source_map.field_syntax(*expr, *field); 687 let field = source_map.field_syntax(*expr, *field);
688 sink.push(NoSuchField { file, field }) 688 sink.push(NoSuchField { file: source.file_id, field: field.value })
689 } 689 }
690 } 690 }
691 } 691 }
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs
index 959b1e212..89200255a 100644
--- a/crates/ra_hir_ty/src/infer/coerce.rs
+++ b/crates/ra_hir_ty/src/infer/coerce.rs
@@ -51,7 +51,7 @@ impl<'a> InferenceContext<'a> {
51 // Trivial cases, this should go after `never` check to 51 // Trivial cases, this should go after `never` check to
52 // avoid infer result type to be never 52 // avoid infer result type to be never
53 _ => { 53 _ => {
54 if self.table.unify_inner_trivial(&from_ty, &to_ty) { 54 if self.table.unify_inner_trivial(&from_ty, &to_ty, 0) {
55 return true; 55 return true;
56 } 56 }
57 } 57 }
@@ -175,7 +175,7 @@ impl<'a> InferenceContext<'a> {
175 return self.table.unify_substs(st1, st2, 0); 175 return self.table.unify_substs(st1, st2, 0);
176 } 176 }
177 _ => { 177 _ => {
178 if self.table.unify_inner_trivial(&derefed_ty, &to_ty) { 178 if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
179 return true; 179 return true;
180 } 180 }
181 } 181 }
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs
index 5f6cea8d3..ab0bc8b70 100644
--- a/crates/ra_hir_ty/src/infer/unify.rs
+++ b/crates/ra_hir_ty/src/infer/unify.rs
@@ -8,7 +8,8 @@ use test_utils::tested_by;
8 8
9use super::{InferenceContext, Obligation}; 9use super::{InferenceContext, Obligation};
10use crate::{ 10use crate::{
11 BoundVar, Canonical, DebruijnIndex, InEnvironment, InferTy, Substs, Ty, TypeCtor, TypeWalk, 11 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
12 TypeCtor, TypeWalk,
12}; 13};
13 14
14impl<'a> InferenceContext<'a> { 15impl<'a> InferenceContext<'a> {
@@ -226,16 +227,26 @@ impl InferenceTable {
226 (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => { 227 (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
227 self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1) 228 self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
228 } 229 }
229 _ => self.unify_inner_trivial(&ty1, &ty2), 230
231 _ => self.unify_inner_trivial(&ty1, &ty2, depth),
230 } 232 }
231 } 233 }
232 234
233 pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty) -> bool { 235 pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
234 match (ty1, ty2) { 236 match (ty1, ty2) {
235 (Ty::Unknown, _) | (_, Ty::Unknown) => true, 237 (Ty::Unknown, _) | (_, Ty::Unknown) => true,
236 238
237 (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true, 239 (Ty::Placeholder(p1), Ty::Placeholder(p2)) if *p1 == *p2 => true,
238 240
241 (Ty::Dyn(dyn1), Ty::Dyn(dyn2)) if dyn1.len() == dyn2.len() => {
242 for (pred1, pred2) in dyn1.iter().zip(dyn2.iter()) {
243 if !self.unify_preds(pred1, pred2, depth + 1) {
244 return false;
245 }
246 }
247 true
248 }
249
239 (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) 250 (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
240 | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) 251 | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
241 | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) 252 | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2)))
@@ -268,6 +279,31 @@ impl InferenceTable {
268 } 279 }
269 } 280 }
270 281
282 fn unify_preds(
283 &mut self,
284 pred1: &GenericPredicate,
285 pred2: &GenericPredicate,
286 depth: usize,
287 ) -> bool {
288 match (pred1, pred2) {
289 (GenericPredicate::Implemented(tr1), GenericPredicate::Implemented(tr2))
290 if tr1.trait_ == tr2.trait_ =>
291 {
292 self.unify_substs(&tr1.substs, &tr2.substs, depth + 1)
293 }
294 (GenericPredicate::Projection(proj1), GenericPredicate::Projection(proj2))
295 if proj1.projection_ty.associated_ty == proj2.projection_ty.associated_ty =>
296 {
297 self.unify_substs(
298 &proj1.projection_ty.parameters,
299 &proj2.projection_ty.parameters,
300 depth + 1,
301 ) && self.unify_inner(&proj1.ty, &proj2.ty, depth + 1)
302 }
303 _ => false,
304 }
305 }
306
271 /// If `ty` is a type variable with known type, returns that type; 307 /// If `ty` is a type variable with known type, returns that type;
272 /// otherwise, return ty. 308 /// otherwise, return ty.
273 pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { 309 pub fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index d69115a2f..61284d672 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -484,3 +484,52 @@ fn main() {
484 484
485 assert_eq!("()", super::type_at_pos(&db, pos)); 485 assert_eq!("()", super::type_at_pos(&db, pos));
486} 486}
487
488#[test]
489fn issue_3999_slice() {
490 assert_snapshot!(
491 infer(r#"
492fn foo(params: &[usize]) {
493 match params {
494 [ps @ .., _] => {}
495 }
496}
497"#),
498 @r###"
499 [8; 14) 'params': &[usize]
500 [26; 81) '{ ... } }': ()
501 [32; 79) 'match ... }': ()
502 [38; 44) 'params': &[usize]
503 [55; 67) '[ps @ .., _]': [usize]
504 [65; 66) '_': usize
505 [71; 73) '{}': ()
506 "###
507 );
508}
509
510#[test]
511fn issue_3999_struct() {
512 // rust-analyzer should not panic on seeing this malformed
513 // record pattern.
514 assert_snapshot!(
515 infer(r#"
516struct Bar {
517 a: bool,
518}
519fn foo(b: Bar) {
520 match b {
521 Bar { a: .. } => {},
522 }
523}
524"#),
525 @r###"
526 [36; 37) 'b': Bar
527 [44; 96) '{ ... } }': ()
528 [50; 94) 'match ... }': ()
529 [56; 57) 'b': Bar
530 [68; 81) 'Bar { a: .. }': Bar
531 [77; 79) '..': bool
532 [85; 87) '{}': ()
533 "###
534 );
535}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 36f53b264..a46f03b7f 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2240,3 +2240,201 @@ fn test(x: Box<dyn Trait>) {
2240 ); 2240 );
2241 assert_eq!(t, "()"); 2241 assert_eq!(t, "()");
2242} 2242}
2243
2244#[test]
2245fn string_to_owned() {
2246 let t = type_at(
2247 r#"
2248//- /main.rs
2249struct String {}
2250pub trait ToOwned {
2251 type Owned;
2252 fn to_owned(&self) -> Self::Owned;
2253}
2254impl ToOwned for str {
2255 type Owned = String;
2256}
2257fn test() {
2258 "foo".to_owned()<|>;
2259}
2260"#,
2261 );
2262 assert_eq!(t, "String");
2263}
2264
2265#[test]
2266fn iterator_chain() {
2267 assert_snapshot!(
2268 infer(r#"
2269//- /main.rs
2270#[lang = "fn_once"]
2271trait FnOnce<Args> {
2272 type Output;
2273}
2274#[lang = "fn_mut"]
2275trait FnMut<Args>: FnOnce<Args> { }
2276
2277enum Option<T> { Some(T), None }
2278use Option::*;
2279
2280pub trait Iterator {
2281 type Item;
2282
2283 fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
2284 where
2285 F: FnMut(Self::Item) -> Option<B>,
2286 { loop {} }
2287
2288 fn for_each<F>(self, f: F)
2289 where
2290 F: FnMut(Self::Item),
2291 { loop {} }
2292}
2293
2294pub trait IntoIterator {
2295 type Item;
2296 type IntoIter: Iterator<Item = Self::Item>;
2297 fn into_iter(self) -> Self::IntoIter;
2298}
2299
2300pub struct FilterMap<I, F> { }
2301impl<B, I: Iterator, F> Iterator for FilterMap<I, F>
2302where
2303 F: FnMut(I::Item) -> Option<B>,
2304{
2305 type Item = B;
2306}
2307
2308#[stable(feature = "rust1", since = "1.0.0")]
2309impl<I: Iterator> IntoIterator for I {
2310 type Item = I::Item;
2311 type IntoIter = I;
2312
2313 fn into_iter(self) -> I {
2314 self
2315 }
2316}
2317
2318struct Vec<T> {}
2319impl<T> Vec<T> {
2320 fn new() -> Self { loop {} }
2321}
2322
2323impl<T> IntoIterator for Vec<T> {
2324 type Item = T;
2325 type IntoIter = IntoIter<T>;
2326}
2327
2328pub struct IntoIter<T> { }
2329impl<T> Iterator for IntoIter<T> {
2330 type Item = T;
2331}
2332
2333fn main() {
2334 Vec::<i32>::new().into_iter()
2335 .filter_map(|x| if x > 0 { Some(x as u32) } else { None })
2336 .for_each(|y| { y; });
2337}
2338"#),
2339 @r###"
2340 [240; 244) 'self': Self
2341 [246; 247) 'f': F
2342 [331; 342) '{ loop {} }': FilterMap<Self, F>
2343 [333; 340) 'loop {}': !
2344 [338; 340) '{}': ()
2345 [363; 367) 'self': Self
2346 [369; 370) 'f': F
2347 [419; 430) '{ loop {} }': ()
2348 [421; 428) 'loop {}': !
2349 [426; 428) '{}': ()
2350 [539; 543) 'self': Self
2351 [868; 872) 'self': I
2352 [879; 899) '{ ... }': I
2353 [889; 893) 'self': I
2354 [958; 969) '{ loop {} }': Vec<T>
2355 [960; 967) 'loop {}': !
2356 [965; 967) '{}': ()
2357 [1156; 1287) '{ ... }); }': ()
2358 [1162; 1177) 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
2359 [1162; 1179) 'Vec::<...:new()': Vec<i32>
2360 [1162; 1191) 'Vec::<...iter()': IntoIter<i32>
2361 [1162; 1256) 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
2362 [1162; 1284) 'Vec::<... y; })': ()
2363 [1210; 1255) '|x| if...None }': |i32| -> Option<u32>
2364 [1211; 1212) 'x': i32
2365 [1214; 1255) 'if x >...None }': Option<u32>
2366 [1217; 1218) 'x': i32
2367 [1217; 1222) 'x > 0': bool
2368 [1221; 1222) '0': i32
2369 [1223; 1241) '{ Some...u32) }': Option<u32>
2370 [1225; 1229) 'Some': Some<u32>(u32) -> Option<u32>
2371 [1225; 1239) 'Some(x as u32)': Option<u32>
2372 [1230; 1231) 'x': i32
2373 [1230; 1238) 'x as u32': u32
2374 [1247; 1255) '{ None }': Option<u32>
2375 [1249; 1253) 'None': Option<u32>
2376 [1273; 1283) '|y| { y; }': |u32| -> ()
2377 [1274; 1275) 'y': u32
2378 [1277; 1283) '{ y; }': ()
2379 [1279; 1280) 'y': u32
2380 "###
2381 );
2382}
2383
2384#[test]
2385fn nested_assoc() {
2386 let t = type_at(
2387 r#"
2388//- /main.rs
2389struct Bar;
2390struct Foo;
2391
2392trait A {
2393 type OutputA;
2394}
2395
2396impl A for Bar {
2397 type OutputA = Foo;
2398}
2399
2400trait B {
2401 type Output;
2402 fn foo() -> Self::Output;
2403}
2404
2405impl<T:A> B for T {
2406 type Output = T::OutputA;
2407 fn foo() -> Self::Output { loop {} }
2408}
2409
2410fn main() {
2411 Bar::foo()<|>;
2412}
2413"#,
2414 );
2415 assert_eq!(t, "Foo");
2416}
2417
2418#[test]
2419fn trait_object_no_coercion() {
2420 assert_snapshot!(
2421 infer_with_mismatches(r#"
2422trait Foo {}
2423
2424fn foo(x: &dyn Foo) {}
2425
2426fn test(x: &dyn Foo) {
2427 foo(x);
2428}
2429"#, true),
2430 @r###"
2431 [22; 23) 'x': &dyn Foo
2432 [35; 37) '{}': ()
2433 [47; 48) 'x': &dyn Foo
2434 [60; 75) '{ foo(x); }': ()
2435 [66; 69) 'foo': fn foo(&dyn Foo)
2436 [66; 72) 'foo(x)': ()
2437 [70; 71) 'x': &dyn Foo
2438 "###
2439 );
2440}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index fab02945c..2ec0e7ce9 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -142,11 +142,11 @@ fn add_function_impl(
142 CompletionItemKind::Function 142 CompletionItemKind::Function
143 }; 143 };
144 144
145 let snippet = format!("{} {{}}", display); 145 let snippet = format!("{} {{\n $0\n}}", display);
146 146
147 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); 147 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
148 148
149 builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); 149 builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc);
150} 150}
151 151
152fn add_type_alias_impl( 152fn add_type_alias_impl(
@@ -217,9 +217,10 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
217 217
218#[cfg(test)] 218#[cfg(test)]
219mod tests { 219mod tests {
220 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
221 use insta::assert_debug_snapshot; 220 use insta::assert_debug_snapshot;
222 221
222 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
223
223 fn complete(code: &str) -> Vec<CompletionItem> { 224 fn complete(code: &str) -> Vec<CompletionItem> {
224 do_completion(code, CompletionKind::Magic) 225 do_completion(code, CompletionKind::Magic)
225 } 226 }
@@ -255,7 +256,7 @@ mod tests {
255 label: "fn test()", 256 label: "fn test()",
256 source_range: [209; 210), 257 source_range: [209; 210),
257 delete: [209; 210), 258 delete: [209; 210),
258 insert: "fn test() {}", 259 insert: "fn test() {\n $0\n}",
259 kind: Function, 260 kind: Function,
260 lookup: "test", 261 lookup: "test",
261 }, 262 },
@@ -313,7 +314,7 @@ mod tests {
313 label: "fn test()", 314 label: "fn test()",
314 source_range: [139; 140), 315 source_range: [139; 140),
315 delete: [139; 140), 316 delete: [139; 140),
316 insert: "fn test() {}", 317 insert: "fn test() {\n $0\n}",
317 kind: Function, 318 kind: Function,
318 lookup: "test", 319 lookup: "test",
319 }, 320 },
@@ -342,7 +343,7 @@ mod tests {
342 label: "fn foo()", 343 label: "fn foo()",
343 source_range: [141; 142), 344 source_range: [141; 142),
344 delete: [138; 142), 345 delete: [138; 142),
345 insert: "fn foo() {}", 346 insert: "fn foo() {\n $0\n}",
346 kind: Function, 347 kind: Function,
347 lookup: "foo", 348 lookup: "foo",
348 }, 349 },
@@ -374,7 +375,7 @@ mod tests {
374 label: "fn foo_bar()", 375 label: "fn foo_bar()",
375 source_range: [200; 201), 376 source_range: [200; 201),
376 delete: [197; 201), 377 delete: [197; 201),
377 insert: "fn foo_bar() {}", 378 insert: "fn foo_bar() {\n $0\n}",
378 kind: Function, 379 kind: Function,
379 lookup: "foo_bar", 380 lookup: "foo_bar",
380 }, 381 },
@@ -425,7 +426,7 @@ mod tests {
425 label: "fn foo()", 426 label: "fn foo()",
426 source_range: [144; 145), 427 source_range: [144; 145),
427 delete: [141; 145), 428 delete: [141; 145),
428 insert: "fn foo<T>() {}", 429 insert: "fn foo<T>() {\n $0\n}",
429 kind: Function, 430 kind: Function,
430 lookup: "foo", 431 lookup: "foo",
431 }, 432 },
@@ -454,7 +455,7 @@ mod tests {
454 label: "fn foo()", 455 label: "fn foo()",
455 source_range: [166; 167), 456 source_range: [166; 167),
456 delete: [163; 167), 457 delete: [163; 167),
457 insert: "fn foo<T>()\nwhere T: Into<String> {}", 458 insert: "fn foo<T>()\nwhere T: Into<String> {\n $0\n}",
458 kind: Function, 459 kind: Function,
459 lookup: "foo", 460 lookup: "foo",
460 }, 461 },
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 2d8e0776c..ad5fdcc4e 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,6 +1,10 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef;
4use test_utils::tested_by;
5
3use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use ra_syntax::AstNode;
4 8
5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
6 if !ctx.is_trivial_path { 10 if !ctx.is_trivial_path {
@@ -14,12 +18,23 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
14 return; 18 return;
15 } 19 }
16 20
17 ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); 21 ctx.scope().process_all_names(&mut |name, res| {
22 if ctx.use_item_syntax.is_some() {
23 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
24 if name_ref.syntax().text() == name.to_string().as_str() {
25 tested_by!(self_fulfilling_completion);
26 return;
27 }
28 }
29 }
30 acc.add_resolution(ctx, name.to_string(), &res)
31 });
18} 32}
19 33
20#[cfg(test)] 34#[cfg(test)]
21mod tests { 35mod tests {
22 use insta::assert_debug_snapshot; 36 use insta::assert_debug_snapshot;
37 use test_utils::covers;
23 38
24 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 39 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind};
25 40
@@ -28,6 +43,29 @@ mod tests {
28 } 43 }
29 44
30 #[test] 45 #[test]
46 fn self_fulfilling_completion() {
47 covers!(self_fulfilling_completion);
48 assert_debug_snapshot!(
49 do_reference_completion(
50 r#"
51 use foo<|>
52 use std::collections;
53 "#,
54 ),
55 @r###"
56 [
57 CompletionItem {
58 label: "collections",
59 source_range: [21; 24),
60 delete: [21; 24),
61 insert: "collections",
62 },
63 ]
64 "###
65 );
66 }
67
68 #[test]
31 fn bind_pat_and_path_ignore_at() { 69 fn bind_pat_and_path_ignore_at() {
32 assert_debug_snapshot!( 70 assert_debug_snapshot!(
33 do_reference_completion( 71 do_reference_completion(
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 901ad104c..e7e201709 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -1,4 +1,8 @@
1//! FIXME: write short doc here 1//! Collects diagnostics & fixits for a single file.
2//!
3//! The tricky bit here is that diagnostics are produced by hir in terms of
4//! macro-expanded files, but we need to present them to the users in terms of
5//! original files. So we need to map the ranges.
2 6
3use std::cell::RefCell; 7use std::cell::RefCell;
4 8
@@ -46,7 +50,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
46 let mut sink = DiagnosticSink::new(|d| { 50 let mut sink = DiagnosticSink::new(|d| {
47 res.borrow_mut().push(Diagnostic { 51 res.borrow_mut().push(Diagnostic {
48 message: d.message(), 52 message: d.message(),
49 range: d.highlight_range(), 53 range: sema.diagnostics_range(d).range,
50 severity: Severity::Error, 54 severity: Severity::Error,
51 fix: None, 55 fix: None,
52 }) 56 })
@@ -62,7 +66,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
62 let create_file = FileSystemEdit::CreateFile { source_root, path }; 66 let create_file = FileSystemEdit::CreateFile { source_root, path };
63 let fix = SourceChange::file_system_edit("create module", create_file); 67 let fix = SourceChange::file_system_edit("create module", create_file);
64 res.borrow_mut().push(Diagnostic { 68 res.borrow_mut().push(Diagnostic {
65 range: d.highlight_range(), 69 range: sema.diagnostics_range(d).range,
66 message: d.message(), 70 message: d.message(),
67 severity: Severity::Error, 71 severity: Severity::Error,
68 fix: Some(fix), 72 fix: Some(fix),
@@ -95,7 +99,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
95 }; 99 };
96 100
97 res.borrow_mut().push(Diagnostic { 101 res.borrow_mut().push(Diagnostic {
98 range: d.highlight_range(), 102 range: sema.diagnostics_range(d).range,
99 message: d.message(), 103 message: d.message(),
100 severity: Severity::Error, 104 severity: Severity::Error,
101 fix, 105 fix,
@@ -103,7 +107,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
103 }) 107 })
104 .on::<hir::diagnostics::MissingMatchArms, _>(|d| { 108 .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
105 res.borrow_mut().push(Diagnostic { 109 res.borrow_mut().push(Diagnostic {
106 range: d.highlight_range(), 110 range: sema.diagnostics_range(d).range,
107 message: d.message(), 111 message: d.message(),
108 severity: Severity::Error, 112 severity: Severity::Error,
109 fix: None, 113 fix: None,
@@ -115,7 +119,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
115 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 119 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
116 let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); 120 let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit);
117 res.borrow_mut().push(Diagnostic { 121 res.borrow_mut().push(Diagnostic {
118 range: d.highlight_range(), 122 range: sema.diagnostics_range(d).range,
119 message: d.message(), 123 message: d.message(),
120 severity: Severity::Error, 124 severity: Severity::Error,
121 fix: Some(fix), 125 fix: Some(fix),
@@ -622,6 +626,62 @@ mod tests {
622 } 626 }
623 627
624 #[test] 628 #[test]
629 fn range_mapping_out_of_macros() {
630 let (analysis, file_id) = single_file(
631 r"
632 fn some() {}
633 fn items() {}
634 fn here() {}
635
636 macro_rules! id {
637 ($($tt:tt)*) => { $($tt)*};
638 }
639
640 fn main() {
641 let _x = id![Foo { a: 42 }];
642 }
643
644 pub struct Foo {
645 pub a: i32,
646 pub b: i32,
647 }
648 ",
649 );
650 let diagnostics = analysis.diagnostics(file_id).unwrap();
651 assert_debug_snapshot!(diagnostics, @r###"
652 [
653 Diagnostic {
654 message: "Missing structure fields:\n- b",
655 range: [224; 233),
656 fix: Some(
657 SourceChange {
658 label: "fill struct fields",
659 source_file_edits: [
660 SourceFileEdit {
661 file_id: FileId(
662 1,
663 ),
664 edit: TextEdit {
665 atoms: [
666 AtomTextEdit {
667 delete: [3; 9),
668 insert: "{a:42, b: ()}",
669 },
670 ],
671 },
672 },
673 ],
674 file_system_edits: [],
675 cursor_position: None,
676 },
677 ),
678 severity: Error,
679 },
680 ]
681 "###);
682 }
683
684 #[test]
625 fn test_check_unnecessary_braces_in_use_statement() { 685 fn test_check_unnecessary_braces_in_use_statement() {
626 check_not_applicable( 686 check_not_applicable(
627 " 687 "
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs
index 5e1f135c5..eee44e886 100644
--- a/crates/ra_ide/src/marks.rs
+++ b/crates/ra_ide/src/marks.rs
@@ -8,4 +8,5 @@ test_utils::marks!(
8 test_resolve_parent_module_on_module_decl 8 test_resolve_parent_module_on_module_decl
9 search_filters_by_range 9 search_filters_by_range
10 dont_insert_macro_call_parens_unncessary 10 dont_insert_macro_call_parens_unncessary
11 self_fulfilling_completion
11); 12);
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 83d161f45..7b15b82bd 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -174,7 +174,8 @@ pub(crate) fn highlight(
174 } 174 }
175 175
176 assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element"); 176 assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element");
177 let res = res.pop().unwrap(); 177 let mut res = res.pop().unwrap();
178 res.sort_by_key(|range| range.range.start());
178 // Check that ranges are sorted and disjoint 179 // Check that ranges are sorted and disjoint
179 assert!(res 180 assert!(res
180 .iter() 181 .iter()
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 110887c2a..73611e23a 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -156,3 +156,15 @@ fn main() {
156 fs::write(dst_file, &actual_html).unwrap(); 156 fs::write(dst_file, &actual_html).unwrap();
157 assert_eq_text!(expected_html, actual_html); 157 assert_eq_text!(expected_html, actual_html);
158} 158}
159
160#[test]
161fn ranges_sorted() {
162 let (analysis, file_id) = single_file(
163 r#"
164#[foo(bar = "bar")]
165macro_rules! test {}
166}"#
167 .trim(),
168 );
169 let _ = analysis.highlight(file_id).unwrap();
170}
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
index 63da9f1b4..b200fd126 100644
--- a/crates/ra_proc_macro/src/lib.rs
+++ b/crates/ra_proc_macro/src/lib.rs
@@ -12,6 +12,7 @@ pub mod msg;
12use process::{ProcMacroProcessSrv, ProcMacroProcessThread}; 12use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
13use ra_tt::{SmolStr, Subtree}; 13use ra_tt::{SmolStr, Subtree};
14use std::{ 14use std::{
15 ffi::OsStr,
15 path::{Path, PathBuf}, 16 path::{Path, PathBuf},
16 sync::Arc, 17 sync::Arc,
17}; 18};
@@ -56,8 +57,15 @@ pub struct ProcMacroClient {
56} 57}
57 58
58impl ProcMacroClient { 59impl ProcMacroClient {
59 pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> { 60 pub fn extern_process<I, S>(
60 let (thread, process) = ProcMacroProcessSrv::run(process_path)?; 61 process_path: &Path,
62 args: I,
63 ) -> Result<ProcMacroClient, std::io::Error>
64 where
65 I: IntoIterator<Item = S>,
66 S: AsRef<OsStr>,
67 {
68 let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
61 Ok(ProcMacroClient { 69 Ok(ProcMacroClient {
62 kind: ProcMacroClientKind::Process { process: Arc::new(process), thread }, 70 kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
63 }) 71 })
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index e8c85be38..f851570bc 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -9,6 +9,7 @@ use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTas
9use io::{BufRead, BufReader}; 9use io::{BufRead, BufReader};
10use std::{ 10use std::{
11 convert::{TryFrom, TryInto}, 11 convert::{TryFrom, TryInto},
12 ffi::OsStr,
12 io::{self, Write}, 13 io::{self, Write},
13 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
14 process::{Child, Command, Stdio}, 15 process::{Child, Command, Stdio},
@@ -44,8 +45,13 @@ impl Drop for Process {
44} 45}
45 46
46impl Process { 47impl Process {
47 fn run(process_path: &Path) -> Result<Process, io::Error> { 48 fn run<I, S>(process_path: &Path, args: I) -> Result<Process, io::Error>
49 where
50 I: IntoIterator<Item = S>,
51 S: AsRef<OsStr>,
52 {
48 let child = Command::new(process_path.clone()) 53 let child = Command::new(process_path.clone())
54 .args(args)
49 .stdin(Stdio::piped()) 55 .stdin(Stdio::piped())
50 .stdout(Stdio::piped()) 56 .stdout(Stdio::piped())
51 .stderr(Stdio::null()) 57 .stderr(Stdio::null())
@@ -74,10 +80,15 @@ impl Process {
74} 80}
75 81
76impl ProcMacroProcessSrv { 82impl ProcMacroProcessSrv {
77 pub fn run( 83 pub fn run<I, S>(
78 process_path: &Path, 84 process_path: &Path,
79 ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> { 85 args: I,
80 let process = Process::run(process_path)?; 86 ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error>
87 where
88 I: IntoIterator<Item = S>,
89 S: AsRef<OsStr>,
90 {
91 let process = Process::run(process_path, args)?;
81 92
82 let (task_tx, task_rx) = bounded(0); 93 let (task_tx, task_rx) = bounded(0);
83 let handle = jod_thread::spawn(move || { 94 let handle = jod_thread::spawn(move || {
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index 1e0f50339..ac2d156dc 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -14,6 +14,7 @@ ra_mbe = { path = "../ra_mbe" }
14ra_proc_macro = { path = "../ra_proc_macro" } 14ra_proc_macro = { path = "../ra_proc_macro" }
15goblin = "0.2.1" 15goblin = "0.2.1"
16libloading = "0.6.0" 16libloading = "0.6.0"
17memmap = "0.7"
17test_utils = { path = "../test_utils" } 18test_utils = { path = "../test_utils" }
18 19
19[dev-dependencies] 20[dev-dependencies]
diff --git a/crates/ra_proc_macro_srv/src/main.rs b/crates/ra_proc_macro_srv/src/cli.rs
index 70743c1f4..c771f2b38 100644
--- a/crates/ra_proc_macro_srv/src/main.rs
+++ b/crates/ra_proc_macro_srv/src/cli.rs
@@ -1,7 +1,7 @@
1//! Driver for proc macro server 1//! Driver for proc macro server
2 2
3use crate::{expand_task, list_macros};
3use ra_proc_macro::msg::{self, Message}; 4use ra_proc_macro::msg::{self, Message};
4use ra_proc_macro_srv::{expand_task, list_macros};
5 5
6use std::io; 6use std::io;
7 7
@@ -24,7 +24,8 @@ fn write_response(res: Result<msg::Response, String>) -> Result<(), io::Error> {
24 let mut stdout = stdout.lock(); 24 let mut stdout = stdout.lock();
25 msg.write(&mut stdout) 25 msg.write(&mut stdout)
26} 26}
27fn main() { 27
28pub fn run() {
28 loop { 29 loop {
29 let req = match read_request() { 30 let req = match read_request() {
30 Err(err) => { 31 Err(err) => {
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index ec63d587b..16bd7466e 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -1,10 +1,12 @@
1//! Handles dynamic library loading for proc macro 1//! Handles dynamic library loading for proc macro
2 2
3use crate::{proc_macro::bridge, rustc_server::TokenStream}; 3use crate::{proc_macro::bridge, rustc_server::TokenStream};
4use std::fs::File;
4use std::path::Path; 5use std::path::Path;
5 6
6use goblin::{mach::Mach, Object}; 7use goblin::{mach::Mach, Object};
7use libloading::Library; 8use libloading::Library;
9use memmap::Mmap;
8use ra_proc_macro::ProcMacroKind; 10use ra_proc_macro::ProcMacroKind;
9 11
10use std::io::Error as IoError; 12use std::io::Error as IoError;
@@ -16,55 +18,54 @@ fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> I
16 IoError::new(IoErrorKind::InvalidData, e) 18 IoError::new(IoErrorKind::InvalidData, e)
17} 19}
18 20
19fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> { 21fn is_derive_registrar_symbol(symbol: &str) -> bool {
20 let buffer = std::fs::read(file)?; 22 symbol.contains(NEW_REGISTRAR_SYMBOL)
23}
24
25fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
26 let file = File::open(file)?;
27 let buffer = unsafe { Mmap::map(&file)? };
21 let object = Object::parse(&buffer).map_err(invalid_data_err)?; 28 let object = Object::parse(&buffer).map_err(invalid_data_err)?;
22 29
23 match object { 30 match object {
24 Object::Elf(elf) => { 31 Object::Elf(elf) => {
25 let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?; 32 let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
26 let names = symbols.iter().map(|s| s.to_string()).collect(); 33 let name =
27 Ok(names) 34 symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
35 Ok(name)
28 } 36 }
29 Object::PE(pe) => { 37 Object::PE(pe) => {
30 let symbol_names = 38 let name = pe
31 pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect(); 39 .exports
32 Ok(symbol_names) 40 .iter()
41 .flat_map(|s| s.name)
42 .find(|s| is_derive_registrar_symbol(s))
43 .map(|s| s.to_string());
44 Ok(name)
33 } 45 }
34 Object::Mach(mach) => match mach { 46 Object::Mach(Mach::Binary(binary)) => {
35 Mach::Binary(binary) => { 47 let exports = binary.exports().map_err(invalid_data_err)?;
36 let exports = binary.exports().map_err(invalid_data_err)?; 48 let name = exports
37 let names = exports 49 .iter()
38 .into_iter() 50 .map(|s| {
39 .map(|s| { 51 // In macos doc:
40 // In macos doc: 52 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
41 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html 53 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
42 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be 54 // prepended with an underscore.
43 // prepended with an underscore. 55 if s.name.starts_with("_") {
44 if s.name.starts_with("_") { 56 &s.name[1..]
45 s.name[1..].to_string() 57 } else {
46 } else { 58 &s.name
47 s.name 59 }
48 } 60 })
49 }) 61 .find(|s| is_derive_registrar_symbol(s))
50 .collect(); 62 .map(|s| s.to_string());
51 Ok(names) 63 Ok(name)
52 } 64 }
53 Mach::Fat(_) => Ok(vec![]), 65 _ => Ok(None),
54 },
55 Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
56 } 66 }
57} 67}
58 68
59fn is_derive_registrar_symbol(symbol: &str) -> bool {
60 symbol.contains(NEW_REGISTRAR_SYMBOL)
61}
62
63fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
64 let symbols = get_symbols_from_lib(file)?;
65 Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s)))
66}
67
68/// Loads dynamic library in platform dependent manner. 69/// Loads dynamic library in platform dependent manner.
69/// 70///
70/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described 71/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index 59716cbb3..c62b0ed89 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -22,7 +22,7 @@ mod dylib;
22use proc_macro::bridge::client::TokenStream; 22use proc_macro::bridge::client::TokenStream;
23use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; 23use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
24 24
25pub fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> { 25pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
26 let expander = dylib::Expander::new(&task.lib) 26 let expander = dylib::Expander::new(&task.lib)
27 .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib)); 27 .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
28 28
@@ -39,7 +39,7 @@ pub fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
39 } 39 }
40} 40}
41 41
42pub fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> { 42pub(crate) fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
43 let expander = dylib::Expander::new(&task.lib) 43 let expander = dylib::Expander::new(&task.lib)
44 .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib)); 44 .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
45 45
@@ -53,5 +53,7 @@ pub fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
53 } 53 }
54} 54}
55 55
56pub mod cli;
57
56#[cfg(test)] 58#[cfg(test)]
57mod tests; 59mod tests;
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 0ab64a1e0..03f2629da 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -5,9 +5,8 @@ mod json_project;
5mod sysroot; 5mod sysroot;
6 6
7use std::{ 7use std::{
8 error::Error,
9 fs::{read_dir, File, ReadDir}, 8 fs::{read_dir, File, ReadDir},
10 io::BufReader, 9 io::{self, BufReader},
11 path::{Path, PathBuf}, 10 path::{Path, PathBuf},
12 process::Command, 11 process::Command,
13}; 12};
@@ -25,25 +24,6 @@ pub use crate::{
25}; 24};
26pub use ra_proc_macro::ProcMacroClient; 25pub use ra_proc_macro::ProcMacroClient;
27 26
28#[derive(Clone, PartialEq, Eq, Hash, Debug)]
29pub struct CargoTomlNotFoundError {
30 pub searched_at: PathBuf,
31 pub reason: String,
32}
33
34impl std::fmt::Display for CargoTomlNotFoundError {
35 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(
37 fmt,
38 "can't find Cargo.toml at {}, due to {}",
39 self.searched_at.display(),
40 self.reason
41 )
42 }
43}
44
45impl Error for CargoTomlNotFoundError {}
46
47#[derive(Debug, Clone)] 27#[derive(Debug, Clone)]
48pub enum ProjectWorkspace { 28pub enum ProjectWorkspace {
49 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@@ -77,31 +57,119 @@ impl PackageRoot {
77 } 57 }
78} 58}
79 59
80impl ProjectWorkspace { 60#[derive(Debug, Clone, PartialEq, Eq, Hash)]
81 pub fn discover(path: &Path, cargo_features: &CargoConfig) -> Result<ProjectWorkspace> { 61pub enum ProjectRoot {
82 ProjectWorkspace::discover_with_sysroot(path, true, cargo_features) 62 ProjectJson(PathBuf),
63 CargoToml(PathBuf),
64}
65
66impl ProjectRoot {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> {
68 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path));
70 }
71 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path));
73 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
83 } 75 }
84 76
85 pub fn discover_with_sysroot( 77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> {
86 path: &Path, 78 let mut candidates = ProjectRoot::discover(path)?;
87 with_sysroot: bool, 79 let res = match candidates.pop() {
80 None => bail!("no projects"),
81 Some(it) => it,
82 };
83
84 if !candidates.is_empty() {
85 bail!("more than one project")
86 }
87 Ok(res)
88 }
89
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> {
91 if let Some(project_json) = find_rust_project_json(path) {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]);
93 }
94 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect());
96
97 fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
98 if path.ends_with("rust-project.json") {
99 return Some(path.to_path_buf());
100 }
101
102 let mut curr = Some(path);
103 while let Some(path) = curr {
104 let candidate = path.join("rust-project.json");
105 if candidate.exists() {
106 return Some(candidate);
107 }
108 curr = path.parent();
109 }
110
111 None
112 }
113
114 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
115 if path.ends_with("Cargo.toml") {
116 return Ok(vec![path.to_path_buf()]);
117 }
118
119 if let Some(p) = find_cargo_toml_in_parent_dir(path) {
120 return Ok(vec![p]);
121 }
122
123 let entities = read_dir(path)?;
124 Ok(find_cargo_toml_in_child_dir(entities))
125 }
126
127 fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
128 let mut curr = Some(path);
129 while let Some(path) = curr {
130 let candidate = path.join("Cargo.toml");
131 if candidate.exists() {
132 return Some(candidate);
133 }
134 curr = path.parent();
135 }
136
137 None
138 }
139
140 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
141 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
142 let mut valid_canditates = vec![];
143 for entity in entities.filter_map(Result::ok) {
144 let candidate = entity.path().join("Cargo.toml");
145 if candidate.exists() {
146 valid_canditates.push(candidate)
147 }
148 }
149 valid_canditates
150 }
151 }
152}
153
154impl ProjectWorkspace {
155 pub fn load(
156 root: ProjectRoot,
88 cargo_features: &CargoConfig, 157 cargo_features: &CargoConfig,
158 with_sysroot: bool,
89 ) -> Result<ProjectWorkspace> { 159 ) -> Result<ProjectWorkspace> {
90 match find_rust_project_json(path) { 160 let res = match root {
91 Some(json_path) => { 161 ProjectRoot::ProjectJson(project_json) => {
92 let file = File::open(&json_path) 162 let file = File::open(&project_json).with_context(|| {
93 .with_context(|| format!("Failed to open json file {}", json_path.display()))?; 163 format!("Failed to open json file {}", project_json.display())
164 })?;
94 let reader = BufReader::new(file); 165 let reader = BufReader::new(file);
95 Ok(ProjectWorkspace::Json { 166 ProjectWorkspace::Json {
96 project: from_reader(reader).with_context(|| { 167 project: from_reader(reader).with_context(|| {
97 format!("Failed to deserialize json file {}", json_path.display()) 168 format!("Failed to deserialize json file {}", project_json.display())
98 })?, 169 })?,
99 }) 170 }
100 } 171 }
101 None => { 172 ProjectRoot::CargoToml(cargo_toml) => {
102 let cargo_toml = find_cargo_toml(path).with_context(|| {
103 format!("Failed to find Cargo.toml for path {}", path.display())
104 })?;
105 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 173 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
106 .with_context(|| { 174 .with_context(|| {
107 format!( 175 format!(
@@ -119,9 +187,11 @@ impl ProjectWorkspace {
119 } else { 187 } else {
120 Sysroot::default() 188 Sysroot::default()
121 }; 189 };
122 Ok(ProjectWorkspace::Cargo { cargo, sysroot }) 190 ProjectWorkspace::Cargo { cargo, sysroot }
123 } 191 }
124 } 192 };
193
194 Ok(res)
125 } 195 }
126 196
127 /// Returns the roots for the current `ProjectWorkspace` 197 /// Returns the roots for the current `ProjectWorkspace`
@@ -469,87 +539,6 @@ impl ProjectWorkspace {
469 } 539 }
470} 540}
471 541
472fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
473 if path.ends_with("rust-project.json") {
474 return Some(path.to_path_buf());
475 }
476
477 let mut curr = Some(path);
478 while let Some(path) = curr {
479 let candidate = path.join("rust-project.json");
480 if candidate.exists() {
481 return Some(candidate);
482 }
483 curr = path.parent();
484 }
485
486 None
487}
488
489fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
490 let mut curr = Some(path);
491 while let Some(path) = curr {
492 let candidate = path.join("Cargo.toml");
493 if candidate.exists() {
494 return Some(candidate);
495 }
496 curr = path.parent();
497 }
498
499 None
500}
501
502fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
503 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
504 let mut valid_canditates = vec![];
505 for entity in entities.filter_map(Result::ok) {
506 let candidate = entity.path().join("Cargo.toml");
507 if candidate.exists() {
508 valid_canditates.push(candidate)
509 }
510 }
511 valid_canditates
512}
513
514fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
515 if path.ends_with("Cargo.toml") {
516 return Ok(path.to_path_buf());
517 }
518
519 if let Some(p) = find_cargo_toml_in_parent_dir(path) {
520 return Ok(p);
521 }
522
523 let entities = match read_dir(path) {
524 Ok(entities) => entities,
525 Err(e) => {
526 return Err(CargoTomlNotFoundError {
527 searched_at: path.to_path_buf(),
528 reason: format!("file system error: {}", e),
529 }
530 .into());
531 }
532 };
533
534 let mut valid_canditates = find_cargo_toml_in_child_dir(entities);
535 match valid_canditates.len() {
536 1 => Ok(valid_canditates.remove(0)),
537 0 => Err(CargoTomlNotFoundError {
538 searched_at: path.to_path_buf(),
539 reason: "no Cargo.toml file found".to_string(),
540 }
541 .into()),
542 _ => Err(CargoTomlNotFoundError {
543 searched_at: path.to_path_buf(),
544 reason: format!(
545 "multiple equally valid Cargo.toml files found: {:?}",
546 valid_canditates
547 ),
548 }
549 .into()),
550 }
551}
552
553pub fn get_rustc_cfg_options() -> CfgOptions { 542pub fn get_rustc_cfg_options() -> CfgOptions {
554 let mut cfg_options = CfgOptions::default(); 543 let mut cfg_options = CfgOptions::default();
555 544
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index f5f773432..cee0248b6 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -46,7 +46,7 @@ ra_db = { path = "../ra_db" }
46hir = { path = "../ra_hir", package = "ra_hir" } 46hir = { path = "../ra_hir", package = "ra_hir" }
47hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } 47hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
48hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } 48hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
49 49ra_proc_macro_srv = { path = "../ra_proc_macro_srv" }
50 50
51[target.'cfg(windows)'.dependencies] 51[target.'cfg(windows)'.dependencies]
52winapi = "0.3.8" 52winapi = "0.3.8"
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index f5981588a..5e19253a6 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -29,19 +29,23 @@ pub(crate) enum Command {
29 with_deps: bool, 29 with_deps: bool,
30 path: PathBuf, 30 path: PathBuf,
31 load_output_dirs: bool, 31 load_output_dirs: bool,
32 with_proc_macro: bool,
32 }, 33 },
33 Bench { 34 Bench {
34 path: PathBuf, 35 path: PathBuf,
35 what: BenchWhat, 36 what: BenchWhat,
36 load_output_dirs: bool, 37 load_output_dirs: bool,
38 with_proc_macro: bool,
37 }, 39 },
38 Diagnostics { 40 Diagnostics {
39 path: PathBuf, 41 path: PathBuf,
40 load_output_dirs: bool, 42 load_output_dirs: bool,
43 with_proc_macro: bool,
41 /// Include files which are not modules. In rust-analyzer 44 /// Include files which are not modules. In rust-analyzer
42 /// this would include the parser test files. 45 /// this would include the parser test files.
43 all: bool, 46 all: bool,
44 }, 47 },
48 ProcMacro,
45 RunServer, 49 RunServer,
46 Version, 50 Version,
47} 51}
@@ -148,6 +152,7 @@ FLAGS:
148 -h, --help Prints help information 152 -h, --help Prints help information
149 --memory-usage 153 --memory-usage
150 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis 154 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
155 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
151 -v, --verbose 156 -v, --verbose
152 -q, --quiet 157 -q, --quiet
153 158
@@ -165,6 +170,7 @@ ARGS:
165 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; 170 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
166 let with_deps: bool = matches.contains("--with-deps"); 171 let with_deps: bool = matches.contains("--with-deps");
167 let load_output_dirs = matches.contains("--load-output-dirs"); 172 let load_output_dirs = matches.contains("--load-output-dirs");
173 let with_proc_macro = matches.contains("--with-proc-macro");
168 let path = { 174 let path = {
169 let mut trailing = matches.free()?; 175 let mut trailing = matches.free()?;
170 if trailing.len() != 1 { 176 if trailing.len() != 1 {
@@ -173,7 +179,15 @@ ARGS:
173 trailing.pop().unwrap().into() 179 trailing.pop().unwrap().into()
174 }; 180 };
175 181
176 Command::Stats { randomize, memory_usage, only, with_deps, path, load_output_dirs } 182 Command::Stats {
183 randomize,
184 memory_usage,
185 only,
186 with_deps,
187 path,
188 load_output_dirs,
189 with_proc_macro,
190 }
177 } 191 }
178 "analysis-bench" => { 192 "analysis-bench" => {
179 if matches.contains(["-h", "--help"]) { 193 if matches.contains(["-h", "--help"]) {
@@ -187,6 +201,7 @@ USAGE:
187FLAGS: 201FLAGS:
188 -h, --help Prints help information 202 -h, --help Prints help information
189 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis 203 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
204 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
190 -v, --verbose 205 -v, --verbose
191 206
192OPTIONS: 207OPTIONS:
@@ -214,7 +229,8 @@ ARGS:
214 ), 229 ),
215 }; 230 };
216 let load_output_dirs = matches.contains("--load-output-dirs"); 231 let load_output_dirs = matches.contains("--load-output-dirs");
217 Command::Bench { path, what, load_output_dirs } 232 let with_proc_macro = matches.contains("--with-proc-macro");
233 Command::Bench { path, what, load_output_dirs, with_proc_macro }
218 } 234 }
219 "diagnostics" => { 235 "diagnostics" => {
220 if matches.contains(["-h", "--help"]) { 236 if matches.contains(["-h", "--help"]) {
@@ -237,6 +253,7 @@ ARGS:
237 } 253 }
238 254
239 let load_output_dirs = matches.contains("--load-output-dirs"); 255 let load_output_dirs = matches.contains("--load-output-dirs");
256 let with_proc_macro = matches.contains("--with-proc-macro");
240 let all = matches.contains("--all"); 257 let all = matches.contains("--all");
241 let path = { 258 let path = {
242 let mut trailing = matches.free()?; 259 let mut trailing = matches.free()?;
@@ -246,8 +263,9 @@ ARGS:
246 trailing.pop().unwrap().into() 263 trailing.pop().unwrap().into()
247 }; 264 };
248 265
249 Command::Diagnostics { path, load_output_dirs, all } 266 Command::Diagnostics { path, load_output_dirs, with_proc_macro, all }
250 } 267 }
268 "proc-macro" => Command::ProcMacro,
251 _ => { 269 _ => {
252 eprintln!( 270 eprintln!(
253 "\ 271 "\
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 7cfc44f01..28b67cfe2 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -25,6 +25,7 @@ fn main() -> Result<()> {
25 with_deps, 25 with_deps,
26 path, 26 path,
27 load_output_dirs, 27 load_output_dirs,
28 with_proc_macro,
28 } => cli::analysis_stats( 29 } => cli::analysis_stats(
29 args.verbosity, 30 args.verbosity,
30 memory_usage, 31 memory_usage,
@@ -33,16 +34,24 @@ fn main() -> Result<()> {
33 with_deps, 34 with_deps,
34 randomize, 35 randomize,
35 load_output_dirs, 36 load_output_dirs,
37 with_proc_macro,
36 )?, 38 )?,
37 39
38 args::Command::Bench { path, what, load_output_dirs } => { 40 args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
39 cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)? 41 cli::analysis_bench(
42 args.verbosity,
43 path.as_ref(),
44 what,
45 load_output_dirs,
46 with_proc_macro,
47 )?
40 } 48 }
41 49
42 args::Command::Diagnostics { path, load_output_dirs, all } => { 50 args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => {
43 cli::diagnostics(path.as_ref(), load_output_dirs, all)? 51 cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
44 } 52 }
45 53
54 args::Command::ProcMacro => run_proc_macro_sv()?,
46 args::Command::RunServer => run_server()?, 55 args::Command::RunServer => run_server()?,
47 args::Command::Version => println!("rust-analyzer {}", env!("REV")), 56 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
48 } 57 }
@@ -56,6 +65,11 @@ fn setup_logging() -> Result<()> {
56 Ok(()) 65 Ok(())
57} 66}
58 67
68fn run_proc_macro_sv() -> Result<()> {
69 ra_proc_macro_srv::cli::run();
70 Ok(())
71}
72
59fn run_server() -> Result<()> { 73fn run_server() -> Result<()> {
60 log::info!("lifecycle: server started"); 74 log::info!("lifecycle: server started");
61 75
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 7667873d5..6147ae207 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -47,12 +47,13 @@ pub fn analysis_bench(
47 path: &Path, 47 path: &Path,
48 what: BenchWhat, 48 what: BenchWhat,
49 load_output_dirs: bool, 49 load_output_dirs: bool,
50 with_proc_macro: bool,
50) -> Result<()> { 51) -> Result<()> {
51 ra_prof::init(); 52 ra_prof::init();
52 53
53 let start = Instant::now(); 54 let start = Instant::now();
54 eprint!("loading: "); 55 eprint!("loading: ");
55 let (mut host, roots) = load_cargo(path, load_output_dirs)?; 56 let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
56 let db = host.raw_database(); 57 let db = host.raw_database();
57 eprintln!("{:?}\n", start.elapsed()); 58 eprintln!("{:?}\n", start.elapsed());
58 59
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index e9ee0b888..d442cbd63 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -25,9 +25,10 @@ pub fn analysis_stats(
25 with_deps: bool, 25 with_deps: bool,
26 randomize: bool, 26 randomize: bool,
27 load_output_dirs: bool, 27 load_output_dirs: bool,
28 with_proc_macro: bool,
28) -> Result<()> { 29) -> Result<()> {
29 let db_load_time = Instant::now(); 30 let db_load_time = Instant::now();
30 let (mut host, roots) = load_cargo(path, load_output_dirs)?; 31 let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
31 let db = host.raw_database(); 32 let db = host.raw_database();
32 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); 33 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed());
33 let analysis_time = Instant::now(); 34 let analysis_time = Instant::now();
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 92664b415..60daefa3e 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -9,8 +9,13 @@ use std::{collections::HashSet, path::Path};
9use crate::cli::{load_cargo::load_cargo, Result}; 9use crate::cli::{load_cargo::load_cargo, Result};
10use hir::Semantics; 10use hir::Semantics;
11 11
12pub fn diagnostics(path: &Path, load_output_dirs: bool, all: bool) -> Result<()> { 12pub fn diagnostics(
13 let (host, roots) = load_cargo(path, load_output_dirs)?; 13 path: &Path,
14 load_output_dirs: bool,
15 with_proc_macro: bool,
16 all: bool,
17) -> Result<()> {
18 let (host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?;
14 let db = host.raw_database(); 19 let db = host.raw_database();
15 let analysis = host.analysis(); 20 let analysis = host.analysis();
16 let semantics = Semantics::new(db); 21 let semantics = Semantics::new(db);
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 43062ea10..762f776fe 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use ra_project_model::{
11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectWorkspace, 11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace,
12}; 12};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 14use rustc_hash::{FxHashMap, FxHashSet};
@@ -25,11 +25,14 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
25pub(crate) fn load_cargo( 25pub(crate) fn load_cargo(
26 root: &Path, 26 root: &Path,
27 load_out_dirs_from_check: bool, 27 load_out_dirs_from_check: bool,
28 with_proc_macro: bool,
28) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
29 let root = std::env::current_dir()?.join(root); 30 let root = std::env::current_dir()?.join(root);
30 let ws = ProjectWorkspace::discover( 31 let root = ProjectRoot::discover_single(&root)?;
31 root.as_ref(), 32 let ws = ProjectWorkspace::load(
33 root,
32 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 34 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
35 true,
33 )?; 36 )?;
34 37
35 let mut extern_dirs = FxHashSet::default(); 38 let mut extern_dirs = FxHashSet::default();
@@ -69,7 +72,12 @@ pub(crate) fn load_cargo(
69 }) 72 })
70 .collect::<FxHashMap<_, _>>(); 73 .collect::<FxHashMap<_, _>>();
71 74
72 let proc_macro_client = ProcMacroClient::dummy(); 75 let proc_macro_client = if !with_proc_macro {
76 ProcMacroClient::dummy()
77 } else {
78 let path = std::env::current_exe()?;
79 ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap()
80 };
73 let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client); 81 let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
74 Ok((host, source_roots)) 82 Ok((host, source_roots))
75} 83}
@@ -175,7 +183,7 @@ mod tests {
175 #[test] 183 #[test]
176 fn test_loading_rust_analyzer() { 184 fn test_loading_rust_analyzer() {
177 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 185 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
178 let (host, _roots) = load_cargo(path, false).unwrap(); 186 let (host, _roots) = load_cargo(path, false, false).unwrap();
179 let n_crates = Crate::all(host.raw_database()).len(); 187 let n_crates = Crate::all(host.raw_database()).len();
180 // RA has quite a few crates, but the exact count doesn't matter 188 // RA has quite a few crates, but the exact count doesn't matter
181 assert!(n_crates > 20); 189 assert!(n_crates > 20);
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 4734df16a..3597a14e3 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -20,7 +20,7 @@ pub struct Config {
20 pub with_sysroot: bool, 20 pub with_sysroot: bool,
21 pub publish_diagnostics: bool, 21 pub publish_diagnostics: bool,
22 pub lru_capacity: Option<usize>, 22 pub lru_capacity: Option<usize>,
23 pub proc_macro_srv: Option<String>, 23 pub proc_macro_srv: Option<(String, Vec<String>)>,
24 pub files: FilesConfig, 24 pub files: FilesConfig,
25 pub notifications: NotificationsConfig, 25 pub notifications: NotificationsConfig,
26 26
@@ -131,6 +131,16 @@ impl Config {
131 set(value, "/cargo/allFeatures", &mut self.cargo.all_features); 131 set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
132 set(value, "/cargo/features", &mut self.cargo.features); 132 set(value, "/cargo/features", &mut self.cargo.features);
133 set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); 133 set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
134
135 match get::<bool>(value, "/procMacro/enabled") {
136 Some(true) => {
137 if let Ok(path) = std::env::current_exe() {
138 self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()]));
139 }
140 }
141 _ => self.proc_macro_srv = None,
142 }
143
134 match get::<Vec<String>>(value, "/rustfmt/overrideCommand") { 144 match get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
135 Some(mut args) if !args.is_empty() => { 145 Some(mut args) if !args.is_empty() => {
136 let command = args.remove(0); 146 let command = args.remove(0);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 8d1429196..fc4c77f8a 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -15,6 +15,7 @@ use std::{
15}; 15};
16 16
17use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
18use itertools::Itertools;
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 19use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ 20use lsp_types::{
20 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, 21 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
@@ -88,37 +89,46 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
88 89
89 let mut loop_state = LoopState::default(); 90 let mut loop_state = LoopState::default();
90 let mut world_state = { 91 let mut world_state = {
91 // FIXME: support dynamic workspace loading.
92 let workspaces = { 92 let workspaces = {
93 let mut loaded_workspaces = Vec::new(); 93 // FIXME: support dynamic workspace loading.
94 for ws_root in &ws_roots { 94 let mut visited = FxHashSet::default();
95 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( 95 let project_roots = ws_roots
96 ws_root.as_path(), 96 .iter()
97 config.with_sysroot, 97 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
98 &config.cargo, 98 .flatten()
99 ); 99 .filter(|it| visited.insert(it.clone()))
100 match workspace { 100 .collect::<Vec<_>>();
101 Ok(workspace) => loaded_workspaces.push(workspace), 101
102 Err(e) => { 102 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
103 log::error!("loading workspace failed: {:?}", e); 103 show_message(
104 104 req::MessageType::Error,
105 if let Some(ra_project_model::CargoTomlNotFoundError { .. }) = 105 format!(
106 e.downcast_ref() 106 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
107 { 107 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
108 if !config.notifications.cargo_toml_not_found { 108 ),
109 continue; 109 &connection.sender,
110 } 110 );
111 } 111 };
112 112
113 project_roots
114 .into_iter()
115 .filter_map(|root| {
116 ra_project_model::ProjectWorkspace::load(
117 root,
118 &config.cargo,
119 config.with_sysroot,
120 )
121 .map_err(|err| {
122 log::error!("failed to load workspace: {:#}", err);
113 show_message( 123 show_message(
114 req::MessageType::Error, 124 req::MessageType::Error,
115 format!("rust-analyzer failed to load workspace: {:?}", e), 125 format!("rust-analyzer failed to load workspace: {:#}", err),
116 &connection.sender, 126 &connection.sender,
117 ); 127 );
118 } 128 })
119 } 129 .ok()
120 } 130 })
121 loaded_workspaces 131 .collect::<Vec<_>>()
122 }; 132 };
123 133
124 let globs = config 134 let globs = config
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 6c42e1d76..f2ad453fa 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -64,6 +64,7 @@ pub struct WorldState {
64 pub latest_requests: Arc<RwLock<LatestRequests>>, 64 pub latest_requests: Arc<RwLock<LatestRequests>>,
65 pub flycheck: Option<Flycheck>, 65 pub flycheck: Option<Flycheck>,
66 pub diagnostics: DiagnosticCollection, 66 pub diagnostics: DiagnosticCollection,
67 pub proc_macro_client: ProcMacroClient,
67} 68}
68 69
69/// An immutable snapshot of the world's state at a point in time. 70/// An immutable snapshot of the world's state at a point in time.
@@ -147,9 +148,9 @@ impl WorldState {
147 148
148 let proc_macro_client = match &config.proc_macro_srv { 149 let proc_macro_client = match &config.proc_macro_srv {
149 None => ProcMacroClient::dummy(), 150 None => ProcMacroClient::dummy(),
150 Some(srv) => { 151 Some((path, args)) => {
151 let path = Path::new(&srv); 152 let path = std::path::Path::new(path);
152 match ProcMacroClient::extern_process(path) { 153 match ProcMacroClient::extern_process(path, args) {
153 Ok(it) => it, 154 Ok(it) => it,
154 Err(err) => { 155 Err(err) => {
155 log::error!( 156 log::error!(
@@ -192,6 +193,7 @@ impl WorldState {
192 latest_requests: Default::default(), 193 latest_requests: Default::default(),
193 flycheck, 194 flycheck,
194 diagnostics: Default::default(), 195 diagnostics: Default::default(),
196 proc_macro_client,
195 } 197 }
196 } 198 }
197 199
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 638813311..1dd2676b6 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -9,7 +9,7 @@ use lsp_types::{
9}; 9};
10use rust_analyzer::req::{ 10use rust_analyzer::req::{
11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, 11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
12 Formatting, GotoDefinition, OnEnter, Runnables, RunnablesParams, 12 Formatting, GotoDefinition, HoverRequest, OnEnter, Runnables, RunnablesParams,
13}; 13};
14use serde_json::json; 14use serde_json::json;
15use tempfile::TempDir; 15use tempfile::TempDir;
@@ -625,3 +625,92 @@ fn main() { message(); }
625 )); 625 ));
626 assert!(format!("{}", res).contains("hello.rs")); 626 assert!(format!("{}", res).contains("hello.rs"));
627} 627}
628
629#[test]
630fn resolve_proc_macro() {
631 if skip_slow_tests() {
632 return;
633 }
634 let server = Project::with_fixture(
635 r###"
636//- foo/Cargo.toml
637[package]
638name = "foo"
639version = "0.0.0"
640edition = "2018"
641[dependencies]
642bar = {path = "../bar"}
643
644//- foo/src/main.rs
645use bar::Bar;
646trait Bar {
647 fn bar();
648}
649#[derive(Bar)]
650struct Foo {}
651fn main() {
652 Foo::bar();
653}
654
655//- bar/Cargo.toml
656[package]
657name = "bar"
658version = "0.0.0"
659edition = "2018"
660
661[lib]
662proc-macro = true
663
664//- bar/src/lib.rs
665extern crate proc_macro;
666use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
667macro_rules! t {
668 ($n:literal) => {
669 TokenTree::from(Ident::new($n, Span::call_site()))
670 };
671 ({}) => {
672 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
673 };
674 (()) => {
675 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
676 };
677}
678#[proc_macro_derive(Bar)]
679pub fn foo(_input: TokenStream) -> TokenStream {
680 // We hard code the output here for preventing to use any deps
681 let mut res = TokenStream::new();
682
683 // impl Bar for Foo { fn bar() {} }
684 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
685 let mut fn_stream = TokenStream::new();
686 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
687 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
688 res.extend(tokens);
689 res
690}
691
692"###,
693 )
694 .with_config(|config| {
695 // FIXME: Use env!("CARGO_BIN_EXE_ra-analyzer") instead after
696 // https://github.com/rust-lang/cargo/pull/7697 landed
697 let macro_srv_path = std::path::Path::new(std::env!("CARGO_MANIFEST_DIR"))
698 .join("../../target/debug/rust-analyzer")
699 .to_string_lossy()
700 .to_string();
701
702 config.cargo.load_out_dirs_from_check = true;
703 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".to_string()]));
704 })
705 .root("foo")
706 .root("bar")
707 .server();
708 server.wait_until_workspace_is_loaded();
709 let res = server.send_request::<HoverRequest>(TextDocumentPositionParams::new(
710 server.doc_id("foo/src/main.rs"),
711 Position::new(7, 9),
712 ));
713
714 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
715 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#)
716}