about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-07-29 17:41:41 +0000
committerbors <bors@rust-lang.org>2014-07-29 17:41:41 +0000
commit6635fe7db4336ef31d2b372e7bfcce01d21853e0 (patch)
treedefdad9e214287bdf4071fcaac2bf886c94052c5 /src/libsyntax/parse
parentb92536ff2f611027b7bc0953c9850b42ed3ddf41 (diff)
parentb2eb88843d1b727551464beea3438e9f0159ebad (diff)
downloadrust-6635fe7db4336ef31d2b372e7bfcce01d21853e0.tar.gz
rust-6635fe7db4336ef31d2b372e7bfcce01d21853e0.zip
auto merge of #15989 : pcwalton/rust/borrowck-pattern-guards, r=pnkfelix
the CFG for match statements.

There were two bugs in issue #14684. One was simply that the borrow
check didn't know about the correct CFG for match statements: the
pattern must be a predecessor of the guard. This disallows the bad
behavior if there are bindings in the pattern. But it isn't enough to
prevent the memory safety problem, because of wildcards; thus, this
patch introduces a more restrictive rule, which disallows assignments
and mutable borrows inside guards outright.

I discussed this with Niko and we decided this was the best plan of
action.

This breaks code that performs mutable borrows in pattern guards. Most
commonly, the code looks like this:

    impl Foo {
        fn f(&mut self, ...) {}
        fn g(&mut self, ...) {
            match bar {
                Baz if self.f(...) => { ... }
                _ => { ... }
            }
        }
    }

Change this code to not use a guard. For example:

    impl Foo {
        fn f(&mut self, ...) {}
        fn g(&mut self, ...) {
            match bar {
                Baz => {
                    if self.f(...) {
                        ...
                    } else {
                        ...
                    }
                }
                _ => { ... }
            }
        }
    }

Sometimes this can result in code duplication, but often it illustrates
a hidden memory safety problem.

Closes #14684.

[breaking-change]

r? @pnkfelix
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/parser.rs391
1 files changed, 208 insertions, 183 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 0116518d537..878994369d0 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1954,19 +1954,6 @@ impl<'a> Parser<'a> {
             token::BINOP(token::OR) |  token::OROR => {
                 return self.parse_lambda_expr();
             },
-            _ if self.eat_keyword(keywords::Proc) => {
-                let decl = self.parse_proc_decl();
-                let body = self.parse_expr();
-                let fakeblock = P(ast::Block {
-                        view_items: Vec::new(),
-                        stmts: Vec::new(),
-                        expr: Some(body),
-                        id: ast::DUMMY_NODE_ID,
-                        rules: DefaultBlock,
-                        span: body.span,
-                    });
-                return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock));
-            },
             // FIXME #13626: Should be able to stick in
             // token::SELF_KEYWORD_NAME
             token::IDENT(id @ ast::Ident{
@@ -1978,48 +1965,6 @@ impl<'a> Parser<'a> {
                 ex = ExprPath(path);
                 hi = self.last_span.hi;
             }
-            _ if self.eat_keyword(keywords::If) => {
-                return self.parse_if_expr();
-            },
-            _ if self.eat_keyword(keywords::For) => {
-                return self.parse_for_expr(None);
-            },
-            _ if self.eat_keyword(keywords::While) => {
-                return self.parse_while_expr();
-            },
-            _ if Parser::token_is_lifetime(&self.token) => {
-                let lifetime = self.get_lifetime();
-                self.bump();
-                self.expect(&token::COLON);
-                if self.eat_keyword(keywords::For) {
-                    return self.parse_for_expr(Some(lifetime))
-                } else if self.eat_keyword(keywords::Loop) {
-                    return self.parse_loop_expr(Some(lifetime))
-                } else {
-                    self.fatal("expected `for` or `loop` after a label")
-                }
-            },
-            _ if self.eat_keyword(keywords::Loop) => {
-                return self.parse_loop_expr(None);
-            },
-            _ if self.eat_keyword(keywords::Continue) => {
-                let lo = self.span.lo;
-                let ex = if Parser::token_is_lifetime(&self.token) {
-                    let lifetime = self.get_lifetime();
-                    self.bump();
-                    ExprAgain(Some(lifetime))
-                } else {
-                    ExprAgain(None)
-                };
-                let hi = self.span.hi;
-                return self.mk_expr(lo, hi, ex);
-            },
-            _ if self.eat_keyword(keywords::Match) => {
-                return self.parse_match_expr();
-            },
-            _ if self.eat_keyword(keywords::Unsafe) => {
-                return self.parse_block_expr(lo, UnsafeBlock(ast::UserProvided));
-            },
             token::LBRACKET => {
                 self.bump();
 
@@ -2057,88 +2002,158 @@ impl<'a> Parser<'a> {
                 }
                 hi = self.last_span.hi;
             },
-            _ if self.eat_keyword(keywords::Return) => {
-                // RETURN expression
-                if can_begin_expr(&self.token) {
-                    let e = self.parse_expr();
-                    hi = e.span.hi;
-                    ex = ExprRet(Some(e));
-                } else { ex = ExprRet(None); }
-            },
-            _ if self.eat_keyword(keywords::Break) => {
-                // BREAK expression
+            _ => {
+                if self.eat_keyword(keywords::Proc) {
+                    let decl = self.parse_proc_decl();
+                    let body = self.parse_expr();
+                    let fakeblock = P(ast::Block {
+                            view_items: Vec::new(),
+                            stmts: Vec::new(),
+                            expr: Some(body),
+                            id: ast::DUMMY_NODE_ID,
+                            rules: DefaultBlock,
+                            span: body.span,
+                        });
+                    return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock));
+                }
+                if self.eat_keyword(keywords::If) {
+                    return self.parse_if_expr();
+                }
+                if self.eat_keyword(keywords::For) {
+                    return self.parse_for_expr(None);
+                }
+                if self.eat_keyword(keywords::While) {
+                    return self.parse_while_expr();
+                }
                 if Parser::token_is_lifetime(&self.token) {
                     let lifetime = self.get_lifetime();
                     self.bump();
-                    ex = ExprBreak(Some(lifetime));
-                } else {
-                    ex = ExprBreak(None);
+                    self.expect(&token::COLON);
+                    if self.eat_keyword(keywords::For) {
+                        return self.parse_for_expr(Some(lifetime))
+                    }
+                    if self.eat_keyword(keywords::Loop) {
+                        return self.parse_loop_expr(Some(lifetime))
+                    }
+                    self.fatal("expected `for` or `loop` after a label")
                 }
-                hi = self.span.hi;
-            },
-            _ if self.token == token::MOD_SEP ||
-                is_ident(&self.token) && !self.is_keyword(keywords::True) &&
-                !self.is_keyword(keywords::False) => {
-                let pth = self.parse_path(LifetimeAndTypesWithColons).path;
-
-                // `!`, as an operator, is prefix, so we know this isn't that
-                if self.token == token::NOT {
-                    // MACRO INVOCATION expression
-                    self.bump();
-
-                    let ket = token::close_delimiter_for(&self.token)
-                        .unwrap_or_else(|| self.fatal("expected open delimiter"));
-                    self.bump();
-
-                    let tts = self.parse_seq_to_end(&ket,
-                                                    seq_sep_none(),
-                                                    |p| p.parse_token_tree());
+                if self.eat_keyword(keywords::Loop) {
+                    return self.parse_loop_expr(None);
+                }
+                if self.eat_keyword(keywords::Continue) {
+                    let lo = self.span.lo;
+                    let ex = if Parser::token_is_lifetime(&self.token) {
+                        let lifetime = self.get_lifetime();
+                        self.bump();
+                        ExprAgain(Some(lifetime))
+                    } else {
+                        ExprAgain(None)
+                    };
                     let hi = self.span.hi;
+                    return self.mk_expr(lo, hi, ex);
+                }
+                if self.eat_keyword(keywords::Match) {
+                    return self.parse_match_expr();
+                }
+                if self.eat_keyword(keywords::Unsafe) {
+                    return self.parse_block_expr(
+                        lo,
+                        UnsafeBlock(ast::UserProvided));
+                }
+                if self.eat_keyword(keywords::Return) {
+                    // RETURN expression
+                    if can_begin_expr(&self.token) {
+                        let e = self.parse_expr();
+                        hi = e.span.hi;
+                        ex = ExprRet(Some(e));
+                    } else {
+                        ex = ExprRet(None);
+                    }
+                } else if self.eat_keyword(keywords::Break) {
+                    // BREAK expression
+                    if Parser::token_is_lifetime(&self.token) {
+                        let lifetime = self.get_lifetime();
+                        self.bump();
+                        ex = ExprBreak(Some(lifetime));
+                    } else {
+                        ex = ExprBreak(None);
+                    }
+                    hi = self.span.hi;
+                } else if self.token == token::MOD_SEP ||
+                        is_ident(&self.token) &&
+                        !self.is_keyword(keywords::True) &&
+                        !self.is_keyword(keywords::False) {
+                    let pth =
+                        self.parse_path(LifetimeAndTypesWithColons).path;
+
+                    // `!`, as an operator, is prefix, so we know this isn't that
+                    if self.token == token::NOT {
+                        // MACRO INVOCATION expression
+                        self.bump();
 
-                    return self.mk_mac_expr(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT));
-                } else if self.token == token::LBRACE {
-                    // This is a struct literal, unless we're prohibited from
-                    // parsing struct literals here.
-                    if self.restriction != RESTRICT_NO_STRUCT_LITERAL {
-                        // It's a struct literal.
+                        let ket = token::close_delimiter_for(&self.token)
+                            .unwrap_or_else(|| {
+                                self.fatal("expected open delimiter")
+                            });
                         self.bump();
-                        let mut fields = Vec::new();
-                        let mut base = None;
 
-                        while self.token != token::RBRACE {
-                            if self.eat(&token::DOTDOT) {
-                                base = Some(self.parse_expr());
-                                break;
+                        let tts = self.parse_seq_to_end(
+                            &ket,
+                            seq_sep_none(),
+                            |p| p.parse_token_tree());
+                        let hi = self.span.hi;
+
+                        return self.mk_mac_expr(lo,
+                                                hi,
+                                                MacInvocTT(pth,
+                                                           tts,
+                                                           EMPTY_CTXT));
+                    }
+                    if self.token == token::LBRACE {
+                        // This is a struct literal, unless we're prohibited
+                        // from parsing struct literals here.
+                        if self.restriction != RESTRICT_NO_STRUCT_LITERAL {
+                            // It's a struct literal.
+                            self.bump();
+                            let mut fields = Vec::new();
+                            let mut base = None;
+
+                            while self.token != token::RBRACE {
+                                if self.eat(&token::DOTDOT) {
+                                    base = Some(self.parse_expr());
+                                    break;
+                                }
+
+                                fields.push(self.parse_field());
+                                self.commit_expr(fields.last().unwrap().expr,
+                                                 &[token::COMMA],
+                                                 &[token::RBRACE]);
                             }
 
-                            fields.push(self.parse_field());
-                            self.commit_expr(fields.last().unwrap().expr,
-                                             &[token::COMMA], &[token::RBRACE]);
-                        }
+                            if fields.len() == 0 && base.is_none() {
+                                let last_span = self.last_span;
+                                self.span_err(last_span,
+                                              "structure literal must either \
+                                              have at least one field or use \
+                                              functional structure update \
+                                              syntax");
+                            }
 
-                        if fields.len() == 0 && base.is_none() {
-                            let last_span = self.last_span;
-                            self.span_err(last_span,
-                                          "structure literal must either have at \
-                                          least one field or use functional \
-                                          structure update syntax");
+                            hi = self.span.hi;
+                            self.expect(&token::RBRACE);
+                            ex = ExprStruct(pth, fields, base);
+                            return self.mk_expr(lo, hi, ex);
                         }
-
-                        hi = self.span.hi;
-                        self.expect(&token::RBRACE);
-                        ex = ExprStruct(pth, fields, base);
-                        return self.mk_expr(lo, hi, ex);
                     }
-                }
 
-            hi = pth.span.hi;
-            ex = ExprPath(pth);
-            },
-            _ => {
-                // other literal expression
-                let lit = self.parse_lit();
-                hi = lit.span.hi;
-                ex = ExprLit(box(GC) lit);
+                    hi = pth.span.hi;
+                    ex = ExprPath(pth);
+                } else {
+                    // other literal expression
+                    let lit = self.parse_lit();
+                    hi = lit.span.hi;
+                    ex = ExprLit(box(GC) lit);
+                }
             }
         }
 
@@ -2501,37 +2516,41 @@ impl<'a> Parser<'a> {
               }
             };
           }
-          token::IDENT(_, _) if self.is_keyword(keywords::Box) => {
-            self.bump();
+          token::IDENT(_, _) => {
+              if self.is_keyword(keywords::Box) {
+                self.bump();
 
-            // Check for a place: `box(PLACE) EXPR`.
-            if self.eat(&token::LPAREN) {
-                // Support `box() EXPR` as the default.
-                if !self.eat(&token::RPAREN) {
-                    let place = self.parse_expr();
-                    self.expect(&token::RPAREN);
-                    let subexpression = self.parse_prefix_expr();
-                    hi = subexpression.span.hi;
-                    ex = ExprBox(place, subexpression);
-                    return self.mk_expr(lo, hi, ex);
+                // Check for a place: `box(PLACE) EXPR`.
+                if self.eat(&token::LPAREN) {
+                    // Support `box() EXPR` as the default.
+                    if !self.eat(&token::RPAREN) {
+                        let place = self.parse_expr();
+                        self.expect(&token::RPAREN);
+                        let subexpression = self.parse_prefix_expr();
+                        hi = subexpression.span.hi;
+                        ex = ExprBox(place, subexpression);
+                        return self.mk_expr(lo, hi, ex);
+                    }
                 }
-            }
 
-            // Otherwise, we use the unique pointer default.
-            let subexpression = self.parse_prefix_expr();
-            hi = subexpression.span.hi;
-            // HACK: turn `box [...]` into a boxed-vec
-            ex = match subexpression.node {
-                ExprVec(..) | ExprRepeat(..) => {
-                    let last_span = self.last_span;
-                    self.obsolete(last_span, ObsoleteOwnedVector);
-                    ExprVstore(subexpression, ExprVstoreUniq)
-                }
-                ExprLit(lit) if lit_is_str(lit) => {
-                    ExprVstore(subexpression, ExprVstoreUniq)
-                }
-                _ => self.mk_unary(UnUniq, subexpression)
-            };
+                // Otherwise, we use the unique pointer default.
+                let subexpression = self.parse_prefix_expr();
+                hi = subexpression.span.hi;
+                // HACK: turn `box [...]` into a boxed-vec
+                ex = match subexpression.node {
+                    ExprVec(..) | ExprRepeat(..) => {
+                        let last_span = self.last_span;
+                        self.obsolete(last_span, ObsoleteOwnedVector);
+                        ExprVstore(subexpression, ExprVstoreUniq)
+                    }
+                    ExprLit(lit) if lit_is_str(lit) => {
+                        ExprVstore(subexpression, ExprVstoreUniq)
+                    }
+                    _ => self.mk_unary(UnUniq, subexpression)
+                };
+              } else {
+                return self.parse_dot_or_call_expr()
+              }
           }
           _ => return self.parse_dot_or_call_expr()
         }
@@ -3832,17 +3851,6 @@ impl<'a> Parser<'a> {
                 }
                 SelfStatic
             }
-            token::IDENT(..) if self.is_self_ident() => {
-                let self_ident = self.expect_self_ident();
-
-                // Determine whether this is the fully explicit form, `self:
-                // TYPE`.
-                if self.eat(&token::COLON) {
-                    SelfExplicit(self.parse_ty(false), self_ident)
-                } else {
-                    SelfValue(self_ident)
-                }
-            }
             token::BINOP(token::STAR) => {
                 // Possibly "*self" or "*mut self" -- not supported. Try to avoid
                 // emitting cryptic "unexpected token" errors.
@@ -3860,30 +3868,47 @@ impl<'a> Parser<'a> {
                 // error case, making bogus self ident:
                 SelfValue(special_idents::self_)
             }
-            _ if Parser::token_is_mutability(&self.token) &&
-                    self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) => {
-                mutbl_self = self.parse_mutability();
-                let self_ident = self.expect_self_ident();
-
-                // Determine whether this is the fully explicit form, `self:
-                // TYPE`.
-                if self.eat(&token::COLON) {
-                    SelfExplicit(self.parse_ty(false), self_ident)
+            token::IDENT(..) => {
+                if self.is_self_ident() {
+                    let self_ident = self.expect_self_ident();
+
+                    // Determine whether this is the fully explicit form, `self:
+                    // TYPE`.
+                    if self.eat(&token::COLON) {
+                        SelfExplicit(self.parse_ty(false), self_ident)
+                    } else {
+                        SelfValue(self_ident)
+                    }
+                } else if Parser::token_is_mutability(&self.token) &&
+                        self.look_ahead(1, |t| {
+                            token::is_keyword(keywords::Self, t)
+                        }) {
+                    mutbl_self = self.parse_mutability();
+                    let self_ident = self.expect_self_ident();
+
+                    // Determine whether this is the fully explicit form,
+                    // `self: TYPE`.
+                    if self.eat(&token::COLON) {
+                        SelfExplicit(self.parse_ty(false), self_ident)
+                    } else {
+                        SelfValue(self_ident)
+                    }
+                } else if Parser::token_is_mutability(&self.token) &&
+                        self.look_ahead(1, |t| *t == token::TILDE) &&
+                        self.look_ahead(2, |t| {
+                            token::is_keyword(keywords::Self, t)
+                        }) {
+                    mutbl_self = self.parse_mutability();
+                    self.bump();
+                    drop(self.expect_self_ident());
+                    let last_span = self.last_span;
+                    self.obsolete(last_span, ObsoleteOwnedSelf);
+                    SelfStatic
                 } else {
-                    SelfValue(self_ident)
+                    SelfStatic
                 }
             }
-            _ if Parser::token_is_mutability(&self.token) &&
-                    self.look_ahead(1, |t| *t == token::TILDE) &&
-                    self.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) => {
-                mutbl_self = self.parse_mutability();
-                self.bump();
-                drop(self.expect_self_ident());
-                let last_span = self.last_span;
-                self.obsolete(last_span, ObsoleteOwnedSelf);
-                SelfStatic
-            }
-            _ => SelfStatic
+            _ => SelfStatic,
         };
 
         let explicit_self_sp = mk_sp(lo, self.span.hi);