Skip to content

Commit 3fb76f4

Browse files
committed
inclusive range syntax lint (.....=)
Our implementation ends up changing the `PatKind::Range` variant in the AST to take a `Spanned<RangeEnd>` instead of just a `RangeEnd`, because the alternative would be to try to infer the span of the range operator from the spans of the start and end subexpressions, which is both hideous and nontrivial to get right (whereas getting the change to the AST right was a simple game of type tennis). This is concerning rust-lang#51043.
1 parent 0577155 commit 3fb76f4

13 files changed

+154
-22
lines changed

src/librustc/hir/lowering.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3356,7 +3356,7 @@ impl<'a> LoweringContext<'a> {
33563356
PatKind::Ref(ref inner, mutbl) => {
33573357
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
33583358
}
3359-
PatKind::Range(ref e1, ref e2, ref end) => hir::PatKind::Range(
3359+
PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => hir::PatKind::Range(
33603360
P(self.lower_expr(e1)),
33613361
P(self.lower_expr(e2)),
33623362
self.lower_range_end(end),

src/librustc_lint/builtin.rs

+38
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use std::collections::HashSet;
4343

4444
use syntax::ast;
4545
use syntax::attr;
46+
use syntax::codemap::Spanned;
4647
use syntax::edition::Edition;
4748
use syntax::feature_gate::{AttributeGate, AttributeType, Stability, deprecated_attributes};
4849
use syntax_pos::{BytePos, Span, SyntaxContext};
@@ -1669,6 +1670,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
16691670
}
16701671
}
16711672

1673+
16721674
/// Does nothing as a lint pass, but registers some `Lint`s
16731675
/// which are used by other parts of the compiler.
16741676
#[derive(Copy, Clone)]
@@ -1701,3 +1703,39 @@ impl LintPass for SoftLints {
17011703
)
17021704
}
17031705
}
1706+
1707+
1708+
declare_lint! {
1709+
pub ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
1710+
Allow,
1711+
"`...` range patterns are deprecated"
1712+
}
1713+
1714+
1715+
pub struct EllipsisInclusiveRangePatterns;
1716+
1717+
impl LintPass for EllipsisInclusiveRangePatterns {
1718+
fn get_lints(&self) -> LintArray {
1719+
lint_array!(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS)
1720+
}
1721+
}
1722+
1723+
impl EarlyLintPass for EllipsisInclusiveRangePatterns {
1724+
fn check_pat(&mut self, cx: &EarlyContext, pat: &ast::Pat) {
1725+
use self::ast::{PatKind, RangeEnd, RangeSyntax};
1726+
1727+
if let PatKind::Range(
1728+
_, _, Spanned { span, node: RangeEnd::Included(RangeSyntax::DotDotDot) }
1729+
) = pat.node {
1730+
let msg = "`...` range patterns are deprecated";
1731+
let mut err = cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, span, msg);
1732+
err.span_suggestion_short_with_applicability(
1733+
span, "use `..=` for an inclusive range", "..=".to_owned(),
1734+
// FIXME: outstanding problem with precedence in ref patterns:
1735+
// https://github.com/rust-lang/rust/issues/51043#issuecomment-392252285
1736+
Applicability::MaybeIncorrect
1737+
);
1738+
err.emit()
1739+
}
1740+
}
1741+
}

src/librustc_lint/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
111111
AnonymousParameters,
112112
UnusedDocComment,
113113
BadRepr,
114+
EllipsisInclusiveRangePatterns,
114115
);
115116

116117
add_early_builtin_with_new!(sess,
@@ -188,7 +189,8 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
188189
"rust_2018_idioms",
189190
BARE_TRAIT_OBJECTS,
190191
UNREACHABLE_PUB,
191-
UNUSED_EXTERN_CRATES);
192+
UNUSED_EXTERN_CRATES,
193+
ELLIPSIS_INCLUSIVE_RANGE_PATTERNS);
192194

193195
// Guidelines for creating a future incompatibility lint:
194196
//

src/libsyntax/ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ pub enum PatKind {
617617
/// A literal
618618
Lit(P<Expr>),
619619
/// A range pattern, e.g. `1...2`, `1..=2` or `1..2`
620-
Range(P<Expr>, P<Expr>, RangeEnd),
620+
Range(P<Expr>, P<Expr>, Spanned<RangeEnd>),
621621
/// `[a, b, ..i, y, z]` is represented as:
622622
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
623623
Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),

src/libsyntax/feature_gate.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use self::AttributeGate::*;
2828
use rustc_target::spec::abi::Abi;
2929
use ast::{self, NodeId, PatKind, RangeEnd};
3030
use attr;
31+
use codemap::Spanned;
3132
use edition::{ALL_EDITIONS, Edition};
3233
use syntax_pos::{Span, DUMMY_SP};
3334
use errors::{DiagnosticBuilder, Handler, FatalError};
@@ -1752,7 +1753,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
17521753
pattern.span,
17531754
"box pattern syntax is experimental");
17541755
}
1755-
PatKind::Range(_, _, RangeEnd::Excluded) => {
1756+
PatKind::Range(_, _, Spanned { node: RangeEnd::Excluded, .. }) => {
17561757
gate_feature_post!(&self, exclusive_range_pattern, pattern.span,
17571758
"exclusive range pattern syntax is experimental");
17581759
}

src/libsyntax/fold.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1137,10 +1137,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
11371137
}
11381138
PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
11391139
PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
1140-
PatKind::Range(e1, e2, end) => {
1140+
PatKind::Range(e1, e2, Spanned { span, node: end }) => {
11411141
PatKind::Range(folder.fold_expr(e1),
11421142
folder.fold_expr(e2),
1143-
folder.fold_range_end(end))
1143+
Spanned { span, node: folder.fold_range_end(end) })
11441144
},
11451145
PatKind::Slice(before, slice, after) => {
11461146
PatKind::Slice(before.move_map(|x| folder.fold_pat(x)),

src/libsyntax/parse/parser.rs

+21-12
Original file line numberDiff line numberDiff line change
@@ -4024,12 +4024,14 @@ impl<'a> Parser<'a> {
40244024
_ => panic!("can only parse `..`/`...`/`..=` for ranges \
40254025
(checked above)"),
40264026
};
4027+
let op_span = self.span;
40274028
// Parse range
40284029
let span = lo.to(self.prev_span);
40294030
let begin = self.mk_expr(span, ExprKind::Path(qself, path), ThinVec::new());
40304031
self.bump();
40314032
let end = self.parse_pat_range_end()?;
4032-
pat = PatKind::Range(begin, end, end_kind);
4033+
let op = Spanned { span: op_span, node: end_kind };
4034+
pat = PatKind::Range(begin, end, op);
40334035
}
40344036
token::OpenDelim(token::Brace) => {
40354037
if qself.is_some() {
@@ -4065,17 +4067,22 @@ impl<'a> Parser<'a> {
40654067
// Try to parse everything else as literal with optional minus
40664068
match self.parse_literal_maybe_minus() {
40674069
Ok(begin) => {
4068-
if self.eat(&token::DotDotDot) {
4070+
let op_span = self.span;
4071+
if self.check(&token::DotDot) || self.check(&token::DotDotEq) ||
4072+
self.check(&token::DotDotDot) {
4073+
let end_kind = if self.eat(&token::DotDotDot) {
4074+
RangeEnd::Included(RangeSyntax::DotDotDot)
4075+
} else if self.eat(&token::DotDotEq) {
4076+
RangeEnd::Included(RangeSyntax::DotDotEq)
4077+
} else if self.eat(&token::DotDot) {
4078+
RangeEnd::Excluded
4079+
} else {
4080+
panic!("impossible case: we already matched \
4081+
on a range-operator token")
4082+
};
40694083
let end = self.parse_pat_range_end()?;
4070-
pat = PatKind::Range(begin, end,
4071-
RangeEnd::Included(RangeSyntax::DotDotDot));
4072-
} else if self.eat(&token::DotDotEq) {
4073-
let end = self.parse_pat_range_end()?;
4074-
pat = PatKind::Range(begin, end,
4075-
RangeEnd::Included(RangeSyntax::DotDotEq));
4076-
} else if self.eat(&token::DotDot) {
4077-
let end = self.parse_pat_range_end()?;
4078-
pat = PatKind::Range(begin, end, RangeEnd::Excluded);
4084+
let op = Spanned { span: op_span, node: end_kind };
4085+
pat = PatKind::Range(begin, end, op);
40794086
} else {
40804087
pat = PatKind::Lit(begin);
40814088
}
@@ -4096,7 +4103,9 @@ impl<'a> Parser<'a> {
40964103

40974104
if !allow_range_pat {
40984105
match pat.node {
4099-
PatKind::Range(_, _, RangeEnd::Included(RangeSyntax::DotDotDot)) => {}
4106+
PatKind::Range(
4107+
_, _, Spanned { node: RangeEnd::Included(RangeSyntax::DotDotDot), .. }
4108+
) => {},
41004109
PatKind::Range(..) => {
41014110
let mut err = self.struct_span_err(
41024111
pat.span,

src/libsyntax/print/pprust.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use ast::{SelfKind, GenericBound, TraitBoundModifier};
1616
use ast::{Attribute, MacDelimiter, GenericArg};
1717
use util::parser::{self, AssocOp, Fixity};
1818
use attr;
19-
use codemap::{self, CodeMap};
19+
use codemap::{self, CodeMap, Spanned};
2020
use syntax_pos::{self, BytePos};
2121
use syntax_pos::hygiene::{Mark, SyntaxContext};
2222
use parse::token::{self, BinOpToken, Token};
@@ -2624,7 +2624,7 @@ impl<'a> State<'a> {
26242624
self.print_pat(inner)?;
26252625
}
26262626
PatKind::Lit(ref e) => self.print_expr(&**e)?,
2627-
PatKind::Range(ref begin, ref end, ref end_kind) => {
2627+
PatKind::Range(ref begin, ref end, Spanned { node: ref end_kind, .. }) => {
26282628
self.print_expr(begin)?;
26292629
self.s.space()?;
26302630
match *end_kind {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-pass
12+
// run-rustfix
13+
14+
#![warn(ellipsis_inclusive_range_patterns)]
15+
16+
fn main() {
17+
let despondency = 2;
18+
match despondency {
19+
1..=2 => {}
20+
//~^ WARN `...` range patterns are deprecated
21+
_ => {}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-pass
12+
// run-rustfix
13+
14+
#![warn(ellipsis_inclusive_range_patterns)]
15+
16+
fn main() {
17+
let despondency = 2;
18+
match despondency {
19+
1...2 => {}
20+
//~^ WARN `...` range patterns are deprecated
21+
_ => {}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
warning: `...` range patterns are deprecated
2+
--> $DIR/inclusive-range-pattern-syntax.rs:19:10
3+
|
4+
LL | 1...2 => {}
5+
| ^^^ help: use `..=` for an inclusive range
6+
|
7+
note: lint level defined here
8+
--> $DIR/inclusive-range-pattern-syntax.rs:14:9
9+
|
10+
LL | #![warn(ellipsis_inclusive_range_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+

src/test/ui/range-inclusive-pattern-precedence.rs

+6
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616
// older ... syntax is still allowed as a stability guarantee.
1717

1818
#![feature(box_patterns)]
19+
#![warn(ellipsis_inclusive_range_patterns)]
20+
1921

2022
pub fn main() {
2123
match &12 {
2224
&0...9 => {}
25+
//~^ WARN `...` range patterns are deprecated
26+
//~| HELP use `..=` for an inclusive range
2327
&10..=15 => {}
2428
//~^ ERROR the range pattern here has ambiguous interpretation
2529
//~^^ HELP add parentheses to clarify the precedence
@@ -29,6 +33,8 @@ pub fn main() {
2933

3034
match Box::new(12) {
3135
box 0...9 => {}
36+
//~^ WARN `...` range patterns are deprecated
37+
//~| HELP use `..=` for an inclusive range
3238
box 10..=15 => {}
3339
//~^ ERROR the range pattern here has ambiguous interpretation
3440
//~^^ HELP add parentheses to clarify the precedence
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
error: the range pattern here has ambiguous interpretation
2-
--> $DIR/range-inclusive-pattern-precedence.rs:23:10
2+
--> $DIR/range-inclusive-pattern-precedence.rs:27:10
33
|
44
LL | &10..=15 => {}
55
| ^^^^^^^ help: add parentheses to clarify the precedence: `(10 ..=15)`
66

77
error: the range pattern here has ambiguous interpretation
8-
--> $DIR/range-inclusive-pattern-precedence.rs:32:13
8+
--> $DIR/range-inclusive-pattern-precedence.rs:38:13
99
|
1010
LL | box 10..=15 => {}
1111
| ^^^^^^^ help: add parentheses to clarify the precedence: `(10 ..=15)`
1212

13+
warning: `...` range patterns are deprecated
14+
--> $DIR/range-inclusive-pattern-precedence.rs:24:11
15+
|
16+
LL | &0...9 => {}
17+
| ^^^ help: use `..=` for an inclusive range
18+
|
19+
note: lint level defined here
20+
--> $DIR/range-inclusive-pattern-precedence.rs:19:9
21+
|
22+
LL | #![warn(ellipsis_inclusive_range_patterns)]
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
warning: `...` range patterns are deprecated
26+
--> $DIR/range-inclusive-pattern-precedence.rs:35:14
27+
|
28+
LL | box 0...9 => {}
29+
| ^^^ help: use `..=` for an inclusive range
30+
1331
error: aborting due to 2 previous errors
1432

0 commit comments

Comments
 (0)