diff options
| author | bors <bors@rust-lang.org> | 2018-07-24 15:11:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-07-24 15:11:56 +0000 |
| commit | f498e4ec1b57c3245a2fb8e0d5d836ed56760d2d (patch) | |
| tree | 870ef7d674c7aee50c7d3b59b4dce7053b1589ac /src/libsyntax | |
| parent | 6a3db033ad05f156281d50ee489d727ee0e5d767 (diff) | |
| parent | 10ee0f68a6815fafa69f58daf347f0c2a8339f32 (diff) | |
| download | rust-f498e4ec1b57c3245a2fb8e0d5d836ed56760d2d.tar.gz rust-f498e4ec1b57c3245a2fb8e0d5d836ed56760d2d.zip | |
Auto merge of #51587 - mark-i-m:at_most_once_rep_2018, r=alexcrichton
2018 edition `?` Kleene operator This is my first attempt at implementing the migration lint + 2018 behavior as discussed in #48075 r? @nikomatsakis
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/early_buffered_lints.rs | 39 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 18 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/macro_rules.rs | 25 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/quoted.rs | 290 | ||||
| -rw-r--r-- | src/libsyntax/lib.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/lexer/mod.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 23 |
7 files changed, 317 insertions, 81 deletions
diff --git a/src/libsyntax/early_buffered_lints.rs b/src/libsyntax/early_buffered_lints.rs new file mode 100644 index 00000000000..a976af1435d --- /dev/null +++ b/src/libsyntax/early_buffered_lints.rs @@ -0,0 +1,39 @@ +// Copyright 2017 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. + +//! Allows the buffering of lints for later. +//! +//! Since we cannot have a dependency on `librustc`, we implement some types here that are somewhat +//! redundant. Later, these types can be converted to types for use by the rest of the compiler. + +use syntax::ast::NodeId; +use syntax_pos::MultiSpan; + +/// Since we cannot import `LintId`s from `rustc::lint`, we define some Ids here which can later be +/// passed to `rustc::lint::Lint::from_parser_lint_id` to get a `rustc::lint::Lint`. +pub enum BufferedEarlyLintId { + /// Usage of `?` as a macro separator is deprecated. + QuestionMarkMacroSep, +} + +/// Stores buffered lint info which can later be passed to `librustc`. +pub struct BufferedEarlyLint { + /// The span of code that we are linting on. + pub span: MultiSpan, + + /// The lint message. + pub msg: String, + + /// The `NodeId` of the AST node that generated the lint. + pub id: NodeId, + + /// A lint Id that can be passed to `rustc::lint::Lint::from_parser_lint_id`. + pub lint_id: BufferedEarlyLintId, +} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index dc461d0a15d..b84046d1050 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -44,8 +44,10 @@ macro_rules! ast_fragments { ( $($Kind:ident($AstTy:ty) { $kind_name:expr; - $(one fn $fold_ast:ident; fn $visit_ast:ident;)? - $(many fn $fold_ast_elt:ident; fn $visit_ast_elt:ident;)? + // FIXME: HACK: this should be `$(one ...)?` and `$(many ...)?` but `?` macro + // repetition was removed from 2015 edition in #51587 because of ambiguities. + $(one fn $fold_ast:ident; fn $visit_ast:ident;)* + $(many fn $fold_ast_elt:ident; fn $visit_ast_elt:ident;)* fn $make_ast:ident; })* ) => { @@ -100,11 +102,11 @@ macro_rules! ast_fragments { AstFragment::OptExpr(expr) => AstFragment::OptExpr(expr.and_then(|expr| folder.fold_opt_expr(expr))), $($(AstFragment::$Kind(ast) => - AstFragment::$Kind(folder.$fold_ast(ast)),)?)* + AstFragment::$Kind(folder.$fold_ast(ast)),)*)* $($(AstFragment::$Kind(ast) => AstFragment::$Kind(ast.into_iter() .flat_map(|ast| folder.$fold_ast_elt(ast)) - .collect()),)?)* + .collect()),)*)* } } @@ -112,10 +114,10 @@ macro_rules! ast_fragments { match *self { AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), AstFragment::OptExpr(None) => {} - $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)* + $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)*)* $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] { visitor.$visit_ast_elt(ast_elt); - })?)* + })*)* } } } @@ -126,10 +128,10 @@ macro_rules! ast_fragments { } $($(fn $fold_ast(&mut self, ast: $AstTy) -> $AstTy { self.expand_fragment(AstFragment::$Kind(ast)).$make_ast() - })?)* + })*)* $($(fn $fold_ast_elt(&mut self, ast_elt: <$AstTy as IntoIterator>::Item) -> $AstTy { self.expand_fragment(AstFragment::$Kind(SmallVector::one(ast_elt))).$make_ast() - })?)* + })*)* } impl<'a> MacResult for ::ext::tt::macro_rules::ParserAnyMacro<'a> { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 9ebead1062e..c9ec2c7d1e8 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -240,8 +240,17 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - let tt = quoted::parse(tt.clone().into(), true, sess, features, &def.attrs) - .pop().unwrap(); + let tt = quoted::parse( + tt.clone().into(), + true, + sess, + features, + &def.attrs, + edition, + def.id, + ) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt); return tt; } @@ -257,8 +266,16 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: s.iter().map(|m| { if let MatchedNonterminal(ref nt) = *m { if let NtTT(ref tt) = **nt { - return quoted::parse(tt.clone().into(), false, sess, features, &def.attrs) - .pop().unwrap(); + return quoted::parse( + tt.clone().into(), + false, + sess, + features, + &def.attrs, + edition, + def.id, + ).pop() + .unwrap(); } } sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index d21ffabb62e..357fc77a3a7 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -8,17 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use {ast, attr}; +use ast::NodeId; +use early_buffered_lints::BufferedEarlyLintId; use ext::tt::macro_parser; use feature_gate::{self, emit_feature_err, Features, GateIssue}; use parse::{token, ParseSess}; use print::pprust; use symbol::keywords; -use syntax_pos::{BytePos, Span}; +use syntax_pos::{edition::Edition, BytePos, Span}; use tokenstream; +use {ast, attr}; -use std::iter::Peekable; use rustc_data_structures::sync::Lrc; +use std::iter::Peekable; /// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note /// that the delimiter itself might be `NoDelim`. @@ -174,6 +176,8 @@ impl TokenTree { /// - `sess`: the parsing session. Any errors will be emitted to this session. /// - `features`, `attrs`: language feature flags and attributes so that we know whether to use /// unstable features or not. +/// - `edition`: which edition are we in. +/// - `macro_node_id`: the NodeId of the macro we are parsing. /// /// # Returns /// @@ -184,6 +188,8 @@ pub fn parse( sess: &ParseSess, features: &Features, attrs: &[ast::Attribute], + edition: Edition, + macro_node_id: NodeId, ) -> Vec<TokenTree> { // Will contain the final collection of `self::TokenTree` let mut result = Vec::new(); @@ -194,7 +200,16 @@ pub fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e. in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess, features, attrs); + let tree = parse_tree( + tree, + &mut trees, + expect_matchers, + sess, + features, + attrs, + edition, + macro_node_id, + ); match tree { TokenTree::MetaVar(start_sp, ident) if expect_matchers => { let span = match trees.next() { @@ -207,11 +222,13 @@ pub fn parse( } _ => end_sp, }, - tree => tree.as_ref() + tree => tree + .as_ref() .map(tokenstream::TokenTree::span) .unwrap_or(span), }, - tree => tree.as_ref() + tree => tree + .as_ref() .map(tokenstream::TokenTree::span) .unwrap_or(start_sp), }; @@ -252,6 +269,8 @@ fn parse_tree<I>( sess: &ParseSess, features: &Features, attrs: &[ast::Attribute], + edition: Edition, + macro_node_id: NodeId, ) -> TokenTree where I: Iterator<Item = tokenstream::TokenTree>, @@ -270,9 +289,26 @@ where sess.span_diagnostic.span_err(span, &msg); } // Parse the contents of the sequence itself - let sequence = parse(delimited.tts.into(), expect_matchers, sess, features, attrs); + let sequence = parse( + delimited.tts.into(), + expect_matchers, + sess, + features, + attrs, + edition, + macro_node_id, + ); // Get the Kleene operator and optional separator - let (separator, op) = parse_sep_and_kleene_op(trees, span, sess, features, attrs); + let (separator, op) = + parse_sep_and_kleene_op( + trees, + span, + sess, + features, + attrs, + edition, + macro_node_id, + ); // Count the number of captured "names" (i.e. named metavars) let name_captures = macro_parser::count_names(&sequence); TokenTree::Sequence( @@ -322,7 +358,15 @@ where span, Lrc::new(Delimited { delim: delimited.delim, - tts: parse(delimited.tts.into(), expect_matchers, sess, features, attrs), + tts: parse( + delimited.tts.into(), + expect_matchers, + sess, + features, + attrs, + edition, + macro_node_id, + ), }), ), } @@ -341,22 +385,23 @@ fn kleene_op(token: &token::Token) -> Option<KleeneOp> { /// Parse the next token tree of the input looking for a KleeneOp. Returns /// -/// - Ok(Ok(op)) if the next token tree is a KleeneOp +/// - Ok(Ok((op, span))) if the next token tree is a KleeneOp /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp /// - Err(span) if the next token tree is not a token fn parse_kleene_op<I>( input: &mut I, span: Span, -) -> Result<Result<KleeneOp, (token::Token, Span)>, Span> +) -> Result<Result<(KleeneOp, Span), (token::Token, Span)>, Span> where I: Iterator<Item = tokenstream::TokenTree>, { match input.next() { Some(tokenstream::TokenTree::Token(span, tok)) => match kleene_op(&tok) { - Some(op) => Ok(Ok(op)), + Some(op) => Ok(Ok((op, span))), None => Ok(Err((tok, span))), }, - tree => Err(tree.as_ref() + tree => Err(tree + .as_ref() .map(tokenstream::TokenTree::span) .unwrap_or(span)), } @@ -374,12 +419,43 @@ where /// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an /// error with the appropriate span is emitted to `sess` and a dummy value is returned. +/// +/// NOTE: In 2015 edition, * and + are the only Kleene operators and `?` is a separator. In 2018, +/// `?` is a Kleene op and not a separator. fn parse_sep_and_kleene_op<I>( input: &mut Peekable<I>, span: Span, sess: &ParseSess, features: &Features, attrs: &[ast::Attribute], + edition: Edition, + macro_node_id: NodeId, +) -> (Option<token::Token>, KleeneOp) +where + I: Iterator<Item = tokenstream::TokenTree>, +{ + match edition { + Edition::Edition2015 => parse_sep_and_kleene_op_2015( + input, + span, + sess, + features, + attrs, + macro_node_id, + ), + Edition::Edition2018 => parse_sep_and_kleene_op_2018(input, span, sess, features, attrs), + _ => unimplemented!(), + } +} + +// `?` is a separator (with a migration warning) and never a KleeneOp. +fn parse_sep_and_kleene_op_2015<I>( + input: &mut Peekable<I>, + span: Span, + sess: &ParseSess, + _features: &Features, + _attrs: &[ast::Attribute], + macro_node_id: NodeId, ) -> (Option<token::Token>, KleeneOp) where I: Iterator<Item = tokenstream::TokenTree>, @@ -388,14 +464,14 @@ where let span = match parse_kleene_op(input, span) { // #1 is a `+` or `*` KleeneOp // - // `?` is ambiguous: it could be a separator or a Kleene::ZeroOrOne, so we need to look - // ahead one more token to be sure. - Ok(Ok(op)) if op != KleeneOp::ZeroOrOne => return (None, op), - - // #1 is `?` token, but it could be a Kleene::ZeroOrOne without a separator or it could - // be a `?` separator followed by any Kleene operator. We need to look ahead 1 token to - // find out which. - Ok(Ok(op)) => { + // `?` is ambiguous: it could be a separator (warning) or a Kleene::ZeroOrOne (error), so + // we need to look ahead one more token to be sure. + Ok(Ok((op, _))) if op != KleeneOp::ZeroOrOne => return (None, op), + + // #1 is `?` token, but it could be a Kleene::ZeroOrOne (error in 2015) without a separator + // or it could be a `?` separator followed by any Kleene operator. We need to look ahead 1 + // token to find out which. + Ok(Ok((op, op1_span))) => { assert_eq!(op, KleeneOp::ZeroOrOne); // Lookahead at #2. If it is a KleenOp, then #1 is a separator. @@ -406,71 +482,149 @@ where }; if is_1_sep { - // #1 is a separator and #2 should be a KleepeOp::* + // #1 is a separator and #2 should be a KleepeOp. // (N.B. We need to advance the input iterator.) match parse_kleene_op(input, span) { - // #2 is a KleeneOp (this is the only valid option) :) - Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => { - if !features.macro_at_most_once_rep - && !attr::contains_name(attrs, "allow_internal_unstable") - { - let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; - emit_feature_err( - sess, - "macro_at_most_once_rep", - span, - GateIssue::Language, - explain, - ); - } + // #2 is `?`, which is not allowed as a Kleene op in 2015 edition. + Ok(Ok((op, op2_span))) if op == KleeneOp::ZeroOrOne => { + sess.span_diagnostic + .struct_span_err(op2_span, "expected `*` or `+`") + .note("`?` is not a macro repetition operator") + .emit(); + + // Return a dummy + return (None, KleeneOp::ZeroOrMore); + } + + // #2 is a Kleene op, which is the the only valid option + Ok(Ok((op, _))) => { + // Warn that `?` as a separator will be deprecated + sess.buffer_lint( + BufferedEarlyLintId::QuestionMarkMacroSep, + op1_span, + macro_node_id, + "using `?` as a separator is deprecated and will be \ + a hard error in an upcoming edition", + ); + return (Some(token::Question), op); } - Ok(Ok(op)) => return (Some(token::Question), op), // #2 is a random token (this is an error) :( - Ok(Err((_, span))) => span, + Ok(Err((_, _))) => op1_span, // #2 is not even a token at all :( - Err(span) => span, + Err(_) => op1_span, } } else { - if !features.macro_at_most_once_rep - && !attr::contains_name(attrs, "allow_internal_unstable") - { - let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; - emit_feature_err( - sess, - "macro_at_most_once_rep", - span, - GateIssue::Language, - explain, - ); - } + // `?` is not allowed as a Kleene op in 2015 + sess.span_diagnostic + .struct_span_err(op1_span, "expected `*` or `+`") + .note("`?` is not a macro repetition operator") + .emit(); + + // Return a dummy + return (None, KleeneOp::ZeroOrMore); + } + } + + // #1 is a separator followed by #2, a KleeneOp + Ok(Err((tok, span))) => match parse_kleene_op(input, span) { + // #2 is a `?`, which is not allowed as a Kleene op in 2015 edition. + Ok(Ok((op, op2_span))) if op == KleeneOp::ZeroOrOne => { + sess.span_diagnostic + .struct_span_err(op2_span, "expected `*` or `+`") + .note("`?` is not a macro repetition operator") + .emit(); + + // Return a dummy + return (None, KleeneOp::ZeroOrMore); + } + + // #2 is a KleeneOp :D + Ok(Ok((op, _))) => return (Some(tok), op), + + // #2 is a random token :( + Ok(Err((_, span))) => span, - // #2 is a random tree and #1 is KleeneOp::ZeroOrOne + // #2 is not a token at all :( + Err(span) => span, + }, + + // #1 is not a token + Err(span) => span, + }; + + sess.span_diagnostic.span_err(span, "expected `*` or `+`"); + + // Return a dummy + (None, KleeneOp::ZeroOrMore) +} + +// `?` is a Kleene op, not a separator +fn parse_sep_and_kleene_op_2018<I>( + input: &mut Peekable<I>, + span: Span, + sess: &ParseSess, + features: &Features, + attrs: &[ast::Attribute], +) -> (Option<token::Token>, KleeneOp) +where + I: Iterator<Item = tokenstream::TokenTree>, +{ + // We basically look at two token trees here, denoted as #1 and #2 below + let span = match parse_kleene_op(input, span) { + // #1 is a `?` (needs feature gate) + Ok(Ok((op, op1_span))) if op == KleeneOp::ZeroOrOne => { + if !features.macro_at_most_once_rep + && !attr::contains_name(attrs, "allow_internal_unstable") + { + let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; + emit_feature_err( + sess, + "macro_at_most_once_rep", + op1_span, + GateIssue::Language, + explain, + ); + + op1_span + } else { return (None, op); } } + // #1 is a `+` or `*` KleeneOp + Ok(Ok((op, _))) => return (None, op), + // #1 is a separator followed by #2, a KleeneOp Ok(Err((tok, span))) => match parse_kleene_op(input, span) { - // #2 is a KleeneOp :D - Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => { + // #2 is the `?` Kleene op, which does not take a separator (error) + Ok(Ok((op, op2_span))) if op == KleeneOp::ZeroOrOne => { + // Error! + if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") { - let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP; - emit_feature_err( - sess, - "macro_at_most_once_rep", + // FIXME: when `?` as a Kleene op is stabilized, we only need the "does not + // take a macro separator" error (i.e. the `else` case). + sess.span_diagnostic + .struct_span_err(op2_span, "expected `*` or `+`") + .note("`?` is not a macro repetition operator") + .emit(); + } else { + sess.span_diagnostic.span_err( span, - GateIssue::Language, - explain, + "the `?` macro repetition operator does not take a separator", ); } - return (Some(tok), op); + + // Return a dummy + return (None, KleeneOp::ZeroOrMore); } - Ok(Ok(op)) => return (Some(tok), op), + + // #2 is a KleeneOp :D + Ok(Ok((op, _))) => return (Some(tok), op), // #2 is a random token :( Ok(Err((_, span))) => span, @@ -483,13 +637,15 @@ where Err(span) => span, }; - if !features.macro_at_most_once_rep - && !attr::contains_name(attrs, "allow_internal_unstable") - { + // If we ever get to this point, we have experienced an "unexpected token" error + + if !features.macro_at_most_once_rep && !attr::contains_name(attrs, "allow_internal_unstable") { + sess.span_diagnostic.span_err(span, "expected `*` or `+`"); + } else { sess.span_diagnostic .span_err(span, "expected one of: `*`, `+`, or `?`"); - } else { - sess.span_diagnostic.span_err(span, "expected `*` or `+`"); } + + // Return a dummy (None, KleeneOp::ZeroOrMore) } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index ffaad9bf94c..d241ae1d442 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -181,6 +181,8 @@ pub mod ext { } } +pub mod early_buffered_lints; + #[cfg(test)] mod test_snippet; diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 9748e2947ee..4b077aa8dd4 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1807,6 +1807,7 @@ mod tests { raw_identifier_spans: Lock::new(Vec::new()), registered_diagnostics: Lock::new(ErrorMap::new()), non_modrs_mods: Lock::new(vec![]), + buffered_lints: Lock::new(vec![]), } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 1754e5f1b9a..d029509f0c1 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -11,9 +11,10 @@ //! The main parser interface use rustc_data_structures::sync::{Lrc, Lock}; -use ast::{self, CrateConfig}; +use ast::{self, CrateConfig, NodeId}; +use early_buffered_lints::{BufferedEarlyLint, BufferedEarlyLintId}; use codemap::{CodeMap, FilePathMapping}; -use syntax_pos::{Span, FileMap, FileName}; +use syntax_pos::{Span, FileMap, FileName, MultiSpan}; use errors::{Handler, ColorConfig, DiagnosticBuilder}; use feature_gate::UnstableFeatures; use parse::parser::Parser; @@ -57,6 +58,7 @@ pub struct ParseSess { /// Used to determine and report recursive mod inclusions included_mod_stack: Lock<Vec<PathBuf>>, code_map: Lrc<CodeMap>, + pub buffered_lints: Lock<Vec<BufferedEarlyLint>>, } impl ParseSess { @@ -80,12 +82,29 @@ impl ParseSess { included_mod_stack: Lock::new(vec![]), code_map, non_modrs_mods: Lock::new(vec![]), + buffered_lints: Lock::new(vec![]), } } pub fn codemap(&self) -> &CodeMap { &self.code_map } + + pub fn buffer_lint<S: Into<MultiSpan>>(&self, + lint_id: BufferedEarlyLintId, + span: S, + id: NodeId, + msg: &str, + ) { + self.buffered_lints.with_lock(|buffered_lints| { + buffered_lints.push(BufferedEarlyLint{ + span: span.into(), + id, + msg: msg.into(), + lint_id, + }); + }); + } } #[derive(Clone)] |
