diff options
Diffstat (limited to 'compiler/rustc_builtin_macros')
20 files changed, 666 insertions, 712 deletions
| diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index fd34f947f72..9031c3b2ecf 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -15,6 +15,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_lexer = { path = "../rustc_lexer" } +rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_parse = { path = "../rustc_parse" } rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 198287f608e..1a6e5694791 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -8,20 +8,21 @@ use rustc_expand::base::{self, *}; use rustc_parse::parser::Parser; use rustc_parse_format as parse; use rustc_session::lint; +use rustc_session::parse::ParseSess; use rustc_span::symbol::Ident; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{InnerSpan, Span}; use rustc_target::asm::InlineAsmArch; use smallvec::smallvec; -struct AsmArgs { - templates: Vec<P<ast::Expr>>, - operands: Vec<(ast::InlineAsmOperand, Span)>, +pub struct AsmArgs { + pub templates: Vec<P<ast::Expr>>, + pub operands: Vec<(ast::InlineAsmOperand, Span)>, named_args: FxHashMap<Symbol, usize>, reg_args: FxHashSet<usize>, - clobber_abi: Option<(Symbol, Span)>, + pub clobber_abis: Vec<(Symbol, Span)>, options: ast::InlineAsmOptions, - options_spans: Vec<Span>, + pub options_spans: Vec<Span>, } fn parse_args<'a>( @@ -31,31 +32,22 @@ fn parse_args<'a>( is_global_asm: bool, ) -> Result<AsmArgs, DiagnosticBuilder<'a>> { let mut p = ecx.new_parser_from_tts(tts); + let sess = &ecx.sess.parse_sess; + parse_asm_args(&mut p, sess, sp, is_global_asm) +} - if p.token == token::Eof { - return Err(ecx.struct_span_err(sp, "requires at least a template string argument")); - } +// Primarily public for rustfmt consumption. +// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm` +pub fn parse_asm_args<'a>( + p: &mut Parser<'a>, + sess: &'a ParseSess, + sp: Span, + is_global_asm: bool, +) -> Result<AsmArgs, DiagnosticBuilder<'a>> { + let diag = &sess.span_diagnostic; - // Detect use of the legacy llvm_asm! syntax (which used to be called asm!) - if !is_global_asm && p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) { - let mut err = - ecx.struct_span_err(sp, "the legacy LLVM-style asm! syntax is no longer supported"); - err.note("consider migrating to the new asm! syntax specified in RFC 2873"); - err.note("alternatively, switch to llvm_asm! to keep your code working as it is"); - - // Find the span of the "asm!" so that we can offer an automatic suggestion - let asm_span = sp.from_inner(InnerSpan::new(0, 4)); - if let Ok(s) = ecx.source_map().span_to_snippet(asm_span) { - if s == "asm!" { - err.span_suggestion( - asm_span, - "replace with", - "llvm_asm!".into(), - Applicability::MachineApplicable, - ); - } - } - return Err(err); + if p.token == token::Eof { + return Err(diag.struct_span_err(sp, "requires at least a template string argument")); } let first_template = p.parse_expr()?; @@ -64,7 +56,7 @@ fn parse_args<'a>( operands: vec![], named_args: FxHashMap::default(), reg_args: FxHashSet::default(), - clobber_abi: None, + clobber_abis: Vec::new(), options: ast::InlineAsmOptions::empty(), options_spans: vec![], }; @@ -74,7 +66,7 @@ fn parse_args<'a>( if !p.eat(&token::Comma) { if allow_templates { // After a template string, we always expect *only* a comma... - let mut err = ecx.struct_span_err(p.token.span, "expected token: `,`"); + let mut err = diag.struct_span_err(p.token.span, "expected token: `,`"); err.span_label(p.token.span, "expected `,`"); p.maybe_annotate_with_ascription(&mut err, false); return Err(err); @@ -89,14 +81,14 @@ fn parse_args<'a>( // Parse clobber_abi if p.eat_keyword(sym::clobber_abi) { - parse_clobber_abi(&mut p, &mut args)?; + parse_clobber_abi(p, &mut args)?; allow_templates = false; continue; } // Parse options if p.eat_keyword(sym::options) { - parse_options(&mut p, &mut args, is_global_asm)?; + parse_options(p, &mut args, is_global_asm)?; allow_templates = false; continue; } @@ -116,25 +108,25 @@ fn parse_args<'a>( let mut explicit_reg = false; let op = if !is_global_asm && p.eat_keyword(kw::In) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { - let err = ecx.struct_span_err(p.token.span, "_ cannot be used for input operands"); + let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); return Err(err); } let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } } else if !is_global_asm && p.eat_keyword(sym::out) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } } else if !is_global_asm && p.eat_keyword(sym::lateout) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } } else if !is_global_asm && p.eat_keyword(sym::inout) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { - let err = ecx.struct_span_err(p.token.span, "_ cannot be used for input operands"); + let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); return Err(err); } let expr = p.parse_expr()?; @@ -146,9 +138,9 @@ fn parse_args<'a>( ast::InlineAsmOperand::InOut { reg, expr, late: false } } } else if !is_global_asm && p.eat_keyword(sym::inlateout) { - let reg = parse_reg(&mut p, &mut explicit_reg)?; + let reg = parse_reg(p, &mut explicit_reg)?; if p.eat_keyword(kw::Underscore) { - let err = ecx.struct_span_err(p.token.span, "_ cannot be used for input operands"); + let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands"); return Err(err); } let expr = p.parse_expr()?; @@ -167,7 +159,7 @@ fn parse_args<'a>( match expr.kind { ast::ExprKind::Path(..) => {} _ => { - let err = ecx + let err = diag .struct_span_err(expr.span, "argument to `sym` must be a path expression"); return Err(err); } @@ -186,7 +178,7 @@ fn parse_args<'a>( } else { "expected operand, clobber_abi, options, or additional template string" }; - let mut err = ecx.struct_span_err(template.span, errstr); + let mut err = diag.struct_span_err(template.span, errstr); err.span_label(template.span, errstr); return Err(err); } @@ -206,31 +198,31 @@ fn parse_args<'a>( // clobber_abi/options. We do this at the end once we have the full span // of the argument available. if !args.options_spans.is_empty() { - ecx.struct_span_err(span, "arguments are not allowed after options") + diag.struct_span_err(span, "arguments are not allowed after options") .span_labels(args.options_spans.clone(), "previous options") .span_label(span, "argument") .emit(); - } else if let Some((_, abi_span)) = args.clobber_abi { - ecx.struct_span_err(span, "arguments are not allowed after clobber_abi") - .span_label(abi_span, "clobber_abi") + } else if let Some((_, abi_span)) = args.clobber_abis.last() { + diag.struct_span_err(span, "arguments are not allowed after clobber_abi") + .span_label(*abi_span, "clobber_abi") .span_label(span, "argument") .emit(); } if explicit_reg { if name.is_some() { - ecx.struct_span_err(span, "explicit register arguments cannot have names").emit(); + diag.struct_span_err(span, "explicit register arguments cannot have names").emit(); } args.reg_args.insert(slot); } else if let Some(name) = name { if let Some(&prev) = args.named_args.get(&name) { - ecx.struct_span_err(span, &format!("duplicate argument named `{}`", name)) + diag.struct_span_err(span, &format!("duplicate argument named `{}`", name)) .span_label(args.operands[prev].1, "previously here") .span_label(span, "duplicate argument") .emit(); continue; } if !args.reg_args.is_empty() { - let mut err = ecx.struct_span_err( + let mut err = diag.struct_span_err( span, "named arguments cannot follow explicit register arguments", ); @@ -243,7 +235,7 @@ fn parse_args<'a>( args.named_args.insert(name, slot); } else { if !args.named_args.is_empty() || !args.reg_args.is_empty() { - let mut err = ecx.struct_span_err( + let mut err = diag.struct_span_err( span, "positional arguments cannot follow named arguments \ or explicit register arguments", @@ -264,21 +256,21 @@ fn parse_args<'a>( && args.options.contains(ast::InlineAsmOptions::READONLY) { let spans = args.options_spans.clone(); - ecx.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive") + diag.struct_span_err(spans, "the `nomem` and `readonly` options are mutually exclusive") .emit(); } if args.options.contains(ast::InlineAsmOptions::PURE) && args.options.contains(ast::InlineAsmOptions::NORETURN) { let spans = args.options_spans.clone(); - ecx.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive") + diag.struct_span_err(spans, "the `pure` and `noreturn` options are mutually exclusive") .emit(); } if args.options.contains(ast::InlineAsmOptions::PURE) && !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY) { let spans = args.options_spans.clone(); - ecx.struct_span_err( + diag.struct_span_err( spans, "the `pure` option must be combined with either `nomem` or `readonly`", ) @@ -309,33 +301,39 @@ fn parse_args<'a>( } } if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { - ecx.struct_span_err( + diag.struct_span_err( args.options_spans.clone(), "asm with the `pure` option must have at least one output", ) .emit(); } if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() { - let err = ecx + let err = diag .struct_span_err(outputs_sp, "asm outputs are not allowed with the `noreturn` option"); // Bail out now since this is likely to confuse MIR return Err(err); } - if let Some((_, abi_span)) = args.clobber_abi { + + if args.clobber_abis.len() > 0 { if is_global_asm { - let err = - ecx.struct_span_err(abi_span, "`clobber_abi` cannot be used with `global_asm!`"); + let err = diag.struct_span_err( + args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), + "`clobber_abi` cannot be used with `global_asm!`", + ); // Bail out now since this is likely to confuse later stages return Err(err); } if !regclass_outputs.is_empty() { - ecx.struct_span_err( + diag.struct_span_err( regclass_outputs.clone(), "asm with `clobber_abi` must specify explicit registers for outputs", ) - .span_label(abi_span, "clobber_abi") + .span_labels( + args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(), + "clobber_abi", + ) .span_labels(regclass_outputs, "generic outputs") .emit(); } @@ -414,6 +412,8 @@ fn parse_options<'a>( try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX); } else if p.eat_keyword(kw::Raw) { try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW); + } else if p.eat_keyword(sym::may_unwind) { + try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::MAY_UNWIND); } else { return p.unexpected(); } @@ -439,37 +439,61 @@ fn parse_clobber_abi<'a>( p.expect(&token::OpenDelim(token::DelimToken::Paren))?; - let clobber_abi = match p.parse_str_lit() { - Ok(str_lit) => str_lit.symbol_unescaped, - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); - err.span_label(span, "not a string literal"); - return Err(err); - } - }; + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + let err = p.sess.span_diagnostic.struct_span_err( + p.token.span, + "at least one abi must be provided as an argument to `clobber_abi`", + ); + return Err(err); + } - p.expect(&token::CloseDelim(token::DelimToken::Paren))?; + let mut new_abis = Vec::new(); + loop { + match p.parse_str_lit() { + Ok(str_lit) => { + new_abis.push((str_lit.symbol_unescaped, str_lit.span)); + } + Err(opt_lit) => { + // If the non-string literal is a closing paren then it's the end of the list and is fine + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + break; + } + let span = opt_lit.map_or(p.token.span, |lit| lit.span); + let mut err = + p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); + err.span_label(span, "not a string literal"); + return Err(err); + } + }; - let new_span = span_start.to(p.prev_token.span); + // Allow trailing commas + if p.eat(&token::CloseDelim(token::DelimToken::Paren)) { + break; + } + p.expect(&token::Comma)?; + } - if let Some((_, prev_span)) = args.clobber_abi { - let mut err = p - .sess - .span_diagnostic - .struct_span_err(new_span, "clobber_abi specified multiple times"); - err.span_label(prev_span, "clobber_abi previously specified here"); - return Err(err); - } else if !args.options_spans.is_empty() { + let full_span = span_start.to(p.prev_token.span); + + if !args.options_spans.is_empty() { let mut err = p .sess .span_diagnostic - .struct_span_err(new_span, "clobber_abi is not allowed after options"); + .struct_span_err(full_span, "clobber_abi is not allowed after options"); err.span_labels(args.options_spans.clone(), "options"); return Err(err); } - args.clobber_abi = Some((clobber_abi, new_span)); + match &new_abis[..] { + // should have errored above during parsing + [] => unreachable!(), + [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), + [abis @ ..] => { + for (abi, span) in abis { + args.clobber_abis.push((*abi, *span)); + } + } + } Ok(()) } @@ -540,14 +564,14 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl template_snippet.as_ref().map(|s| Symbol::intern(s)), template_sp, )); - let template_str = &template_str.as_str(); + let template_str = template_str.as_str(); if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch { let find_span = |needle: &str| -> Span { if let Some(snippet) = &template_snippet { if let Some(pos) = snippet.find(needle) { let end = pos - + &snippet[pos..] + + snippet[pos..] .find(|c| matches!(c, '\n' | ';' | '\\' | '"')) .unwrap_or(snippet[pos..].len() - 1); let inner = InnerSpan::new(pos, end); @@ -680,7 +704,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl Some(&idx) => Some(idx), None => { let msg = format!("there is no argument named `{}`", name); - ecx.struct_span_err(span, &msg[..]).emit(); + ecx.struct_span_err(span, &msg).emit(); None } }, @@ -770,13 +794,13 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl template, template_strs: template_strs.into_boxed_slice(), operands: args.operands, - clobber_abi: args.clobber_abi, + clobber_abis: args.clobber_abis, options: args.options, line_spans, }) } -pub fn expand_asm<'cx>( +pub(super) fn expand_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -803,7 +827,7 @@ pub fn expand_asm<'cx>( } } -pub fn expand_global_asm<'cx>( +pub(super) fn expand_global_asm<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, @@ -815,7 +839,7 @@ pub fn expand_global_asm<'cx>( ident: Ident::empty(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(inline_asm), + kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), vis: ast::Visibility { span: sp.shrink_to_lo(), kind: ast::VisibilityKind::Inherited, diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 307730f7f5f..31086a2acf8 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -1,4 +1,4 @@ -use crate::util::check_builtin_macro_attribute; +use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; use rustc_ast as ast; use rustc_ast::mut_visit::MutVisitor; @@ -11,7 +11,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; use rustc_feature::Features; -use rustc_parse::parser::ForceCollect; +use rustc_parse::parser::{ForceCollect, Parser}; use rustc_session::utils::FlattenNonterminals; use rustc_session::Session; use rustc_span::symbol::sym; @@ -25,6 +25,7 @@ crate fn expand( annotatable: Annotatable, ) -> Vec<Annotatable> { check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); + warn_on_duplicate_attribute(&ecx, &annotatable, sym::cfg_eval); vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable)] } @@ -77,6 +78,10 @@ fn flat_map_annotatable( Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param), Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef), Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant), + Annotatable::Crate(mut krate) => { + vis.visit_crate(&mut krate); + Some(Annotatable::Crate(krate)) + } } } @@ -101,6 +106,7 @@ impl CfgFinder { Annotatable::Param(param) => finder.visit_param(¶m), Annotatable::FieldDef(field) => finder.visit_field_def(&field), Annotatable::Variant(variant) => finder.visit_variant(&variant), + Annotatable::Crate(krate) => finder.visit_crate(krate), }; finder.has_cfg_or_cfg_attr } @@ -138,8 +144,34 @@ impl CfgEval<'_, '_> { // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization // process is lossless, so this process is invisible to proc-macros. - // FIXME - get rid of this clone - let nt = annotatable.clone().into_nonterminal(); + let parse_annotatable_with: fn(&mut Parser<'_>) -> _ = match annotatable { + Annotatable::Item(_) => { + |parser| Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap()) + } + Annotatable::TraitItem(_) => |parser| { + Annotatable::TraitItem( + parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ) + }, + Annotatable::ImplItem(_) => |parser| { + Annotatable::ImplItem( + parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ) + }, + Annotatable::ForeignItem(_) => |parser| { + Annotatable::ForeignItem( + parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), + ) + }, + Annotatable::Stmt(_) => |parser| { + Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap())) + }, + Annotatable::Expr(_) => { + |parser| Annotatable::Expr(parser.parse_expr_force_collect().unwrap()) + } + _ => unreachable!(), + }; + let nt = annotatable.into_nonterminal(); let mut orig_tokens = rustc_parse::nt_to_tokenstream( &nt, @@ -173,25 +205,7 @@ impl CfgEval<'_, '_> { let mut parser = rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None); parser.capture_cfg = true; - annotatable = match annotatable { - Annotatable::Item(_) => { - Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap()) - } - Annotatable::TraitItem(_) => Annotatable::TraitItem( - parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), - ), - Annotatable::ImplItem(_) => Annotatable::ImplItem( - parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), - ), - Annotatable::ForeignItem(_) => Annotatable::ForeignItem( - parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(), - ), - Annotatable::Stmt(_) => { - Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap())) - } - Annotatable::Expr(_) => Annotatable::Expr(parser.parse_expr_force_collect().unwrap()), - _ => unreachable!(), - }; + annotatable = parse_annotatable_with(&mut parser); // Now that we have our re-parsed `AttrAnnotatedTokenStream`, recursively configuring // our attribute target will correctly the tokens as well. diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs index e5077d93674..59361510a67 100644 --- a/compiler/rustc_builtin_macros/src/concat.rs +++ b/compiler/rustc_builtin_macros/src/concat.rs @@ -21,7 +21,7 @@ pub fn expand_concat( match e.kind { ast::ExprKind::Lit(ref lit) => match lit.kind { ast::LitKind::Str(ref s, _) | ast::LitKind::Float(ref s, _) => { - accumulator.push_str(&s.as_str()); + accumulator.push_str(s.as_str()); } ast::LitKind::Char(c) => { accumulator.push(c); diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs new file mode 100644 index 00000000000..eb08170959b --- /dev/null +++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs @@ -0,0 +1,192 @@ +use rustc_ast as ast; +use rustc_ast::{ptr::P, tokenstream::TokenStream}; +use rustc_data_structures::sync::Lrc; +use rustc_errors::Applicability; +use rustc_expand::base::{self, DummyResult}; + +/// Emits errors for literal expressions that are invalid inside and outside of an array. +fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) { + let lit = if let ast::ExprKind::Lit(lit) = &expr.kind { + lit + } else { + unreachable!(); + }; + match lit.kind { + ast::LitKind::Char(_) => { + let mut err = cx.struct_span_err(expr.span, "cannot concatenate character literals"); + if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) { + err.span_suggestion( + expr.span, + "try using a byte character", + format!("b{}", snippet), + Applicability::MachineApplicable, + ) + .emit(); + } + } + ast::LitKind::Str(_, _) => { + let mut err = cx.struct_span_err(expr.span, "cannot concatenate string literals"); + // suggestion would be invalid if we are nested + if !is_nested { + if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) { + err.span_suggestion( + expr.span, + "try using a byte string", + format!("b{}", snippet), + Applicability::MachineApplicable, + ); + } + } + err.emit(); + } + ast::LitKind::Float(_, _) => { + cx.span_err(expr.span, "cannot concatenate float literals"); + } + ast::LitKind::Bool(_) => { + cx.span_err(expr.span, "cannot concatenate boolean literals"); + } + ast::LitKind::Err(_) => {} + ast::LitKind::Int(_, _) if !is_nested => { + let mut err = cx.struct_span_err(expr.span, "cannot concatenate numeric literals"); + if let Ok(snippet) = cx.sess.source_map().span_to_snippet(expr.span) { + err.span_suggestion( + expr.span, + "try wrapping the number in an array", + format!("[{}]", snippet), + Applicability::MachineApplicable, + ); + } + err.emit(); + } + ast::LitKind::Int( + val, + ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), + ) => { + assert!(val > u8::MAX.into()); // must be an error + cx.span_err(expr.span, "numeric literal is out of bounds"); + } + ast::LitKind::Int(_, _) => { + cx.span_err(expr.span, "numeric literal is not a `u8`"); + } + _ => unreachable!(), + } +} + +fn handle_array_element( + cx: &mut base::ExtCtxt<'_>, + has_errors: &mut bool, + missing_literals: &mut Vec<rustc_span::Span>, + expr: &P<rustc_ast::Expr>, +) -> Option<u8> { + match expr.kind { + ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => { + if !*has_errors { + cx.span_err(expr.span, "cannot concatenate doubly nested array"); + } + *has_errors = true; + None + } + ast::ExprKind::Lit(ref lit) => match lit.kind { + ast::LitKind::Int( + val, + ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), + ) if val <= u8::MAX.into() => Some(val as u8), + + ast::LitKind::Byte(val) => Some(val), + ast::LitKind::ByteStr(_) => { + if !*has_errors { + cx.struct_span_err(expr.span, "cannot concatenate doubly nested array") + .note("byte strings are treated as arrays of bytes") + .help("try flattening the array") + .emit(); + } + *has_errors = true; + None + } + _ => { + if !*has_errors { + invalid_type_err(cx, expr, true); + } + *has_errors = true; + None + } + }, + _ => { + missing_literals.push(expr.span); + None + } + } +} + +pub fn expand_concat_bytes( + cx: &mut base::ExtCtxt<'_>, + sp: rustc_span::Span, + tts: TokenStream, +) -> Box<dyn base::MacResult + 'static> { + let es = match base::get_exprs_from_tts(cx, sp, tts) { + Some(e) => e, + None => return DummyResult::any(sp), + }; + let mut accumulator = Vec::new(); + let mut missing_literals = vec![]; + let mut has_errors = false; + for e in es { + match e.kind { + ast::ExprKind::Array(ref exprs) => { + for expr in exprs { + if let Some(elem) = + handle_array_element(cx, &mut has_errors, &mut missing_literals, expr) + { + accumulator.push(elem); + } + } + } + ast::ExprKind::Repeat(ref expr, ref count) => { + if let ast::ExprKind::Lit(ast::Lit { + kind: ast::LitKind::Int(count_val, _), .. + }) = count.value.kind + { + if let Some(elem) = + handle_array_element(cx, &mut has_errors, &mut missing_literals, expr) + { + for _ in 0..count_val { + accumulator.push(elem); + } + } + } else { + cx.span_err(count.value.span, "repeat count is not a positive number"); + } + } + ast::ExprKind::Lit(ref lit) => match lit.kind { + ast::LitKind::Byte(val) => { + accumulator.push(val); + } + ast::LitKind::ByteStr(ref bytes) => { + accumulator.extend_from_slice(&bytes); + } + _ => { + if !has_errors { + invalid_type_err(cx, &e, false); + } + has_errors = true; + } + }, + ast::ExprKind::Err => { + has_errors = true; + } + _ => { + missing_literals.push(e.span); + } + } + } + if !missing_literals.is_empty() { + let mut err = cx.struct_span_err(missing_literals.clone(), "expected a byte literal"); + err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`"); + err.emit(); + return base::MacEager::expr(DummyResult::raw_expr(sp, true)); + } else if has_errors { + return base::MacEager::expr(DummyResult::raw_expr(sp, true)); + } + let sp = cx.with_def_site_ctxt(sp); + base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::from(accumulator)))) +} diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs index 53a456b69ac..239bafb266a 100644 --- a/compiler/rustc_builtin_macros/src/concat_idents.rs +++ b/compiler/rustc_builtin_macros/src/concat_idents.rs @@ -29,7 +29,7 @@ pub fn expand_concat_idents<'cx>( } else { if let TokenTree::Token(token) = e { if let Some((ident, _)) = token.ident() { - res_str.push_str(&ident.name.as_str()); + res_str.push_str(ident.name.as_str()); continue; } } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 31a35b9b7b4..47d7b6c259e 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -121,7 +121,7 @@ fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool { fn report_unexpected_literal(sess: &Session, lit: &ast::Lit) { let help_msg = match lit.token.kind { - token::Str if rustc_lexer::is_ident(&lit.token.symbol.as_str()) => { + token::Str if rustc_lexer::is_ident(lit.token.symbol.as_str()) => { format!("try using `#[derive({})]`", lit.token.symbol) } _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(), diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index a225b328ab6..985c45e2253 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -557,18 +557,21 @@ impl<'a> TraitDef<'a> { tokens: None, }, attrs: Vec::new(), - kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAliasKind( - ast::Defaultness::Final, - Generics::default(), - Vec::new(), - Some(type_def.to_ty(cx, self.span, type_ident, generics)), - ))), + kind: ast::AssocItemKind::TyAlias(Box::new(ast::TyAlias { + defaultness: ast::Defaultness::Final, + generics: Generics::default(), + bounds: Vec::new(), + ty: Some(type_def.to_ty(cx, self.span, type_ident, generics)), + })), tokens: None, }) }); - let Generics { mut params, mut where_clause, span } = + let Generics { mut params, mut where_clause, .. } = self.generics.to_generics(cx, self.span, type_ident, generics); + where_clause.span = generics.where_clause.span; + let ctxt = self.span.ctxt(); + let span = generics.span.with_ctxt(ctxt); // Create the generic parameters params.extend(generics.params.iter().map(|param| match ¶m.kind { @@ -589,12 +592,12 @@ impl<'a> TraitDef<'a> { param.bounds.iter().cloned() ).collect(); - cx.typaram(self.span, param.ident, vec![], bounds, None) + cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, vec![], bounds, None) } GenericParamKind::Const { ty, kw_span, .. } => { let const_nodefault_kind = GenericParamKind::Const { ty: ty.clone(), - kw_span: *kw_span, + kw_span: kw_span.with_ctxt(ctxt), // We can't have default values inside impl block default: None, @@ -607,28 +610,27 @@ impl<'a> TraitDef<'a> { // and similarly for where clauses where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { - match *clause { - ast::WherePredicate::BoundPredicate(ref wb) => { + match clause { + ast::WherePredicate::BoundPredicate(wb) => { + let span = wb.span.with_ctxt(ctxt); ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span: self.span, - bound_generic_params: wb.bound_generic_params.clone(), - bounded_ty: wb.bounded_ty.clone(), - bounds: wb.bounds.to_vec(), + span, + ..wb.clone() }) } - ast::WherePredicate::RegionPredicate(ref rb) => { + ast::WherePredicate::RegionPredicate(wr) => { + let span = wr.span.with_ctxt(ctxt); ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { - span: self.span, - lifetime: rb.lifetime, - bounds: rb.bounds.to_vec(), + span, + ..wr.clone() }) } - ast::WherePredicate::EqPredicate(ref we) => { + ast::WherePredicate::EqPredicate(we) => { + let span = we.span.with_ctxt(ctxt); ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { id: ast::DUMMY_NODE_ID, - span: self.span, - lhs_ty: we.lhs_ty.clone(), - rhs_ty: we.rhs_ty.clone(), + span, + ..we.clone() }) } } @@ -691,13 +693,13 @@ impl<'a> TraitDef<'a> { .iter() .map(|param| match param.kind { GenericParamKind::Lifetime { .. } => { - GenericArg::Lifetime(cx.lifetime(self.span, param.ident)) + GenericArg::Lifetime(cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident)) } GenericParamKind::Type { .. } => { - GenericArg::Type(cx.ty_ident(self.span, param.ident)) + GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) } GenericParamKind::Const { .. } => { - GenericArg::Const(cx.const_ident(self.span, param.ident)) + GenericArg::Const(cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident)) } }) .collect(); @@ -726,7 +728,7 @@ impl<'a> TraitDef<'a> { self.span, Ident::empty(), a, - ast::ItemKind::Impl(Box::new(ast::ImplKind { + ast::ItemKind::Impl(Box::new(ast::Impl { unsafety, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, @@ -764,8 +766,8 @@ impl<'a> TraitDef<'a> { self, struct_def, type_ident, - &self_args[..], - &nonself_args[..], + &self_args, + &nonself_args, ) } else { method_def.expand_struct_method_body( @@ -773,8 +775,8 @@ impl<'a> TraitDef<'a> { self, struct_def, type_ident, - &self_args[..], - &nonself_args[..], + &self_args, + &nonself_args, use_temporaries, ) }; @@ -813,8 +815,8 @@ impl<'a> TraitDef<'a> { self, enum_def, type_ident, - &self_args[..], - &nonself_args[..], + &self_args, + &nonself_args, ) } else { method_def.expand_enum_method_body( @@ -823,7 +825,7 @@ impl<'a> TraitDef<'a> { enum_def, type_ident, self_args, - &nonself_args[..], + &nonself_args, ) }; @@ -845,16 +847,17 @@ impl<'a> MethodDef<'a> { nonself_args: &[P<Expr>], fields: &SubstructureFields<'_>, ) -> P<Expr> { + let span = trait_.span; let substructure = Substructure { type_ident, - method_ident: Ident::new(self.name, trait_.span), + method_ident: Ident::new(self.name, span), self_args, nonself_args, fields, }; let mut f = self.combine_substructure.borrow_mut(); let f: &mut CombineSubstructureFunc<'_> = &mut *f; - f(cx, trait_.span, &substructure) + f(cx, span, &substructure) } fn get_ret_ty( @@ -882,9 +885,10 @@ impl<'a> MethodDef<'a> { let mut nonself_args = Vec::new(); let mut arg_tys = Vec::new(); let mut nonstatic = false; + let span = trait_.span; let ast_explicit_self = self.explicit_self.as_ref().map(|self_ptr| { - let (self_expr, explicit_self) = ty::get_explicit_self(cx, trait_.span, self_ptr); + let (self_expr, explicit_self) = ty::get_explicit_self(cx, span, self_ptr); self_args.push(self_expr); nonstatic = true; @@ -893,11 +897,11 @@ impl<'a> MethodDef<'a> { }); for (ty, name) in self.args.iter() { - let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics); - let ident = Ident::new(*name, trait_.span); + let ast_ty = ty.to_ty(cx, span, type_ident, generics); + let ident = Ident::new(*name, span); arg_tys.push((ident, ast_ty)); - let arg_expr = cx.expr_ident(trait_.span, ident); + let arg_expr = cx.expr_ident(span, ident); match *ty { // for static methods, just treat any Self @@ -906,7 +910,7 @@ impl<'a> MethodDef<'a> { self_args.push(arg_expr); } Ptr(ref ty, _) if matches!(**ty, Self_) && nonstatic => { - self_args.push(cx.expr_deref(trait_.span, arg_expr)) + self_args.push(cx.expr_deref(span, arg_expr)) } _ => { nonself_args.push(arg_expr); @@ -927,53 +931,53 @@ impl<'a> MethodDef<'a> { arg_types: Vec<(Ident, P<ast::Ty>)>, body: P<Expr>, ) -> P<ast::AssocItem> { + let span = trait_.span; // Create the generics that aren't for `Self`. - let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics); + let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); let args = { let self_args = explicit_self.map(|explicit_self| { - let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(trait_.span); + let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span); ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) }); - let nonself_args = - arg_types.into_iter().map(|(name, ty)| cx.param(trait_.span, name, ty)); + let nonself_args = arg_types.into_iter().map(|(name, ty)| cx.param(span, name, ty)); self_args.into_iter().chain(nonself_args).collect() }; let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); - let method_ident = Ident::new(self.name, trait_.span); + let method_ident = Ident::new(self.name, span); let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type)); let body_block = cx.block_expr(body); - let unsafety = if self.is_unsafe { ast::Unsafe::Yes(trait_.span) } else { ast::Unsafe::No }; + let unsafety = if self.is_unsafe { ast::Unsafe::Yes(span) } else { ast::Unsafe::No }; - let trait_lo_sp = trait_.span.shrink_to_lo(); + let trait_lo_sp = span.shrink_to_lo(); let sig = ast::FnSig { header: ast::FnHeader { unsafety, ext: ast::Extern::None, ..ast::FnHeader::default() }, decl: fn_decl, - span: trait_.span, + span, }; - let def = ast::Defaultness::Final; + let defaultness = ast::Defaultness::Final; // Create the method. P(ast::AssocItem { id: ast::DUMMY_NODE_ID, attrs: self.attributes.clone(), - span: trait_.span, + span, vis: ast::Visibility { span: trait_lo_sp, kind: ast::VisibilityKind::Inherited, tokens: None, }, ident: method_ident, - kind: ast::AssocItemKind::Fn(Box::new(ast::FnKind( - def, + kind: ast::AssocItemKind::Fn(Box::new(ast::Fn { + defaultness, sig, - fn_generics, - Some(body_block), - ))), + generics: fn_generics, + body: Some(body_block), + })), tokens: None, }) } @@ -1024,11 +1028,11 @@ impl<'a> MethodDef<'a> { nonself_args: &[P<Expr>], use_temporaries: bool, ) -> P<Expr> { - let mut raw_fields = Vec::new(); // Vec<[fields of self], - // [fields of next Self arg], [etc]> + let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]> + let span = trait_.span; let mut patterns = Vec::new(); for i in 0..self_args.len() { - let struct_path = cx.path(trait_.span, vec![type_ident]); + let struct_path = cx.path(span, vec![type_ident]); let (pat, ident_expr) = trait_.create_struct_pattern( cx, struct_path, @@ -1048,7 +1052,7 @@ impl<'a> MethodDef<'a> { let mut other_fields: Vec<vec::IntoIter<_>> = raw_fields.collect(); first_field .map(|(span, opt_id, field, attrs)| FieldInfo { - span, + span: span.with_ctxt(trait_.span.ctxt()), name: opt_id, self_: field, other: other_fields @@ -1062,7 +1066,7 @@ impl<'a> MethodDef<'a> { }) .collect() } else { - cx.span_bug(trait_.span, "no `self` parameter for method in generic `derive`") + cx.span_bug(span, "no `self` parameter for method in generic `derive`") }; // body of the inner most destructuring match @@ -1079,11 +1083,7 @@ impl<'a> MethodDef<'a> { // structs. This is actually right-to-left, but it shouldn't // matter. for (arg_expr, pat) in iter::zip(self_args, patterns) { - body = cx.expr_match( - trait_.span, - arg_expr.clone(), - vec![cx.arm(trait_.span, pat.clone(), body)], - ) + body = cx.expr_match(span, arg_expr.clone(), vec![cx.arm(span, pat.clone(), body)]) } body @@ -1193,7 +1193,7 @@ impl<'a> MethodDef<'a> { mut self_args: Vec<P<Expr>>, nonself_args: &[P<Expr>], ) -> P<Expr> { - let sp = trait_.span; + let span = trait_.span; let variants = &enum_def.variants; let self_arg_names = iter::once("__self".to_string()) @@ -1208,7 +1208,7 @@ impl<'a> MethodDef<'a> { let self_arg_idents = self_arg_names .iter() - .map(|name| Ident::from_str_and_span(name, sp)) + .map(|name| Ident::from_str_and_span(name, span)) .collect::<Vec<Ident>>(); // The `vi_idents` will be bound, solely in the catch-all, to @@ -1217,8 +1217,8 @@ impl<'a> MethodDef<'a> { let vi_idents = self_arg_names .iter() .map(|name| { - let vi_suffix = format!("{}_vi", &name[..]); - Ident::from_str_and_span(&vi_suffix, trait_.span) + let vi_suffix = format!("{}_vi", name); + Ident::from_str_and_span(&vi_suffix, span) }) .collect::<Vec<Ident>>(); @@ -1226,7 +1226,7 @@ impl<'a> MethodDef<'a> { // delegated expression that handles the catch-all case, // using `__variants_tuple` to drive logic if necessary. let catch_all_substructure = - EnumNonMatchingCollapsed(self_arg_idents, &variants[..], &vi_idents[..]); + EnumNonMatchingCollapsed(self_arg_idents, &variants, &vi_idents); let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); @@ -1248,7 +1248,7 @@ impl<'a> MethodDef<'a> { self_arg_name, ast::Mutability::Not, ); - (cx.pat(sp, PatKind::Ref(p, ast::Mutability::Not)), idents) + (cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents) }; // A single arm has form (&VariantK, &VariantK, ...) => BodyK @@ -1261,13 +1261,13 @@ impl<'a> MethodDef<'a> { idents }; for self_arg_name in &self_arg_names[1..] { - let (p, idents) = mk_self_pat(cx, &self_arg_name[..]); + let (p, idents) = mk_self_pat(cx, &self_arg_name); subpats.push(p); self_pats_idents.push(idents); } // Here is the pat = `(&VariantK, &VariantK, ...)` - let single_pat = cx.pat_tuple(sp, subpats); + let single_pat = cx.pat_tuple(span, subpats); // For the BodyK, we need to delegate to our caller, // passing it an EnumMatching to indicate which case @@ -1284,7 +1284,7 @@ impl<'a> MethodDef<'a> { .into_iter() .enumerate() // For each arg field of self, pull out its getter expr ... - .map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| { + .map(|(field_index, (span, opt_ident, self_getter_expr, attrs))| { // ... but FieldInfo also wants getter expr // for matching other arguments of Self type; // so walk across the *other* self_pats_idents @@ -1307,7 +1307,7 @@ impl<'a> MethodDef<'a> { .collect::<Vec<P<Expr>>>(); FieldInfo { - span: sp, + span, name: opt_ident, self_: self_getter_expr, other: others, @@ -1330,7 +1330,7 @@ impl<'a> MethodDef<'a> { &substructure, ); - cx.arm(sp, single_pat, arm_expr) + cx.arm(span, single_pat, arm_expr) }) .collect(); @@ -1353,12 +1353,12 @@ impl<'a> MethodDef<'a> { // Since we know that all the arguments will match if we reach // the match expression we add the unreachable intrinsics as the // result of the catch all which should help llvm in optimizing it - Some(deriving::call_unreachable(cx, sp)) + Some(deriving::call_unreachable(cx, span)) } _ => None, }; if let Some(arm) = default { - match_arms.push(cx.arm(sp, cx.pat_wild(sp), arm)); + match_arms.push(cx.arm(span, cx.pat_wild(span), arm)); } // We will usually need the catch-all after matching the @@ -1392,23 +1392,23 @@ impl<'a> MethodDef<'a> { // We also build an expression which checks whether all discriminants are equal // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... - let mut discriminant_test = cx.expr_bool(sp, true); + let mut discriminant_test = cx.expr_bool(span, true); let mut first_ident = None; for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) { - let self_addr = cx.expr_addr_of(sp, self_arg.clone()); + let self_addr = cx.expr_addr_of(span, self_arg.clone()); let variant_value = - deriving::call_intrinsic(cx, sp, sym::discriminant_value, vec![self_addr]); - let let_stmt = cx.stmt_let(sp, false, ident, variant_value); + deriving::call_intrinsic(cx, span, sym::discriminant_value, vec![self_addr]); + let let_stmt = cx.stmt_let(span, false, ident, variant_value); index_let_stmts.push(let_stmt); match first_ident { Some(first) => { - let first_expr = cx.expr_ident(sp, first); - let id = cx.expr_ident(sp, ident); - let test = cx.expr_binary(sp, BinOpKind::Eq, first_expr, id); + let first_expr = cx.expr_ident(span, first); + let id = cx.expr_ident(span, ident); + let test = cx.expr_binary(span, BinOpKind::Eq, first_expr, id); discriminant_test = - cx.expr_binary(sp, BinOpKind::And, discriminant_test, test) + cx.expr_binary(span, BinOpKind::And, discriminant_test, test) } None => { first_ident = Some(ident); @@ -1430,8 +1430,8 @@ impl<'a> MethodDef<'a> { // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - self_args.map_in_place(|self_arg| cx.expr_addr_of(sp, self_arg)); - let match_arg = cx.expr(sp, ast::ExprKind::Tup(self_args)); + self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); + let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); // Lastly we create an expression which branches on all discriminants being equal // if discriminant_test { @@ -1445,10 +1445,10 @@ impl<'a> MethodDef<'a> { // else { // <delegated expression referring to __self0_vi, et al.> // } - let all_match = cx.expr_match(sp, match_arg, match_arms); - let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr)); + let all_match = cx.expr_match(span, match_arg, match_arms); + let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr)); index_let_stmts.push(cx.stmt_expr(arm_expr)); - cx.expr_block(cx.block(sp, index_let_stmts)) + cx.expr_block(cx.block(span, index_let_stmts)) } else if variants.is_empty() { // As an additional wrinkle, For a zero-variant enum A, // currently the compiler @@ -1499,16 +1499,16 @@ impl<'a> MethodDef<'a> { // derive Debug on such a type could here generate code // that needs the feature gate enabled.) - deriving::call_unreachable(cx, sp) + deriving::call_unreachable(cx, span) } else { // Final wrinkle: the self_args are expressions that deref // down to desired places, but we cannot actually deref // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - self_args.map_in_place(|self_arg| cx.expr_addr_of(sp, self_arg)); - let match_arg = cx.expr(sp, ast::ExprKind::Tup(self_args)); - cx.expr_match(sp, match_arg, match_arms) + self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); + let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); + cx.expr_match(span, match_arg, match_arms) } } @@ -1556,11 +1556,9 @@ impl<'a> TraitDef<'a> { let is_tuple = matches!(struct_def, ast::VariantData::Tuple(..)); match (just_spans.is_empty(), named_idents.is_empty()) { - (false, false) => cx.span_bug( - self.span, - "a struct with named and unnamed \ - fields in generic `derive`", - ), + (false, false) => { + cx.span_bug(self.span, "a struct with named and unnamed fields in generic `derive`") + } // named fields (_, false) => Named(named_idents), // unnamed fields diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 00d75be4399..7a418003250 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -211,14 +211,6 @@ fn mk_ty_param( cx.typaram(span, Ident::new(name, span), attrs.to_owned(), bounds, None) } -fn mk_generics(params: Vec<ast::GenericParam>, span: Span) -> Generics { - Generics { - params, - where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span }, - span, - } -} - /// Bounds on type parameters. #[derive(Clone)] pub struct Bounds { @@ -236,7 +228,7 @@ impl Bounds { self_ty: Ident, self_generics: &Generics, ) -> Generics { - let generic_params = self + let params = self .bounds .iter() .map(|t| { @@ -245,7 +237,11 @@ impl Bounds { }) .collect(); - mk_generics(generic_params, span) + Generics { + params, + where_clause: ast::WhereClause { has_where_token: false, predicates: Vec::new(), span }, + span, + } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index fa389a51115..367a5aa7323 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -2,7 +2,7 @@ use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::{ImplKind, ItemKind, MetaItem}; +use rustc_ast::{Impl, ItemKind, MetaItem}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -180,7 +180,7 @@ fn inject_impl_of_structural_trait( span, Ident::empty(), attrs, - ItemKind::Impl(Box::new(ImplKind { + ItemKind::Impl(Box::new(Impl { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 6de12acfb94..285027fc632 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -80,11 +80,11 @@ pub fn expand_env<'cx>( } let sp = cx.with_def_site_ctxt(sp); - let value = env::var(&*var.as_str()).ok().as_deref().map(Symbol::intern); + let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { - cx.span_err(sp, &msg.as_str()); + cx.span_err(sp, msg.as_str()); return DummyResult::any(sp); } Some(value) => cx.expr_str(sp, value), diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index f0056cb7976..41b56cca1dc 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -11,6 +11,7 @@ use rustc_expand::base::{self, *}; use rustc_parse_format as parse; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{MultiSpan, Span}; +use smallvec::SmallVec; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -88,8 +89,8 @@ struct Context<'a, 'b> { /// * Implicit argument resolution: `"{1:.0$} {2:.foo$} {1:.3$} {4:.0$}"` /// * Name resolution: `"{1:.0$} {2:.5$} {1:.3$} {4:.0$}"` /// * `count_positions` (in JSON): `{0: 0, 5: 1, 3: 2}` - /// * `count_args`: `vec![Exact(0), Exact(5), Exact(3)]` - count_args: Vec<Position>, + /// * `count_args`: `vec![0, 5, 3]` + count_args: Vec<usize>, /// Relative slot numbers for count arguments. count_positions: FxHashMap<usize, usize>, /// Number of count slots assigned. @@ -513,7 +514,7 @@ impl<'a, 'b> Context<'a, 'b> { if let Entry::Vacant(e) = self.count_positions.entry(arg) { let i = self.count_positions_count; e.insert(i); - self.count_args.push(Exact(arg)); + self.count_args.push(arg); self.count_positions_count += 1; } } @@ -527,17 +528,9 @@ impl<'a, 'b> Context<'a, 'b> { self.verify_arg_type(Exact(idx), ty) } None => { - let capture_feature_enabled = self - .ecx - .ecfg - .features - .map_or(false, |features| features.format_args_capture); - // For the moment capturing variables from format strings expanded from macros is // disabled (see RFC #2795) - let can_capture = capture_feature_enabled && self.is_literal; - - if can_capture { + if self.is_literal { // Treat this name as a variable to capture from the surrounding scope let idx = self.args.len(); self.arg_types.push(Vec::new()); @@ -557,25 +550,17 @@ impl<'a, 'b> Context<'a, 'b> { } else { self.fmtsp }; - let mut err = self.ecx.struct_span_err(sp, &msg[..]); - - if capture_feature_enabled && !self.is_literal { - err.note(&format!( - "did you intend to capture a variable `{}` from \ - the surrounding scope?", - name - )); - err.note( - "to avoid ambiguity, `format_args!` cannot capture variables \ - when the format string is expanded from a macro", - ); - } else if self.ecx.parse_sess().unstable_features.is_nightly_build() { - err.help(&format!( - "if you intended to capture `{}` from the surrounding scope, add \ - `#![feature(format_args_capture)]` to the crate attributes", - name - )); - } + let mut err = self.ecx.struct_span_err(sp, &msg); + + err.note(&format!( + "did you intend to capture a variable `{}` from \ + the surrounding scope?", + name + )); + err.note( + "to avoid ambiguity, `format_args!` cannot capture variables \ + when the format string is expanded from a macro", + ); err.emit(); } @@ -760,91 +745,95 @@ impl<'a, 'b> Context<'a, 'b> { /// Actually builds the expression which the format_args! block will be /// expanded to. fn into_expr(self) -> P<ast::Expr> { - let mut locals = - Vec::with_capacity((0..self.args.len()).map(|i| self.arg_unique_types[i].len()).sum()); - let mut counts = Vec::with_capacity(self.count_args.len()); - let mut pats = Vec::with_capacity(self.args.len()); - let mut heads = Vec::with_capacity(self.args.len()); - - let names_pos: Vec<_> = (0..self.args.len()) - .map(|i| Ident::from_str_and_span(&format!("arg{}", i), self.macsp)) - .collect(); + let mut original_args = self.args; + let mut fmt_args = Vec::with_capacity( + self.arg_unique_types.iter().map(|v| v.len()).sum::<usize>() + self.count_args.len(), + ); // First, build up the static array which will become our precompiled // format "string" let pieces = self.ecx.expr_vec_slice(self.fmtsp, self.str_pieces); - // Before consuming the expressions, we have to remember spans for - // count arguments as they are now generated separate from other - // arguments, hence have no access to the `P<ast::Expr>`'s. - let spans_pos: Vec<_> = self.args.iter().map(|e| e.span).collect(); - - // Right now there is a bug such that for the expression: - // foo(bar(&1)) - // the lifetime of `1` doesn't outlast the call to `bar`, so it's not - // valid for the call to `foo`. To work around this all arguments to the - // format! string are shoved into locals. Furthermore, we shove the address - // of each variable because we don't want to move out of the arguments - // passed to this function. - for (i, e) in self.args.into_iter().enumerate() { - let name = names_pos[i]; - let span = self.ecx.with_def_site_ctxt(e.span); - pats.push(self.ecx.pat_ident(span, name)); - for arg_ty in self.arg_unique_types[i].iter() { - locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, name)); - } - heads.push(self.ecx.expr_addr_of(e.span, e)); - } - for pos in self.count_args { - let index = match pos { - Exact(i) => i, - _ => panic!("should never happen"), - }; - let name = names_pos[index]; - let span = spans_pos[index]; - counts.push(Context::format_arg(self.ecx, self.macsp, span, &Count, name)); + // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments + // constructor. In general the expressions in this slice might be + // permuted from their order in original_args (such as in the case of + // "{1} {0}"), or may have multiple entries referring to the same + // element of original_args ("{0} {0}"). + // + // The following vector has one item per element of our output slice, + // identifying the index of which element of original_args it's passing, + // and that argument's type. + let mut fmt_arg_index_and_ty = SmallVec::<[(usize, &ArgumentType); 8]>::new(); + for (i, unique_types) in self.arg_unique_types.iter().enumerate() { + fmt_arg_index_and_ty.extend(unique_types.iter().map(|ty| (i, ty))); } + fmt_arg_index_and_ty.extend(self.count_args.iter().map(|&i| (i, &Count))); - // Now create a vector containing all the arguments - let args = locals.into_iter().chain(counts.into_iter()); - - let args_array = self.ecx.expr_vec(self.macsp, args.collect()); - - // Constructs an AST equivalent to: + // Figure out whether there are permuted or repeated elements. If not, + // we can generate simpler code. // - // match (&arg0, &arg1) { - // (tmp0, tmp1) => args_array - // } - // - // It was: + // The sequence has no indices out of order or repeated if: for every + // adjacent pair of elements, the first one's index is less than the + // second one's index. + let nicely_ordered = + fmt_arg_index_and_ty.array_windows().all(|[(i, _i_ty), (j, _j_ty)]| i < j); + + // We want to emit: // - // let tmp0 = &arg0; - // let tmp1 = &arg1; - // args_array + // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …] // - // Because of #11585 the new temporary lifetime rule, the enclosing - // statements for these temporaries become the let's themselves. - // If one or more of them are RefCell's, RefCell borrow() will also - // end there; they don't last long enough for args_array to use them. - // The match expression solves the scope problem. + // However, it's only legal to do so if $arg0, $arg1, … were written in + // exactly that order by the programmer. When arguments are permuted, we + // want them evaluated in the order written by the programmer, not in + // the order provided to fmt::Arguments. When arguments are repeated, we + // want the expression evaluated only once. // - // Note, it may also very well be transformed to: + // Thus in the not nicely ordered case we emit the following instead: // - // match arg0 { - // ref tmp0 => { - // match arg1 => { - // ref tmp1 => args_array } } } + // match (&$arg0, &$arg1, …) { + // args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …] + // } // - // But the nested match expression is proved to perform not as well - // as series of let's; the first approach does. - let args_match = { - let pat = self.ecx.pat_tuple(self.macsp, pats); - let arm = self.ecx.arm(self.macsp, pat, args_array); - let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads)); - self.ecx.expr_match(self.macsp, head, vec![arm]) - }; + // for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty. + for (arg_index, arg_ty) in fmt_arg_index_and_ty { + let e = &mut original_args[arg_index]; + let span = e.span; + let arg = if nicely_ordered { + let expansion_span = e.span.with_ctxt(self.macsp.ctxt()); + // The indices are strictly ordered so e has not been taken yet. + self.ecx.expr_addr_of(expansion_span, P(e.take())) + } else { + let def_site = self.ecx.with_def_site_ctxt(span); + let args_tuple = self.ecx.expr_ident(def_site, Ident::new(sym::args, def_site)); + let member = Ident::new(sym::integer(arg_index), def_site); + self.ecx.expr(def_site, ast::ExprKind::Field(args_tuple, member)) + }; + fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg)); + } - let args_slice = self.ecx.expr_addr_of(self.macsp, args_match); + let args_array = self.ecx.expr_vec(self.macsp, fmt_args); + let args_slice = self.ecx.expr_addr_of( + self.macsp, + if nicely_ordered { + args_array + } else { + // In the !nicely_ordered case, none of the exprs were moved + // away in the previous loop. + // + // This uses the arg span for `&arg` so that borrowck errors + // point to the specific expression passed to the macro (the + // span is otherwise unavailable in the MIR used by borrowck). + let heads = original_args + .into_iter() + .map(|e| self.ecx.expr_addr_of(e.span.with_ctxt(self.macsp.ctxt()), e)) + .collect(); + + let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::args, self.macsp)); + let arm = self.ecx.arm(self.macsp, pat, args_array); + let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads)); + self.ecx.expr_match(self.macsp, head, vec![arm]) + }, + ); // Now create the fmt::Arguments struct with all our locals we created. let (fn_name, fn_args) = if self.all_pieces_simple { @@ -877,10 +866,9 @@ impl<'a, 'b> Context<'a, 'b> { macsp: Span, mut sp: Span, ty: &ArgumentType, - arg: Ident, + arg: P<ast::Expr>, ) -> P<ast::Expr> { sp = ecx.with_def_site_ctxt(sp); - let arg = ecx.expr_ident(sp, arg); let trait_ = match *ty { Placeholder(trait_) if trait_ == "<invalid>" => return DummyResult::raw_expr(sp, true), Placeholder(trait_) => trait_, @@ -889,11 +877,21 @@ impl<'a, 'b> Context<'a, 'b> { return ecx.expr_call_global(macsp, path, vec![arg]); } }; + let new_fn_name = match trait_ { + "Display" => "new_display", + "Debug" => "new_debug", + "LowerExp" => "new_lower_exp", + "UpperExp" => "new_upper_exp", + "Octal" => "new_octal", + "Pointer" => "new_pointer", + "Binary" => "new_binary", + "LowerHex" => "new_lower_hex", + "UpperHex" => "new_upper_hex", + _ => unreachable!(), + }; - let path = ecx.std_path(&[sym::fmt, Symbol::intern(trait_), sym::fmt]); - let format_fn = ecx.path_global(sp, path); - let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::new]); - ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)]) + let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, Symbol::intern(new_fn_name)]); + ecx.expr_call_global(sp, path, vec![arg]) } } @@ -983,7 +981,7 @@ pub fn expand_preparsed_format_args( ast::StrStyle::Raw(raw) => Some(raw as usize), }; - let fmt_str = &fmt_str.as_str(); // for the suggestions below + let fmt_str = fmt_str.as_str(); // for the suggestions below let fmt_snippet = ecx.source_map().span_to_snippet(fmt_sp).ok(); let mut parser = parse::Parser::new( fmt_str, @@ -1023,8 +1021,9 @@ pub fn expand_preparsed_format_args( e.note(¬e); } if let Some((label, span)) = err.secondary_label { - let sp = fmt_span.from_inner(span); - e.span_label(sp, label); + if efmt_kind_is_lit { + e.span_label(fmt_span.from_inner(span), label); + } } e.emit(); return DummyResult::raw_expr(sp, true); diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index bfddd7073ff..ecd16736e7c 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -7,28 +7,29 @@ pub(crate) mod printf { pub enum Substitution<'a> { /// A formatted output substitution with its internal byte offset. Format(Format<'a>), - /// A literal `%%` escape. - Escape, + /// A literal `%%` escape, with its start and end indices. + Escape((usize, usize)), } impl<'a> Substitution<'a> { pub fn as_str(&self) -> &str { match *self { Substitution::Format(ref fmt) => fmt.span, - Substitution::Escape => "%%", + Substitution::Escape(_) => "%%", } } pub fn position(&self) -> Option<InnerSpan> { match *self { Substitution::Format(ref fmt) => Some(fmt.position), - _ => None, + Substitution::Escape((start, end)) => Some(InnerSpan::new(start, end)), } } pub fn set_position(&mut self, start: usize, end: usize) { - if let Substitution::Format(ref mut fmt) = self { - fmt.position = InnerSpan::new(start, end); + match self { + Substitution::Format(ref mut fmt) => fmt.position = InnerSpan::new(start, end), + Substitution::Escape(ref mut pos) => *pos = (start, end), } } @@ -39,7 +40,7 @@ pub(crate) mod printf { pub fn translate(&self) -> Result<String, Option<String>> { match *self { Substitution::Format(ref fmt) => fmt.translate(), - Substitution::Escape => Err(None), + Substitution::Escape(_) => Err(None), } } } @@ -304,14 +305,9 @@ pub(crate) mod printf { fn next(&mut self) -> Option<Self::Item> { let (mut sub, tail) = parse_next_substitution(self.s)?; self.s = tail; - match sub { - Substitution::Format(_) => { - if let Some(inner_span) = sub.position() { - sub.set_position(inner_span.start + self.pos, inner_span.end + self.pos); - self.pos += inner_span.end; - } - } - Substitution::Escape => self.pos += 2, + if let Some(InnerSpan { start, end }) = sub.position() { + sub.set_position(start + self.pos, end + self.pos); + self.pos += end; } Some(sub) } @@ -340,7 +336,7 @@ pub(crate) mod printf { let at = { let start = s.find('%')?; if let '%' = s[start + 1..].chars().next()? { - return Some((Substitution::Escape, &s[start + 2..])); + return Some((Substitution::Escape((start, start + 2)), &s[start + 2..])); } Cur::new_at(s, start) diff --git a/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs b/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs index 1336aab7316..fc7442470ac 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs @@ -13,9 +13,9 @@ macro_rules! assert_eq_pnsat { fn test_escape() { assert_eq!(pns("has no escapes"), None); assert_eq!(pns("has no escapes, either %"), None); - assert_eq!(pns("*so* has a %% escape"), Some((S::Escape, " escape"))); - assert_eq!(pns("%% leading escape"), Some((S::Escape, " leading escape"))); - assert_eq!(pns("trailing escape %%"), Some((S::Escape, ""))); + assert_eq!(pns("*so* has a %% escape"), Some((S::Escape((11, 13)), " escape"))); + assert_eq!(pns("%% leading escape"), Some((S::Escape((0, 2)), " leading escape"))); + assert_eq!(pns("trailing escape %%"), Some((S::Escape((16, 18)), ""))); } #[test] diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 3f71ee6f489..a433876147f 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -5,7 +5,7 @@ use rustc_ast::expand::allocator::{ }; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; -use rustc_ast::{FnKind, ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; +use rustc_ast::{Fn, ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -84,13 +84,13 @@ impl AllocFnFactory<'_, '_> { let decl = self.cx.fn_decl(abi_args, ast::FnRetTy::Ty(output_ty)); let header = FnHeader { unsafety: Unsafe::Yes(self.span), ..FnHeader::default() }; let sig = FnSig { decl, header, span: self.span }; - let block = Some(self.cx.block_expr(output_expr)); - let kind = ItemKind::Fn(Box::new(FnKind( - ast::Defaultness::Final, + let body = Some(self.cx.block_expr(output_expr)); + let kind = ItemKind::Fn(Box::new(Fn { + defaultness: ast::Defaultness::Final, sig, - Generics::default(), - block, - ))); + generics: Generics::default(), + body, + })); let item = self.cx.item( self.span, Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span), diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index d1d276930b9..6c16c285492 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -2,11 +2,12 @@ //! injecting code into the crate before it is lowered to HIR. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_windows)] #![feature(box_patterns)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] -#![feature(iter_zip)] +#![feature(is_sorted)] #![feature(nll)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] @@ -20,13 +21,13 @@ use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; use rustc_expand::proc_macro::BangProcMacro; use rustc_span::symbol::sym; -mod asm; mod assert; mod cfg; mod cfg_accessible; mod cfg_eval; mod compile_error; mod concat; +mod concat_bytes; mod concat_idents; mod derive; mod deriving; @@ -34,7 +35,6 @@ mod env; mod format; mod format_foreign; mod global_allocator; -mod llvm_asm; mod log_syntax; mod panic; mod source_util; @@ -42,6 +42,7 @@ mod test; mod trace_macros; mod util; +pub mod asm; pub mod cmdline_attrs; pub mod proc_macro_harness; pub mod standard_library_imports; @@ -65,6 +66,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { cfg: cfg::expand_cfg, column: source_util::expand_column, compile_error: compile_error::expand_compile_error, + concat_bytes: concat_bytes::expand_concat_bytes, concat_idents: concat_idents::expand_concat_idents, concat: concat::expand_concat, env: env::expand_env, @@ -77,7 +79,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { include_str: source_util::expand_include_str, include: source_util::expand_include, line: source_util::expand_line, - llvm_asm: llvm_asm::expand_llvm_asm, log_syntax: log_syntax::expand_log_syntax, module_path: source_util::expand_mod, option_env: env::expand_option_env, diff --git a/compiler/rustc_builtin_macros/src/llvm_asm.rs b/compiler/rustc_builtin_macros/src/llvm_asm.rs deleted file mode 100644 index d72bfa660e5..00000000000 --- a/compiler/rustc_builtin_macros/src/llvm_asm.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Llvm-style inline assembly support. -// -use State::*; - -use rustc_ast as ast; -use rustc_ast::ptr::P; -use rustc_ast::token::{self, Token}; -use rustc_ast::tokenstream::{self, TokenStream}; -use rustc_ast::LlvmAsmDialect; -use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult}; -use rustc_expand::base::*; -use rustc_parse::parser::Parser; -use rustc_span::symbol::{kw, sym, Symbol}; -use rustc_span::Span; - -enum State { - Asm, - Outputs, - Inputs, - Clobbers, - Options, - StateNone, -} - -impl State { - fn next(&self) -> State { - match *self { - Asm => Outputs, - Outputs => Inputs, - Inputs => Clobbers, - Clobbers => Options, - Options => StateNone, - StateNone => StateNone, - } - } -} - -const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel]; - -pub fn expand_llvm_asm<'cx>( - cx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> Box<dyn MacResult + 'cx> { - let mut inline_asm = match parse_inline_asm(cx, sp, tts) { - Ok(Some(inline_asm)) => inline_asm, - Ok(None) => return DummyResult::any(sp), - Err(mut err) => { - err.emit(); - return DummyResult::any(sp); - } - }; - - // If there are no outputs, the inline assembly is executed just for its side effects, - // so ensure that it is volatile - if inline_asm.outputs.is_empty() { - inline_asm.volatile = true; - } - - MacEager::expr(P(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::LlvmInlineAsm(P(inline_asm)), - span: cx.with_def_site_ctxt(sp), - attrs: ast::AttrVec::new(), - tokens: None, - })) -} - -fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> { - match p.parse_str_lit() { - Ok(str_lit) => Ok(str_lit.symbol_unescaped), - Err(opt_lit) => { - let span = opt_lit.map_or(p.token.span, |lit| lit.span); - let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal"); - err.span_label(span, "not a string literal"); - Err(err) - } - } -} - -fn parse_inline_asm<'a>( - cx: &mut ExtCtxt<'a>, - sp: Span, - tts: TokenStream, -) -> Result<Option<ast::LlvmInlineAsm>, DiagnosticBuilder<'a>> { - // Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being - // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription. - let first_colon = tts - .trees() - .position(|tt| { - matches!( - tt, - tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) - ) - }) - .unwrap_or(tts.len()); - let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect()); - let mut asm = kw::Empty; - let mut asm_str_style = None; - let mut outputs = Vec::new(); - let mut inputs = Vec::new(); - let mut clobs = Vec::new(); - let mut volatile = false; - let mut alignstack = false; - let mut dialect = LlvmAsmDialect::Att; - - let mut state = Asm; - - 'statement: loop { - match state { - Asm => { - if asm_str_style.is_some() { - // If we already have a string with instructions, - // ending up in Asm state again is an error. - return Err(struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - sp, - E0660, - "malformed inline assembly" - )); - } - // Nested parser, stop before the first colon (see above). - let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect()); - - if p2.token == token::Eof { - let mut err = - cx.struct_span_err(sp, "macro requires a string literal as an argument"); - err.span_label(sp, "string literal required"); - return Err(err); - } - - let expr = p2.parse_expr()?; - let (s, style) = - match expr_to_string(cx, expr, "inline assembly must be a string literal") { - Some((s, st)) => (s, st), - None => return Ok(None), - }; - - // This is most likely malformed. - if p2.token != token::Eof { - let mut extra_tts = p2.parse_all_token_trees()?; - extra_tts.extend(tts.trees().skip(first_colon)); - p = cx.new_parser_from_tts(extra_tts.into_iter().collect()); - } - - asm = s; - asm_str_style = Some(style); - } - Outputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !outputs.is_empty() { - p.eat(&token::Comma); - } - - let constraint = parse_asm_str(&mut p)?; - - let span = p.prev_token.span; - - p.expect(&token::OpenDelim(token::Paren))?; - let expr = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; - - // Expands a read+write operand into two operands. - // - // Use '+' modifier when you want the same expression - // to be both an input and an output at the same time. - // It's the opposite of '=&' which means that the memory - // cannot be shared with any other operand (usually when - // a register is clobbered early.) - let constraint_str = constraint.as_str(); - let mut ch = constraint_str.chars(); - let output = match ch.next() { - Some('=') => None, - Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))), - _ => { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - span, - E0661, - "output operand constraint lacks '=' or '+'" - ) - .emit(); - None - } - }; - - let is_rw = output.is_some(); - let is_indirect = constraint_str.contains('*'); - outputs.push(ast::LlvmInlineAsmOutput { - constraint: output.unwrap_or(constraint), - expr, - is_rw, - is_indirect, - }); - } - } - Inputs => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !inputs.is_empty() { - p.eat(&token::Comma); - } - - let constraint = parse_asm_str(&mut p)?; - - if constraint.as_str().starts_with('=') { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - p.prev_token.span, - E0662, - "input operand constraint contains '='" - ) - .emit(); - } else if constraint.as_str().starts_with('+') { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - p.prev_token.span, - E0663, - "input operand constraint contains '+'" - ) - .emit(); - } - - p.expect(&token::OpenDelim(token::Paren))?; - let input = p.parse_expr()?; - p.expect(&token::CloseDelim(token::Paren))?; - - inputs.push((constraint, input)); - } - } - Clobbers => { - while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep { - if !clobs.is_empty() { - p.eat(&token::Comma); - } - - let s = parse_asm_str(&mut p)?; - - if OPTIONS.iter().any(|&opt| s == opt) { - cx.span_warn(p.prev_token.span, "expected a clobber, found an option"); - } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') { - struct_span_err!( - cx.sess.parse_sess.span_diagnostic, - p.prev_token.span, - E0664, - "clobber should not be surrounded by braces" - ) - .emit(); - } - - clobs.push(s); - } - } - Options => { - let option = parse_asm_str(&mut p)?; - - if option == sym::volatile { - // Indicates that the inline assembly has side effects - // and must not be optimized out along with its outputs. - volatile = true; - } else if option == sym::alignstack { - alignstack = true; - } else if option == sym::intel { - dialect = LlvmAsmDialect::Intel; - } else { - cx.span_warn(p.prev_token.span, "unrecognized option"); - } - - if p.token == token::Comma { - p.eat(&token::Comma); - } - } - StateNone => (), - } - - loop { - // MOD_SEP is a double colon '::' without space in between. - // When encountered, the state must be advanced twice. - match (&p.token.kind, state.next(), state.next().next()) { - (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => { - p.bump(); - break 'statement; - } - (&token::Colon, st, _) | (&token::ModSep, _, st) => { - p.bump(); - state = st; - } - (&token::Eof, ..) => break 'statement, - _ => break, - } - } - } - - Ok(Some(ast::LlvmInlineAsm { - asm, - asm_str_style: asm_str_style.unwrap(), - outputs, - inputs, - clobbers: clobs, - volatile, - alignstack, - dialect, - })) -} diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index bbca07085ea..c08b141b557 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -1,6 +1,6 @@ /// The expansion from a test function to the appropriate test struct for libtest /// Ideally, this code would be in libtest but for efficiency and error messages it lives here. -use crate::util::check_builtin_macro_attribute; +use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; use rustc_ast as ast; use rustc_ast::attr; @@ -27,6 +27,7 @@ pub fn expand_test_case( anno_item: Annotatable, ) -> Vec<Annotatable> { check_builtin_macro_attribute(ecx, meta_item, sym::test_case); + warn_on_duplicate_attribute(&ecx, &anno_item, sym::test_case); if !ecx.ecfg.should_test { return vec![]; @@ -55,6 +56,7 @@ pub fn expand_test( item: Annotatable, ) -> Vec<Annotatable> { check_builtin_macro_attribute(cx, meta_item, sym::test); + warn_on_duplicate_attribute(&cx, &item, sym::test); expand_test_or_bench(cx, attr_sp, item, false) } @@ -65,6 +67,7 @@ pub fn expand_bench( item: Annotatable, ) -> Vec<Annotatable> { check_builtin_macro_attribute(cx, meta_item, sym::bench); + warn_on_duplicate_attribute(&cx, &item, sym::bench); expand_test_or_bench(cx, attr_sp, item, true) } @@ -429,7 +432,7 @@ fn test_type(cx: &ExtCtxt<'_>) -> TestType { fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic); let sd = &cx.sess.parse_sess.span_diagnostic; - if let ast::ItemKind::Fn(box ast::FnKind(_, ref sig, ref generics, _)) = i.kind { + if let ast::ItemKind::Fn(box ast::Fn { ref sig, ref generics, .. }) = i.kind { if let ast::Unsafe::Yes(span) = sig.header.unsafety { sd.struct_span_err(i.span, "unsafe functions cannot be used for tests") .span_label(span, "`unsafe` because of this") @@ -478,7 +481,7 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { } fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { - let has_sig = if let ast::ItemKind::Fn(box ast::FnKind(_, ref sig, _, _)) = i.kind { + let has_sig = if let ast::ItemKind::Fn(box ast::Fn { ref sig, .. }) = i.kind { // N.B., inadequate check, but we're running // well before resolve, can't get too deep. sig.decl.inputs.len() == 1 diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index d791677cb8e..418729e7843 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -84,9 +84,35 @@ struct TestHarnessGenerator<'a> { tests: Vec<Test>, } +impl TestHarnessGenerator<'_> { + fn add_test_cases(&mut self, node_id: ast::NodeId, span: Span, prev_tests: Vec<Test>) { + let mut tests = mem::replace(&mut self.tests, prev_tests); + + if !tests.is_empty() { + // Create an identifier that will hygienically resolve the test + // case name, even in another module. + let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass( + span, + AstPass::TestHarness, + &[], + Some(node_id), + ); + for test in &mut tests { + // See the comment on `mk_main` for why we're using + // `apply_mark` directly. + test.ident.span = + test.ident.span.apply_mark(expn_id.to_expn_id(), Transparency::Opaque); + } + self.cx.test_cases.extend(tests); + } + } +} + impl<'a> MutVisitor for TestHarnessGenerator<'a> { fn visit_crate(&mut self, c: &mut ast::Crate) { + let prev_tests = mem::take(&mut self.tests); noop_visit_crate(c, self); + self.add_test_cases(ast::CRATE_NODE_ID, c.span, prev_tests); // Create a main function to run our tests c.items.push(mk_main(&mut self.cx)); @@ -103,34 +129,10 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { // We don't want to recurse into anything other than mods, since // mods or tests inside of functions will break things - if let ast::ItemKind::Mod(..) = item.kind { - let tests = mem::take(&mut self.tests); + if let ast::ItemKind::Mod(_, ModKind::Loaded(.., span)) = item.kind { + let prev_tests = mem::take(&mut self.tests); noop_visit_item_kind(&mut item.kind, self); - let mut tests = mem::replace(&mut self.tests, tests); - - if !tests.is_empty() { - let parent = - if item.id == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { item.id }; - // Create an identifier that will hygienically resolve the test - // case name, even in another module. - let inner_span = match item.kind { - ast::ItemKind::Mod(_, ModKind::Loaded(.., span)) => span, - _ => unreachable!(), - }; - let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass( - inner_span, - AstPass::TestHarness, - &[], - Some(parent), - ); - for test in &mut tests { - // See the comment on `mk_main` for why we're using - // `apply_mark` directly. - test.ident.span = - test.ident.span.apply_mark(expn_id.to_expn_id(), Transparency::Opaque); - } - self.cx.test_cases.extend(tests); - } + self.add_test_cases(item.id, span, prev_tests); } smallvec![P(item)] } @@ -146,7 +148,7 @@ fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPoin } else if sess.contains_name(&item.attrs, sym::rustc_main) { EntryPointType::MainAttr } else if item.ident.name == sym::main { - if depth == 1 { + if depth == 0 { // This is a top-level function so can be 'main' EntryPointType::MainNamed } else { @@ -313,13 +315,13 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> { let decl = ecx.fn_decl(vec![], ast::FnRetTy::Ty(main_ret_ty)); let sig = ast::FnSig { decl, header: ast::FnHeader::default(), span: sp }; - let def = ast::Defaultness::Final; - let main = ast::ItemKind::Fn(Box::new(ast::FnKind( - def, + let defaultness = ast::Defaultness::Final; + let main = ast::ItemKind::Fn(Box::new(ast::Fn { + defaultness, sig, - ast::Generics::default(), - Some(main_body), - ))); + generics: ast::Generics::default(), + body: Some(main_body), + })); // Honor the reexport_test_harness_main attribute let main_id = match cx.reexport_test_harness_main { diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 01ea80c4c8a..527fe50eff0 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -1,6 +1,7 @@ -use rustc_ast::MetaItem; -use rustc_expand::base::ExtCtxt; +use rustc_ast::{Attribute, MetaItem}; +use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_feature::AttributeTemplate; +use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; use rustc_parse::validate_attr; use rustc_span::Symbol; @@ -10,3 +11,33 @@ pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, na let attr = ecx.attribute(meta_item.clone()); validate_attr::check_builtin_attribute(&ecx.sess.parse_sess, &attr, name, template); } + +/// Emit a warning if the item is annotated with the given attribute. This is used to diagnose when +/// an attribute may have been mistakenly duplicated. +pub fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, name: Symbol) { + let attrs: Option<&[Attribute]> = match item { + Annotatable::Item(item) => Some(&item.attrs), + Annotatable::TraitItem(item) => Some(&item.attrs), + Annotatable::ImplItem(item) => Some(&item.attrs), + Annotatable::ForeignItem(item) => Some(&item.attrs), + Annotatable::Expr(expr) => Some(&expr.attrs), + Annotatable::Arm(arm) => Some(&arm.attrs), + Annotatable::ExprField(field) => Some(&field.attrs), + Annotatable::PatField(field) => Some(&field.attrs), + Annotatable::GenericParam(param) => Some(¶m.attrs), + Annotatable::Param(param) => Some(¶m.attrs), + Annotatable::FieldDef(def) => Some(&def.attrs), + Annotatable::Variant(variant) => Some(&variant.attrs), + _ => None, + }; + if let Some(attrs) = attrs { + if let Some(attr) = ecx.sess.find_by_name(attrs, name) { + ecx.parse_sess().buffer_lint( + DUPLICATE_MACRO_ATTRIBUTES, + attr.span, + ecx.current_expansion.lint_node_id, + "duplicated attribute", + ); + } + } +} | 
