]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #41282 - arielb1:missing-impl-item, r=petrochenkov
authorbors <bors@rust-lang.org>
Mon, 17 Apr 2017 22:22:56 +0000 (22:22 +0000)
committerbors <bors@rust-lang.org>
Mon, 17 Apr 2017 22:22:56 +0000 (22:22 +0000)
libsyntax/parse: fix missing kind error reporting

Fixes #41161.
Fixes #41239.

1  2 
src/libsyntax/ext/expand.rs
src/libsyntax/parse/parser.rs

index 48bfc050223ab82ab60fe6581280c16fe70410a0,20a858b80df4769e37ef2376584d8cd8cb725230..680bd7599ace06e9baf4eedd9568e2e8cbc3c03b
@@@ -617,14 -617,14 +617,14 @@@ impl<'a> Parser<'a> 
              ExpansionKind::TraitItems => {
                  let mut items = SmallVector::new();
                  while self.token != token::Eof {
-                     items.push(self.parse_trait_item()?);
+                     items.push(self.parse_trait_item(&mut false)?);
                  }
                  Expansion::TraitItems(items)
              }
              ExpansionKind::ImplItems => {
                  let mut items = SmallVector::new();
                  while self.token != token::Eof {
-                     items.push(self.parse_impl_item()?);
+                     items.push(self.parse_impl_item(&mut false)?);
                  }
                  Expansion::ImplItems(items)
              }
@@@ -1039,7 -1039,6 +1039,7 @@@ impl<'feat> ExpansionConfig<'feat> 
      feature_tests! {
          fn enable_quotes = quote,
          fn enable_asm = asm,
 +        fn enable_global_asm = global_asm,
          fn enable_log_syntax = log_syntax,
          fn enable_concat_idents = concat_idents,
          fn enable_trace_macros = trace_macros,
index 3e71c0f0f68e3c63bc01da9d4df91aaa9824a5ef,dfb82d40d568da50d161d76f72fa0792abba1773..0cdb09a842f4651d1f5d35ef0cfe909b6789f3b6
@@@ -85,12 -85,18 +85,18 @@@ pub enum PathStyle 
      Expr,
  }
  
- #[derive(Clone, Copy, PartialEq)]
+ #[derive(Clone, Copy, Debug, PartialEq)]
  pub enum SemiColonMode {
      Break,
      Ignore,
  }
  
+ #[derive(Clone, Copy, Debug, PartialEq)]
+ pub enum BlockMode {
+     Break,
+     Ignore,
+ }
  /// Possibly accept an `token::Interpolated` expression (a pre-parsed expression
  /// dropped into the token stream, which happens while parsing the result of
  /// macro expansion). Placement of these is not as complex as I feared it would
@@@ -1204,7 -1210,7 +1210,7 @@@ impl<'a> Parser<'a> 
      }
  
      /// Parse the items in a trait declaration
-     pub fn parse_trait_item(&mut self) -> PResult<'a, TraitItem> {
+     pub fn parse_trait_item(&mut self, at_end: &mut bool) -> PResult<'a, TraitItem> {
          maybe_whole!(self, NtTraitItem, |x| x);
          let mut attrs = self.parse_outer_attributes()?;
          let lo = self.span;
              self.expect(&token::Semi)?;
              (ident, TraitItemKind::Type(bounds, default))
          } else if self.is_const_item() {
-                 self.expect_keyword(keywords::Const)?;
+             self.expect_keyword(keywords::Const)?;
              let ident = self.parse_ident()?;
              self.expect(&token::Colon)?;
              let ty = self.parse_ty()?;
          } else if self.token.is_path_start() {
              // trait item macro.
              // code copied from parse_macro_use_or_failure... abstraction!
+             let prev_span = self.prev_span;
              let lo = self.span;
              let pth = self.parse_path(PathStyle::Mod)?;
-             self.expect(&token::Not)?;
+             if pth.segments.len() == 1 {
+                 if !self.eat(&token::Not) {
+                     return Err(self.missing_assoc_item_kind_err("trait", prev_span));
+                 }
+             } else {
+                 self.expect(&token::Not)?;
+             }
  
              // eat a matched-delimiter token tree:
              let (delim, tts) = self.expect_delimited_token_tree()?;
          } else {
              let (constness, unsafety, abi) = match self.parse_fn_front_matter() {
                  Ok(cua) => cua,
-                 Err(e) => {
-                     loop {
-                         match self.token {
-                             token::Eof => break,
-                             token::CloseDelim(token::Brace) |
-                             token::Semi => {
-                                 self.bump();
-                                 break;
-                             }
-                             token::OpenDelim(token::Brace) => {
-                                 self.parse_token_tree();
-                                 break;
-                             }
-                             _ => self.bump(),
-                         }
-                     }
-                     return Err(e);
-                 }
+                 Err(e) => return Err(e),
              };
  
              let ident = self.parse_ident()?;
              let body = match self.token {
                  token::Semi => {
                      self.bump();
+                     *at_end = true;
                      debug!("parse_trait_methods(): parsing required method");
                      None
                  }
                  token::OpenDelim(token::Brace) => {
                      debug!("parse_trait_methods(): parsing provided method");
+                     *at_end = true;
                      let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
                      attrs.extend(inner_attrs.iter().cloned());
                      Some(body)
          })
      }
  
-     /// Parse the items in a trait declaration
-     pub fn parse_trait_items(&mut self) -> PResult<'a,  Vec<TraitItem>> {
-         self.parse_unspanned_seq(
-             &token::OpenDelim(token::Brace),
-             &token::CloseDelim(token::Brace),
-             SeqSep::none(),
-             |p| -> PResult<'a, TraitItem> {
-                 p.parse_trait_item()
-             })
-     }
      /// Parse optional return type [ -> TY ] in function decl
      pub fn parse_ret_ty(&mut self) -> PResult<'a, FunctionRetTy> {
          if self.eat(&token::RArrow) {
                    }
                    token::Literal(token::Float(n), _suf) => {
                      self.bump();
 -                    let prev_span = self.prev_span;
                      let fstr = n.as_str();
 -                    let mut err = self.diagnostic().struct_span_err(prev_span,
 +                    let mut err = self.diagnostic().struct_span_err(self.prev_span,
                          &format!("unexpected token: `{}`", n));
 +                    err.span_label(self.prev_span, &"unexpected token");
                      if fstr.chars().all(|x| "0123456789.".contains(x)) {
                          let float = match fstr.parse::<f64>().ok() {
                              Some(f) => f,
                              word(&mut s.s, fstr.splitn(2, ".").last().unwrap())
                          });
                          err.span_suggestion(
 -                            prev_span,
 +                            lo.to(self.prev_span),
                              "try parenthesizing the first index",
                              sugg);
                      }
      //
      // We terminate when we find an unmatched `}` (without consuming it).
      fn recover_stmt(&mut self) {
-         self.recover_stmt_(SemiColonMode::Ignore)
+         self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore)
      }
      // If `break_on_semi` is `Break`, then we will stop consuming tokens after
      // finding (and consuming) a `;` outside of `{}` or `[]` (note that this is
      // approximate - it can mean we break too early due to macros, but that
      // shoud only lead to sub-optimal recovery, not inaccurate parsing).
-     fn recover_stmt_(&mut self, break_on_semi: SemiColonMode) {
+     //
+     // If `break_on_block` is `Break`, then we will stop consuming tokens
+     // after finding (and consuming) a brace-delimited block.
+     fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockMode) {
          let mut brace_depth = 0;
          let mut bracket_depth = 0;
-         debug!("recover_stmt_ enter loop");
+         let mut in_block = false;
+         debug!("recover_stmt_ enter loop (semi={:?}, block={:?})",
+                break_on_semi, break_on_block);
          loop {
              debug!("recover_stmt_ loop {:?}", self.token);
              match self.token {
                  token::OpenDelim(token::DelimToken::Brace) => {
                      brace_depth += 1;
                      self.bump();
+                     if break_on_block == BlockMode::Break &&
+                        brace_depth == 1 &&
+                        bracket_depth == 0 {
+                         in_block = true;
+                     }
                  }
                  token::OpenDelim(token::DelimToken::Bracket) => {
                      bracket_depth += 1;
                      }
                      brace_depth -= 1;
                      self.bump();
+                     if in_block && bracket_depth == 0 && brace_depth == 0 {
+                         debug!("recover_stmt_ return - block end {:?}", self.token);
+                         return;
+                     }
                  }
                  token::CloseDelim(token::DelimToken::Bracket) => {
                      bracket_depth -= 1;
      fn parse_stmt_(&mut self, macro_legacy_warnings: bool) -> Option<Stmt> {
          self.parse_stmt_without_recovery(macro_legacy_warnings).unwrap_or_else(|mut e| {
              e.emit();
-             self.recover_stmt_(SemiColonMode::Break);
+             self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
              None
          })
      }
                      e.span_suggestion(stmt_span, "try placing this code inside a block", sugg);
                  }
                  Err(mut e) => {
-                     self.recover_stmt_(SemiColonMode::Break);
+                     self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
                      self.cancel(&mut e);
                  }
                  _ => ()
          }).emit();
      }
  
 -    // Parse bounds of a type parameter `BOUND + BOUND + BOUND` without trailing `+`.
 +    // Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
      // BOUND = TY_BOUND | LT_BOUND
      // LT_BOUND = LIFETIME (e.g. `'a`)
      // TY_BOUND = [?] [for<LT_PARAM_DEFS>] SIMPLE_PATH (e.g. `?for<'a: 'b> m::Trait<'a>`)
          self.parse_ty_param_bounds_common(true)
      }
  
 -    // Parse bounds of a type parameter `BOUND + BOUND + BOUND` without trailing `+`.
 +    // Parse bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
      // BOUND = LT_BOUND (e.g. `'a`)
      fn parse_lt_param_bounds(&mut self) -> Vec<Lifetime> {
          let mut lifetimes = Vec::new();
      }
  
      /// Parse an impl item.
-     pub fn parse_impl_item(&mut self) -> PResult<'a, ImplItem> {
+     pub fn parse_impl_item(&mut self, at_end: &mut bool) -> PResult<'a, ImplItem> {
          maybe_whole!(self, NtImplItem, |x| x);
  
          let mut attrs = self.parse_outer_attributes()?;
              self.expect(&token::Semi)?;
              (name, ast::ImplItemKind::Const(typ, expr))
          } else {
-             let (name, inner_attrs, node) = self.parse_impl_method(&vis)?;
+             let (name, inner_attrs, node) = self.parse_impl_method(&vis, at_end)?;
              attrs.extend(inner_attrs);
              (name, node)
          };
          }
      }
  
+     fn missing_assoc_item_kind_err(&mut self, item_type: &str, prev_span: Span)
+                                    -> DiagnosticBuilder<'a>
+     {
+         // Given this code `path(`, it seems like this is not
+         // setting the visibility of a macro invocation, but rather
+         // a mistyped method declaration.
+         // Create a diagnostic pointing out that `fn` is missing.
+         //
+         // x |     pub path(&self) {
+         //   |        ^ missing `fn`, `type`, or `const`
+         //     pub  path(
+         //        ^^ `sp` below will point to this
+         let sp = prev_span.between(self.prev_span);
+         let mut err = self.diagnostic().struct_span_err(
+             sp,
+             &format!("missing `fn`, `type`, or `const` for {}-item declaration",
+                      item_type));
+         err.span_label(sp, &"missing `fn`, `type`, or `const`");
+         err
+     }
      /// Parse a method or a macro invocation in a trait impl.
-     fn parse_impl_method(&mut self, vis: &Visibility)
+     fn parse_impl_method(&mut self, vis: &Visibility, at_end: &mut bool)
                           -> PResult<'a, (Ident, Vec<ast::Attribute>, ast::ImplItemKind)> {
          // code copied from parse_macro_use_or_failure... abstraction!
          if self.token.is_path_start() {
              // Method macro.
  
              let prev_span = self.prev_span;
-             // Before complaining about trying to set a macro as `pub`,
-             // check if `!` comes after the path.
-             let err = self.complain_if_pub_macro_diag(&vis, prev_span);
  
              let lo = self.span;
              let pth = self.parse_path(PathStyle::Mod)?;
-             let bang_err = self.expect(&token::Not);
-             if let Err(mut err) = err {
-                 if let Err(mut bang_err) = bang_err {
-                     // Given this code `pub path(`, it seems like this is not setting the
-                     // visibility of a macro invocation, but rather a mistyped method declaration.
-                     // Create a diagnostic pointing out that `fn` is missing.
-                     //
-                     // x |     pub path(&self) {
-                     //   |        ^ missing `fn` for method declaration
-                     err.cancel();
-                     bang_err.cancel();
-                     //     pub  path(
-                     //        ^^ `sp` below will point to this
-                     let sp = prev_span.between(self.prev_span);
-                     err = self.diagnostic()
-                         .struct_span_err(sp, "missing `fn` for method declaration");
-                     err.span_label(sp, &"missing `fn`");
+             if pth.segments.len() == 1 {
+                 if !self.eat(&token::Not) {
+                     return Err(self.missing_assoc_item_kind_err("impl", prev_span));
                  }
-                 return Err(err);
+             } else {
+                 self.expect(&token::Not)?;
              }
  
+             self.complain_if_pub_macro(&vis, prev_span);
              // eat a matched-delimiter token tree:
+             *at_end = true;
              let (delim, tts) = self.expect_delimited_token_tree()?;
              if delim != token::Brace {
                  self.expect(&token::Semi)?
              let mut generics = self.parse_generics()?;
              let decl = self.parse_fn_decl_with_self(|p| p.parse_arg())?;
              generics.where_clause = self.parse_where_clause()?;
+             *at_end = true;
              let (inner_attrs, body) = self.parse_inner_attrs_and_block()?;
              Ok((ident, inner_attrs, ast::ImplItemKind::Method(ast::MethodSig {
                  generics: generics,
  
          tps.where_clause = self.parse_where_clause()?;
  
-         let meths = self.parse_trait_items()?;
-         Ok((ident, ItemKind::Trait(unsafety, tps, bounds, meths), None))
+         self.expect(&token::OpenDelim(token::Brace))?;
+         let mut trait_items = vec![];
+         while !self.eat(&token::CloseDelim(token::Brace)) {
+             let mut at_end = false;
+             match self.parse_trait_item(&mut at_end) {
+                 Ok(item) => trait_items.push(item),
+                 Err(mut e) => {
+                     e.emit();
+                     if !at_end {
+                         self.recover_stmt_(SemiColonMode::Break, BlockMode::Break);
+                     }
+                 }
+             }
+         }
+         Ok((ident, ItemKind::Trait(unsafety, tps, bounds, trait_items), None))
      }
  
      /// Parses items implementations variants
  
              let mut impl_items = vec![];
              while !self.eat(&token::CloseDelim(token::Brace)) {
-                 impl_items.push(self.parse_impl_item()?);
+                 let mut at_end = false;
+                 match self.parse_impl_item(&mut at_end) {
+                     Ok(item) => impl_items.push(item),
+                     Err(mut e) => {
+                         e.emit();
+                         if !at_end {
+                             self.recover_stmt_(SemiColonMode::Break, BlockMode::Break);
+                         }
+                     }
+                 }
              }
  
              Ok((keywords::Invalid.ident(),
      /// and `pub(super)` for `pub(in super)`.  If the following element can't be a tuple (i.e. it's
      /// a function definition, it's not a tuple struct field) and the contents within the parens
      /// isn't valid, emit a proper diagnostic.
 -    fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> {
 +    pub fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> {
 +        maybe_whole!(self, NtVis, |x| x);
 +
          if !self.eat_keyword(keywords::Pub) {
              return Ok(Visibility::Inherited)
          }