Skip to content

Commit dfdc369

Browse files
committed
review comments
1 parent d27683a commit dfdc369

File tree

4 files changed

+70
-48
lines changed

4 files changed

+70
-48
lines changed

src/libsyntax/parse/diagnostics.rs

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError};
1717
use log::{debug, trace};
1818
use std::mem;
1919

20+
const TURBOFISH: &'static str = "use the \"turbofish\" `::<...>` instead of `<...>` to specify \
21+
type arguments";
2022
/// Creates a placeholder argument.
2123
crate fn dummy_arg(ident: Ident) -> Param {
2224
let pat = P(Pat {
@@ -544,10 +546,20 @@ impl<'a> Parser<'a> {
544546

545547
/// Produces an error if comparison operators are chained (RFC #558).
546548
/// We only need to check the LHS, not the RHS, because all comparison ops have same
547-
/// precedence and are left-associative.
549+
/// precedence (see `fn precedence`) and are left-associative (see `fn fixity`).
548550
///
549551
/// This can also be hit if someone incorrectly writes `foo<bar>()` when they should have used
550-
/// the turbofish syntax. We attempt some heuristic recovery if that is the case.
552+
/// the turbofish (`foo::<bar>()`) syntax. We attempt some heuristic recovery if that is the
553+
/// case.
554+
///
555+
/// Keep in mind that given that `outer_op.is_comparison()` holds and comparison ops are left
556+
/// associative we can infer that we have:
557+
///
558+
/// outer_op
559+
/// / \
560+
/// inner_op r2
561+
/// / \
562+
/// l1 r1
551563
crate fn check_no_chained_comparison(
552564
&mut self,
553565
lhs: &Expr,
@@ -560,30 +572,36 @@ impl<'a> Parser<'a> {
560572
);
561573
match lhs.kind {
562574
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
563-
564575
// Respan to include both operators.
565576
let op_span = op.span.to(self.prev_span);
566577
let mut err = self.struct_span_err(
567578
op_span,
568579
"chained comparison operators require parentheses",
569580
);
581+
582+
let suggest = |err: &mut DiagnosticBuilder<'_>| {
583+
err.span_suggestion(
584+
op_span.shrink_to_lo(),
585+
TURBOFISH,
586+
"::".to_string(),
587+
Applicability::MaybeIncorrect,
588+
);
589+
};
590+
570591
if op.node == BinOpKind::Lt &&
571592
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
572593
*outer_op == AssocOp::Greater // even in a case like the following:
573594
{ // Foo<Bar<Baz<Qux, ()>>>
574-
let msg = "use `::<...>` instead of `<...>` if you meant to specify type \
575-
arguments";
576595
if *outer_op == AssocOp::Less {
577596
let snapshot = self.clone();
578597
self.bump();
579-
// So far we have parsed `foo<bar<`, consume the rest of the type params
580-
let modifiers = vec![
598+
// So far we have parsed `foo<bar<`, consume the rest of the type args.
599+
let modifiers = [
581600
(token::Lt, 1),
582601
(token::Gt, -1),
583602
(token::BinOp(token::Shr), -2),
584603
];
585-
let early_return = vec![token::Eof];
586-
self.consume_tts(1, &modifiers[..], &early_return[..]);
604+
self.consume_tts(1, &modifiers[..], &[]);
587605

588606
if !&[
589607
token::OpenDelim(token::Paren),
@@ -597,16 +615,11 @@ impl<'a> Parser<'a> {
597615
if token::ModSep == self.token.kind {
598616
// We have some certainty that this was a bad turbofish at this point.
599617
// `foo< bar >::`
600-
err.span_suggestion(
601-
op_span.shrink_to_lo(),
602-
msg,
603-
"::".to_string(),
604-
Applicability::MaybeIncorrect,
605-
);
618+
suggest(&mut err);
606619

607620
let snapshot = self.clone();
608-
609621
self.bump(); // `::`
622+
610623
// Consume the rest of the likely `foo<bar>::new()` or return at `foo<bar>`.
611624
match self.parse_expr() {
612625
Ok(_) => {
@@ -621,8 +634,8 @@ impl<'a> Parser<'a> {
621634
ThinVec::new(),
622635
)));
623636
}
624-
Err(mut err) => {
625-
err.cancel();
637+
Err(mut expr_err) => {
638+
expr_err.cancel();
626639
// Not entirely sure now, but we bubble the error up with the
627640
// suggestion.
628641
mem::replace(self, snapshot);
@@ -632,45 +645,39 @@ impl<'a> Parser<'a> {
632645
} else if token::OpenDelim(token::Paren) == self.token.kind {
633646
// We have high certainty that this was a bad turbofish at this point.
634647
// `foo< bar >(`
635-
err.span_suggestion(
636-
op_span.shrink_to_lo(),
637-
msg,
638-
"::".to_string(),
639-
Applicability::MaybeIncorrect,
640-
);
648+
suggest(&mut err);
641649

642650
let snapshot = self.clone();
643651
self.bump(); // `(`
644652

645653
// Consume the fn call arguments.
646-
let modifiers = vec![
654+
let modifiers = [
647655
(token::OpenDelim(token::Paren), 1),
648656
(token::CloseDelim(token::Paren), -1),
649657
];
650-
let early_return = vec![token::Eof];
651-
self.consume_tts(1, &modifiers[..], &early_return[..]);
658+
self.consume_tts(1, &modifiers[..], &[]);
652659

653-
if self.token.kind == token::Eof {
660+
return if self.token.kind == token::Eof {
654661
// Not entirely sure now, but we bubble the error up with the
655662
// suggestion.
656663
mem::replace(self, snapshot);
657-
return Err(err);
664+
Err(err)
658665
} else {
659666
// 99% certain that the suggestion is correct, continue parsing.
660667
err.emit();
661668
// FIXME: actually check that the two expressions in the binop are
662669
// paths and resynthesize new fn call expression instead of using
663670
// `ExprKind::Err` placeholder.
664-
return Ok(Some(self.mk_expr(
671+
Ok(Some(self.mk_expr(
665672
lhs.span.to(self.prev_span),
666673
ExprKind::Err,
667674
ThinVec::new(),
668-
)));
675+
)))
669676
}
670677
} else {
671678
// All we know is that this is `foo < bar >` and *nothing* else. Try to
672679
// be helpful, but don't attempt to recover.
673-
err.help(msg);
680+
err.help(TURBOFISH);
674681
err.help("or use `(...)` if you meant to specify fn arguments");
675682
// These cases cause too many knock-down errors, bail out (#61329).
676683
}
@@ -1459,15 +1466,15 @@ impl<'a> Parser<'a> {
14591466

14601467
fn consume_tts(
14611468
&mut self,
1462-
mut acc: i64,
1463-
modifier: &[(token::TokenKind, i64)], // Not using FxHasMap and FxHashSet due to
1469+
mut acc: i64, // `i64` because malformed code can have more closing delims than opening.
1470+
modifier: &[(token::TokenKind, i64)], // Not using `FxHashMap` and `FxHashSet` due to
14641471
early_return: &[token::TokenKind], // `token::TokenKind: !Eq + !Hash`.
14651472
) {
14661473
while acc > 0 {
1467-
if let Some((_, val)) = modifier.iter().filter(|(t, _)| *t == self.token.kind).next() {
1474+
if let Some((_, val)) = modifier.iter().find(|(t, _)| *t == self.token.kind) {
14681475
acc += *val;
14691476
}
1470-
if early_return.contains(&self.token.kind) {
1477+
if self.token.kind == token::Eof || early_return.contains(&self.token.kind) {
14711478
break;
14721479
}
14731480
self.bump();

src/test/ui/did_you_mean/issue-40396.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ error: chained comparison operators require parentheses
33
|
44
LL | (0..13).collect<Vec<i32>>();
55
| ^^^^^
6-
help: use `::<...>` instead of `<...>` if you meant to specify type arguments
6+
help: use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
77
|
88
LL | (0..13).collect::<Vec<i32>>();
99
| ^^
@@ -13,7 +13,7 @@ error: chained comparison operators require parentheses
1313
|
1414
LL | Vec<i32>::new();
1515
| ^^^^^
16-
help: use `::<...>` instead of `<...>` if you meant to specify type arguments
16+
help: use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
1717
|
1818
LL | Vec::<i32>::new();
1919
| ^^
@@ -23,7 +23,7 @@ error: chained comparison operators require parentheses
2323
|
2424
LL | (0..13).collect<Vec<i32>();
2525
| ^^^^^
26-
help: use `::<...>` instead of `<...>` if you meant to specify type arguments
26+
help: use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
2727
|
2828
LL | (0..13).collect::<Vec<i32>();
2929
| ^^

src/test/ui/parser/require-parens-for-chained-comparison.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ struct X;
33

44
fn main() {
55
false == false == false;
6-
//~^ ERROR: chained comparison operators require parentheses
6+
//~^ ERROR chained comparison operators require parentheses
77

88
false == 0 < 2;
9-
//~^ ERROR: chained comparison operators require parentheses
10-
//~| ERROR: mismatched types
11-
//~| ERROR: mismatched types
9+
//~^ ERROR chained comparison operators require parentheses
10+
//~| ERROR mismatched types
11+
//~| ERROR mismatched types
1212

1313
f<X>();
1414
//~^ ERROR chained comparison operators require parentheses
15-
//~| HELP: use `::<...>` instead of `<...>`
15+
//~| HELP use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
1616

1717
f<Result<Option<X>, Option<Option<X>>>(1, 2);
1818
//~^ ERROR chained comparison operators require parentheses
19-
//~| HELP: use `::<...>` instead of `<...>`
19+
//~| HELP use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
20+
21+
use std::convert::identity;
22+
let _ = identity<u8>;
23+
//~^ ERROR chained comparison operators require parentheses
24+
//~| HELP use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
25+
//~| HELP or use `(...)` if you meant to specify fn arguments
2026
}

src/test/ui/parser/require-parens-for-chained-comparison.stderr

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ error: chained comparison operators require parentheses
1515
|
1616
LL | f<X>();
1717
| ^^^
18-
help: use `::<...>` instead of `<...>` if you meant to specify type arguments
18+
help: use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
1919
|
2020
LL | f::<X>();
2121
| ^^
@@ -25,11 +25,20 @@ error: chained comparison operators require parentheses
2525
|
2626
LL | f<Result<Option<X>, Option<Option<X>>>(1, 2);
2727
| ^^^^^^^^
28-
help: use `::<...>` instead of `<...>` if you meant to specify type arguments
28+
help: use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
2929
|
3030
LL | f::<Result<Option<X>, Option<Option<X>>>(1, 2);
3131
| ^^
3232

33+
error: chained comparison operators require parentheses
34+
--> $DIR/require-parens-for-chained-comparison.rs:22:21
35+
|
36+
LL | let _ = identity<u8>;
37+
| ^^^^
38+
|
39+
= help: use the "turbofish" `::<...>` instead of `<...>` to specify type arguments
40+
= help: or use `(...)` if you meant to specify fn arguments
41+
3342
error[E0308]: mismatched types
3443
--> $DIR/require-parens-for-chained-comparison.rs:8:14
3544
|
@@ -48,6 +57,6 @@ LL | false == 0 < 2;
4857
= note: expected type `bool`
4958
found type `{integer}`
5059

51-
error: aborting due to 6 previous errors
60+
error: aborting due to 7 previous errors
5261

5362
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)