diff options
Diffstat (limited to 'crates')
28 files changed, 691 insertions, 180 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 | ||
21 | use crate::{ | 21 | use 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 | }; |
24 | use ra_syntax::{ | 24 | use ra_syntax::{ |
25 | ast::{self, AstNode}, | 25 | ast::{self, AstNode}, |
26 | SyntaxNode, SyntaxNodePtr, TextUnit, | 26 | SyntaxNode, TextRange, TextUnit, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | use crate::{ | 29 | use 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 | ||
306 | fn scope_for_offset( | 306 | fn 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... | ||
340 | fn 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 | |||
335 | pub(crate) fn resolve_hir_path( | 378 | pub(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... | ||
382 | fn 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/builtin_type.rs b/crates/ra_hir_def/src/builtin_type.rs index d14901a9b..0f872b5c0 100644 --- a/crates/ra_hir_def/src/builtin_type.rs +++ b/crates/ra_hir_def/src/builtin_type.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | 5 | ||
6 | use std::fmt; | 6 | use std::fmt; |
7 | 7 | ||
8 | use hir_expand::name::{name, Name}; | 8 | use hir_expand::name::{name, AsName, Name}; |
9 | 9 | ||
10 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] | 10 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
11 | pub enum Signedness { | 11 | pub enum Signedness { |
@@ -75,33 +75,39 @@ impl BuiltinType { | |||
75 | ]; | 75 | ]; |
76 | } | 76 | } |
77 | 77 | ||
78 | impl fmt::Display for BuiltinType { | 78 | impl AsName for BuiltinType { |
79 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 79 | fn as_name(&self) -> Name { |
80 | let type_name = match self { | 80 | match self { |
81 | BuiltinType::Char => "char", | 81 | BuiltinType::Char => name![char], |
82 | BuiltinType::Bool => "bool", | 82 | BuiltinType::Bool => name![bool], |
83 | BuiltinType::Str => "str", | 83 | BuiltinType::Str => name![str], |
84 | BuiltinType::Int(BuiltinInt { signedness, bitness }) => match (signedness, bitness) { | 84 | BuiltinType::Int(BuiltinInt { signedness, bitness }) => match (signedness, bitness) { |
85 | (Signedness::Signed, IntBitness::Xsize) => "isize", | 85 | (Signedness::Signed, IntBitness::Xsize) => name![isize], |
86 | (Signedness::Signed, IntBitness::X8) => "i8", | 86 | (Signedness::Signed, IntBitness::X8) => name![i8], |
87 | (Signedness::Signed, IntBitness::X16) => "i16", | 87 | (Signedness::Signed, IntBitness::X16) => name![i16], |
88 | (Signedness::Signed, IntBitness::X32) => "i32", | 88 | (Signedness::Signed, IntBitness::X32) => name![i32], |
89 | (Signedness::Signed, IntBitness::X64) => "i64", | 89 | (Signedness::Signed, IntBitness::X64) => name![i64], |
90 | (Signedness::Signed, IntBitness::X128) => "i128", | 90 | (Signedness::Signed, IntBitness::X128) => name![i128], |
91 | 91 | ||
92 | (Signedness::Unsigned, IntBitness::Xsize) => "usize", | 92 | (Signedness::Unsigned, IntBitness::Xsize) => name![usize], |
93 | (Signedness::Unsigned, IntBitness::X8) => "u8", | 93 | (Signedness::Unsigned, IntBitness::X8) => name![u8], |
94 | (Signedness::Unsigned, IntBitness::X16) => "u16", | 94 | (Signedness::Unsigned, IntBitness::X16) => name![u16], |
95 | (Signedness::Unsigned, IntBitness::X32) => "u32", | 95 | (Signedness::Unsigned, IntBitness::X32) => name![u32], |
96 | (Signedness::Unsigned, IntBitness::X64) => "u64", | 96 | (Signedness::Unsigned, IntBitness::X64) => name![u64], |
97 | (Signedness::Unsigned, IntBitness::X128) => "u128", | 97 | (Signedness::Unsigned, IntBitness::X128) => name![u128], |
98 | }, | 98 | }, |
99 | BuiltinType::Float(BuiltinFloat { bitness }) => match bitness { | 99 | BuiltinType::Float(BuiltinFloat { bitness }) => match bitness { |
100 | FloatBitness::X32 => "f32", | 100 | FloatBitness::X32 => name![f32], |
101 | FloatBitness::X64 => "f64", | 101 | FloatBitness::X64 => name![f64], |
102 | }, | 102 | }, |
103 | }; | 103 | } |
104 | f.write_str(type_name) | 104 | } |
105 | } | ||
106 | |||
107 | impl fmt::Display for BuiltinType { | ||
108 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
109 | let type_name = self.as_name(); | ||
110 | type_name.fmt(f) | ||
105 | } | 111 | } |
106 | } | 112 | } |
107 | 113 | ||
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_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index d58ac6ba5..81eff5bfe 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -7,7 +7,7 @@ use crate::{ | |||
7 | visibility::Visibility, | 7 | visibility::Visibility, |
8 | CrateId, ModuleDefId, ModuleId, | 8 | CrateId, ModuleDefId, ModuleId, |
9 | }; | 9 | }; |
10 | use hir_expand::name::{known, Name}; | 10 | use hir_expand::name::{known, AsName, Name}; |
11 | use test_utils::tested_by; | 11 | use test_utils::tested_by; |
12 | 12 | ||
13 | const MAX_PATH_LEN: usize = 15; | 13 | const MAX_PATH_LEN: usize = 15; |
@@ -113,6 +113,11 @@ fn find_path_inner( | |||
113 | } | 113 | } |
114 | } | 114 | } |
115 | 115 | ||
116 | // - if the item is a builtin, it's in scope | ||
117 | if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item { | ||
118 | return Some(ModPath::from_segments(PathKind::Plain, vec![builtin.as_name()])); | ||
119 | } | ||
120 | |||
116 | // Recursive case: | 121 | // Recursive case: |
117 | // - if the item is an enum variant, refer to it via the enum | 122 | // - if the item is an enum variant, refer to it via the enum |
118 | if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { | 123 | if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() { |
@@ -523,4 +528,18 @@ mod tests { | |||
523 | "#; | 528 | "#; |
524 | check_found_path(code, "megaalloc::Arc"); | 529 | check_found_path(code, "megaalloc::Arc"); |
525 | } | 530 | } |
531 | |||
532 | #[test] | ||
533 | fn builtins_are_in_scope() { | ||
534 | let code = r#" | ||
535 | //- /main.rs | ||
536 | <|> | ||
537 | |||
538 | pub mod primitive { | ||
539 | pub use u8; | ||
540 | } | ||
541 | "#; | ||
542 | check_found_path(code, "u8"); | ||
543 | check_found_path(code, "u16"); | ||
544 | } | ||
526 | } | 545 | } |
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 | ||
17 | use std::{any::Any, fmt}; | 17 | use std::{any::Any, fmt}; |
18 | 18 | ||
19 | use ra_syntax::{SyntaxNode, SyntaxNodePtr, TextRange}; | 19 | use ra_syntax::{SyntaxNode, SyntaxNodePtr}; |
20 | 20 | ||
21 | use crate::{db::AstDatabase, InFile}; | 21 | use crate::{db::AstDatabase, InFile}; |
22 | 22 | ||
23 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | 23 | pub 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 | ||
9 | use super::{InferenceContext, Obligation}; | 9 | use super::{InferenceContext, Obligation}; |
10 | use crate::{ | 10 | use 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 | ||
14 | impl<'a> InferenceContext<'a> { | 15 | impl<'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/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2677f3af2..a4b8d6683 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -396,12 +396,12 @@ impl Substs { | |||
396 | } | 396 | } |
397 | 397 | ||
398 | /// Return Substs that replace each parameter by a bound variable. | 398 | /// Return Substs that replace each parameter by a bound variable. |
399 | pub(crate) fn bound_vars(generic_params: &Generics) -> Substs { | 399 | pub(crate) fn bound_vars(generic_params: &Generics, debruijn: DebruijnIndex) -> Substs { |
400 | Substs( | 400 | Substs( |
401 | generic_params | 401 | generic_params |
402 | .iter() | 402 | .iter() |
403 | .enumerate() | 403 | .enumerate() |
404 | .map(|(idx, _)| Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx))) | 404 | .map(|(idx, _)| Ty::Bound(BoundVar::new(debruijn, idx))) |
405 | .collect(), | 405 | .collect(), |
406 | ) | 406 | ) |
407 | } | 407 | } |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index cc1ac8e3e..c2812e178 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -39,6 +39,7 @@ use crate::{ | |||
39 | pub struct TyLoweringContext<'a> { | 39 | pub struct TyLoweringContext<'a> { |
40 | pub db: &'a dyn HirDatabase, | 40 | pub db: &'a dyn HirDatabase, |
41 | pub resolver: &'a Resolver, | 41 | pub resolver: &'a Resolver, |
42 | in_binders: DebruijnIndex, | ||
42 | /// Note: Conceptually, it's thinkable that we could be in a location where | 43 | /// Note: Conceptually, it's thinkable that we could be in a location where |
43 | /// some type params should be represented as placeholders, and others | 44 | /// some type params should be represented as placeholders, and others |
44 | /// should be converted to variables. I think in practice, this isn't | 45 | /// should be converted to variables. I think in practice, this isn't |
@@ -53,7 +54,27 @@ impl<'a> TyLoweringContext<'a> { | |||
53 | let impl_trait_counter = std::cell::Cell::new(0); | 54 | let impl_trait_counter = std::cell::Cell::new(0); |
54 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; | 55 | let impl_trait_mode = ImplTraitLoweringMode::Disallowed; |
55 | let type_param_mode = TypeParamLoweringMode::Placeholder; | 56 | let type_param_mode = TypeParamLoweringMode::Placeholder; |
56 | Self { db, resolver, impl_trait_mode, impl_trait_counter, type_param_mode } | 57 | let in_binders = DebruijnIndex::INNERMOST; |
58 | Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } | ||
59 | } | ||
60 | |||
61 | pub fn with_shifted_in<T>( | ||
62 | &self, | ||
63 | debruijn: DebruijnIndex, | ||
64 | f: impl FnOnce(&TyLoweringContext) -> T, | ||
65 | ) -> T { | ||
66 | let new_ctx = Self { | ||
67 | in_binders: self.in_binders.shifted_in_from(debruijn), | ||
68 | impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), | ||
69 | ..*self | ||
70 | }; | ||
71 | let result = f(&new_ctx); | ||
72 | self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); | ||
73 | result | ||
74 | } | ||
75 | |||
76 | pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { | ||
77 | Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } | ||
57 | } | 78 | } |
58 | 79 | ||
59 | pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { | 80 | pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { |
@@ -134,22 +155,26 @@ impl Ty { | |||
134 | } | 155 | } |
135 | TypeRef::DynTrait(bounds) => { | 156 | TypeRef::DynTrait(bounds) => { |
136 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 157 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); |
137 | let predicates = bounds | 158 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { |
138 | .iter() | 159 | bounds |
139 | .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) | 160 | .iter() |
140 | .collect(); | 161 | .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone())) |
162 | .collect() | ||
163 | }); | ||
141 | Ty::Dyn(predicates) | 164 | Ty::Dyn(predicates) |
142 | } | 165 | } |
143 | TypeRef::ImplTrait(bounds) => { | 166 | TypeRef::ImplTrait(bounds) => { |
144 | match ctx.impl_trait_mode { | 167 | match ctx.impl_trait_mode { |
145 | ImplTraitLoweringMode::Opaque => { | 168 | ImplTraitLoweringMode::Opaque => { |
146 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 169 | let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); |
147 | let predicates = bounds | 170 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { |
148 | .iter() | 171 | bounds |
149 | .flat_map(|b| { | 172 | .iter() |
150 | GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) | 173 | .flat_map(|b| { |
151 | }) | 174 | GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) |
152 | .collect(); | 175 | }) |
176 | .collect() | ||
177 | }); | ||
153 | Ty::Opaque(predicates) | 178 | Ty::Opaque(predicates) |
154 | } | 179 | } |
155 | ImplTraitLoweringMode::Param => { | 180 | ImplTraitLoweringMode::Param => { |
@@ -180,7 +205,7 @@ impl Ty { | |||
180 | (0, 0, 0, 0) | 205 | (0, 0, 0, 0) |
181 | }; | 206 | }; |
182 | Ty::Bound(BoundVar::new( | 207 | Ty::Bound(BoundVar::new( |
183 | DebruijnIndex::INNERMOST, | 208 | ctx.in_binders, |
184 | idx as usize + parent_params + self_params + list_params, | 209 | idx as usize + parent_params + self_params + list_params, |
185 | )) | 210 | )) |
186 | } | 211 | } |
@@ -293,7 +318,7 @@ impl Ty { | |||
293 | TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id), | 318 | TypeParamLoweringMode::Placeholder => Ty::Placeholder(param_id), |
294 | TypeParamLoweringMode::Variable => { | 319 | TypeParamLoweringMode::Variable => { |
295 | let idx = generics.param_idx(param_id).expect("matching generics"); | 320 | let idx = generics.param_idx(param_id).expect("matching generics"); |
296 | Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, idx)) | 321 | Ty::Bound(BoundVar::new(ctx.in_binders, idx)) |
297 | } | 322 | } |
298 | } | 323 | } |
299 | } | 324 | } |
@@ -303,7 +328,9 @@ impl Ty { | |||
303 | TypeParamLoweringMode::Placeholder => { | 328 | TypeParamLoweringMode::Placeholder => { |
304 | Substs::type_params_for_generics(&generics) | 329 | Substs::type_params_for_generics(&generics) |
305 | } | 330 | } |
306 | TypeParamLoweringMode::Variable => Substs::bound_vars(&generics), | 331 | TypeParamLoweringMode::Variable => { |
332 | Substs::bound_vars(&generics, ctx.in_binders) | ||
333 | } | ||
307 | }; | 334 | }; |
308 | ctx.db.impl_self_ty(impl_id).subst(&substs) | 335 | ctx.db.impl_self_ty(impl_id).subst(&substs) |
309 | } | 336 | } |
@@ -313,7 +340,9 @@ impl Ty { | |||
313 | TypeParamLoweringMode::Placeholder => { | 340 | TypeParamLoweringMode::Placeholder => { |
314 | Substs::type_params_for_generics(&generics) | 341 | Substs::type_params_for_generics(&generics) |
315 | } | 342 | } |
316 | TypeParamLoweringMode::Variable => Substs::bound_vars(&generics), | 343 | TypeParamLoweringMode::Variable => { |
344 | Substs::bound_vars(&generics, ctx.in_binders) | ||
345 | } | ||
317 | }; | 346 | }; |
318 | ctx.db.ty(adt.into()).subst(&substs) | 347 | ctx.db.ty(adt.into()).subst(&substs) |
319 | } | 348 | } |
@@ -797,7 +826,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { | |||
797 | /// function body. | 826 | /// function body. |
798 | fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> { | 827 | fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> { |
799 | let generics = generics(db.upcast(), def.into()); | 828 | let generics = generics(db.upcast(), def.into()); |
800 | let substs = Substs::bound_vars(&generics); | 829 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
801 | Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) | 830 | Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) |
802 | } | 831 | } |
803 | 832 | ||
@@ -851,7 +880,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T | |||
851 | return type_for_adt(db, def.into()); | 880 | return type_for_adt(db, def.into()); |
852 | } | 881 | } |
853 | let generics = generics(db.upcast(), def.into()); | 882 | let generics = generics(db.upcast(), def.into()); |
854 | let substs = Substs::bound_vars(&generics); | 883 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
855 | Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) | 884 | Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) |
856 | } | 885 | } |
857 | 886 | ||
@@ -876,13 +905,13 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) - | |||
876 | return type_for_adt(db, def.parent.into()); | 905 | return type_for_adt(db, def.parent.into()); |
877 | } | 906 | } |
878 | let generics = generics(db.upcast(), def.parent.into()); | 907 | let generics = generics(db.upcast(), def.parent.into()); |
879 | let substs = Substs::bound_vars(&generics); | 908 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
880 | Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) | 909 | Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs)) |
881 | } | 910 | } |
882 | 911 | ||
883 | fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { | 912 | fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { |
884 | let generics = generics(db.upcast(), adt.into()); | 913 | let generics = generics(db.upcast(), adt.into()); |
885 | let substs = Substs::bound_vars(&generics); | 914 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
886 | Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs)) | 915 | Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs)) |
887 | } | 916 | } |
888 | 917 | ||
@@ -892,7 +921,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { | |||
892 | let ctx = | 921 | let ctx = |
893 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); | 922 | TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); |
894 | let type_ref = &db.type_alias_data(t).type_ref; | 923 | let type_ref = &db.type_alias_data(t).type_ref; |
895 | let substs = Substs::bound_vars(&generics); | 924 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
896 | let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); | 925 | let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error)); |
897 | Binders::new(substs.len(), inner) | 926 | Binders::new(substs.len(), inner) |
898 | } | 927 | } |
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] | ||
489 | fn issue_3999_slice() { | ||
490 | assert_snapshot!( | ||
491 | infer(r#" | ||
492 | fn 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] | ||
511 | fn issue_3999_struct() { | ||
512 | // rust-analyzer should not panic on seeing this malformed | ||
513 | // record pattern. | ||
514 | assert_snapshot!( | ||
515 | infer(r#" | ||
516 | struct Bar { | ||
517 | a: bool, | ||
518 | } | ||
519 | fn 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 0a889f805..a46f03b7f 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1211,6 +1211,42 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) { | |||
1211 | } | 1211 | } |
1212 | 1212 | ||
1213 | #[test] | 1213 | #[test] |
1214 | fn dyn_trait_in_impl() { | ||
1215 | assert_snapshot!( | ||
1216 | infer(r#" | ||
1217 | trait Trait<T, U> { | ||
1218 | fn foo(&self) -> (T, U); | ||
1219 | } | ||
1220 | struct S<T, U> {} | ||
1221 | impl<T, U> S<T, U> { | ||
1222 | fn bar(&self) -> &dyn Trait<T, U> { loop {} } | ||
1223 | } | ||
1224 | trait Trait2<T, U> { | ||
1225 | fn baz(&self) -> (T, U); | ||
1226 | } | ||
1227 | impl<T, U> Trait2<T, U> for dyn Trait<T, U> { } | ||
1228 | |||
1229 | fn test(s: S<u32, i32>) { | ||
1230 | s.bar().baz(); | ||
1231 | } | ||
1232 | "#), | ||
1233 | @r###" | ||
1234 | [33; 37) 'self': &Self | ||
1235 | [103; 107) 'self': &S<T, U> | ||
1236 | [129; 140) '{ loop {} }': &dyn Trait<T, U> | ||
1237 | [131; 138) 'loop {}': ! | ||
1238 | [136; 138) '{}': () | ||
1239 | [176; 180) 'self': &Self | ||
1240 | [252; 253) 's': S<u32, i32> | ||
1241 | [268; 290) '{ ...z(); }': () | ||
1242 | [274; 275) 's': S<u32, i32> | ||
1243 | [274; 281) 's.bar()': &dyn Trait<u32, i32> | ||
1244 | [274; 287) 's.bar().baz()': (u32, i32) | ||
1245 | "### | ||
1246 | ); | ||
1247 | } | ||
1248 | |||
1249 | #[test] | ||
1214 | fn dyn_trait_bare() { | 1250 | fn dyn_trait_bare() { |
1215 | assert_snapshot!( | 1251 | assert_snapshot!( |
1216 | infer(r#" | 1252 | infer(r#" |
@@ -2204,3 +2240,201 @@ fn test(x: Box<dyn Trait>) { | |||
2204 | ); | 2240 | ); |
2205 | assert_eq!(t, "()"); | 2241 | assert_eq!(t, "()"); |
2206 | } | 2242 | } |
2243 | |||
2244 | #[test] | ||
2245 | fn string_to_owned() { | ||
2246 | let t = type_at( | ||
2247 | r#" | ||
2248 | //- /main.rs | ||
2249 | struct String {} | ||
2250 | pub trait ToOwned { | ||
2251 | type Owned; | ||
2252 | fn to_owned(&self) -> Self::Owned; | ||
2253 | } | ||
2254 | impl ToOwned for str { | ||
2255 | type Owned = String; | ||
2256 | } | ||
2257 | fn test() { | ||
2258 | "foo".to_owned()<|>; | ||
2259 | } | ||
2260 | "#, | ||
2261 | ); | ||
2262 | assert_eq!(t, "String"); | ||
2263 | } | ||
2264 | |||
2265 | #[test] | ||
2266 | fn iterator_chain() { | ||
2267 | assert_snapshot!( | ||
2268 | infer(r#" | ||
2269 | //- /main.rs | ||
2270 | #[lang = "fn_once"] | ||
2271 | trait FnOnce<Args> { | ||
2272 | type Output; | ||
2273 | } | ||
2274 | #[lang = "fn_mut"] | ||
2275 | trait FnMut<Args>: FnOnce<Args> { } | ||
2276 | |||
2277 | enum Option<T> { Some(T), None } | ||
2278 | use Option::*; | ||
2279 | |||
2280 | pub 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 | |||
2294 | pub trait IntoIterator { | ||
2295 | type Item; | ||
2296 | type IntoIter: Iterator<Item = Self::Item>; | ||
2297 | fn into_iter(self) -> Self::IntoIter; | ||
2298 | } | ||
2299 | |||
2300 | pub struct FilterMap<I, F> { } | ||
2301 | impl<B, I: Iterator, F> Iterator for FilterMap<I, F> | ||
2302 | where | ||
2303 | F: FnMut(I::Item) -> Option<B>, | ||
2304 | { | ||
2305 | type Item = B; | ||
2306 | } | ||
2307 | |||
2308 | #[stable(feature = "rust1", since = "1.0.0")] | ||
2309 | impl<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 | |||
2318 | struct Vec<T> {} | ||
2319 | impl<T> Vec<T> { | ||
2320 | fn new() -> Self { loop {} } | ||
2321 | } | ||
2322 | |||
2323 | impl<T> IntoIterator for Vec<T> { | ||
2324 | type Item = T; | ||
2325 | type IntoIter = IntoIter<T>; | ||
2326 | } | ||
2327 | |||
2328 | pub struct IntoIter<T> { } | ||
2329 | impl<T> Iterator for IntoIter<T> { | ||
2330 | type Item = T; | ||
2331 | } | ||
2332 | |||
2333 | fn 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] | ||
2385 | fn nested_assoc() { | ||
2386 | let t = type_at( | ||
2387 | r#" | ||
2388 | //- /main.rs | ||
2389 | struct Bar; | ||
2390 | struct Foo; | ||
2391 | |||
2392 | trait A { | ||
2393 | type OutputA; | ||
2394 | } | ||
2395 | |||
2396 | impl A for Bar { | ||
2397 | type OutputA = Foo; | ||
2398 | } | ||
2399 | |||
2400 | trait B { | ||
2401 | type Output; | ||
2402 | fn foo() -> Self::Output; | ||
2403 | } | ||
2404 | |||
2405 | impl<T:A> B for T { | ||
2406 | type Output = T::OutputA; | ||
2407 | fn foo() -> Self::Output { loop {} } | ||
2408 | } | ||
2409 | |||
2410 | fn main() { | ||
2411 | Bar::foo()<|>; | ||
2412 | } | ||
2413 | "#, | ||
2414 | ); | ||
2415 | assert_eq!(t, "Foo"); | ||
2416 | } | ||
2417 | |||
2418 | #[test] | ||
2419 | fn trait_object_no_coercion() { | ||
2420 | assert_snapshot!( | ||
2421 | infer_with_mismatches(r#" | ||
2422 | trait Foo {} | ||
2423 | |||
2424 | fn foo(x: &dyn Foo) {} | ||
2425 | |||
2426 | fn 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_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 60d70d18e..e00a82db2 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs | |||
@@ -17,7 +17,7 @@ use ra_db::{ | |||
17 | use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; | 17 | use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; |
18 | use crate::{ | 18 | use crate::{ |
19 | db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, | 19 | db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, |
20 | ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 20 | ApplicationTy, DebruijnIndex, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | pub(super) mod tls; | 23 | pub(super) mod tls; |
@@ -815,7 +815,7 @@ pub(crate) fn associated_ty_data_query( | |||
815 | // Lower bounds -- we could/should maybe move this to a separate query in `lower` | 815 | // Lower bounds -- we could/should maybe move this to a separate query in `lower` |
816 | let type_alias_data = db.type_alias_data(type_alias); | 816 | let type_alias_data = db.type_alias_data(type_alias); |
817 | let generic_params = generics(db.upcast(), type_alias.into()); | 817 | let generic_params = generics(db.upcast(), type_alias.into()); |
818 | let bound_vars = Substs::bound_vars(&generic_params); | 818 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
819 | let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); | 819 | let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); |
820 | let ctx = crate::TyLoweringContext::new(db, &resolver) | 820 | let ctx = crate::TyLoweringContext::new(db, &resolver) |
821 | .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); | 821 | .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); |
@@ -849,7 +849,7 @@ pub(crate) fn trait_datum_query( | |||
849 | let trait_data = db.trait_data(trait_); | 849 | let trait_data = db.trait_data(trait_); |
850 | debug!("trait {:?} = {:?}", trait_id, trait_data.name); | 850 | debug!("trait {:?} = {:?}", trait_id, trait_data.name); |
851 | let generic_params = generics(db.upcast(), trait_.into()); | 851 | let generic_params = generics(db.upcast(), trait_.into()); |
852 | let bound_vars = Substs::bound_vars(&generic_params); | 852 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
853 | let flags = chalk_rust_ir::TraitFlags { | 853 | let flags = chalk_rust_ir::TraitFlags { |
854 | auto: trait_data.auto, | 854 | auto: trait_data.auto, |
855 | upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, | 855 | upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, |
@@ -888,7 +888,7 @@ pub(crate) fn struct_datum_query( | |||
888 | .as_generic_def() | 888 | .as_generic_def() |
889 | .map(|generic_def| { | 889 | .map(|generic_def| { |
890 | let generic_params = generics(db.upcast(), generic_def); | 890 | let generic_params = generics(db.upcast(), generic_def); |
891 | let bound_vars = Substs::bound_vars(&generic_params); | 891 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
892 | convert_where_clauses(db, generic_def, &bound_vars) | 892 | convert_where_clauses(db, generic_def, &bound_vars) |
893 | }) | 893 | }) |
894 | .unwrap_or_else(Vec::new); | 894 | .unwrap_or_else(Vec::new); |
@@ -934,7 +934,7 @@ fn impl_def_datum( | |||
934 | let impl_data = db.impl_data(impl_id); | 934 | let impl_data = db.impl_data(impl_id); |
935 | 935 | ||
936 | let generic_params = generics(db.upcast(), impl_id.into()); | 936 | let generic_params = generics(db.upcast(), impl_id.into()); |
937 | let bound_vars = Substs::bound_vars(&generic_params); | 937 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
938 | let trait_ = trait_ref.trait_; | 938 | let trait_ = trait_ref.trait_; |
939 | let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { | 939 | let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate { |
940 | chalk_rust_ir::ImplType::Local | 940 | chalk_rust_ir::ImplType::Local |
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 | ||
152 | fn add_type_alias_impl( | 152 | fn 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)] |
219 | mod tests { | 219 | mod 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 | ||
3 | use hir::ScopeDef; | ||
4 | use test_utils::tested_by; | ||
5 | |||
3 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use ra_syntax::AstNode; | ||
4 | 8 | ||
5 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(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)] |
21 | mod tests { | 35 | mod 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 | ||
3 | use std::cell::RefCell; | 7 | use 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/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 45b9f7802..0774fa0a1 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -237,7 +237,8 @@ fn should_show_param_hint( | |||
237 | ) -> bool { | 237 | ) -> bool { |
238 | if param_name.is_empty() | 238 | if param_name.is_empty() |
239 | || is_argument_similar_to_param(argument, param_name) | 239 | || is_argument_similar_to_param(argument, param_name) |
240 | || Some(param_name) == fn_signature.name.as_ref().map(String::as_str) | 240 | || Some(param_name.trim_start_matches('_')) |
241 | == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) | ||
241 | { | 242 | { |
242 | return false; | 243 | return false; |
243 | } | 244 | } |
@@ -255,6 +256,8 @@ fn should_show_param_hint( | |||
255 | 256 | ||
256 | fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { | 257 | fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { |
257 | let argument_string = remove_ref(argument.clone()).syntax().to_string(); | 258 | let argument_string = remove_ref(argument.clone()).syntax().to_string(); |
259 | let param_name = param_name.trim_start_matches('_'); | ||
260 | let argument_string = argument_string.trim_start_matches('_'); | ||
258 | argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) | 261 | argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) |
259 | } | 262 | } |
260 | 263 | ||
@@ -1094,8 +1097,10 @@ struct Param {} | |||
1094 | 1097 | ||
1095 | fn different_order(param: &Param) {} | 1098 | fn different_order(param: &Param) {} |
1096 | fn different_order_mut(param: &mut Param) {} | 1099 | fn different_order_mut(param: &mut Param) {} |
1100 | fn has_underscore(_param: bool) {} | ||
1097 | 1101 | ||
1098 | fn twiddle(twiddle: bool) {} | 1102 | fn twiddle(twiddle: bool) {} |
1103 | fn doo(_doo: bool) {} | ||
1099 | 1104 | ||
1100 | fn main() { | 1105 | fn main() { |
1101 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; | 1106 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; |
@@ -1112,11 +1117,15 @@ fn main() { | |||
1112 | test_processed.frob(false); | 1117 | test_processed.frob(false); |
1113 | 1118 | ||
1114 | twiddle(true); | 1119 | twiddle(true); |
1120 | doo(true); | ||
1115 | 1121 | ||
1116 | let param_begin: Param = Param {}; | 1122 | let param_begin: Param = Param {}; |
1117 | different_order(¶m_begin); | 1123 | different_order(¶m_begin); |
1118 | different_order(&mut param_begin); | 1124 | different_order(&mut param_begin); |
1119 | 1125 | ||
1126 | let param: bool = true; | ||
1127 | has_underscore(param); | ||
1128 | |||
1120 | let a: f64 = 7.0; | 1129 | let a: f64 = 7.0; |
1121 | let b: f64 = 4.0; | 1130 | let b: f64 = 4.0; |
1122 | let _: f64 = a.div_euclid(b); | 1131 | let _: f64 = a.div_euclid(b); |
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] | ||
161 | fn ranges_sorted() { | ||
162 | let (analysis, file_id) = single_file( | ||
163 | r#" | ||
164 | #[foo(bar = "bar")] | ||
165 | macro_rules! test {} | ||
166 | }"# | ||
167 | .trim(), | ||
168 | ); | ||
169 | let _ = analysis.highlight(file_id).unwrap(); | ||
170 | } | ||
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" } | |||
14 | ra_proc_macro = { path = "../ra_proc_macro" } | 14 | ra_proc_macro = { path = "../ra_proc_macro" } |
15 | goblin = "0.2.1" | 15 | goblin = "0.2.1" |
16 | libloading = "0.6.0" | 16 | libloading = "0.6.0" |
17 | memmap = "0.7" | ||
17 | test_utils = { path = "../test_utils" } | 18 | test_utils = { path = "../test_utils" } |
18 | 19 | ||
19 | [dev-dependencies] | 20 | [dev-dependencies] |
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 | ||
3 | use crate::{proc_macro::bridge, rustc_server::TokenStream}; | 3 | use crate::{proc_macro::bridge, rustc_server::TokenStream}; |
4 | use std::fs::File; | ||
4 | use std::path::Path; | 5 | use std::path::Path; |
5 | 6 | ||
6 | use goblin::{mach::Mach, Object}; | 7 | use goblin::{mach::Mach, Object}; |
7 | use libloading::Library; | 8 | use libloading::Library; |
9 | use memmap::Mmap; | ||
8 | use ra_proc_macro::ProcMacroKind; | 10 | use ra_proc_macro::ProcMacroKind; |
9 | 11 | ||
10 | use std::io::Error as IoError; | 12 | use 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 | ||
19 | fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> { | 21 | fn is_derive_registrar_symbol(symbol: &str) -> bool { |
20 | let buffer = std::fs::read(file)?; | 22 | symbol.contains(NEW_REGISTRAR_SYMBOL) |
23 | } | ||
24 | |||
25 | fn 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 | ||
59 | fn is_derive_registrar_symbol(symbol: &str) -> bool { | ||
60 | symbol.contains(NEW_REGISTRAR_SYMBOL) | ||
61 | } | ||
62 | |||
63 | fn 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/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index eb9ac32c3..762f776fe 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -75,9 +75,7 @@ pub(crate) fn load_cargo( | |||
75 | let proc_macro_client = if !with_proc_macro { | 75 | let proc_macro_client = if !with_proc_macro { |
76 | ProcMacroClient::dummy() | 76 | ProcMacroClient::dummy() |
77 | } else { | 77 | } else { |
78 | let mut path = std::env::current_exe()?; | 78 | let path = std::env::current_exe()?; |
79 | path.pop(); | ||
80 | path.push("rust-analyzer"); | ||
81 | ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap() | 79 | ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap() |
82 | }; | 80 | }; |
83 | 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); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2b45f1310..3597a14e3 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -134,9 +134,7 @@ impl Config { | |||
134 | 134 | ||
135 | match get::<bool>(value, "/procMacro/enabled") { | 135 | match get::<bool>(value, "/procMacro/enabled") { |
136 | Some(true) => { | 136 | Some(true) => { |
137 | if let Ok(mut path) = std::env::current_exe() { | 137 | if let Ok(path) = std::env::current_exe() { |
138 | path.pop(); | ||
139 | path.push("rust-analyzer"); | ||
140 | self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()])); | 138 | self.proc_macro_srv = Some((path.to_string_lossy().to_string(), vec!["proc-macro".to_string()])); |
141 | } | 139 | } |
142 | } | 140 | } |