diff options
Diffstat (limited to 'compiler/rustc_builtin_macros/src')
20 files changed, 799 insertions, 446 deletions
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 8d8b3f4f6aa..652165fb9b6 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -7,17 +7,19 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_expand::base::{self, *}; use rustc_parse::parser::Parser; use rustc_parse_format as parse; -use rustc_span::{ - symbol::{kw, sym, Symbol}, - BytePos, -}; +use rustc_session::lint; +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)>, named_args: FxHashMap<Symbol, usize>, reg_args: FxHashSet<usize>, + clobber_abi: Option<(Symbol, Span)>, options: ast::InlineAsmOptions, options_spans: Vec<Span>, } @@ -26,6 +28,7 @@ fn parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, + is_global_asm: bool, ) -> Result<AsmArgs, DiagnosticBuilder<'a>> { let mut p = ecx.new_parser_from_tts(tts); @@ -34,7 +37,7 @@ fn parse_args<'a>( } // Detect use of the legacy llvm_asm! syntax (which used to be called asm!) - if p.look_ahead(1, |t| *t == token::Colon || *t == token::ModSep) { + 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"); @@ -61,6 +64,7 @@ fn parse_args<'a>( operands: vec![], named_args: FxHashMap::default(), reg_args: FxHashSet::default(), + clobber_abi: None, options: ast::InlineAsmOptions::empty(), options_spans: vec![], }; @@ -83,9 +87,16 @@ fn parse_args<'a>( break; } // accept trailing commas + // Parse clobber_abi + if p.eat_keyword(sym::clobber_abi) { + parse_clobber_abi(&mut p, &mut args)?; + allow_templates = false; + continue; + } + // Parse options if p.eat_keyword(sym::options) { - parse_options(&mut p, &mut args)?; + parse_options(&mut p, &mut args, is_global_asm)?; allow_templates = false; continue; } @@ -104,19 +115,19 @@ fn parse_args<'a>( }; let mut explicit_reg = false; - let op = if p.eat_keyword(kw::In) { + let op = if !is_global_asm && p.eat_keyword(kw::In) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } - } else if p.eat_keyword(sym::out) { + } else if !is_global_asm && p.eat_keyword(sym::out) { let reg = parse_reg(&mut 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 p.eat_keyword(sym::lateout) { + } else if !is_global_asm && p.eat_keyword(sym::lateout) { let reg = parse_reg(&mut 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 p.eat_keyword(sym::inout) { + } else if !is_global_asm && p.eat_keyword(sym::inout) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = p.parse_expr()?; if p.eat(&token::FatArrow) { @@ -126,7 +137,7 @@ fn parse_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: false } } - } else if p.eat_keyword(sym::inlateout) { + } else if !is_global_asm && p.eat_keyword(sym::inlateout) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = p.parse_expr()?; if p.eat(&token::FatArrow) { @@ -137,9 +148,9 @@ fn parse_args<'a>( ast::InlineAsmOperand::InOut { reg, expr, late: true } } } else if p.eat_keyword(kw::Const) { - let expr = p.parse_expr()?; - ast::InlineAsmOperand::Const { expr } - } else if p.eat_keyword(sym::sym) { + let anon_const = p.parse_anon_const_expr()?; + ast::InlineAsmOperand::Const { anon_const } + } else if !is_global_asm && p.eat_keyword(sym::sym) { let expr = p.parse_expr()?; match expr.kind { ast::ExprKind::Path(..) => {} @@ -158,7 +169,11 @@ fn parse_args<'a>( ast::ExprKind::Lit(ast::Lit { kind: ast::LitKind::Str(..), .. }) => {} ast::ExprKind::MacCall(..) => {} _ => { - let errstr = "expected operand, options, or additional template string"; + let errstr = if is_global_asm { + "expected operand, options, or additional template string" + } else { + "expected operand, clobber_abi, options, or additional template string" + }; let mut err = ecx.struct_span_err(template.span, errstr); err.span_label(template.span, errstr); return Err(err); @@ -175,13 +190,19 @@ fn parse_args<'a>( let slot = args.operands.len(); args.operands.push((op, span)); - // Validate the order of named, positional & explicit register operands and options. We do - // this at the end once we have the full span of the argument available. + // Validate the order of named, positional & explicit register operands and + // 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") .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") + .span_label(span, "argument") + .emit(); } if explicit_reg { if name.is_some() { @@ -254,16 +275,23 @@ fn parse_args<'a>( let mut have_real_output = false; let mut outputs_sp = vec![]; + let mut regclass_outputs = vec![]; for (op, op_sp) in &args.operands { match op { - ast::InlineAsmOperand::Out { expr, .. } - | ast::InlineAsmOperand::SplitInOut { out_expr: expr, .. } => { + ast::InlineAsmOperand::Out { reg, expr, .. } + | ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => { outputs_sp.push(*op_sp); have_real_output |= expr.is_some(); + if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg { + regclass_outputs.push(*op_sp); + } } - ast::InlineAsmOperand::InOut { .. } => { + ast::InlineAsmOperand::InOut { reg, .. } => { outputs_sp.push(*op_sp); have_real_output = true; + if let ast::InlineAsmRegOrRegClass::RegClass(_) = reg { + regclass_outputs.push(*op_sp); + } } _ => {} } @@ -271,7 +299,7 @@ fn parse_args<'a>( if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output { ecx.struct_span_err( args.options_spans.clone(), - "asm with `pure` option must have at least one output", + "asm with the `pure` option must have at least one output", ) .emit(); } @@ -282,6 +310,24 @@ fn parse_args<'a>( // Bail out now since this is likely to confuse MIR return Err(err); } + if let Some((_, abi_span)) = args.clobber_abi { + if is_global_asm { + let err = + ecx.struct_span_err(abi_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( + regclass_outputs.clone(), + "asm with `clobber_abi` must specify explicit registers for outputs", + ) + .span_label(abi_span, "clobber_abi") + .span_labels(regclass_outputs, "generic outputs") + .emit(); + } + } Ok(args) } @@ -330,26 +376,32 @@ fn try_set_option<'a>( } } -fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), DiagnosticBuilder<'a>> { +fn parse_options<'a>( + p: &mut Parser<'a>, + args: &mut AsmArgs, + is_global_asm: bool, +) -> Result<(), DiagnosticBuilder<'a>> { let span_start = p.prev_token.span; p.expect(&token::OpenDelim(token::DelimToken::Paren))?; while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) { - if p.eat_keyword(sym::pure) { + if !is_global_asm && p.eat_keyword(sym::pure) { try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE); - } else if p.eat_keyword(sym::nomem) { + } else if !is_global_asm && p.eat_keyword(sym::nomem) { try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM); - } else if p.eat_keyword(sym::readonly) { + } else if !is_global_asm && p.eat_keyword(sym::readonly) { try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY); - } else if p.eat_keyword(sym::preserves_flags) { + } else if !is_global_asm && p.eat_keyword(sym::preserves_flags) { try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS); - } else if p.eat_keyword(sym::noreturn) { + } else if !is_global_asm && p.eat_keyword(sym::noreturn) { try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN); - } else if p.eat_keyword(sym::nostack) { + } else if !is_global_asm && p.eat_keyword(sym::nostack) { try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK); } else if p.eat_keyword(sym::att_syntax) { 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 { return p.unexpected(); } @@ -367,6 +419,49 @@ fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), Diagn Ok(()) } +fn parse_clobber_abi<'a>( + p: &mut Parser<'a>, + args: &mut AsmArgs, +) -> Result<(), DiagnosticBuilder<'a>> { + let span_start = p.prev_token.span; + + 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); + } + }; + + p.expect(&token::CloseDelim(token::DelimToken::Paren))?; + + let new_span = span_start.to(p.prev_token.span); + + 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 mut err = p + .sess + .span_diagnostic + .struct_span_err(new_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)); + + Ok(()) +} + fn parse_reg<'a>( p: &mut Parser<'a>, explicit_reg: &mut bool, @@ -389,7 +484,7 @@ fn parse_reg<'a>( Ok(result) } -fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast::Expr> { +fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> { let mut template = vec![]; // Register operands are implicitly used since they are not allowed to be // referenced in the template string. @@ -402,7 +497,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast let mut line_spans = Vec::with_capacity(args.templates.len()); let mut curarg = 0; - let default_dialect = ecx.sess.inline_asm_dialect(); + let mut template_strs = Vec::with_capacity(args.templates.len()); for template_expr in args.templates.into_iter() { if !template.is_empty() { @@ -418,7 +513,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast if let Some(mut err) = err { err.emit(); } - return DummyResult::raw_expr(sp, true); + return None; } }; @@ -427,62 +522,55 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast ast::StrStyle::Raw(raw) => Some(raw as usize), }; - let template_str = &template_str.as_str(); let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok(); + template_strs.push(( + template_str, + template_snippet.as_ref().map(|s| Symbol::intern(s)), + template_sp, + )); + let template_str = &template_str.as_str(); - if let Some(snippet) = &template_snippet { - let snippet = snippet.trim_matches('"'); - match default_dialect { - ast::LlvmAsmDialect::Intel => { - if let Some(span) = check_syntax_directive(snippet, ".intel_syntax") { - let span = template_span.from_inner(span); - let mut err = ecx.struct_span_err(span, "intel syntax is the default syntax on this target, and trying to use this directive may cause issues"); - err.span_suggestion( - span, - "remove this assembler directive", - "".to_string(), - Applicability::MachineApplicable, - ); - err.emit(); - } - - if let Some(span) = check_syntax_directive(snippet, ".att_syntax") { - let span = template_span.from_inner(span); - let mut err = ecx.struct_span_err(span, "using the .att_syntax directive may cause issues, use the att_syntax option instead"); - let asm_end = sp.hi() - BytePos(2); - let suggestions = vec![ - (span, "".to_string()), - ( - Span::new(asm_end, asm_end, sp.ctxt()), - ", options(att_syntax)".to_string(), - ), - ]; - err.multipart_suggestion( - "remove the assembler directive and replace it with options(att_syntax)", - suggestions, - Applicability::MachineApplicable, - ); - err.emit(); + 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..] + .find(|c| matches!(c, '\n' | ';' | '\\' | '"')) + .unwrap_or(snippet[pos..].len() - 1); + let inner = InnerSpan::new(pos, end); + return template_sp.from_inner(inner); } } - ast::LlvmAsmDialect::Att => { - if let Some(span) = check_syntax_directive(snippet, ".att_syntax") { - let span = template_span.from_inner(span); - let mut err = ecx.struct_span_err(span, "att syntax is the default syntax on this target, and trying to use this directive may cause issues"); - err.span_suggestion( - span, - "remove this assembler directive", - "".to_string(), - Applicability::MachineApplicable, - ); - err.emit(); - } + template_sp + }; - // Use of .intel_syntax is ignored - } + if template_str.contains(".intel_syntax") { + ecx.parse_sess().buffer_lint( + lint::builtin::BAD_ASM_STYLE, + find_span(".intel_syntax"), + ecx.current_expansion.lint_node_id, + "avoid using `.intel_syntax`, Intel syntax is the default", + ); + } + if template_str.contains(".att_syntax") { + ecx.parse_sess().buffer_lint( + lint::builtin::BAD_ASM_STYLE, + find_span(".att_syntax"), + ecx.current_expansion.lint_node_id, + "avoid using `.att_syntax`, prefer using `options(att_syntax)` instead", + ); } } + // Don't treat raw asm as a format string. + if args.options.contains(ast::InlineAsmOptions::RAW) { + template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string())); + let template_num_lines = 1 + template_str.matches('\n').count(); + line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); + continue; + } + let mut parser = parse::Parser::new( template_str, str_style, @@ -515,7 +603,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast e.span_label(err_sp, label); } e.emit(); - return DummyResult::raw_expr(sp, true); + return None; } curarg = parser.curarg; @@ -666,14 +754,13 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P<ast } } - let inline_asm = - ast::InlineAsm { template, operands: args.operands, options: args.options, line_spans }; - P(ast::Expr { - id: ast::DUMMY_NODE_ID, - kind: ast::ExprKind::InlineAsm(P(inline_asm)), - span: sp, - attrs: ast::AttrVec::new(), - tokens: None, + Some(ast::InlineAsm { + template, + template_strs: template_strs.into_boxed_slice(), + operands: args.operands, + clobber_abi: args.clobber_abi, + options: args.options, + line_spans, }) } @@ -682,8 +769,21 @@ pub fn expand_asm<'cx>( sp: Span, tts: TokenStream, ) -> Box<dyn base::MacResult + 'cx> { - match parse_args(ecx, sp, tts) { - Ok(args) => MacEager::expr(expand_preparsed_asm(ecx, sp, args)), + match parse_args(ecx, sp, tts, false) { + Ok(args) => { + let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) { + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + kind: ast::ExprKind::InlineAsm(P(inline_asm)), + span: sp, + attrs: ast::AttrVec::new(), + tokens: None, + }) + } else { + DummyResult::raw_expr(sp, true) + }; + MacEager::expr(expr) + } Err(mut err) => { err.emit(); DummyResult::any(sp) @@ -691,14 +791,34 @@ pub fn expand_asm<'cx>( } } -fn check_syntax_directive<S: AsRef<str>>(piece: S, syntax: &str) -> Option<InnerSpan> { - let piece = piece.as_ref(); - if let Some(idx) = piece.find(syntax) { - let end = - idx + &piece[idx..].find(|c| matches!(c, '\n' | ';')).unwrap_or(piece[idx..].len()); - // Offset by one because these represent the span with the " removed - Some(InnerSpan::new(idx + 1, end + 1)) - } else { - None +pub fn expand_global_asm<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box<dyn base::MacResult + 'cx> { + match parse_args(ecx, sp, tts, true) { + Ok(args) => { + if let Some(inline_asm) = expand_preparsed_asm(ecx, args) { + MacEager::items(smallvec![P(ast::Item { + ident: Ident::invalid(), + attrs: Vec::new(), + id: ast::DUMMY_NODE_ID, + kind: ast::ItemKind::GlobalAsm(inline_asm), + vis: ast::Visibility { + span: sp.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + span: ecx.with_def_site_ctxt(sp), + tokens: None, + })]) + } else { + DummyResult::any(sp) + } + } + Err(mut err) => { + err.emit(); + DummyResult::any(sp) + } } } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 025872df017..d7b46f28215 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -1,11 +1,18 @@ use crate::util::check_builtin_macro_attribute; -use rustc_ast::mut_visit::{self, MutVisitor}; -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, AstLike}; +use rustc_ast as ast; +use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::tokenstream::CanSynthesizeMissingTokens; +use rustc_ast::visit::Visitor; +use rustc_ast::{mut_visit, visit}; +use rustc_ast::{AstLike, Attribute}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; +use rustc_parse::parser::ForceCollect; +use rustc_session::utils::FlattenNonterminals; + +use rustc_ast::ptr::P; use rustc_span::symbol::sym; use rustc_span::Span; use smallvec::SmallVec; @@ -17,79 +24,183 @@ crate fn expand( annotatable: Annotatable, ) -> Vec<Annotatable> { check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); - cfg_eval(ecx, annotatable) + vec![cfg_eval(ecx, annotatable)] +} + +crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable { + CfgEval { + cfg: &mut StripUnconfigured { + sess: ecx.sess, + features: ecx.ecfg.features, + config_tokens: true, + }, + } + .configure_annotatable(annotatable) + // Since the item itself has already been configured by the `InvocationCollector`, + // we know that fold result vector will contain exactly one element. + .unwrap() +} + +struct CfgEval<'a, 'b> { + cfg: &'a mut StripUnconfigured<'b>, } -crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatable> { - let mut visitor = CfgEval { - cfg: StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, modified: false }, - }; - let mut annotatable = visitor.configure_annotatable(annotatable); - if visitor.cfg.modified { - // Erase the tokens if cfg-stripping modified the item - // This will cause us to synthesize fake tokens - // when `nt_to_tokenstream` is called on this item. - if let Some(tokens) = annotatable.tokens_mut() { - *tokens = None; +fn flat_map_annotatable( + vis: &mut impl MutVisitor, + annotatable: Annotatable, +) -> Option<Annotatable> { + match annotatable { + Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item), + Annotatable::TraitItem(item) => { + vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem) + } + Annotatable::ImplItem(item) => { + vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem) } + Annotatable::ForeignItem(item) => { + vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem) + } + Annotatable::Stmt(stmt) => { + vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt) + } + Annotatable::Expr(mut expr) => { + vis.visit_expr(&mut expr); + Some(Annotatable::Expr(expr)) + } + Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm), + Annotatable::ExprField(field) => { + vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField) + } + Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField), + Annotatable::GenericParam(param) => { + vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam) + } + 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), } - vec![annotatable] } -struct CfgEval<'a> { - cfg: StripUnconfigured<'a>, +struct CfgFinder { + has_cfg_or_cfg_attr: bool, } -impl CfgEval<'_> { +impl CfgFinder { + fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { + let mut finder = CfgFinder { has_cfg_or_cfg_attr: false }; + match annotatable { + Annotatable::Item(item) => finder.visit_item(&item), + Annotatable::TraitItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Trait), + Annotatable::ImplItem(item) => finder.visit_assoc_item(&item, visit::AssocCtxt::Impl), + Annotatable::ForeignItem(item) => finder.visit_foreign_item(&item), + Annotatable::Stmt(stmt) => finder.visit_stmt(&stmt), + Annotatable::Expr(expr) => finder.visit_expr(&expr), + Annotatable::Arm(arm) => finder.visit_arm(&arm), + Annotatable::ExprField(field) => finder.visit_expr_field(&field), + Annotatable::PatField(field) => finder.visit_pat_field(&field), + Annotatable::GenericParam(param) => finder.visit_generic_param(¶m), + Annotatable::Param(param) => finder.visit_param(¶m), + Annotatable::FieldDef(field) => finder.visit_field_def(&field), + Annotatable::Variant(variant) => finder.visit_variant(&variant), + }; + finder.has_cfg_or_cfg_attr + } +} + +impl<'ast> visit::Visitor<'ast> for CfgFinder { + fn visit_attribute(&mut self, attr: &'ast Attribute) { + // We want short-circuiting behavior, so don't use the '|=' operator. + self.has_cfg_or_cfg_attr = self.has_cfg_or_cfg_attr + || attr + .ident() + .map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr); + } +} + +impl CfgEval<'_, '_> { fn configure<T: AstLike>(&mut self, node: T) -> Option<T> { self.cfg.configure(node) } - fn configure_annotatable(&mut self, annotatable: Annotatable) -> Annotatable { - // Since the item itself has already been configured by the InvocationCollector, - // we know that fold result vector will contain exactly one element - match annotatable { - Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()), - Annotatable::TraitItem(item) => { - Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap()) - } - Annotatable::ImplItem(item) => { - Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap()) - } - Annotatable::ForeignItem(item) => { - Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) - } - Annotatable::Stmt(stmt) => { - Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap())) - } - Annotatable::Expr(mut expr) => Annotatable::Expr({ - self.visit_expr(&mut expr); - expr - }), - Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()), - Annotatable::ExprField(field) => { - Annotatable::ExprField(self.flat_map_expr_field(field).pop().unwrap()) - } - Annotatable::PatField(fp) => { - Annotatable::PatField(self.flat_map_pat_field(fp).pop().unwrap()) - } - Annotatable::GenericParam(param) => { - Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap()) - } - Annotatable::Param(param) => { - Annotatable::Param(self.flat_map_param(param).pop().unwrap()) - } - Annotatable::FieldDef(sf) => { - Annotatable::FieldDef(self.flat_map_field_def(sf).pop().unwrap()) + fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> { + // Tokenizing and re-parsing the `Annotatable` can have a significant + // performance impact, so try to avoid it if possible + if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) { + return Some(annotatable); + } + + // The majority of parsed attribute targets will never need to have early cfg-expansion + // run (e.g. they are not part of a `#[derive]` or `#[cfg_eval]` macro inoput). + // Therefore, we normally do not capture the necessary information about `#[cfg]` + // and `#[cfg_attr]` attributes during parsing. + // + // Therefore, when we actually *do* run early cfg-expansion, we need to tokenize + // and re-parse the attribute target, this time capturing information about + // 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 mut orig_tokens = rustc_parse::nt_to_tokenstream( + &nt, + &self.cfg.sess.parse_sess, + CanSynthesizeMissingTokens::No, + ); + + // 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`) + // to `None`-delimited groups containing the corresponding tokens. This + // is normally delayed until the proc-macro server actually needs to + // provide a `TokenKind::Interpolated` to a proc-macro. We do this earlier, + // so that we can handle cases like: + // + // ```rust + // #[cfg_eval] #[cfg] $item + //``` + // + // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make + // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest + // way to do this is to do a single parse of a stream without any nonterminals. + let mut flatten = FlattenNonterminals { + nt_to_tokenstream: rustc_parse::nt_to_tokenstream, + parse_sess: &self.cfg.sess.parse_sess, + synthesize_tokens: CanSynthesizeMissingTokens::No, + }; + orig_tokens = flatten.process_token_stream(orig_tokens); + + // Re-parse the tokens, setting the `capture_cfg` flag to save extra information + // to the captured `AttrAnnotatedTokenStream` (specifically, we capture + // `AttrAnnotatedTokenTree::AttributesData` for all occurences of `#[cfg]` and `#[cfg_attr]`) + 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::Variant(v) => { - Annotatable::Variant(self.flat_map_variant(v).pop().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!(), + }; + + // Now that we have our re-parsed `AttrAnnotatedTokenStream`, recursively configuring + // our attribute target will correctly the tokens as well. + flat_map_annotatable(self, annotatable) } } -impl MutVisitor for CfgEval<'_> { +impl MutVisitor for CfgEval<'_, '_> { fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { self.cfg.configure_expr(expr); mut_visit::noop_visit_expr(expr, self); diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 0da2c1c1021..e0389f448eb 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,6 +1,6 @@ use crate::cfg_eval::cfg_eval; -use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; +use rustc_ast::{self as ast, attr, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_feature::AttributeTemplate; @@ -26,33 +26,42 @@ impl MultiItemModifier for Expander { return ExpandResult::Ready(vec![item]); } - let template = - AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }; - let attr = ecx.attribute(meta_item.clone()); - validate_attr::check_builtin_attribute(&sess.parse_sess, &attr, sym::derive, template); + let item = cfg_eval(ecx, item); - let derives: Vec<_> = attr - .meta_item_list() - .unwrap_or_default() - .into_iter() - .filter_map(|nested_meta| match nested_meta { - NestedMetaItem::MetaItem(meta) => Some(meta), - NestedMetaItem::Literal(lit) => { - // Reject `#[derive("Debug")]`. - report_unexpected_literal(sess, &lit); - None - } - }) - .map(|meta| { - // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths. - report_path_args(sess, &meta); - meta.path - }) - .collect(); + let result = + ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| { + let template = + AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }; + let attr = attr::mk_attr_outer(meta_item.clone()); + validate_attr::check_builtin_attribute( + &sess.parse_sess, + &attr, + sym::derive, + template, + ); - // FIXME: Try to cache intermediate results to avoid collecting same paths multiple times. - match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) { - Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)), + attr.meta_item_list() + .unwrap_or_default() + .into_iter() + .filter_map(|nested_meta| match nested_meta { + NestedMetaItem::MetaItem(meta) => Some(meta), + NestedMetaItem::Literal(lit) => { + // Reject `#[derive("Debug")]`. + report_unexpected_literal(sess, &lit); + None + } + }) + .map(|meta| { + // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths. + report_path_args(sess, &meta); + meta.path + }) + .map(|path| (path, item.clone(), None)) + .collect() + }); + + match result { + Ok(()) => ExpandResult::Ready(vec![item]), Err(Indeterminate) => ExpandResult::Retry(item), } } @@ -75,8 +84,10 @@ fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool { sess, span, E0774, - "`derive` may only be applied to structs, enums and unions", + "`derive` may only be applied to `struct`s, `enum`s and `union`s", ) + .span_label(span, "not applicable here") + .span_label(item.span(), "not a `struct`, `enum` or `union`") .emit(); } bad_target @@ -90,6 +101,7 @@ fn report_unexpected_literal(sess: &Session, lit: &ast::Lit) { _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(), }; struct_span_err!(sess, lit.span, E0777, "expected path to a trait, found literal",) + .span_label(lit.span, "not a trait") .help(&help_msg) .emit(); } diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index ca1226b445d..2e5ad66c60b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -36,8 +36,9 @@ pub fn expand_deriving_clone( Annotatable::Item(ref annitem) => match annitem.kind { ItemKind::Struct(_, Generics { ref params, .. }) | ItemKind::Enum(_, Generics { ref params, .. }) => { - let container_id = cx.current_expansion.id.expn_data().parent; - if cx.resolver.has_derive_copy(container_id) + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let has_derive_copy = cx.resolver.has_derive_copy(container_id); + if has_derive_copy && !params .iter() .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) @@ -148,11 +149,7 @@ fn cs_clone_shallow( } _ => cx.span_bug( trait_span, - &format!( - "unexpected substructure in \ - shallow `derive({})`", - name - ), + &format!("unexpected substructure in shallow `derive({})`", name), ), } } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 79f35ad5819..54ab88dc3ff 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -15,10 +15,12 @@ pub fn expand_deriving_eq( item: &Annotatable, push: &mut dyn FnMut(Annotatable), ) { + let span = cx.with_def_site_ctxt(span); let inline = cx.meta_word(span, sym::inline); let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span)); let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]); - let attrs = vec![cx.attribute(inline), cx.attribute(doc)]; + let no_coverage = cx.meta_word(span, sym::no_coverage); + let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)]; let trait_def = TraitDef { span, attributes: Vec::new(), diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index cc6dac52d76..14506f296bf 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -3,7 +3,7 @@ use crate::deriving::generic::*; use crate::deriving::path_std; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, MetaItem}; +use rustc_ast::{self as ast, Expr, LocalKind, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -135,8 +135,8 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as let local = P(ast::Local { pat: cx.pat_wild(sp), ty: None, - init: Some(expr), id: ast::DUMMY_NODE_ID, + kind: LocalKind::Init(expr), span: sp, attrs: ast::AttrVec::new(), tokens: None, diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 980be3a0050..8c53094b624 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -2,11 +2,16 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use rustc_ast::ptr::P; +use rustc_ast::walk_list; +use rustc_ast::EnumDef; +use rustc_ast::VariantData; use rustc_ast::{Expr, MetaItem}; -use rustc_errors::struct_span_err; +use rustc_errors::Applicability; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; +use rustc_span::symbol::Ident; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; +use smallvec::SmallVec; pub fn expand_deriving_default( cx: &mut ExtCtxt<'_>, @@ -15,6 +20,8 @@ pub fn expand_deriving_default( item: &Annotatable, push: &mut dyn FnMut(Annotatable), ) { + item.visit_with(&mut DetectNonVariantDefaultAttr { cx }); + let inline = cx.meta_word(span, sym::inline); let attrs = vec![cx.attribute(inline)]; let trait_def = TraitDef { @@ -34,8 +41,25 @@ pub fn expand_deriving_default( attributes: attrs, is_unsafe: false, unify_fieldless_variants: false, - combine_substructure: combine_substructure(Box::new(|a, b, c| { - default_substructure(a, b, c) + combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| { + match substr.fields { + StaticStruct(_, fields) => { + default_struct_substructure(cx, trait_span, substr, fields) + } + StaticEnum(enum_def, _) => { + if !cx.sess.features_untracked().derive_default_enum { + rustc_session::parse::feature_err( + cx.parse_sess(), + sym::derive_default_enum, + span, + "deriving `Default` on enums is experimental", + ) + .emit(); + } + default_enum_substructure(cx, trait_span, enum_def) + } + _ => cx.span_bug(trait_span, "method in `derive(Default)`"), + } })), }], associated_types: Vec::new(), @@ -43,44 +67,223 @@ pub fn expand_deriving_default( trait_def.expand(cx, mitem, item, push) } -fn default_substructure( +fn default_struct_substructure( cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, + summary: &StaticFields, ) -> P<Expr> { // Note that `kw::Default` is "default" and `sym::Default` is "Default"! let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); - match *substr.fields { - StaticStruct(_, ref summary) => match *summary { - Unnamed(ref fields, is_tuple) => { - if !is_tuple { - cx.expr_ident(trait_span, substr.type_ident) - } else { - let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); - cx.expr_call_ident(trait_span, substr.type_ident, exprs) - } + match summary { + Unnamed(ref fields, is_tuple) => { + if !is_tuple { + cx.expr_ident(trait_span, substr.type_ident) + } else { + let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + cx.expr_call_ident(trait_span, substr.type_ident, exprs) + } + } + Named(ref fields) => { + let default_fields = fields + .iter() + .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span))) + .collect(); + cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + } + } +} + +fn default_enum_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + enum_def: &EnumDef, +) -> P<Expr> { + let default_variant = match extract_default_variant(cx, enum_def, trait_span) { + Ok(value) => value, + Err(()) => return DummyResult::raw_expr(trait_span, true), + }; + + // At this point, we know that there is exactly one variant with a `#[default]` attribute. The + // attribute hasn't yet been validated. + + if let Err(()) = validate_default_attribute(cx, default_variant) { + return DummyResult::raw_expr(trait_span, true); + } + + // We now know there is exactly one unit variant with exactly one `#[default]` attribute. + + cx.expr_path(cx.path( + default_variant.span, + vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident], + )) +} + +fn extract_default_variant<'a>( + cx: &mut ExtCtxt<'_>, + enum_def: &'a EnumDef, + trait_span: Span, +) -> Result<&'a rustc_ast::Variant, ()> { + let default_variants: SmallVec<[_; 1]> = enum_def + .variants + .iter() + .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default)) + .collect(); + + let variant = match default_variants.as_slice() { + [variant] => variant, + [] => { + let possible_defaults = enum_def + .variants + .iter() + .filter(|variant| matches!(variant.data, VariantData::Unit(..))) + .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive)); + + let mut diag = cx.struct_span_err(trait_span, "no default declared"); + diag.help("make a unit variant default by placing `#[default]` above it"); + for variant in possible_defaults { + // Suggest making each unit variant default. + diag.tool_only_span_suggestion( + variant.span, + &format!("make `{}` default", variant.ident), + format!("#[default] {}", variant.ident), + Applicability::MaybeIncorrect, + ); } - Named(ref fields) => { - let default_fields = fields + diag.emit(); + + return Err(()); + } + [first, rest @ ..] => { + let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults"); + diag.span_label(first.span, "first default"); + diag.span_labels(rest.iter().map(|variant| variant.span), "additional default"); + diag.note("only one variant can be default"); + for variant in &default_variants { + // Suggest making each variant already tagged default. + let suggestion = default_variants .iter() - .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span))) + .filter_map(|v| { + if v.ident == variant.ident { + None + } else { + Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new())) + } + }) .collect(); - cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + + diag.tool_only_multipart_suggestion( + &format!("make `{}` default", variant.ident), + suggestion, + Applicability::MaybeIncorrect, + ); } - }, - StaticEnum(..) => { - struct_span_err!( - &cx.sess.parse_sess.span_diagnostic, - trait_span, - E0665, - "`Default` cannot be derived for enums, only structs" + diag.emit(); + + return Err(()); + } + }; + + if !matches!(variant.data, VariantData::Unit(..)) { + cx.struct_span_err( + variant.ident.span, + "the `#[default]` attribute may only be used on unit enum variants", + ) + .help("consider a manual implementation of `Default`") + .emit(); + + return Err(()); + } + + if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) { + cx.struct_span_err(variant.ident.span, "default variant must be exhaustive") + .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here") + .help("consider a manual implementation of `Default`") + .emit(); + + return Err(()); + } + + Ok(variant) +} + +fn validate_default_attribute( + cx: &mut ExtCtxt<'_>, + default_variant: &rustc_ast::Variant, +) -> Result<(), ()> { + let attrs: SmallVec<[_; 1]> = + cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect(); + + let attr = match attrs.as_slice() { + [attr] => attr, + [] => cx.bug( + "this method must only be called with a variant that has a `#[default]` attribute", + ), + [first, rest @ ..] => { + // FIXME(jhpratt) Do we want to perform this check? It doesn't exist + // for `#[inline]`, `#[non_exhaustive]`, and presumably others. + + let suggestion_text = + if rest.len() == 1 { "try removing this" } else { "try removing these" }; + + cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes") + .note("only one `#[default]` attribute is needed") + .span_label(first.span, "`#[default]` used here") + .span_label(rest[0].span, "`#[default]` used again here") + .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text) + // This would otherwise display the empty replacement, hence the otherwise + // repetitive `.span_help` call above. + .tool_only_multipart_suggestion( + suggestion_text, + rest.iter().map(|attr| (attr.span, String::new())).collect(), + Applicability::MachineApplicable, + ) + .emit(); + + return Err(()); + } + }; + if !attr.is_word() { + cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value") + .span_suggestion_hidden( + attr.span, + "try using `#[default]`", + "#[default]".into(), + Applicability::MaybeIncorrect, ) .emit(); - // let compilation continue - DummyResult::raw_expr(trait_span, true) + + return Err(()); + } + Ok(()) +} + +struct DetectNonVariantDefaultAttr<'a, 'b> { + cx: &'a ExtCtxt<'b>, +} + +impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { + fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { + if attr.has_name(kw::Default) { + self.cx + .struct_span_err( + attr.span, + "the `#[default]` attribute may only be used on unit enum variants", + ) + .emit(); + } + + rustc_ast::visit::walk_attribute(self, attr); + } + fn visit_variant(&mut self, v: &'a rustc_ast::Variant) { + self.visit_ident(v.ident); + self.visit_vis(&v.vis); + self.visit_variant_data(&v.data); + walk_list!(self, visit_anon_const, &v.disr_expr); + for attr in &v.attrs { + rustc_ast::visit::walk_attribute(self, attr); } - _ => cx.span_bug(trait_span, "method in `derive(Default)`"), } } diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 01a57bea14e..c5f3a9d3379 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -124,12 +124,7 @@ pub fn expand_deriving_rustc_encodable( explicit_self: borrowed_explicit_self(), args: vec![( Ptr(Box::new(Literal(Path::new_local(typaram))), Borrowed(None, Mutability::Mut)), - // FIXME: we could use `sym::s` here, but making `s` a static - // symbol changes the symbol index ordering in a way that makes - // ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs - // fail. The linting code should be fixed so that its output - // does not depend on the symbol index ordering. - Symbol::intern("s"), + sym::s, )], ret_ty: Literal(Path::new_( pathvec_std!(result::Result), diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index da85cc73ffd..59f933d422d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -410,7 +410,7 @@ impl<'a> TraitDef<'a> { .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })), _ => unreachable!(), }; - let container_id = cx.current_expansion.id.expn_data().parent; + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let always_copy = has_no_type_params && cx.resolver.has_derive_copy(container_id); let use_temporaries = is_packed && always_copy; @@ -527,12 +527,12 @@ impl<'a> TraitDef<'a> { tokens: None, }, attrs: Vec::new(), - kind: ast::AssocItemKind::TyAlias(box ast::TyAliasKind( + 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)), - )), + ))), tokens: None, }) }); @@ -541,7 +541,7 @@ impl<'a> TraitDef<'a> { self.generics.to_generics(cx, self.span, type_ident, generics); // Create the generic parameters - params.extend(generics.params.iter().map(|param| match param.kind { + params.extend(generics.params.iter().map(|param| match ¶m.kind { GenericParamKind::Lifetime { .. } => param.clone(), GenericParamKind::Type { .. } => { // I don't think this can be moved out of the loop, since @@ -561,7 +561,18 @@ impl<'a> TraitDef<'a> { cx.typaram(self.span, param.ident, vec![], bounds, None) } - GenericParamKind::Const { .. } => param.clone(), + GenericParamKind::Const { ty, kw_span, .. } => { + let const_nodefault_kind = GenericParamKind::Const { + ty: ty.clone(), + kw_span: kw_span.clone(), + + // We can't have default values inside impl block + default: None, + }; + let mut param_clone = param.clone(); + param_clone.kind = const_nodefault_kind; + param_clone + } })); // and similarly for where clauses @@ -666,8 +677,6 @@ impl<'a> TraitDef<'a> { let self_type = cx.ty_path(path); let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); - // Just mark it now since we know that it'll end up used downstream - cx.sess.mark_attr_used(&attr); let opt_trait_ref = Some(trait_ref); let unused_qual = { let word = rustc_ast::attr::mk_nested_word_item(Ident::new( @@ -687,7 +696,7 @@ impl<'a> TraitDef<'a> { self.span, Ident::invalid(), a, - ast::ItemKind::Impl(box ast::ImplKind { + ast::ItemKind::Impl(Box::new(ast::ImplKind { unsafety, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, @@ -696,7 +705,7 @@ impl<'a> TraitDef<'a> { of_trait: opt_trait_ref, self_ty: self_type, items: methods.into_iter().chain(associated_types).collect(), - }), + })), ) } @@ -929,7 +938,12 @@ impl<'a> MethodDef<'a> { tokens: None, }, ident: method_ident, - kind: ast::AssocItemKind::Fn(box ast::FnKind(def, sig, fn_generics, Some(body_block))), + kind: ast::AssocItemKind::Fn(Box::new(ast::FnKind( + def, + sig, + fn_generics, + Some(body_block), + ))), tokens: None, }) } @@ -1034,7 +1048,7 @@ impl<'a> MethodDef<'a> { // make a series of nested matches, to destructure the // structs. This is actually right-to-left, but it shouldn't // matter. - for (arg_expr, pat) in self_args.iter().zip(patterns) { + for (arg_expr, pat) in iter::zip(self_args, patterns) { body = cx.expr_match( trait_.span, arg_expr.clone(), @@ -1351,7 +1365,7 @@ impl<'a> MethodDef<'a> { let mut discriminant_test = cx.expr_bool(sp, true); let mut first_ident = None; - for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { + for (&ident, self_arg) in iter::zip(&vi_idents, &self_args) { let self_addr = cx.expr_addr_of(sp, self_arg.clone()); let variant_value = deriving::call_intrinsic(cx, sp, sym::discriminant_value, vec![self_addr]); @@ -1571,9 +1585,7 @@ impl<'a> TraitDef<'a> { let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries); let pattern = match *struct_def { VariantData::Struct(..) => { - let field_pats = subpats - .into_iter() - .zip(&ident_exprs) + let field_pats = iter::zip(subpats, &ident_exprs) .map(|(pat, &(sp, ident, ..))| { if ident.is_none() { cx.span_bug(sp, "a braced struct with unnamed fields in `derive`"); @@ -1686,7 +1698,7 @@ where /// One or more fields: call the base case function on the first value (which depends on /// `use_fold`), and use that as the base case. Then perform `cs_fold` on the remainder of the /// fields. -/// When the `substructure` is a `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f` +/// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f` /// is returned. Statics may not be folded over. /// See `cs_op` in `partial_ord.rs` for a model example. pub fn cs_fold1<F, B>( diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 6b7d0e1f204..00d75be4399 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -72,13 +72,9 @@ impl Path { ) -> ast::Path { let mut idents = self.path.iter().map(|s| Ident::new(*s, span)).collect(); let lt = mk_lifetimes(cx, span, &self.lifetime); - let tys: Vec<P<ast::Ty>> = - self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect(); - let params = lt - .into_iter() - .map(GenericArg::Lifetime) - .chain(tys.into_iter().map(GenericArg::Type)) - .collect(); + let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)); + let params = + lt.into_iter().map(GenericArg::Lifetime).chain(tys.map(GenericArg::Type)).collect(); match self.kind { PathKind::Global => cx.path_all(span, true, idents, params), diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 7dea6099f8f..572ec6e242e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -179,7 +179,7 @@ fn inject_impl_of_structural_trait( span, Ident::invalid(), attrs, - ItemKind::Impl(box ImplKind { + ItemKind::Impl(Box::new(ImplKind { unsafety: ast::Unsafe::No, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, @@ -188,7 +188,7 @@ fn inject_impl_of_structural_trait( of_trait: Some(trait_ref), self_ty: self_type, items: Vec::new(), - }), + })), ); push(Annotatable::Item(newitem)); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 7e88b58c0e2..1dbf7728421 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -3,8 +3,8 @@ use Position::*; use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{token, BlockCheckMode, UnsafeSource}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; use rustc_expand::base::{self, *}; @@ -838,12 +838,15 @@ impl<'a, 'b> Context<'a, 'b> { // // But the nested match expression is proved to perform not as well // as series of let's; the first approach does. - 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)); - let result = self.ecx.expr_match(self.macsp, head, vec![arm]); + 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]) + }; - let args_slice = self.ecx.expr_addr_of(self.macsp, result); + let ident = Ident::from_str_and_span("args", self.macsp); + let args_slice = self.ecx.expr_ident(self.macsp, ident); // Now create the fmt::Arguments struct with all our locals we created. let (fn_name, fn_args) = if self.all_pieces_simple { @@ -857,7 +860,20 @@ impl<'a, 'b> Context<'a, 'b> { }; let path = self.ecx.std_path(&[sym::fmt, sym::Arguments, Symbol::intern(fn_name)]); - self.ecx.expr_call_global(self.macsp, path, fn_args) + let arguments = self.ecx.expr_call_global(self.macsp, path, fn_args); + let body = self.ecx.expr_block(P(ast::Block { + stmts: vec![self.ecx.stmt_expr(arguments)], + id: ast::DUMMY_NODE_ID, + rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated), + span: self.macsp, + tokens: None, + })); + + let ident = Ident::from_str_and_span("args", self.macsp); + let binding_mode = ast::BindingMode::ByRef(ast::Mutability::Not); + let pat = self.ecx.pat_ident_binding_mode(self.macsp, ident, binding_mode); + let arm = self.ecx.arm(self.macsp, pat, body); + self.ecx.expr_match(self.macsp, args_match, vec![arm]) } fn format_arg( @@ -939,6 +955,7 @@ pub fn expand_preparsed_format_args( let msg = "format argument must be a string literal"; let fmt_sp = efmt.span; + let efmt_kind_is_lit: bool = matches!(efmt.kind, ast::ExprKind::Lit(_)); let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) { Ok(mut fmt) if append_newline => { fmt.0 = Symbol::intern(&format!("{}\n", fmt.0)); @@ -989,7 +1006,19 @@ pub fn expand_preparsed_format_args( if !parser.errors.is_empty() { let err = parser.errors.remove(0); - let sp = fmt_span.from_inner(err.span); + let sp = if efmt_kind_is_lit { + fmt_span.from_inner(err.span) + } else { + // The format string could be another macro invocation, e.g.: + // format!(concat!("abc", "{}"), 4); + // However, `err.span` is an inner span relative to the *result* of + // the macro invocation, which is why we would get a nonsensical + // result calling `fmt_span.from_inner(err.span)` as above, and + // might even end up inside a multibyte character (issue #86085). + // Therefore, we conservatively report the error for the entire + // argument span here. + fmt_span + }; let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}", err.description)); e.span_label(sp, err.label + " in format string"); if let Some(note) = err.note { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 9b43c11f0f3..3f71ee6f489 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -14,31 +14,31 @@ pub fn expand( ecx: &mut ExtCtxt<'_>, _span: Span, meta_item: &ast::MetaItem, - mut item: Annotatable, + item: Annotatable, ) -> Vec<Annotatable> { check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator); - let not_static = |item: Annotatable| { + let orig_item = item.clone(); + let not_static = || { ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics"); - vec![item] + vec![orig_item.clone()] }; - let orig_item = item.clone(); - let mut is_stmt = false; // Allow using `#[global_allocator]` on an item statement - if let Annotatable::Stmt(stmt) = &item { - if let StmtKind::Item(item_) = &stmt.kind { - item = Annotatable::Item(item_.clone()); - is_stmt = true; - } - } - - let item = match item { + // FIXME - if we get deref patterns, use them to reduce duplication here + let (item, is_stmt) = match &item { Annotatable::Item(item) => match item.kind { - ItemKind::Static(..) => item, - _ => return not_static(Annotatable::Item(item)), + ItemKind::Static(..) => (item, false), + _ => return not_static(), + }, + Annotatable::Stmt(stmt) => match &stmt.kind { + StmtKind::Item(item_) => match item_.kind { + ItemKind::Static(..) => (item_, true), + _ => return not_static(), + }, + _ => return not_static(), }, - _ => return not_static(item), + _ => return not_static(), }; // Generate a bunch of new items using the AllocFnFactory @@ -85,8 +85,12 @@ impl AllocFnFactory<'_, '_> { 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 FnKind(ast::Defaultness::Final, sig, Generics::default(), block)); + let kind = ItemKind::Fn(Box::new(FnKind( + ast::Defaultness::Final, + sig, + Generics::default(), + block, + ))); 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/global_asm.rs b/compiler/rustc_builtin_macros/src/global_asm.rs deleted file mode 100644 index 76d87452927..00000000000 --- a/compiler/rustc_builtin_macros/src/global_asm.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Module-level assembly support. -//! -//! The macro defined here allows you to specify "top-level", -//! "file-scoped", or "module-level" assembly. These synonyms -//! all correspond to LLVM's module-level inline assembly instruction. -//! -//! For example, `global_asm!("some assembly here")` codegens to -//! LLVM's `module asm "some assembly here"`. All of LLVM's caveats -//! therefore apply. - -use rustc_ast as ast; -use rustc_ast::ptr::P; -use rustc_ast::token; -use rustc_ast::tokenstream::TokenStream; -use rustc_errors::DiagnosticBuilder; -use rustc_expand::base::{self, *}; -use rustc_span::symbol::Ident; -use rustc_span::Span; -use smallvec::smallvec; - -pub fn expand_global_asm<'cx>( - cx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> Box<dyn base::MacResult + 'cx> { - match parse_global_asm(cx, sp, tts) { - Ok(Some(global_asm)) => MacEager::items(smallvec![P(ast::Item { - ident: Ident::invalid(), - attrs: Vec::new(), - id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(global_asm), - vis: ast::Visibility { - span: sp.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, - tokens: None, - }, - span: cx.with_def_site_ctxt(sp), - tokens: None, - })]), - Ok(None) => DummyResult::any(sp), - Err(mut err) => { - err.emit(); - DummyResult::any(sp) - } - } -} - -fn parse_global_asm<'a>( - cx: &mut ExtCtxt<'a>, - sp: Span, - tts: TokenStream, -) -> Result<Option<ast::GlobalAsm>, DiagnosticBuilder<'a>> { - let mut p = cx.new_parser_from_tts(tts); - - if p.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 = p.parse_expr()?; - let (asm, _) = match expr_to_string(cx, expr, "inline assembly must be a string literal") { - Some((s, st)) => (s, st), - None => return Ok(None), - }; - - Ok(Some(ast::GlobalAsm { asm })) -} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index fe4bede6a48..d1d276930b9 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -3,12 +3,11 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(box_patterns)] -#![feature(box_syntax)] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(iter_zip)] #![feature(nll)] -#![cfg_attr(bootstrap, feature(or_patterns))] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![recursion_limit = "256"] @@ -35,7 +34,6 @@ mod env; mod format; mod format_foreign; mod global_allocator; -mod global_asm; mod llvm_asm; mod log_syntax; mod panic; @@ -73,7 +71,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { file: source_util::expand_file, format_args_nl: format::expand_format_args_nl, format_args: format::expand_format_args, - global_asm: global_asm::expand_global_asm, + const_format_args: format::expand_format_args, + global_asm: asm::expand_global_asm, include_bytes: source_util::expand_include_bytes, include_str: source_util::expand_include_str, include: source_util::expand_include, diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 71bbae1161b..7971c1fff42 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -5,7 +5,7 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, NodeId}; use rustc_ast_pretty::pprust; -use rustc_expand::base::{ExtCtxt, ResolverExpand}; +use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_session::Session; use rustc_span::hygiene::AstPass; @@ -109,86 +109,17 @@ impl<'a> CollectProcMacros<'a> { } fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { - // Once we've located the `#[proc_macro_derive]` attribute, verify - // that it's of the form `#[proc_macro_derive(Foo)]` or - // `#[proc_macro_derive(Foo, attributes(A, ..))]` - let list = match attr.meta_item_list() { - Some(list) => list, - None => return, - }; - if list.len() != 1 && list.len() != 2 { - self.handler.span_err(attr.span, "attribute must have either one or two arguments"); - return; - } - let trait_attr = match list[0].meta_item() { - Some(meta_item) => meta_item, - _ => { - self.handler.span_err(list[0].span(), "not a meta item"); - return; - } - }; - let trait_ident = match trait_attr.ident() { - Some(trait_ident) if trait_attr.is_word() => trait_ident, - _ => { - self.handler.span_err(trait_attr.span, "must only be one word"); - return; - } - }; - - if !trait_ident.name.can_be_raw() { - self.handler.span_err( - trait_attr.span, - &format!("`{}` cannot be a name of derive macro", trait_ident), - ); - } - - let attributes_attr = list.get(1); - let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { - if !attr.has_name(sym::attributes) { - self.handler.span_err(attr.span(), "second argument must be `attributes`") - } - attr.meta_item_list() - .unwrap_or_else(|| { - self.handler - .span_err(attr.span(), "attribute must be of form: `attributes(foo, bar)`"); - &[] - }) - .iter() - .filter_map(|attr| { - let attr = match attr.meta_item() { - Some(meta_item) => meta_item, - _ => { - self.handler.span_err(attr.span(), "not a meta item"); - return None; - } - }; - - let ident = match attr.ident() { - Some(ident) if attr.is_word() => ident, - _ => { - self.handler.span_err(attr.span, "must only be one word"); - return None; - } - }; - if !ident.name.can_be_raw() { - self.handler.span_err( - attr.span, - &format!("`{}` cannot be a name of derive helper attribute", ident), - ); - } - - Some(ident.name) - }) - .collect() - } else { - Vec::new() - }; + let (trait_name, proc_attrs) = + match parse_macro_name_and_helper_attrs(self.handler, attr, "derive") { + Some(name_and_attrs) => name_and_attrs, + None => return, + }; if self.in_root && item.vis.kind.is_pub() { self.macros.push(ProcMacro::Derive(ProcMacroDerive { id: item.id, span: item.span, - trait_name: trait_ident.name, + trait_name, function_name: item.ident, attrs: proc_attrs, })); @@ -329,11 +260,11 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { return; } - if self.sess.check_name(attr, sym::proc_macro_derive) { + if attr.has_name(sym::proc_macro_derive) { self.collect_custom_derive(item, attr); - } else if self.sess.check_name(attr, sym::proc_macro_attribute) { + } else if attr.has_name(sym::proc_macro_attribute) { self.collect_attr_proc_macro(item); - } else if self.sess.check_name(attr, sym::proc_macro) { + } else if attr.has_name(sym::proc_macro) { self.collect_bang_proc_macro(item); }; @@ -373,7 +304,7 @@ fn mk_decls( &[sym::rustc_attrs, sym::proc_macro_internals], None, ); - let span = DUMMY_SP.with_def_site_ctxt(expn_id); + let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); let proc_macro = Ident::new(sym::proc_macro, span); let krate = cx.item(span, proc_macro, Vec::new(), ast::ItemKind::ExternCrate(None)); diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 4aafcb2fb6d..1ea2c8843d6 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -61,7 +61,9 @@ pub fn expand_file( let topmost = cx.expansion_cause().unwrap_or(sp); let loc = cx.source_map().lookup_char_pos(topmost.lo()); - base::MacEager::expr(cx.expr_str(topmost, Symbol::intern(&loc.file.name.to_string()))) + base::MacEager::expr( + cx.expr_str(topmost, Symbol::intern(&loc.file.name.prefer_remapped().to_string_lossy())), + ) } pub fn expand_stringify( @@ -157,7 +159,7 @@ pub fn expand_include<'cx>( } } - Box::new(ExpandResult { p, node_id: cx.resolver.lint_node_id(cx.current_expansion.id) }) + Box::new(ExpandResult { p, node_id: cx.current_expansion.lint_node_id }) } // include_str! : read the given file, insert it as a literal string expr diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index fbd8be22a9d..e0d57267525 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -34,8 +34,8 @@ pub fn inject( &[sym::prelude_import], None, ); - let span = DUMMY_SP.with_def_site_ctxt(expn_id); - let call_site = DUMMY_SP.with_call_site_ctxt(expn_id); + let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); + let call_site = DUMMY_SP.with_call_site_ctxt(expn_id.to_expn_id()); let ecfg = ExpansionConfig::default("std_lib_injection".to_string()); let cx = ExtCtxt::new(sess, ecfg, resolver, None); diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index e845f9ec55a..99544ddb66e 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -254,6 +254,10 @@ pub fn expand_test_or_bench( "allow_fail", cx.expr_bool(sp, should_fail(&cx.sess, &item)), ), + // compile_fail: true | false + field("compile_fail", cx.expr_bool(sp, false)), + // no_run: true | false + field("no_run", cx.expr_bool(sp, false)), // should_panic: ... field( "should_panic", diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 28e82597843..d791677cb8e 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -126,7 +126,8 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { 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, Transparency::Opaque); + test.ident.span = + test.ident.span.apply_mark(expn_id.to_expn_id(), Transparency::Opaque); } self.cx.test_cases.extend(tests); } @@ -142,7 +143,7 @@ fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPoin ast::ItemKind::Fn(..) => { if sess.contains_name(&item.attrs, sym::start) { EntryPointType::Start - } else if sess.contains_name(&item.attrs, sym::main) { + } else if sess.contains_name(&item.attrs, sym::rustc_main) { EntryPointType::MainAttr } else if item.ident.name == sym::main { if depth == 1 { @@ -187,8 +188,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { let attrs = attrs .into_iter() .filter(|attr| { - !self.sess.check_name(attr, sym::main) - && !self.sess.check_name(attr, sym::start) + !attr.has_name(sym::rustc_main) && !attr.has_name(sym::start) }) .chain(iter::once(allow_dead_code)) .collect(); @@ -220,10 +220,10 @@ fn generate_test_harness( let expn_id = ext_cx.resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::TestHarness, - &[sym::main, sym::test, sym::rustc_attrs], + &[sym::test, sym::rustc_attrs], None, ); - let def_site = DUMMY_SP.with_def_site_ctxt(expn_id); + let def_site = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); // Remove the entry points let mut cleaner = EntryPointCleaner { sess, depth: 0, def_site }; @@ -247,7 +247,7 @@ fn generate_test_harness( /// By default this expands to /// /// ``` -/// #[main] +/// #[rustc_main] /// pub fn main() { /// extern crate test; /// test::test_main_static(&[ @@ -297,8 +297,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> { let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp, test_id, vec![], ast::ItemKind::ExternCrate(None))); - // #[main] - let main_meta = ecx.meta_word(sp, sym::main); + // #[rustc_main] + let main_meta = ecx.meta_word(sp, sym::rustc_main); let main_attr = ecx.attribute(main_meta); // pub fn main() { ... } @@ -314,8 +314,12 @@ 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 ast::FnKind(def, sig, ast::Generics::default(), Some(main_body))); + let main = ast::ItemKind::Fn(Box::new(ast::FnKind( + def, + sig, + ast::Generics::default(), + Some(main_body), + ))); // Honor the reexport_test_harness_main attribute let main_id = match cx.reexport_test_harness_main { |
