aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/source_analyzer.rs99
-rw-r--r--crates/ra_hir_def/src/builtin_type.rs54
-rw-r--r--crates/ra_hir_def/src/find_path.rs21
-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/lib.rs4
-rw-r--r--crates/ra_hir_ty/src/lower.rs69
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs234
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs10
-rw-r--r--crates/ra_ide/src/inlay_hints.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs3
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs12
12 files changed, 457 insertions, 106 deletions
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/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
6use std::fmt; 6use std::fmt;
7 7
8use hir_expand::name::{name, Name}; 8use hir_expand::name::{name, AsName, Name};
9 9
10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 10#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
11pub enum Signedness { 11pub enum Signedness {
@@ -75,33 +75,39 @@ impl BuiltinType {
75 ]; 75 ];
76} 76}
77 77
78impl fmt::Display for BuiltinType { 78impl 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
107impl 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/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};
10use hir_expand::name::{known, Name}; 10use hir_expand::name::{known, AsName, Name};
11use test_utils::tested_by; 11use test_utils::tested_by;
12 12
13const MAX_PATH_LEN: usize = 15; 13const 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_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/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::{
39pub struct TyLoweringContext<'a> { 39pub 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.
798fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> { 827fn 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
883fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { 912fn 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/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]
1214fn dyn_trait_in_impl() {
1215 assert_snapshot!(
1216 infer(r#"
1217trait Trait<T, U> {
1218 fn foo(&self) -> (T, U);
1219}
1220struct S<T, U> {}
1221impl<T, U> S<T, U> {
1222 fn bar(&self) -> &dyn Trait<T, U> { loop {} }
1223}
1224trait Trait2<T, U> {
1225 fn baz(&self) -> (T, U);
1226}
1227impl<T, U> Trait2<T, U> for dyn Trait<T, U> { }
1228
1229fn 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]
1214fn dyn_trait_bare() { 1250fn 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]
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_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::{
17use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; 17use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
18use crate::{ 18use 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
23pub(super) mod tls; 23pub(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/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
256fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { 257fn 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(&param_name) || argument_string.ends_with(&param_name) 261 argument_string.starts_with(&param_name) || argument_string.ends_with(&param_name)
259} 262}
260 263
@@ -1094,8 +1097,10 @@ struct Param {}
1094 1097
1095fn different_order(param: &Param) {} 1098fn different_order(param: &Param) {}
1096fn different_order_mut(param: &mut Param) {} 1099fn different_order_mut(param: &mut Param) {}
1100fn has_underscore(_param: bool) {}
1097 1101
1098fn twiddle(twiddle: bool) {} 1102fn twiddle(twiddle: bool) {}
1103fn doo(_doo: bool) {}
1099 1104
1100fn main() { 1105fn 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(&param_begin); 1123 different_order(&param_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/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}