1
Fork 0

Use the ecx.call_site() span for generating refs to format_args! internals

`format_args!` uses `#[allow_internal_unstable]` to access internal
functions and structs that are marked unstable. For this to work, the
spans on AST nodes referencing unstable internals must be equal (same
lo/hi values) to the `format_args!` call site, so that the stability
checker can recognize that the AST node was generated by the macro.
This commit is contained in:
Ryan Prichard 2015-04-10 04:11:21 -07:00
parent 47def3ef27
commit bd26307411

View file

@ -38,6 +38,10 @@ enum Position {
struct Context<'a, 'b:'a> { struct Context<'a, 'b:'a> {
ecx: &'a mut ExtCtxt<'b>, ecx: &'a mut ExtCtxt<'b>,
/// The macro's call site. References to unstable formatting internals must
/// use this span to pass the stability checker.
macsp: Span,
/// The span of the format string literal.
fmtsp: Span, fmtsp: Span,
/// Parsed argument expressions and the types that we've found so far for /// Parsed argument expressions and the types that we've found so far for
@ -308,7 +312,7 @@ impl<'a, 'b> Context<'a, 'b> {
} }
fn trans_count(&self, c: parse::Count) -> P<ast::Expr> { fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
let sp = self.fmtsp; let sp = self.macsp;
let count = |c, arg| { let count = |c, arg| {
let mut path = Context::rtpath(self.ecx, "Count"); let mut path = Context::rtpath(self.ecx, "Count");
path.push(self.ecx.ident_of(c)); path.push(self.ecx.ident_of(c));
@ -346,7 +350,7 @@ impl<'a, 'b> Context<'a, 'b> {
/// Translate a `parse::Piece` to a static `rt::Argument` or append /// Translate a `parse::Piece` to a static `rt::Argument` or append
/// to the `literal` string. /// to the `literal` string.
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> { fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
let sp = self.fmtsp; let sp = self.macsp;
match *piece { match *piece {
parse::String(s) => { parse::String(s) => {
self.literal.push_str(s); self.literal.push_str(s);
@ -442,22 +446,22 @@ impl<'a, 'b> Context<'a, 'b> {
piece_ty: P<ast::Ty>, piece_ty: P<ast::Ty>,
pieces: Vec<P<ast::Expr>>) pieces: Vec<P<ast::Expr>>)
-> P<ast::Expr> { -> P<ast::Expr> {
let fmtsp = piece_ty.span; let sp = piece_ty.span;
let ty = ecx.ty_rptr(fmtsp, let ty = ecx.ty_rptr(sp,
ecx.ty(fmtsp, ast::TyVec(piece_ty)), ecx.ty(sp, ast::TyVec(piece_ty)),
Some(ecx.lifetime(fmtsp, special_idents::static_lifetime.name)), Some(ecx.lifetime(sp, special_idents::static_lifetime.name)),
ast::MutImmutable); ast::MutImmutable);
let slice = ecx.expr_vec_slice(fmtsp, pieces); let slice = ecx.expr_vec_slice(sp, pieces);
let st = ast::ItemStatic(ty, ast::MutImmutable, slice); let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
let name = ecx.ident_of(name); let name = ecx.ident_of(name);
let item = ecx.item(fmtsp, name, vec![], st); let item = ecx.item(sp, name, vec![], st);
let decl = respan(fmtsp, ast::DeclItem(item)); let decl = respan(sp, ast::DeclItem(item));
// Wrap the declaration in a block so that it forms a single expression. // Wrap the declaration in a block so that it forms a single expression.
ecx.expr_block(ecx.block(fmtsp, ecx.expr_block(ecx.block(sp,
vec![P(respan(fmtsp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))], vec![P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))],
Some(ecx.expr_ident(fmtsp, name)))) Some(ecx.expr_ident(sp, name))))
} }
/// Actually builds the expression which the iformat! block will be expanded /// Actually builds the expression which the iformat! block will be expanded
@ -497,7 +501,7 @@ impl<'a, 'b> Context<'a, 'b> {
let name = self.ecx.ident_of(&format!("__arg{}", i)); let name = self.ecx.ident_of(&format!("__arg{}", i));
pats.push(self.ecx.pat_ident(e.span, name)); pats.push(self.ecx.pat_ident(e.span, name));
locals.push(Context::format_arg(self.ecx, e.span, arg_ty, locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
self.ecx.expr_ident(e.span, name))); self.ecx.expr_ident(e.span, name)));
heads.push(self.ecx.expr_addr_of(e.span, e)); heads.push(self.ecx.expr_addr_of(e.span, e));
} }
@ -515,7 +519,7 @@ impl<'a, 'b> Context<'a, 'b> {
*name)); *name));
pats.push(self.ecx.pat_ident(e.span, lname)); pats.push(self.ecx.pat_ident(e.span, lname));
names[*self.name_positions.get(name).unwrap()] = names[*self.name_positions.get(name).unwrap()] =
Some(Context::format_arg(self.ecx, e.span, arg_ty, Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
self.ecx.expr_ident(e.span, lname))); self.ecx.expr_ident(e.span, lname)));
heads.push(self.ecx.expr_addr_of(e.span, e)); heads.push(self.ecx.expr_addr_of(e.span, e));
} }
@ -566,7 +570,7 @@ impl<'a, 'b> Context<'a, 'b> {
// Build up the static array which will store our precompiled // Build up the static array which will store our precompiled
// nonstandard placeholders, if there are any. // nonstandard placeholders, if there are any.
let piece_ty = self.ecx.ty_path(self.ecx.path_global( let piece_ty = self.ecx.ty_path(self.ecx.path_global(
self.fmtsp, self.macsp,
Context::rtpath(self.ecx, "Argument"))); Context::rtpath(self.ecx, "Argument")));
let fmt = Context::static_array(self.ecx, let fmt = Context::static_array(self.ecx,
"__STATIC_FMTARGS", "__STATIC_FMTARGS",
@ -576,14 +580,14 @@ impl<'a, 'b> Context<'a, 'b> {
("new_v1_formatted", vec![pieces, args_slice, fmt]) ("new_v1_formatted", vec![pieces, args_slice, fmt])
}; };
self.ecx.expr_call_global(self.fmtsp, vec!( self.ecx.expr_call_global(self.macsp, vec!(
self.ecx.ident_of_std("core"), self.ecx.ident_of_std("core"),
self.ecx.ident_of("fmt"), self.ecx.ident_of("fmt"),
self.ecx.ident_of("Arguments"), self.ecx.ident_of("Arguments"),
self.ecx.ident_of(fn_name)), fn_args) self.ecx.ident_of(fn_name)), fn_args)
} }
fn format_arg(ecx: &ExtCtxt, sp: Span, fn format_arg(ecx: &ExtCtxt, macsp: Span, sp: Span,
ty: &ArgumentType, arg: P<ast::Expr>) ty: &ArgumentType, arg: P<ast::Expr>)
-> P<ast::Expr> { -> P<ast::Expr> {
let trait_ = match *ty { let trait_ = match *ty {
@ -607,7 +611,7 @@ impl<'a, 'b> Context<'a, 'b> {
} }
} }
Unsigned => { Unsigned => {
return ecx.expr_call_global(sp, vec![ return ecx.expr_call_global(macsp, vec![
ecx.ident_of_std("core"), ecx.ident_of_std("core"),
ecx.ident_of("fmt"), ecx.ident_of("fmt"),
ecx.ident_of("ArgumentV1"), ecx.ident_of("ArgumentV1"),
@ -620,7 +624,7 @@ impl<'a, 'b> Context<'a, 'b> {
ecx.ident_of("fmt"), ecx.ident_of("fmt"),
ecx.ident_of(trait_), ecx.ident_of(trait_),
ecx.ident_of("fmt")]); ecx.ident_of("fmt")]);
ecx.expr_call_global(sp, vec![ ecx.expr_call_global(macsp, vec![
ecx.ident_of_std("core"), ecx.ident_of_std("core"),
ecx.ident_of("fmt"), ecx.ident_of("fmt"),
ecx.ident_of("ArgumentV1"), ecx.ident_of("ArgumentV1"),
@ -650,6 +654,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
names: HashMap<String, P<ast::Expr>>) names: HashMap<String, P<ast::Expr>>)
-> P<ast::Expr> { -> P<ast::Expr> {
let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect(); let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
let macsp = ecx.call_site();
// Expand the format literal so that efmt.span will have a backtrace. This // Expand the format literal so that efmt.span will have a backtrace. This
// is essential for locating a bug when the format literal is generated in // is essential for locating a bug when the format literal is generated in
// a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")). // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
@ -668,6 +673,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
pieces: Vec::new(), pieces: Vec::new(),
str_pieces: Vec::new(), str_pieces: Vec::new(),
all_pieces_simple: true, all_pieces_simple: true,
macsp: macsp,
fmtsp: efmt.span, fmtsp: efmt.span,
}; };
let fmt = match expr_to_string(cx.ecx, let fmt = match expr_to_string(cx.ecx,