Skip to content

in which parentheses are suggested for should-have-been-tuple-patterns #48527

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3318,7 +3318,7 @@ impl<'a> Parser<'a> {
mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
// Parse: `for <src_pat> in <src_expr> <src_loop_block>`

let pat = self.parse_pat()?;
let pat = self.parse_top_level_pat()?;
if !self.eat_keyword(keywords::In) {
let in_span = self.prev_span.between(self.span);
let mut err = self.sess.span_diagnostic
Expand Down Expand Up @@ -3528,7 +3528,7 @@ impl<'a> Parser<'a> {
fn parse_pats(&mut self) -> PResult<'a, Vec<P<Pat>>> {
let mut pats = Vec::new();
loop {
pats.push(self.parse_pat()?);
pats.push(self.parse_top_level_pat()?);

if self.token == token::OrOr {
let mut err = self.struct_span_err(self.span,
Expand All @@ -3554,7 +3554,12 @@ impl<'a> Parser<'a> {
// Trailing commas are significant because (p) and (p,) are different patterns.
fn parse_parenthesized_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
self.expect(&token::OpenDelim(token::Paren))?;
let result = self.parse_pat_list()?;
self.expect(&token::CloseDelim(token::Paren))?;
Ok(result)
}

fn parse_pat_list(&mut self) -> PResult<'a, (Vec<P<Pat>>, Option<usize>, bool)> {
let mut fields = Vec::new();
let mut ddpos = None;
let mut trailing_comma = false;
Expand Down Expand Up @@ -3584,8 +3589,6 @@ impl<'a> Parser<'a> {
self.span_err(self.prev_span, "trailing comma is not permitted after `..`");
}

self.expect(&token::CloseDelim(token::Paren))?;

Ok((fields, ddpos, trailing_comma))
}

Expand Down Expand Up @@ -3767,6 +3770,37 @@ impl<'a> Parser<'a> {
}))
}

/// A wrapper around `parse_pat` with some special error handling for the
/// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contast
/// to subpatterns within such).
pub fn parse_top_level_pat(&mut self) -> PResult<'a, P<Pat>> {
let pat = self.parse_pat()?;
if self.token == token::Comma {
// An unexpected comma after a top-level pattern is a clue that the
// user (perhaps more accustomed to some other language) forgot the
// parentheses in what should have been a tuple pattern; return a
// suggestion-enhanced error here rather than choking on the comma
// later.
let comma_span = self.span;
self.bump();
if let Err(mut err) = self.parse_pat_list() {
// We didn't expect this to work anyway; we just wanted
// to advance to the end of the comma-sequence so we know
// the span to suggest parenthesizing
err.cancel();
}
let seq_span = pat.span.to(self.prev_span);
let mut err = self.struct_span_err(comma_span,
"unexpected `,` in pattern");
if let Ok(seq_snippet) = self.sess.codemap().span_to_snippet(seq_span) {
err.span_suggestion(seq_span, "try adding parentheses",
format!("({})", seq_snippet));
}
return Err(err);
}
Ok(pat)
}

/// Parse a pattern.
pub fn parse_pat(&mut self) -> PResult<'a, P<Pat>> {
maybe_whole!(self, NtPat, |x| x);
Expand Down Expand Up @@ -3969,7 +4003,7 @@ impl<'a> Parser<'a> {
/// Parse a local variable declaration
fn parse_local(&mut self, attrs: ThinVec<Attribute>) -> PResult<'a, P<Local>> {
let lo = self.prev_span;
let pat = self.parse_pat()?;
let pat = self.parse_top_level_pat()?;

let (err, ty) = if self.eat(&token::Colon) {
// Save the state of the parser before parsing type normally, in case there is a `:`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(unused)]

#[derive(Copy, Clone)]
enum Nucleotide {
Adenine,
Thymine,
Cytosine,
Guanine
}

#[derive(Clone)]
struct Autosome;

#[derive(Clone)]
enum Allosome {
X(Vec<Nucleotide>),
Y(Vec<Nucleotide>)
}

impl Allosome {
fn is_x(&self) -> bool {
match *self {
Allosome::X(_) => true,
Allosome::Y(_) => false,
}
}
}

#[derive(Clone)]
struct Genome {
autosomes: [Autosome; 22],
allosomes: (Allosome, Allosome)
}

fn find_start_codon(strand: &[Nucleotide]) -> Option<usize> {
let mut reading_frame = strand.windows(3);
// (missing parentheses in `while let` tuple pattern)
while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
//~^ ERROR unexpected `,` in pattern
// ...
}
None
}

fn find_thr(strand: &[Nucleotide]) -> Option<usize> {
let mut reading_frame = strand.windows(3);
let mut i = 0;
// (missing parentheses in `if let` tuple pattern)
if let b1, b2, b3 = reading_frame.next().unwrap() {
//~^ ERROR unexpected `,` in pattern
// ...
}
None
}

fn is_thr(codon: (Nucleotide, Nucleotide, Nucleotide)) -> bool {
match codon {
// (missing parentheses in match arm tuple pattern)
Nucleotide::Adenine, Nucleotide::Cytosine, _ => true
//~^ ERROR unexpected `,` in pattern
_ => false
}
}

fn analyze_female_sex_chromosomes(women: &[Genome]) {
// (missing parentheses in `for` tuple pattern)
for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
//~^ ERROR unexpected `,` in pattern
// ...
}
}

fn analyze_male_sex_chromosomes(men: &[Genome]) {
// (missing parentheses in pattern with `@` binding)
for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
//~^ ERROR unexpected `,` in pattern
// ...
}
}

fn main() {
let genomes = Vec::new();
// (missing parentheses in `let` pattern)
let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
//~^ ERROR unexpected `,` in pattern
.partition(|g: &Genome| g.allosomes.0.is_x() && g.allosomes.1.is_x());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:48:17
|
LL | while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
| --^------- help: try adding parentheses: `(b1, b2, b3)`

error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:59:14
|
LL | if let b1, b2, b3 = reading_frame.next().unwrap() {
| --^------- help: try adding parentheses: `(b1, b2, b3)`

error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:69:28
|
LL | Nucleotide::Adenine, Nucleotide::Cytosine, _ => true
| -------------------^------------------------ help: try adding parentheses: `(Nucleotide::Adenine, Nucleotide::Cytosine, _)`

error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:77:10
|
LL | for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
| -^----------- help: try adding parentheses: `(x, _barr_body)`

error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:85:10
|
LL | for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
| -^------------------- help: try adding parentheses: `(x, y @ Allosome::Y(_))`

error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:94:14
|
LL | let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
| -----^---- help: try adding parentheses: `(women, men)`

error: aborting due to 6 previous errors