]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/matches.rs
Adapt codebase to the tool_lints
[rust.git] / clippy_lints / src / matches.rs
1 use rustc::hir::*;
2 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass, in_external_macro, LintContext};
3 use rustc::{declare_tool_lint, lint_array};
4 use if_chain::if_chain;
5 use rustc::ty::{self, Ty};
6 use std::cmp::Ordering;
7 use std::collections::Bound;
8 use syntax::ast::LitKind;
9 use syntax::source_map::Span;
10 use crate::utils::paths;
11 use crate::utils::{expr_block, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg,
12             remove_blocks, snippet, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty};
13 use crate::utils::sugg::Sugg;
14 use crate::consts::{constant, Constant};
15
16 /// **What it does:** Checks for matches with a single arm where an `if let`
17 /// will usually suffice.
18 ///
19 /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
20 ///
21 /// **Known problems:** None.
22 ///
23 /// **Example:**
24 /// ```rust
25 /// match x {
26 ///     Some(ref foo) => bar(foo),
27 ///     _ => ()
28 /// }
29 /// ```
30 declare_clippy_lint! {
31     pub SINGLE_MATCH,
32     style,
33     "a match statement with a single nontrivial arm (i.e. where the other arm \
34      is `_ => {}`) instead of `if let`"
35 }
36
37 /// **What it does:** Checks for matches with a two arms where an `if let` will
38 /// usually suffice.
39 ///
40 /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
41 ///
42 /// **Known problems:** Personal style preferences may differ.
43 ///
44 /// **Example:**
45 /// ```rust
46 /// match x {
47 ///     Some(ref foo) => bar(foo),
48 ///     _ => bar(other_ref),
49 /// }
50 /// ```
51 declare_clippy_lint! {
52     pub SINGLE_MATCH_ELSE,
53     pedantic,
54     "a match statement with a two arms where the second arm's pattern is a wildcard \
55      instead of `if let`"
56 }
57
58 /// **What it does:** Checks for matches where all arms match a reference,
59 /// suggesting to remove the reference and deref the matched expression
60 /// instead. It also checks for `if let &foo = bar` blocks.
61 ///
62 /// **Why is this bad?** It just makes the code less readable. That reference
63 /// destructuring adds nothing to the code.
64 ///
65 /// **Known problems:** None.
66 ///
67 /// **Example:**
68 /// ```rust
69 /// match x {
70 ///     &A(ref y) => foo(y),
71 ///     &B => bar(),
72 ///     _ => frob(&x),
73 /// }
74 /// ```
75 declare_clippy_lint! {
76     pub MATCH_REF_PATS,
77     style,
78     "a match or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
79 }
80
81 /// **What it does:** Checks for matches where match expression is a `bool`. It
82 /// suggests to replace the expression with an `if...else` block.
83 ///
84 /// **Why is this bad?** It makes the code less readable.
85 ///
86 /// **Known problems:** None.
87 ///
88 /// **Example:**
89 /// ```rust
90 /// let condition: bool = true;
91 /// match condition {
92 ///     true => foo(),
93 ///     false => bar(),
94 /// }
95 /// ```
96 /// Use if/else instead:
97 /// ```rust
98 /// let condition: bool = true;
99 /// if condition {
100 ///     foo();
101 /// } else {
102 ///     bar();
103 /// }
104 /// ```
105 declare_clippy_lint! {
106     pub MATCH_BOOL,
107     style,
108     "a match on a boolean expression instead of an `if..else` block"
109 }
110
111 /// **What it does:** Checks for overlapping match arms.
112 ///
113 /// **Why is this bad?** It is likely to be an error and if not, makes the code
114 /// less obvious.
115 ///
116 /// **Known problems:** None.
117 ///
118 /// **Example:**
119 /// ```rust
120 /// let x = 5;
121 /// match x {
122 ///     1 ... 10 => println!("1 ... 10"),
123 ///     5 ... 15 => println!("5 ... 15"),
124 ///     _ => (),
125 /// }
126 /// ```
127 declare_clippy_lint! {
128     pub MATCH_OVERLAPPING_ARM,
129     style,
130     "a match with overlapping arms"
131 }
132
133 /// **What it does:** Checks for arm which matches all errors with `Err(_)`
134 /// and take drastic actions like `panic!`.
135 ///
136 /// **Why is this bad?** It is generally a bad practice, just like
137 /// catching all exceptions in java with `catch(Exception)`
138 ///
139 /// **Known problems:** None.
140 ///
141 /// **Example:**
142 /// ```rust
143 /// let x : Result(i32, &str) = Ok(3);
144 /// match x {
145 ///     Ok(_) => println!("ok"),
146 ///     Err(_) => panic!("err"),
147 /// }
148 /// ```
149 declare_clippy_lint! {
150     pub MATCH_WILD_ERR_ARM,
151     style,
152     "a match with `Err(_)` arm and take drastic actions"
153 }
154
155 /// **What it does:** Checks for match which is used to add a reference to an
156 /// `Option` value.
157 ///
158 /// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
159 ///
160 /// **Known problems:** None.
161 ///
162 /// **Example:**
163 /// ```rust
164 /// let x: Option<()> = None;
165 /// let r: Option<&()> = match x {
166 ///   None => None,
167 ///   Some(ref v) => Some(v),
168 /// };
169 /// ```
170 declare_clippy_lint! {
171     pub MATCH_AS_REF,
172     complexity,
173     "a match on an Option value instead of using `as_ref()` or `as_mut`"
174 }
175
176 #[allow(missing_copy_implementations)]
177 pub struct MatchPass;
178
179 impl LintPass for MatchPass {
180     fn get_lints(&self) -> LintArray {
181         lint_array!(
182             SINGLE_MATCH,
183             MATCH_REF_PATS,
184             MATCH_BOOL,
185             SINGLE_MATCH_ELSE,
186             MATCH_OVERLAPPING_ARM,
187             MATCH_WILD_ERR_ARM,
188             MATCH_AS_REF
189         )
190     }
191 }
192
193 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchPass {
194     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
195         if in_external_macro(cx.sess(), expr.span) {
196             return;
197         }
198         if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.node {
199             check_single_match(cx, ex, arms, expr);
200             check_match_bool(cx, ex, arms, expr);
201             check_overlapping_arms(cx, ex, arms);
202             check_wild_err_arm(cx, ex, arms);
203             check_match_as_ref(cx, ex, arms, expr);
204         }
205         if let ExprKind::Match(ref ex, ref arms, _) = expr.node {
206             check_match_ref_pats(cx, ex, arms, expr);
207         }
208     }
209 }
210
211 #[rustfmt::skip]
212 fn check_single_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
213     if arms.len() == 2 &&
214       arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
215       arms[1].pats.len() == 1 && arms[1].guard.is_none() {
216         let els = remove_blocks(&arms[1].body);
217         let els = if is_unit_expr(els) {
218             None
219         } else if let ExprKind::Block(_, _) = els.node {
220             // matches with blocks that contain statements are prettier as `if let + else`
221             Some(els)
222         } else {
223             // allow match arms with just expressions
224             return;
225         };
226         let ty = cx.tables.expr_ty(ex);
227         if ty.sty != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.id) {
228             check_single_match_single_pattern(cx, ex, arms, expr, els);
229             check_single_match_opt_like(cx, ex, arms, expr, ty, els);
230         }
231     }
232 }
233
234 fn check_single_match_single_pattern(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) {
235     if is_wild(&arms[1].pats[0]) {
236         report_single_match_single_pattern(cx, ex, arms, expr, els);
237     }
238 }
239
240 fn report_single_match_single_pattern(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) {
241     let lint = if els.is_some() {
242         SINGLE_MATCH_ELSE
243     } else {
244         SINGLE_MATCH
245     };
246     let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
247     span_lint_and_sugg(
248         cx,
249         lint,
250         expr.span,
251         "you seem to be trying to use match for destructuring a single pattern. Consider using `if \
252          let`",
253         "try this",
254         format!(
255             "if let {} = {} {}{}",
256             snippet(cx, arms[0].pats[0].span, ".."),
257             snippet(cx, ex.span, ".."),
258             expr_block(cx, &arms[0].body, None, ".."),
259             els_str
260         ),
261     );
262 }
263
264 fn check_single_match_opt_like(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr, ty: Ty<'_>, els: Option<&Expr>) {
265     // list of candidate Enums we know will never get any more members
266     let candidates = &[
267         (&paths::COW, "Borrowed"),
268         (&paths::COW, "Cow::Borrowed"),
269         (&paths::COW, "Cow::Owned"),
270         (&paths::COW, "Owned"),
271         (&paths::OPTION, "None"),
272         (&paths::RESULT, "Err"),
273         (&paths::RESULT, "Ok"),
274     ];
275
276     let path = match arms[1].pats[0].node {
277         PatKind::TupleStruct(ref path, ref inner, _) => {
278             // contains any non wildcard patterns? e.g. Err(err)
279             if !inner.iter().all(is_wild) {
280                 return;
281             }
282             print::to_string(print::NO_ANN, |s| s.print_qpath(path, false))
283         },
284         PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None) => ident.to_string(),
285         PatKind::Path(ref path) => print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
286         _ => return,
287     };
288
289     for &(ty_path, pat_path) in candidates {
290         if path == *pat_path && match_type(cx, ty, ty_path) {
291             report_single_match_single_pattern(cx, ex, arms, expr, els);
292         }
293     }
294 }
295
296 fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
297     // type of expression == bool
298     if cx.tables.expr_ty(ex).sty == ty::Bool {
299         span_lint_and_then(
300             cx,
301             MATCH_BOOL,
302             expr.span,
303             "you seem to be trying to match on a boolean expression",
304             move |db| {
305                 if arms.len() == 2 && arms[0].pats.len() == 1 {
306                     // no guards
307                     let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
308                         if let ExprKind::Lit(ref lit) = arm_bool.node {
309                             match lit.node {
310                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
311                                 LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
312                                 _ => None,
313                             }
314                         } else {
315                             None
316                         }
317                     } else {
318                         None
319                     };
320
321                     if let Some((true_expr, false_expr)) = exprs {
322                         let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
323                             (false, false) => Some(format!(
324                                 "if {} {} else {}",
325                                 snippet(cx, ex.span, "b"),
326                                 expr_block(cx, true_expr, None, ".."),
327                                 expr_block(cx, false_expr, None, "..")
328                             )),
329                             (false, true) => Some(format!(
330                                 "if {} {}",
331                                 snippet(cx, ex.span, "b"),
332                                 expr_block(cx, true_expr, None, "..")
333                             )),
334                             (true, false) => {
335                                 let test = Sugg::hir(cx, ex, "..");
336                                 Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
337                             },
338                             (true, true) => None,
339                         };
340
341                         if let Some(sugg) = sugg {
342                             db.span_suggestion(expr.span, "consider using an if/else expression", sugg);
343                         }
344                     }
345                 }
346             },
347         );
348     }
349 }
350
351 fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr, arms: &'tcx [Arm]) {
352     if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
353         let ranges = all_ranges(cx, arms);
354         let type_ranges = type_ranges(&ranges);
355         if !type_ranges.is_empty() {
356             if let Some((start, end)) = overlapping(&type_ranges) {
357                 span_note_and_lint(
358                     cx,
359                     MATCH_OVERLAPPING_ARM,
360                     start.span,
361                     "some ranges overlap",
362                     end.span,
363                     "overlaps with this",
364                 );
365             }
366         }
367     }
368 }
369
370 fn is_wild(pat: &impl std::ops::Deref<Target = Pat>) -> bool {
371     match pat.node {
372         PatKind::Wild => true,
373         _ => false,
374     }
375 }
376
377 fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm]) {
378     let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex));
379     if match_type(cx, ex_ty, &paths::RESULT) {
380         for arm in arms {
381             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pats[0].node {
382                 let path_str = print::to_string(print::NO_ANN, |s| s.print_qpath(path, false));
383                 if_chain! {
384                     if path_str == "Err";
385                     if inner.iter().any(is_wild);
386                     if let ExprKind::Block(ref block, _) = arm.body.node;
387                     if is_panic_block(block);
388                     then {
389                         // `Err(_)` arm with `panic!` found
390                         span_note_and_lint(cx,
391                                            MATCH_WILD_ERR_ARM,
392                                            arm.pats[0].span,
393                                            "Err(_) will match all errors, maybe not a good idea",
394                                            arm.pats[0].span,
395                                            "to remove this warning, match each error separately \
396                                             or use unreachable macro");
397                     }
398                 }
399             }
400         }
401     }
402 }
403
404 // If the block contains only a `panic!` macro (as expression or statement)
405 fn is_panic_block(block: &Block) -> bool {
406     match (&block.expr, block.stmts.len(), block.stmts.first()) {
407         (&Some(ref exp), 0, _) => {
408             is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
409         },
410         (&None, 1, Some(stmt)) => {
411             is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
412         },
413         _ => false,
414     }
415 }
416
417 fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
418     if has_only_ref_pats(arms) {
419         let mut suggs = Vec::new();
420         let (title, msg) = if let ExprKind::AddrOf(Mutability::MutImmutable, ref inner) = ex.node {
421             suggs.push((ex.span, Sugg::hir(cx, inner, "..").to_string()));
422             (
423                 "you don't need to add `&` to both the expression and the patterns",
424                 "try",
425             )
426         } else {
427             suggs.push((ex.span, Sugg::hir(cx, ex, "..").deref().to_string()));
428             (
429                 "you don't need to add `&` to all patterns",
430                 "instead of prefixing all patterns with `&`, you can dereference the expression",
431             )
432         };
433
434         suggs.extend(arms.iter().flat_map(|a| &a.pats).filter_map(|p| {
435             if let PatKind::Ref(ref refp, _) = p.node {
436                 Some((p.span, snippet(cx, refp.span, "..").to_string()))
437             } else {
438                 None
439             }
440         }));
441
442         span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |db| {
443             multispan_sugg(db, msg.to_owned(), suggs);
444         });
445     }
446 }
447
448 fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
449     if arms.len() == 2 &&
450         arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
451         arms[1].pats.len() == 1 && arms[1].guard.is_none() {
452         let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
453             is_ref_some_arm(&arms[1])
454         } else if is_none_arm(&arms[1]) {
455             is_ref_some_arm(&arms[0])
456         } else {
457             None
458         };
459         if let Some(rb) = arm_ref {
460             let suggestion = if rb == BindingAnnotation::Ref { "as_ref" } else { "as_mut" };
461             span_lint_and_sugg(
462                 cx,
463                 MATCH_AS_REF,
464                 expr.span,
465                 &format!("use {}() instead", suggestion),
466                 "try this",
467                 format!("{}.{}()", snippet(cx, ex.span, "_"), suggestion)
468             )
469         }
470     }
471 }
472
473 /// Get all arms that are unbounded `PatRange`s.
474 fn all_ranges<'a, 'tcx>(
475     cx: &LateContext<'a, 'tcx>,
476     arms: &'tcx [Arm],
477 ) -> Vec<SpannedRange<Constant>> {
478     arms.iter()
479         .flat_map(|arm| {
480             if let Arm {
481                 ref pats,
482                 guard: None,
483                 ..
484             } = *arm
485             {
486                 pats.iter()
487             } else {
488                 [].iter()
489             }.filter_map(|pat| {
490                 if let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node {
491                     let lhs = constant(cx, cx.tables, lhs)?.0;
492                     let rhs = constant(cx, cx.tables, rhs)?.0;
493                     let rhs = match *range_end {
494                         RangeEnd::Included => Bound::Included(rhs),
495                         RangeEnd::Excluded => Bound::Excluded(rhs),
496                     };
497                     return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
498                 }
499
500                 if let PatKind::Lit(ref value) = pat.node {
501                     let value = constant(cx, cx.tables, value)?.0;
502                     return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
503                 }
504
505                 None
506             })
507         })
508         .collect()
509 }
510
511 #[derive(Debug, Eq, PartialEq)]
512 pub struct SpannedRange<T> {
513     pub span: Span,
514     pub node: (T, Bound<T>),
515 }
516
517 type TypedRanges = Vec<SpannedRange<u128>>;
518
519 /// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
520 /// and other types than
521 /// `Uint` and `Int` probably don't make sense.
522 fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
523     ranges
524         .iter()
525         .filter_map(|range| match range.node {
526             (
527                 Constant::Int(start),
528                 Bound::Included(Constant::Int(end)),
529             ) => Some(SpannedRange {
530                 span: range.span,
531                 node: (start, Bound::Included(end)),
532             }),
533             (
534                 Constant::Int(start),
535                 Bound::Excluded(Constant::Int(end)),
536             ) => Some(SpannedRange {
537                 span: range.span,
538                 node: (start, Bound::Excluded(end)),
539             }),
540             (
541                 Constant::Int(start),
542                 Bound::Unbounded,
543             ) => Some(SpannedRange {
544                 span: range.span,
545                 node: (start, Bound::Unbounded),
546             }),
547             _ => None,
548         })
549         .collect()
550 }
551
552 fn is_unit_expr(expr: &Expr) -> bool {
553     match expr.node {
554         ExprKind::Tup(ref v) if v.is_empty() => true,
555         ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
556         _ => false,
557     }
558 }
559
560 // Checks if arm has the form `None => None`
561 fn is_none_arm(arm: &Arm) -> bool {
562     match arm.pats[0].node {
563         PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
564         _ => false,
565     }
566 }
567
568 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
569 fn is_ref_some_arm(arm: &Arm) -> Option<BindingAnnotation> {
570     if_chain! {
571         if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pats[0].node;
572         if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
573         if let PatKind::Binding(rb, _, ident, _) = pats[0].node;
574         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
575         if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).node;
576         if let ExprKind::Path(ref some_path) = e.node;
577         if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
578         if let ExprKind::Path(ref qpath) = args[0].node;
579         if let &QPath::Resolved(_, ref path2) = qpath;
580         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
581         then {
582             return Some(rb)
583         }
584     }
585     None
586 }
587
588 fn has_only_ref_pats(arms: &[Arm]) -> bool {
589     let mapped = arms.iter()
590         .flat_map(|a| &a.pats)
591         .map(|p| {
592             match p.node {
593                 PatKind::Ref(..) => Some(true), // &-patterns
594                 PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
595                 _ => None,                      // any other pattern is not fine
596             }
597         })
598         .collect::<Option<Vec<bool>>>();
599     // look for Some(v) where there's at least one true element
600     mapped.map_or(false, |v| v.iter().any(|el| *el))
601 }
602
603 pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
604 where
605     T: Copy + Ord,
606 {
607     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
608     enum Kind<'a, T: 'a> {
609         Start(T, &'a SpannedRange<T>),
610         End(Bound<T>, &'a SpannedRange<T>),
611     }
612
613     impl<'a, T: Copy> Kind<'a, T> {
614         fn range(&self) -> &'a SpannedRange<T> {
615             match *self {
616                 Kind::Start(_, r) | Kind::End(_, r) => r,
617             }
618         }
619
620         fn value(self) -> Bound<T> {
621             match self {
622                 Kind::Start(t, _) => Bound::Included(t),
623                 Kind::End(t, _) => t,
624             }
625         }
626     }
627
628     impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
629         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
630             Some(self.cmp(other))
631         }
632     }
633
634     impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
635         fn cmp(&self, other: &Self) -> Ordering {
636             match (self.value(), other.value()) {
637                 (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
638                 // Range patterns cannot be unbounded (yet)
639                 (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
640                 (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
641                     Ordering::Equal => Ordering::Greater,
642                     other => other,
643                 },
644                 (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
645                     Ordering::Equal => Ordering::Less,
646                     other => other,
647                 },
648             }
649         }
650     }
651
652     let mut values = Vec::with_capacity(2 * ranges.len());
653
654     for r in ranges {
655         values.push(Kind::Start(r.node.0, r));
656         values.push(Kind::End(r.node.1, r));
657     }
658
659     values.sort();
660
661     for (a, b) in values.iter().zip(values.iter().skip(1)) {
662         match (a, b) {
663             (&Kind::Start(_, ra), &Kind::End(_, rb)) => if ra.node != rb.node {
664                 return Some((ra, rb));
665             },
666             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
667             _ => return Some((a.range(), b.range())),
668         }
669     }
670
671     None
672 }