rustc: Implement custom derive (macros 1.1)

This commit is an implementation of [RFC 1681] which adds support to the
compiler for first-class user-define custom `#[derive]` modes with a far more
stable API than plugins have today.

[RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md

The main features added by this commit are:

* A new `rustc-macro` crate-type. This crate type represents one which will
  provide custom `derive` implementations and perhaps eventually flower into the
  implementation of macros 2.0 as well.

* A new `rustc_macro` crate in the standard distribution. This crate will
  provide the runtime interface between macro crates and the compiler. The API
  here is particularly conservative right now but has quite a bit of room to
  expand into any manner of APIs required by macro authors.

* The ability to load new derive modes through the `#[macro_use]` annotations on
  other crates.

All support added here is gated behind the `rustc_macro` feature gate, both for
the library support (the `rustc_macro` crate) as well as the language features.

There are a few minor differences from the implementation outlined in the RFC,
such as the `rustc_macro` crate being available as a dylib and all symbols are
`dlsym`'d directly instead of having a shim compiled. These should only affect
the implementation, however, not the public interface.

This commit also ended up touching a lot of code related to `#[derive]`, making
a few notable changes:

* Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't
  sure how to keep this behavior and *not* expose it to custom derive.

* Derive attributes no longer have access to unstable features by default, they
  have to opt in on a granular level.

* The `derive(Copy,Clone)` optimization is now done through another "obscure
  attribute" which is just intended to ferry along in the compiler that such an
  optimization is possible. The `derive(PartialEq,Eq)` optimization was also
  updated to do something similar.

---

One part of this PR which needs to be improved before stabilizing are the errors
and exact interfaces here. The error messages are relatively poor quality and
there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]`
not working by default. The custom attributes added by the compiler end up
becoming unstable again when going through a custom impl.

Hopefully though this is enough to start allowing experimentation on crates.io!

syntax-[breaking-change]
This commit is contained in:
Alex Crichton 2016-08-22 17:07:11 -07:00
parent 8aeb15acc7
commit ecc6c39e87
84 changed files with 2213 additions and 279 deletions

View File

@ -59,7 +59,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_
rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
rustc_data_structures rustc_platform_intrinsics rustc_errors \
rustc_plugin rustc_metadata rustc_passes rustc_save_analysis \
rustc_const_eval rustc_const_math rustc_incremental
rustc_const_eval rustc_const_math rustc_incremental rustc_macro
HOST_CRATES := syntax syntax_ext proc_macro syntax_pos $(RUSTC_CRATES) rustdoc fmt_macros \
flate arena graphviz rbml log serialize
TOOLS := compiletest rustdoc rustc rustbook error_index_generator
@ -99,7 +99,7 @@ DEPS_term := std
DEPS_test := std getopts term native:rust_test_helpers
DEPS_syntax := std term serialize log arena libc rustc_bitflags rustc_unicode rustc_errors syntax_pos
DEPS_syntax_ext := syntax syntax_pos rustc_errors fmt_macros
DEPS_syntax_ext := syntax syntax_pos rustc_errors fmt_macros rustc_macro
DEPS_proc_macro := syntax syntax_pos rustc_plugin log
DEPS_syntax_pos := serialize
@ -118,11 +118,13 @@ DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_bo
rustc_trans rustc_privacy rustc_lint rustc_plugin \
rustc_metadata syntax_ext proc_macro \
rustc_passes rustc_save_analysis rustc_const_eval \
rustc_incremental syntax_pos rustc_errors
rustc_incremental syntax_pos rustc_errors rustc_macro
DEPS_rustc_errors := log libc serialize syntax_pos
DEPS_rustc_lint := rustc log syntax syntax_pos rustc_const_eval
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
DEPS_rustc_metadata := rustc syntax syntax_pos rustc_errors rbml rustc_const_math
DEPS_rustc_macro := std syntax
DEPS_rustc_metadata := rustc syntax syntax_pos rustc_errors rbml rustc_const_math \
rustc_macro syntax_ext
DEPS_rustc_passes := syntax syntax_pos rustc core rustc_const_eval rustc_errors
DEPS_rustc_mir := rustc syntax syntax_pos rustc_const_math rustc_const_eval rustc_bitflags
DEPS_rustc_resolve := arena rustc log syntax syntax_pos rustc_errors

View File

@ -139,8 +139,13 @@ fn calculate_type(sess: &session::Session,
}
}
// Everything else falls through below
config::CrateTypeExecutable | config::CrateTypeDylib => {},
// Everything else falls through below. This will happen either with the
// `-C prefer-dynamic` or because we're a rustc-macro crate. Note that
// rustc-macro crates are required to be dylibs, and they're currently
// required to link to libsyntax as well.
config::CrateTypeExecutable |
config::CrateTypeDylib |
config::CrateTypeRustcMacro => {},
}
let mut formats = FnvHashMap();

View File

@ -138,7 +138,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
// Creates a new reachability computation context.
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ReachableContext<'a, 'tcx> {
let any_library = tcx.sess.crate_types.borrow().iter().any(|ty| {
*ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib
*ty == config::CrateTypeRlib || *ty == config::CrateTypeDylib ||
*ty == config::CrateTypeRustcMacro
});
ReachableContext {
tcx: tcx,

View File

@ -70,6 +70,7 @@ fn verify(sess: &Session, items: &lang_items::LanguageItems) {
let needs_check = sess.crate_types.borrow().iter().any(|kind| {
match *kind {
config::CrateTypeDylib |
config::CrateTypeRustcMacro |
config::CrateTypeCdylib |
config::CrateTypeExecutable |
config::CrateTypeStaticlib => true,

View File

@ -475,6 +475,7 @@ pub enum CrateType {
CrateTypeRlib,
CrateTypeStaticlib,
CrateTypeCdylib,
CrateTypeRustcMacro,
}
#[derive(Clone, Hash)]
@ -962,6 +963,9 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
if sess.opts.debug_assertions {
ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
}
if sess.opts.crate_types.contains(&CrateTypeRustcMacro) {
ret.push(attr::mk_word_item(InternedString::new("rustc_macro")));
}
return ret;
}
@ -1547,6 +1551,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
"dylib" => CrateTypeDylib,
"cdylib" => CrateTypeCdylib,
"bin" => CrateTypeExecutable,
"rustc-macro" => CrateTypeRustcMacro,
_ => {
return Err(format!("unknown crate type: `{}`",
part));
@ -1635,6 +1640,7 @@ impl fmt::Display for CrateType {
CrateTypeRlib => "rlib".fmt(f),
CrateTypeStaticlib => "staticlib".fmt(f),
CrateTypeCdylib => "cdylib".fmt(f),
CrateTypeRustcMacro => "rustc-macro".fmt(f),
}
}
}

View File

@ -62,6 +62,7 @@ pub struct Session {
pub entry_fn: RefCell<Option<(NodeId, Span)>>,
pub entry_type: Cell<Option<config::EntryFnType>>,
pub plugin_registrar_fn: Cell<Option<ast::NodeId>>,
pub derive_registrar_fn: Cell<Option<ast::NodeId>>,
pub default_sysroot: Option<PathBuf>,
// The name of the root source file of the crate, in the local file system.
// The path is always expected to be absolute. `None` means that there is no
@ -314,6 +315,12 @@ impl Session {
format!("__rustc_plugin_registrar__{}_{}", svh, index.as_usize())
}
pub fn generate_derive_registrar_symbol(&self,
svh: &Svh,
index: DefIndex) -> String {
format!("__rustc_derive_registrar__{}_{}", svh, index.as_usize())
}
pub fn sysroot<'a>(&'a self) -> &'a Path {
match self.opts.maybe_sysroot {
Some (ref sysroot) => sysroot,
@ -501,6 +508,7 @@ pub fn build_session_(sopts: config::Options,
entry_fn: RefCell::new(None),
entry_type: Cell::new(None),
plugin_registrar_fn: Cell::new(None),
derive_registrar_fn: Cell::new(None),
default_sysroot: default_sysroot,
local_crate_source_file: local_crate_source_file,
working_dir: env::current_dir().unwrap(),

View File

@ -495,6 +495,10 @@ pub struct GlobalCtxt<'tcx> {
/// Cache for layouts computed from types.
pub layout_cache: RefCell<FnvHashMap<Ty<'tcx>, &'tcx Layout>>,
/// Map from function to the `#[derive]` mode that it's defining. Only used
/// by `rustc-macro` crates.
pub derive_macros: RefCell<NodeMap<token::InternedString>>,
}
impl<'tcx> GlobalCtxt<'tcx> {
@ -756,6 +760,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
crate_name: token::intern_and_get_ident(crate_name),
data_layout: data_layout,
layout_cache: RefCell::new(FnvHashMap()),
derive_macros: RefCell::new(NodeMap()),
}, f)
}
}

View File

@ -0,0 +1,37 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::dep_graph::DepNode;
use rustc::hir::intravisit::Visitor;
use rustc::hir::map::Map;
use rustc::hir;
use syntax::ast;
use syntax::attr;
pub fn find(hir_map: &Map) -> Option<ast::NodeId> {
let _task = hir_map.dep_graph.in_task(DepNode::PluginRegistrar);
let krate = hir_map.krate();
let mut finder = Finder { registrar: None };
krate.visit_all_items(&mut finder);
finder.registrar
}
struct Finder {
registrar: Option<ast::NodeId>,
}
impl<'v> Visitor<'v> for Finder {
fn visit_item(&mut self, item: &hir::Item) {
if attr::contains_name(&item.attrs, "rustc_derive_registrar") {
self.registrar = Some(item.id);
}
}
}

View File

@ -55,6 +55,8 @@ use syntax::util::node_count::NodeCounter;
use syntax;
use syntax_ext;
use derive_registrar;
#[derive(Clone)]
pub struct Resolutions {
pub def_map: DefMap,
@ -696,6 +698,18 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session,
sess.diagnostic())
});
krate = time(time_passes, "maybe creating a macro crate", || {
let crate_types = sess.crate_types.borrow();
let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro);
let num_crate_types = crate_types.len();
syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess,
krate,
is_rustc_macro_crate,
num_crate_types,
sess.diagnostic(),
&sess.features.borrow())
});
let resolver_arenas = Resolver::arenas();
let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas);
@ -838,6 +852,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
sess.plugin_registrar_fn.set(time(time_passes, "looking for plugin registrar", || {
plugin::build::find_plugin_registrar(sess.diagnostic(), &hir_map)
}));
sess.derive_registrar_fn.set(derive_registrar::find(&hir_map));
let region_map = time(time_passes,
"region resolution",
@ -1171,6 +1186,9 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<c
Some(ref n) if *n == "staticlib" => {
Some(config::CrateTypeStaticlib)
}
Some(ref n) if *n == "rustc-macro" => {
Some(config::CrateTypeRustcMacro)
}
Some(ref n) if *n == "bin" => Some(config::CrateTypeExecutable),
Some(_) => {
session.add_lint(lint::builtin::UNKNOWN_CRATE_TYPES,

View File

@ -107,7 +107,7 @@ pub mod test;
pub mod driver;
pub mod pretty;
pub mod target_features;
mod derive_registrar;
const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
md#bug-reports";

View File

@ -0,0 +1,12 @@
[package]
authors = ["The Rust Project Developers"]
name = "rustc_macro"
version = "0.0.0"
[lib]
name = "rustc_macro"
path = "lib.rs"
crate-type = ["dylib"]
[dependencies]
syntax = { path = "../libsyntax" }

169
src/librustc_macro/lib.rs Normal file
View File

@ -0,0 +1,169 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A support library for macro authors when defining new macros.
//!
//! This library, provided by the standard distribution, provides the types
//! consumed in the interfaces of procedurally defined macro definitions.
//! Currently the primary use of this crate is to provide the ability to define
//! new custom derive modes through `#[rustc_macro_derive]`.
//!
//! Added recently as part of [RFC 1681] this crate is currently *unstable* and
//! requires the `#![feature(rustc_macro_lib)]` directive to use. Eventually,
//! though, it is intended for this crate to become stable to use (perhaps under
//! a different name).
//!
//! [RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md
//!
//! Note that this crate is intentionally very bare-bones currently. The main
//! type, `TokenStream`, only supports `fmt::Display` and `FromStr`
//! implementations, indicating that it can only go to and come from a string.
//! This functionality is intended to be expanded over time as more surface
//! area for macro authors is stabilized.
#![crate_name = "rustc_macro"]
#![unstable(feature = "rustc_macro_lib", issue = "27812")]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![cfg_attr(not(stage0), deny(warnings))]
#![deny(missing_docs)]
#![feature(rustc_private)]
#![feature(staged_api)]
#![feature(lang_items)]
extern crate syntax;
use std::fmt;
use std::str::FromStr;
use syntax::ast;
use syntax::parse;
use syntax::ptr::P;
/// The main type provided by this crate, representing an abstract stream of
/// tokens.
///
/// This is both the input and output of `#[rustc_macro_derive]` definitions.
/// Currently it's required to be a list of valid Rust items, but this
/// restriction may be lifted in the future.
///
/// The API of this type is intentionally bare-bones, but it'll be expanded over
/// time!
pub struct TokenStream {
inner: Vec<P<ast::Item>>,
}
/// Error returned from `TokenStream::from_str`.
#[derive(Debug)]
pub struct LexError {
_inner: (),
}
/// Permanently unstable internal implementation details of this crate. This
/// should not be used.
///
/// These methods are used by the rest of the compiler to generate instances of
/// `TokenStream` to hand to macro definitions, as well as consume the output.
///
/// Note that this module is also intentionally separate from the rest of the
/// crate. This allows the `#[unstable]` directive below to naturally apply to
/// all of the contents.
#[unstable(feature = "rustc_macro_internals", issue = "27812")]
#[doc(hidden)]
pub mod __internal {
use std::cell::Cell;
use syntax::ast;
use syntax::ptr::P;
use syntax::parse::ParseSess;
use super::TokenStream;
pub fn new_token_stream(item: P<ast::Item>) -> TokenStream {
TokenStream { inner: vec![item] }
}
pub fn token_stream_items(stream: TokenStream) -> Vec<P<ast::Item>> {
stream.inner
}
pub trait Registry {
fn register_custom_derive(&mut self,
trait_name: &str,
expand: fn(TokenStream) -> TokenStream);
}
// Emulate scoped_thread_local!() here essentially
thread_local! {
static CURRENT_SESS: Cell<*const ParseSess> = Cell::new(0 as *const _);
}
pub fn set_parse_sess<F, R>(sess: &ParseSess, f: F) -> R
where F: FnOnce() -> R
{
struct Reset { prev: *const ParseSess }
impl Drop for Reset {
fn drop(&mut self) {
CURRENT_SESS.with(|p| p.set(self.prev));
}
}
CURRENT_SESS.with(|p| {
let _reset = Reset { prev: p.get() };
p.set(sess);
f()
})
}
pub fn with_parse_sess<F, R>(f: F) -> R
where F: FnOnce(&ParseSess) -> R
{
let p = CURRENT_SESS.with(|p| p.get());
assert!(!p.is_null());
f(unsafe { &*p })
}
}
impl FromStr for TokenStream {
type Err = LexError;
fn from_str(src: &str) -> Result<TokenStream, LexError> {
__internal::with_parse_sess(|sess| {
let src = src.to_string();
let cfg = Vec::new();
let name = "rustc-macro source code".to_string();
let mut parser = parse::new_parser_from_source_str(sess, cfg, name,
src);
let mut ret = TokenStream { inner: Vec::new() };
loop {
match parser.parse_item() {
Ok(Some(item)) => ret.inner.push(item),
Ok(None) => return Ok(ret),
Err(mut err) => {
err.cancel();
return Err(LexError { _inner: () })
}
}
}
})
}
}
impl fmt::Display for TokenStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for item in self.inner.iter() {
let item = syntax::print::pprust::item_to_string(item);
try!(f.write_str(&item));
try!(f.write_str("\n"));
}
Ok(())
}
}

View File

@ -19,6 +19,8 @@ rustc_const_math = { path = "../librustc_const_math" }
rustc_data_structures = { path = "../librustc_data_structures" }
rustc_errors = { path = "../librustc_errors" }
rustc_llvm = { path = "../librustc_llvm" }
rustc_macro = { path = "../librustc_macro" }
serialize = { path = "../libserialize" }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
syntax_ext = { path = "../libsyntax_ext" }
syntax_pos = { path = "../libsyntax_pos" }

View File

@ -234,6 +234,8 @@ pub fn rustc_version() -> String {
pub const tag_panic_strategy: usize = 0x114;
pub const tag_macro_derive_registrar: usize = 0x115;
// NB: increment this if you change the format of metadata such that
// rustc_version can't be found.
pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2];

View File

@ -130,18 +130,23 @@ struct ExtensionCrate {
metadata: PMDSource,
dylib: Option<PathBuf>,
target_only: bool,
ident: String,
name: String,
span: Span,
should_link: bool,
}
enum PMDSource {
Registered(Rc<cstore::CrateMetadata>),
Owned(MetadataBlob),
Owned(loader::Library),
}
impl PMDSource {
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
match *self {
PMDSource::Registered(ref cmd) => cmd.data(),
PMDSource::Owned(ref mdb) => mdb.as_slice(),
PMDSource::Owned(ref lib) => lib.metadata.as_slice(),
}
}
}
@ -151,6 +156,17 @@ enum LoadResult {
Loaded(loader::Library),
}
pub struct Macros {
pub macro_rules: Vec<ast::MacroDef>,
/// An array of pairs where the first element is the name of the custom
/// derive (e.g. the trait being derived) and the second element is the
/// index of the definition.
pub custom_derive_registrar: Option<DefIndex>,
pub svh: Svh,
pub dylib: Option<PathBuf>,
}
impl<'a> CrateReader<'a> {
pub fn new(sess: &'a Session,
cstore: &'a CStore,
@ -281,6 +297,7 @@ impl<'a> CrateReader<'a> {
explicitly_linked: bool)
-> (ast::CrateNum, Rc<cstore::CrateMetadata>,
cstore::CrateSource) {
info!("register crate `extern crate {} as {}`", name, ident);
self.verify_no_symbol_conflicts(span, &lib.metadata);
// Claim this crate number and cache it
@ -319,6 +336,11 @@ impl<'a> CrateReader<'a> {
explicitly_linked: Cell::new(explicitly_linked),
});
if decoder::get_derive_registrar_fn(cmeta.data.as_slice()).is_some() {
self.sess.span_err(span, "crates of the `rustc-macro` crate type \
cannot be linked at runtime");
}
let source = cstore::CrateSource {
dylib: dylib,
rlib: rlib,
@ -349,9 +371,11 @@ impl<'a> CrateReader<'a> {
kind: PathKind,
explicitly_linked: bool)
-> (ast::CrateNum, Rc<cstore::CrateMetadata>, cstore::CrateSource) {
info!("resolving crate `extern crate {} as {}`", name, ident);
let result = match self.existing_match(name, hash, kind) {
Some(cnum) => LoadResult::Previous(cnum),
None => {
info!("falling back to a load");
let mut load_ctxt = loader::Context {
sess: self.sess,
span: span,
@ -412,6 +436,7 @@ impl<'a> CrateReader<'a> {
self.cstore.iter_crate_data(|cnum, data| {
if data.name() == meta_name && meta_hash == data.hash() {
assert!(loader.hash.is_none());
info!("load success, going to previous cnum: {}", cnum);
result = LoadResult::Previous(cnum);
}
});
@ -483,6 +508,8 @@ impl<'a> CrateReader<'a> {
}
fn read_extension_crate(&mut self, span: Span, info: &CrateInfo) -> ExtensionCrate {
info!("read extension crate {} `extern crate {} as {}` linked={}",
info.id, info.name, info.ident, info.should_link);
let target_triple = &self.sess.opts.target_triple[..];
let is_cross = target_triple != config::host_triple();
let mut should_link = info.should_link && !is_cross;
@ -533,16 +560,7 @@ impl<'a> CrateReader<'a> {
}
LoadResult::Loaded(library) => {
let dylib = library.dylib.clone();
let metadata = if should_link {
// Register crate now to avoid double-reading metadata
let (_, cmd, _) = self.register_crate(&None, &info.ident,
&info.name, span,
library, true);
PMDSource::Registered(cmd)
} else {
// Not registering the crate; just hold on to the metadata
PMDSource::Owned(library.metadata)
};
let metadata = PMDSource::Owned(library);
(dylib, metadata)
}
};
@ -551,59 +569,97 @@ impl<'a> CrateReader<'a> {
metadata: metadata,
dylib: dylib.map(|p| p.0),
target_only: target_only,
name: info.name.to_string(),
ident: info.ident.to_string(),
span: span,
should_link: should_link,
}
}
/// Read exported macros.
pub fn read_exported_macros(&mut self, item: &ast::Item) -> Vec<ast::MacroDef> {
pub fn read_macros(&mut self, item: &ast::Item) -> Macros {
let ci = self.extract_crate_info(item).unwrap();
let ekrate = self.read_extension_crate(item.span, &ci);
let source_name = format!("<{} macros>", item.ident);
let mut macros = vec![];
let mut ret = Macros {
macro_rules: Vec::new(),
custom_derive_registrar: None,
svh: decoder::get_crate_hash(ekrate.metadata.as_slice()),
dylib: None,
};
decoder::each_exported_macro(ekrate.metadata.as_slice(),
|name, attrs, span, body| {
// NB: Don't use parse::parse_tts_from_source_str because it parses with
// quote_depth > 0.
let mut p = parse::new_parser_from_source_str(&self.sess.parse_sess,
self.local_crate_config.clone(),
source_name.clone(),
body);
let lo = p.span.lo;
let body = match p.parse_all_token_trees() {
Ok(body) => body,
Err(mut err) => {
err.emit();
self.sess.abort_if_errors();
unreachable!();
}
};
let local_span = mk_sp(lo, p.last_span.hi);
// Mark the attrs as used
for attr in &attrs {
attr::mark_used(attr);
|name, attrs, span, body| {
// NB: Don't use parse::parse_tts_from_source_str because it parses with
// quote_depth > 0.
let mut p = parse::new_parser_from_source_str(&self.sess.parse_sess,
self.local_crate_config.clone(),
source_name.clone(),
body);
let lo = p.span.lo;
let body = match p.parse_all_token_trees() {
Ok(body) => body,
Err(mut err) => {
err.emit();
self.sess.abort_if_errors();
unreachable!();
}
};
let local_span = mk_sp(lo, p.last_span.hi);
macros.push(ast::MacroDef {
ident: ast::Ident::with_empty_ctxt(name),
attrs: attrs,
id: ast::DUMMY_NODE_ID,
span: local_span,
imported_from: Some(item.ident),
// overridden in plugin/load.rs
export: false,
use_locally: false,
allow_internal_unstable: false,
body: body,
});
self.sess.imported_macro_spans.borrow_mut()
.insert(local_span, (name.as_str().to_string(), span));
true
// Mark the attrs as used
for attr in &attrs {
attr::mark_used(attr);
}
);
macros
ret.macro_rules.push(ast::MacroDef {
ident: ast::Ident::with_empty_ctxt(name),
attrs: attrs,
id: ast::DUMMY_NODE_ID,
span: local_span,
imported_from: Some(item.ident),
// overridden in plugin/load.rs
export: false,
use_locally: false,
allow_internal_unstable: false,
body: body,
});
self.sess.imported_macro_spans.borrow_mut()
.insert(local_span, (name.as_str().to_string(), span));
true
});
match decoder::get_derive_registrar_fn(ekrate.metadata.as_slice()) {
Some(id) => ret.custom_derive_registrar = Some(id),
// If this crate is not a rustc-macro crate then we might be able to
// register it with the local crate store to prevent loading the
// metadata twice.
//
// If it's a rustc-macro crate, though, then we definitely don't
// want to register it with the local crate store as we're just
// going to use it as we would a plugin.
None => {
ekrate.register(self);
return ret
}
}
self.cstore.add_used_for_derive_macros(item);
ret.dylib = ekrate.dylib.clone();
if ret.dylib.is_none() {
span_bug!(item.span, "rustc-macro crate not dylib");
}
if ekrate.target_only {
let message = format!("rustc-macro crate is not available for \
triple `{}` (only found {})",
config::host_triple(),
self.sess.opts.target_triple);
self.sess.span_fatal(item.span, &message);
}
return ret
}
/// Look for a plugin registrar. Returns library path, crate
@ -774,6 +830,7 @@ impl<'a> CrateReader<'a> {
match *ct {
config::CrateTypeExecutable => need_exe_alloc = true,
config::CrateTypeDylib |
config::CrateTypeRustcMacro |
config::CrateTypeCdylib |
config::CrateTypeStaticlib => need_lib_alloc = true,
config::CrateTypeRlib => {}
@ -858,6 +915,27 @@ impl<'a> CrateReader<'a> {
}
}
impl ExtensionCrate {
fn register(self, creader: &mut CrateReader) {
if !self.should_link {
return
}
let library = match self.metadata {
PMDSource::Owned(lib) => lib,
PMDSource::Registered(_) => return,
};
// Register crate now to avoid double-reading metadata
creader.register_crate(&None,
&self.ident,
&self.name,
self.span,
library,
true);
}
}
impl<'a> LocalCrateReader<'a> {
fn new(sess: &'a Session,
cstore: &'a CStore,
@ -906,11 +984,25 @@ impl<'a> LocalCrateReader<'a> {
fn process_item(&mut self, i: &ast::Item) {
match i.node {
ast::ItemKind::ExternCrate(_) => {
if !should_link(i) {
return;
// If this `extern crate` item has `#[macro_use]` then we can
// safely skip it. These annotations were processed during macro
// expansion and are already loaded (if necessary) into our
// crate store.
//
// Note that it's important we *don't* fall through below as
// some `#[macro_use]` crate are explicitly not linked (e.g.
// macro crates) so we want to ensure we avoid `resolve_crate`
// with those.
if attr::contains_name(&i.attrs, "macro_use") {
if self.cstore.was_used_for_derive_macros(i) {
return
}
}
if let Some(info) = self.creader.extract_crate_info(i) {
if !info.should_link {
return;
}
let (cnum, _, _) = self.creader.resolve_crate(&None,
&info.ident,
&info.name,

View File

@ -28,13 +28,13 @@ use rustc::hir::svh::Svh;
use rustc::middle::cstore::ExternCrate;
use rustc::session::config::PanicStrategy;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap};
use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet, DefIdMap, FnvHashSet};
use std::cell::{RefCell, Ref, Cell};
use std::rc::Rc;
use std::path::PathBuf;
use flate::Bytes;
use syntax::ast;
use syntax::ast::{self, Ident};
use syntax::attr;
use syntax::codemap;
use syntax_pos;
@ -115,6 +115,7 @@ pub struct CStore {
pub inlined_item_cache: RefCell<DefIdMap<Option<CachedInlinedItem>>>,
pub defid_for_inlined_node: RefCell<NodeMap<DefId>>,
pub visible_parent_map: RefCell<DefIdMap<DefId>>,
pub used_for_derive_macro: RefCell<FnvHashSet<Ident>>,
}
impl CStore {
@ -130,6 +131,7 @@ impl CStore {
visible_parent_map: RefCell::new(FnvHashMap()),
inlined_item_cache: RefCell::new(FnvHashMap()),
defid_for_inlined_node: RefCell::new(FnvHashMap()),
used_for_derive_macro: RefCell::new(FnvHashSet()),
}
}
@ -286,6 +288,14 @@ impl CStore {
{
self.extern_mod_crate_map.borrow().get(&emod_id).cloned()
}
pub fn was_used_for_derive_macros(&self, i: &ast::Item) -> bool {
self.used_for_derive_macro.borrow().contains(&i.ident)
}
pub fn add_used_for_derive_macros(&self, i: &ast::Item) {
self.used_for_derive_macro.borrow_mut().insert(i.ident);
}
}
impl CrateMetadata {

View File

@ -1398,6 +1398,11 @@ pub fn each_exported_macro<F>(data: &[u8], mut f: F) where
}
}
pub fn get_derive_registrar_fn(data: &[u8]) -> Option<DefIndex> {
reader::maybe_get_doc(rbml::Doc::new(data), tag_macro_derive_registrar)
.map(|doc| DefIndex::from_u32(reader::doc_as_u32(doc)))
}
pub fn get_macro_span(doc: rbml::Doc) -> Span {
let lo_doc = reader::get_doc(doc, tag_macro_def_span_lo);
let lo = BytePos(reader::doc_as_u32(lo_doc));

View File

@ -31,7 +31,7 @@ use rustc::ty::{self, Ty, TyCtxt};
use rustc::hir::svh::Svh;
use rustc::mir::mir_map::MirMap;
use rustc::session::config::{self, PanicStrategy};
use rustc::session::config::{self, PanicStrategy, CrateTypeRustcMacro};
use rustc::util::nodemap::{FnvHashMap, NodeSet};
use rustc_serialize::Encodable;
@ -1567,7 +1567,8 @@ fn encode_codemap(ecx: &EncodeContext, rbml_w: &mut Encoder) {
/// Serialize the text of the exported macros
fn encode_macro_defs(rbml_w: &mut Encoder,
krate: &hir::Crate) {
krate: &hir::Crate,
tcx: TyCtxt) {
rbml_w.start_tag(tag_macro_defs);
for def in &krate.exported_macros {
rbml_w.start_tag(tag_macro_def);
@ -1585,6 +1586,12 @@ fn encode_macro_defs(rbml_w: &mut Encoder,
rbml_w.end_tag();
}
rbml_w.end_tag();
if tcx.sess.crate_types.borrow().contains(&CrateTypeRustcMacro) {
let id = tcx.sess.derive_registrar_fn.get().unwrap();
let did = tcx.map.local_def_id(id);
rbml_w.wr_tagged_u32(tag_macro_derive_registrar, did.index.as_u32());
}
}
fn encode_struct_field_attrs(ecx: &EncodeContext,
@ -1882,7 +1889,7 @@ fn encode_metadata_inner(rbml_w: &mut Encoder,
// Encode macro definitions
i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap();
encode_macro_defs(rbml_w, krate);
encode_macro_defs(rbml_w, krate, ecx.tcx);
stats.macro_defs_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i;
// Encode the def IDs of impls, for coherence checking.

View File

@ -19,11 +19,13 @@
#![feature(box_patterns)]
#![feature(enumset)]
#![feature(question_mark)]
#![feature(quote)]
#![feature(rustc_diagnostic_macros)]
#![feature(rustc_macro_lib)]
#![feature(rustc_macro_internals)]
#![feature(rustc_private)]
#![feature(staged_api)]
#![feature(question_mark)]
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
@ -33,12 +35,14 @@ extern crate flate;
extern crate rbml;
extern crate serialize as rustc_serialize; // used by deriving
extern crate rustc_errors as errors;
extern crate syntax_ext;
#[macro_use]
extern crate rustc;
extern crate rustc_data_structures;
extern crate rustc_back;
extern crate rustc_llvm;
extern crate rustc_macro;
extern crate rustc_const_math;
pub use rustc::middle;

View File

@ -10,16 +10,25 @@
//! Used by `rustc` when loading a crate with exported macros.
use creader::CrateReader;
use std::collections::HashSet;
use std::env;
use std::mem;
use creader::{CrateReader, Macros};
use cstore::CStore;
use rustc::hir::def_id::DefIndex;
use rustc::session::Session;
use rustc::util::nodemap::{FnvHashSet, FnvHashMap};
use syntax::parse::token;
use rustc::util::nodemap::FnvHashMap;
use rustc_back::dynamic_lib::DynamicLibrary;
use rustc_macro::TokenStream;
use rustc_macro::__internal::Registry;
use syntax::ast;
use syntax::attr;
use syntax::ext::base::LoadedMacro;
use syntax::ext;
use syntax::parse::token;
use syntax_ext::deriving::custom::CustomDerive;
use syntax_pos::Span;
pub struct MacroLoader<'a> {
@ -47,7 +56,9 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) {
pub type MacroSelection = FnvHashMap<token::InternedString, Span>;
impl<'a> ext::base::MacroLoader for MacroLoader<'a> {
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<ast::MacroDef> {
fn load_crate(&mut self,
extern_crate: &ast::Item,
allows_macros: bool) -> Vec<LoadedMacro> {
// Parse the attributes relating to macros.
let mut import = Some(FnvHashMap()); // None => load all
let mut reexport = FnvHashMap();
@ -105,7 +116,7 @@ impl<'a> MacroLoader<'a> {
allows_macros: bool,
import: Option<MacroSelection>,
reexport: MacroSelection)
-> Vec<ast::MacroDef> {
-> Vec<LoadedMacro> {
if let Some(sel) = import.as_ref() {
if sel.is_empty() && reexport.is_empty() {
return Vec::new();
@ -118,10 +129,11 @@ impl<'a> MacroLoader<'a> {
return Vec::new();
}
let mut macros = Vec::new();
let mut seen = FnvHashSet();
let mut macros = self.reader.read_macros(vi);
let mut ret = Vec::new();
let mut seen = HashSet::new();
for mut def in self.reader.read_exported_macros(vi) {
for mut def in macros.macro_rules.drain(..) {
let name = def.ident.name.as_str();
def.use_locally = match import.as_ref() {
@ -132,10 +144,29 @@ impl<'a> MacroLoader<'a> {
def.allow_internal_unstable = attr::contains_name(&def.attrs,
"allow_internal_unstable");
debug!("load_macros: loaded: {:?}", def);
macros.push(def);
ret.push(LoadedMacro::Def(def));
seen.insert(name);
}
if let Some(index) = macros.custom_derive_registrar {
// custom derive crates currently should not have any macro_rules!
// exported macros, enforced elsewhere
assert_eq!(ret.len(), 0);
if import.is_some() {
self.sess.span_err(vi.span, "`rustc-macro` crates cannot be \
selectively imported from, must \
use `#[macro_use]`");
}
if reexport.len() > 0 {
self.sess.span_err(vi.span, "`rustc-macro` crates cannot be \
reexported from");
}
self.load_derive_macros(vi.span, &macros, index, &mut ret);
}
if let Some(sel) = import.as_ref() {
for (name, span) in sel {
if !seen.contains(&name) {
@ -152,6 +183,54 @@ impl<'a> MacroLoader<'a> {
}
}
macros
return ret
}
/// Load the custom derive macros into the list of macros we're loading.
///
/// Note that this is intentionally similar to how we load plugins today,
/// but also intentionally separate. Plugins are likely always going to be
/// implemented as dynamic libraries, but we have a possible future where
/// custom derive (and other macro-1.1 style features) are implemented via
/// executables and custom IPC.
fn load_derive_macros(&mut self,
span: Span,
macros: &Macros,
index: DefIndex,
ret: &mut Vec<LoadedMacro>) {
// Make sure the path contains a / or the linker will search for it.
let path = macros.dylib.as_ref().unwrap();
let path = env::current_dir().unwrap().join(path);
let lib = match DynamicLibrary::open(Some(&path)) {
Ok(lib) => lib,
Err(err) => self.sess.span_fatal(span, &err),
};
let sym = self.sess.generate_derive_registrar_symbol(&macros.svh, index);
let registrar = unsafe {
let sym = match lib.symbol(&sym) {
Ok(f) => f,
Err(err) => self.sess.span_fatal(span, &err),
};
mem::transmute::<*mut u8, fn(&mut Registry)>(sym)
};
struct MyRegistrar<'a>(&'a mut Vec<LoadedMacro>);
impl<'a> Registry for MyRegistrar<'a> {
fn register_custom_derive(&mut self,
trait_name: &str,
expand: fn(TokenStream) -> TokenStream) {
let derive = Box::new(CustomDerive::new(expand));
self.0.push(LoadedMacro::CustomDerive(trait_name.to_string(),
derive));
}
}
registrar(&mut MyRegistrar(ret));
// Intentionally leak the dynamic library. We can't ever unload it
// since the library can make things that will live arbitrarily long.
mem::forget(lib);
}
}

View File

@ -156,7 +156,6 @@ impl<'a> Registry<'a> {
self.llvm_passes.push(name.to_owned());
}
/// Register an attribute with an attribute type.
///
/// Registered attributes will bypass the `custom_attribute` feature gate.

View File

@ -238,6 +238,7 @@ pub fn invalid_output_for_target(sess: &Session,
match (sess.target.target.options.dynamic_linking,
sess.target.target.options.executables, crate_type) {
(false, _, config::CrateTypeCdylib) |
(false, _, config::CrateTypeRustcMacro) |
(false, _, config::CrateTypeDylib) => true,
(_, false, config::CrateTypeExecutable) => true,
_ => false
@ -261,6 +262,7 @@ pub fn filename_for_input(sess: &Session,
outputs.out_directory.join(&format!("lib{}.rlib", libname))
}
config::CrateTypeCdylib |
config::CrateTypeRustcMacro |
config::CrateTypeDylib => {
let (prefix, suffix) = (&sess.target.target.options.dll_prefix,
&sess.target.target.options.dll_suffix);
@ -291,7 +293,8 @@ pub fn each_linked_rlib(sess: &Session,
let fmts = sess.dependency_formats.borrow();
let fmts = fmts.get(&config::CrateTypeExecutable)
.or_else(|| fmts.get(&config::CrateTypeStaticlib))
.or_else(|| fmts.get(&config::CrateTypeCdylib));
.or_else(|| fmts.get(&config::CrateTypeCdylib))
.or_else(|| fmts.get(&config::CrateTypeRustcMacro));
let fmts = fmts.unwrap_or_else(|| {
bug!("could not find formats for rlibs")
});
@ -738,7 +741,8 @@ fn link_args(cmd: &mut Linker,
// When linking a dynamic library, we put the metadata into a section of the
// executable. This metadata is in a separate object file from the main
// object file, so we link that in here.
if crate_type == config::CrateTypeDylib {
if crate_type == config::CrateTypeDylib ||
crate_type == config::CrateTypeRustcMacro {
cmd.add_object(&outputs.with_extension("metadata.o"));
}

View File

@ -8,10 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{self, BufWriter};
use std::io::prelude::*;
use std::io::{self, BufWriter};
use std::path::{Path, PathBuf};
use std::process::Command;
@ -28,16 +29,16 @@ use syntax::ast;
/// For all the linkers we support, and information they might
/// need out of the shared crate context before we get rid of it.
pub struct LinkerInfo {
dylib_exports: Vec<String>,
cdylib_exports: Vec<String>
exports: HashMap<CrateType, Vec<String>>,
}
impl<'a, 'tcx> LinkerInfo {
pub fn new(scx: &SharedCrateContext<'a, 'tcx>,
reachable: &[String]) -> LinkerInfo {
LinkerInfo {
dylib_exports: exported_symbols(scx, reachable, CrateType::CrateTypeDylib),
cdylib_exports: exported_symbols(scx, reachable, CrateType::CrateTypeCdylib)
exports: scx.sess().crate_types.borrow().iter().map(|&c| {
(c, exported_symbols(scx, reachable, c))
}).collect(),
}
}
@ -243,7 +244,8 @@ impl<'a> Linker for GnuLinker<'a> {
// exported symbols to ensure we don't expose any more. The object files
// have far more public symbols than we actually want to export, so we
// hide them all here.
if crate_type == CrateType::CrateTypeDylib {
if crate_type == CrateType::CrateTypeDylib ||
crate_type == CrateType::CrateTypeRustcMacro {
return
}
@ -254,7 +256,7 @@ impl<'a> Linker for GnuLinker<'a> {
let res = (|| -> io::Result<()> {
let mut f = BufWriter::new(File::create(&path)?);
writeln!(f, "{{\n global:")?;
for sym in &self.info.cdylib_exports {
for sym in self.info.exports[&crate_type].iter() {
writeln!(f, " {};", sym)?;
}
writeln!(f, "\n local:\n *;\n}};")?;
@ -274,7 +276,7 @@ impl<'a> Linker for GnuLinker<'a> {
};
let res = (|| -> io::Result<()> {
let mut f = BufWriter::new(File::create(&path)?);
for sym in &self.info.cdylib_exports {
for sym in self.info.exports[&crate_type].iter() {
writeln!(f, "{}{}", prefix, sym)?;
}
Ok(())
@ -427,12 +429,7 @@ impl<'a> Linker for MsvcLinker<'a> {
// straight to exports.
writeln!(f, "LIBRARY")?;
writeln!(f, "EXPORTS")?;
let symbols = if crate_type == CrateType::CrateTypeCdylib {
&self.info.cdylib_exports
} else {
&self.info.dylib_exports
};
for symbol in symbols {
for symbol in self.info.exports[&crate_type].iter() {
writeln!(f, " {}", symbol)?;
}
Ok(())
@ -450,13 +447,10 @@ fn exported_symbols(scx: &SharedCrateContext,
reachable: &[String],
crate_type: CrateType)
-> Vec<String> {
if !scx.sess().crate_types.borrow().contains(&crate_type) {
return vec![];
}
// See explanation in GnuLinker::export_symbols, for
// why we don't ever need dylib symbols on non-MSVC.
if crate_type == CrateType::CrateTypeDylib {
if crate_type == CrateType::CrateTypeDylib ||
crate_type == CrateType::CrateTypeRustcMacro {
if !scx.sess().target.target.options.is_like_msvc {
return vec![];
}

View File

@ -188,6 +188,11 @@ impl<'a, 'tcx> Instance<'tcx> {
let idx = def_id.index;
return scx.sess().generate_plugin_registrar_symbol(svh, idx);
}
if scx.sess().derive_registrar_fn.get() == Some(id) {
let svh = &scx.link_meta().crate_hash;
let idx = def_id.index;
return scx.sess().generate_derive_registrar_symbol(svh, idx);
}
}
// FIXME(eddyb) Precompute a custom symbol name based on attributes.

View File

@ -27,10 +27,10 @@ use ptr::P;
use util::small_vector::SmallVector;
use util::lev_distance::find_best_match_for_name;
use fold::Folder;
use feature_gate;
use std::collections::{HashMap, HashSet};
use std::rc::Rc;
use std::default::Default;
use tokenstream;
@ -568,12 +568,18 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
}
pub trait MacroLoader {
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<ast::MacroDef>;
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool)
-> Vec<LoadedMacro>;
}
pub enum LoadedMacro {
Def(ast::MacroDef),
CustomDerive(String, Box<MultiItemModifier>),
}
pub struct DummyMacroLoader;
impl MacroLoader for DummyMacroLoader {
fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<ast::MacroDef> {
fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<LoadedMacro> {
Vec::new()
}
}
@ -593,6 +599,7 @@ pub struct ExtCtxt<'a> {
pub exported_macros: Vec<ast::MacroDef>,
pub syntax_env: SyntaxEnv,
pub derive_modes: HashMap<InternedString, Box<MultiItemModifier>>,
pub recursion_count: usize,
pub filename: Option<String>,
@ -616,6 +623,7 @@ impl<'a> ExtCtxt<'a> {
exported_macros: Vec::new(),
loader: loader,
syntax_env: env,
derive_modes: HashMap::new(),
recursion_count: 0,
filename: None,
@ -714,6 +722,25 @@ impl<'a> ExtCtxt<'a> {
}
}
pub fn insert_custom_derive(&mut self,
name: &str,
ext: Box<MultiItemModifier>,
sp: Span) {
if !self.ecfg.enable_rustc_macro() {
feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic,
"rustc_macro",
sp,
feature_gate::GateIssue::Language,
"loading custom derive macro crates \
is experimentally supported");
}
let name = token::intern_and_get_ident(name);
if self.derive_modes.insert(name.clone(), ext).is_some() {
self.span_err(sp, &format!("cannot shadow existing derive mode `{}`",
name));
}
}
pub fn struct_span_warn(&self,
sp: Span,
msg: &str)

View File

@ -440,8 +440,7 @@ fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVe
callee: NameAndSpan {
format: MacroAttribute(intern(&attr.name())),
span: Some(attr.span),
// attributes can do whatever they like, for now
allow_internal_unstable: true,
allow_internal_unstable: false,
}
});
@ -538,7 +537,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
// We need to error on `#[macro_use] extern crate` when it isn't at the
// crate root, because `$crate` won't work properly.
for def in self.cx.loader.load_crate(item, self.at_crate_root) {
self.cx.insert_macro(def);
match def {
LoadedMacro::Def(def) => self.cx.insert_macro(def),
LoadedMacro::CustomDerive(name, ext) => {
self.cx.insert_custom_derive(&name, ext, item.span);
}
}
}
} else {
let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
@ -688,6 +692,7 @@ impl<'feat> ExpansionConfig<'feat> {
fn enable_allow_internal_unstable = allow_internal_unstable,
fn enable_custom_derive = custom_derive,
fn enable_pushpop_unsafe = pushpop_unsafe,
fn enable_rustc_macro = rustc_macro,
}
}

View File

@ -47,7 +47,7 @@ macro_rules! setter {
}
macro_rules! declare_features {
($((active, $feature: ident, $ver: expr, $issue: expr)),+) => {
($((active, $feature: ident, $ver: expr, $issue: expr),)+) => {
/// Represents active features that are currently being implemented or
/// currently being considered for addition/removal.
const ACTIVE_FEATURES: &'static [(&'static str, &'static str,
@ -75,14 +75,14 @@ macro_rules! declare_features {
}
};
($((removed, $feature: ident, $ver: expr, $issue: expr)),+) => {
($((removed, $feature: ident, $ver: expr, $issue: expr),)+) => {
/// Represents features which has since been removed (it was once Active)
const REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
$((stringify!($feature), $ver, $issue)),+
];
};
($((accepted, $feature: ident, $ver: expr, $issue: expr)),+) => {
($((accepted, $feature: ident, $ver: expr, $issue: expr),)+) => {
/// Those language feature has since been Accepted (it was once Active)
const ACCEPTED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[
$((stringify!($feature), $ver, $issue)),+
@ -288,7 +288,10 @@ declare_features! (
(active, abi_sysv64, "1.13.0", Some(36167)),
// Use the import semantics from RFC 1560.
(active, item_like_imports, "1.13.0", Some(35120))
(active, item_like_imports, "1.13.0", Some(35120)),
// Macros 1.1
(active, rustc_macro, "1.13.0", Some(35900)),
);
declare_features! (
@ -302,7 +305,6 @@ declare_features! (
(removed, struct_inherit, "1.0.0", None),
(removed, test_removed_feature, "1.0.0", None),
(removed, visible_private_types, "1.0.0", None),
(removed, unsafe_no_drop_flag, "1.0.0", None)
);
declare_features! (
@ -330,7 +332,7 @@ declare_features! (
(accepted, type_macros, "1.13.0", Some(27245)),
(accepted, while_let, "1.0.0", None),
// Allows `#[deprecated]` attribute
(accepted, deprecated, "1.9.0", Some(29935))
(accepted, deprecated, "1.9.0", Some(29935)),
);
// (changing above list without updating src/doc/reference.md makes @cmr sad)
@ -543,6 +545,15 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
is an experimental feature",
cfg_fn!(linked_from))),
("rustc_macro_derive", Normal, Gated("rustc_macro",
"the `#[rustc_macro_derive]` attribute \
is an experimental feature",
cfg_fn!(rustc_macro))),
("rustc_copy_clone_marker", Whitelisted, Gated("rustc_attrs",
"internal implementation detail",
cfg_fn!(rustc_attrs))),
// FIXME: #14408 whitelist docs since rustdoc looks at them
("doc", Whitelisted, Ungated),
@ -616,6 +627,7 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)]
("target_vendor", "cfg_target_vendor", cfg_fn!(cfg_target_vendor)),
("target_thread_local", "cfg_target_thread_local", cfg_fn!(cfg_target_thread_local)),
("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)),
("rustc_macro", "rustc_macro", cfg_fn!(rustc_macro)),
];
#[derive(Debug, Eq, PartialEq)]

View File

@ -11,6 +11,7 @@ crate-type = ["dylib"]
[dependencies]
fmt_macros = { path = "../libfmt_macros" }
log = { path = "../liblog" }
rustc_errors = { path = "../librustc_errors" }
rustc_macro = { path = "../librustc_macro" }
syntax = { path = "../libsyntax" }
syntax_pos = { path = "../libsyntax_pos" }
rustc_errors = { path = "../librustc_errors" }

View File

@ -49,7 +49,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
ItemKind::Struct(_, Generics { ref ty_params, .. }) |
ItemKind::Enum(_, Generics { ref ty_params, .. })
if ty_params.is_empty() &&
attr::contains_name(&annitem.attrs, "derive_Copy") => {
attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => {
bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
unify_fieldless_variants = true;
@ -110,12 +110,12 @@ fn cs_clone(name: &str,
Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]),
Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]),
};
let subcall = |field: &FieldInfo| {
let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| {
let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
let span = if mode == Mode::Shallow {
// set the expn ID so we can call the unstable method
Span { expn_id: cx.backtrace(), ..trait_span }
super::allow_unstable(cx, field.span, "derive(Clone)")
} else {
field.span
};
@ -147,8 +147,10 @@ fn cs_clone(name: &str,
match mode {
Mode::Shallow => {
let mut stmts: Vec<_> =
all_fields.iter().map(subcall).map(|e| cx.stmt_expr(e)).collect();
let mut stmts = all_fields.iter().map(|f| {
let call = subcall(cx, f);
cx.stmt_expr(call)
}).collect::<Vec<_>>();
stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span))));
cx.expr_block(cx.block(trait_span, stmts))
}
@ -166,14 +168,15 @@ fn cs_clone(name: &str,
name))
}
};
cx.field_imm(field.span, ident, subcall(field))
let call = subcall(cx, field);
cx.field_imm(field.span, ident, call)
})
.collect::<Vec<_>>();
cx.expr_struct(trait_span, ctor_path, fields)
}
VariantData::Tuple(..) => {
let subcalls = all_fields.iter().map(subcall).collect();
let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect();
let path = cx.expr_path(ctor_path);
cx.expr_call(trait_span, path, subcalls)
}

View File

@ -0,0 +1,97 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::panic;
use rustc_macro::{TokenStream, __internal};
use syntax::ast::{self, ItemKind};
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::fold::{self, Folder};
use errors::FatalError;
pub struct CustomDerive {
inner: fn(TokenStream) -> TokenStream,
}
impl CustomDerive {
pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive {
CustomDerive { inner: inner }
}
}
impl MultiItemModifier for CustomDerive {
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
_meta_item: &ast::MetaItem,
item: Annotatable)
-> Vec<Annotatable> {
let item = match item {
Annotatable::Item(item) => item,
Annotatable::ImplItem(_) |
Annotatable::TraitItem(_) => {
ecx.span_err(span, "custom derive attributes may only be \
applied to struct/enum items");
return Vec::new()
}
};
match item.node {
ItemKind::Struct(..) |
ItemKind::Enum(..) => {}
_ => {
ecx.span_err(span, "custom derive attributes may only be \
applied to struct/enum items");
return Vec::new()
}
}
let input = __internal::new_token_stream(item);
let res = __internal::set_parse_sess(&ecx.parse_sess, || {
let inner = self.inner;
panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
});
let item = match res {
Ok(stream) => __internal::token_stream_items(stream),
Err(e) => {
let msg = "custom derive attribute panicked";
let mut err = ecx.struct_span_fatal(span, msg);
if let Some(s) = e.downcast_ref::<String>() {
err.help(&format!("message: {}", s));
}
if let Some(s) = e.downcast_ref::<&'static str>() {
err.help(&format!("message: {}", s));
}
err.emit();
panic!(FatalError);
}
};
// Right now we have no knowledge of spans at all in custom derive
// macros, everything is just parsed as a string. Reassign all spans to
// the #[derive] attribute for better errors here.
item.into_iter().flat_map(|item| {
ChangeSpan { span: span }.fold_item(item)
}).map(Annotatable::Item).collect()
}
}
struct ChangeSpan { span: Span }
impl Folder for ChangeSpan {
fn new_span(&mut self, _sp: Span) -> Span {
self.span
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}

View File

@ -12,7 +12,7 @@
use syntax::ast::{self, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv};
use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier};
use syntax::ext::base::MultiModifier;
use syntax::ext::build::AstBuilder;
use syntax::feature_gate;
use syntax::codemap;
@ -61,6 +61,7 @@ pub mod decodable;
pub mod hash;
pub mod debug;
pub mod default;
pub mod custom;
#[path="cmp/partial_eq.rs"]
pub mod partial_eq;
@ -74,156 +75,201 @@ pub mod ord;
pub mod generic;
fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span {
Span {
expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
call_site: span,
callee: codemap::NameAndSpan {
format: codemap::MacroAttribute(intern(attr_name)),
span: Some(span),
allow_internal_unstable: true,
},
}),
..span
}
}
fn expand_derive(cx: &mut ExtCtxt,
span: Span,
mitem: &MetaItem,
annotatable: Annotatable)
-> Annotatable {
-> Vec<Annotatable> {
debug!("expand_derive: span = {:?}", span);
debug!("expand_derive: mitem = {:?}", mitem);
debug!("expand_derive: annotatable input = {:?}", annotatable);
let annot = annotatable.map_item_or(|item| {
item.map(|mut item| {
if mitem.value_str().is_some() {
cx.span_err(mitem.span, "unexpected value in `derive`");
let mut item = match annotatable {
Annotatable::Item(item) => item,
other => {
cx.span_err(span, "`derive` can only be applied to items");
return vec![other]
}
};
if mitem.value_str().is_some() {
cx.span_err(mitem.span, "unexpected value in `derive`");
}
let traits = mitem.meta_item_list().unwrap_or(&[]);
if traits.is_empty() {
cx.span_warn(mitem.span, "empty trait list in `derive`");
}
// RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
// `#[structural_match]` attribute.
if traits.iter().filter_map(|t| t.name()).any(|t| t == "PartialEq") &&
traits.iter().filter_map(|t| t.name()).any(|t| t == "Eq") {
let structural_match = intern_and_get_ident("structural_match");
let span = allow_unstable(cx, span, "derive(PartialEq, Eq)");
let meta = cx.meta_word(span, structural_match);
item = item.map(|mut i| {
i.attrs.push(cx.attribute(span, meta));
i
});
}
// RFC #1521. `Clone` can assume that `Copy` types' clone implementation is
// the same as the copy implementation.
//
// Add a marker attribute here picked up during #[derive(Clone)]
if traits.iter().filter_map(|t| t.name()).any(|t| t == "Clone") &&
traits.iter().filter_map(|t| t.name()).any(|t| t == "Copy") {
let marker = intern_and_get_ident("rustc_copy_clone_marker");
let span = allow_unstable(cx, span, "derive(Copy, Clone)");
let meta = cx.meta_word(span, marker);
item = item.map(|mut i| {
i.attrs.push(cx.attribute(span, meta));
i
});
}
let mut other_items = Vec::new();
let mut iter = traits.iter();
while let Some(titem) = iter.next() {
let tword = match titem.word() {
Some(name) => name,
None => {
cx.span_err(titem.span, "malformed `derive` entry");
continue
}
};
let tname = tword.name();
let traits = mitem.meta_item_list().unwrap_or(&[]);
if traits.is_empty() {
cx.span_warn(mitem.span, "empty trait list in `derive`");
// If this is a built-in derive mode, then we expand it immediately
// here.
if is_builtin_trait(&tname) {
let name = intern_and_get_ident(&format!("derive({})", tname));
let mitem = cx.meta_word(titem.span, name);
let span = Span {
expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
call_site: titem.span,
callee: codemap::NameAndSpan {
format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
span: Some(titem.span),
allow_internal_unstable: true,
},
}),
..titem.span
};
let my_item = Annotatable::Item(item);
expand_builtin(&tname, cx, span, &mitem, &my_item, &mut |a| {
other_items.push(a);
});
item = my_item.expect_item();
// Otherwise if this is a `rustc_macro`-style derive mode, we process it
// here. The logic here is to:
//
// 1. Collect the remaining `#[derive]` annotations into a list. If
// there are any left, attach a `#[derive]` attribute to the item
// that we're currently expanding with the remaining derive modes.
// 2. Manufacture a `#[derive(Foo)]` attribute to pass to the expander.
// 3. Expand the current item we're expanding, getting back a list of
// items that replace it.
// 4. Extend the returned list with the current list of items we've
// collected so far.
// 5. Return everything!
//
// If custom derive extensions end up threading through the `#[derive]`
// attribute, we'll get called again later on to continue expanding
// those modes.
} else if let Some(ext) = cx.derive_modes.remove(&tname) {
let remaining_derives = iter.cloned().collect::<Vec<_>>();
if remaining_derives.len() > 0 {
let list = cx.meta_list(titem.span,
intern_and_get_ident("derive"),
remaining_derives);
let attr = cx.attribute(titem.span, list);
item = item.map(|mut i| {
i.attrs.push(attr);
i
});
}
let titem = cx.meta_list_item_word(titem.span, tname.clone());
let mitem = cx.meta_list(titem.span,
intern_and_get_ident("derive"),
vec![titem]);
let item = Annotatable::Item(item);
let mut items = ext.expand(cx, mitem.span, &mitem, item);
items.extend(other_items);
cx.derive_modes.insert(tname.clone(), ext);
return items
let mut found_partial_eq = false;
let mut eq_span = None;
// If we've gotten this far then it means that we're in the territory of
// the old custom derive mechanism. If the feature isn't enabled, we
// issue an error, otherwise manufacture the `derive_Foo` attribute.
} else if !cx.ecfg.enable_custom_derive() {
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
"custom_derive",
titem.span,
feature_gate::GateIssue::Language,
feature_gate::EXPLAIN_CUSTOM_DERIVE);
} else {
let name = intern_and_get_ident(&format!("derive_{}", tname));
let mitem = cx.meta_word(titem.span, name);
item = item.map(|mut i| {
i.attrs.push(cx.attribute(mitem.span, mitem));
i
});
}
}
for titem in traits.iter().rev() {
let tname = if let Some(word) = titem.word() {
word.name()
} else {
cx.span_err(titem.span, "malformed `derive` entry");
continue;
};
if !(is_builtin_trait(&tname) || cx.ecfg.enable_custom_derive()) {
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
"custom_derive",
titem.span,
feature_gate::GateIssue::Language,
feature_gate::EXPLAIN_CUSTOM_DERIVE);
continue;
}
let span = Span {
expn_id: cx.codemap().record_expansion(codemap::ExpnInfo {
call_site: titem.span,
callee: codemap::NameAndSpan {
format: codemap::MacroAttribute(intern(&format!("derive({})", tname))),
span: Some(titem.span),
allow_internal_unstable: true,
},
}),
..titem.span
};
if &tname[..] == "Eq" {
eq_span = Some(span);
} else if &tname[..] == "PartialEq" {
found_partial_eq = true;
}
// #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
item.attrs.push(cx.attribute(span,
cx.meta_word(titem.span,
intern_and_get_ident(&format!("derive_{}", tname)))));
}
// RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
// `#[structural_match]` attribute.
if let Some(eq_span) = eq_span {
if found_partial_eq {
let structural_match = intern_and_get_ident("structural_match");
item.attrs.push(cx.attribute(eq_span, cx.meta_word(eq_span, structural_match)));
}
}
item
})
},
|a| {
cx.span_err(span,
"`derive` can only be applied to items");
a
});
debug!("expand_derive: annotatable output = {:?}", annot);
annot
other_items.insert(0, Annotatable::Item(item));
return other_items
}
macro_rules! derive_traits {
($( $name:expr => $func:path, )+) => {
pub fn register_all(env: &mut SyntaxEnv) {
// Define the #[derive_*] extensions.
$({
struct DeriveExtension;
impl MultiItemDecorator for DeriveExtension {
fn expand(&self,
ecx: &mut ExtCtxt,
sp: Span,
mitem: &MetaItem,
annotatable: &Annotatable,
push: &mut FnMut(Annotatable)) {
if !ecx.parse_sess.codemap().span_allows_unstable(sp)
&& !ecx.ecfg.features.unwrap().custom_derive {
// FIXME:
// https://github.com/rust-lang/rust/pull/32671#issuecomment-206245303
// This is just to avoid breakage with syntex.
// Remove that to spawn an error instead.
let cm = ecx.parse_sess.codemap();
let parent = cm.with_expn_info(ecx.backtrace(),
|info| info.unwrap().call_site.expn_id);
cm.with_expn_info(parent, |info| {
if info.is_some() {
let mut w = ecx.parse_sess.span_diagnostic.struct_span_warn(
sp, feature_gate::EXPLAIN_DERIVE_UNDERSCORE,
);
if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_none() {
w.help(
&format!("add #![feature(custom_derive)] to \
the crate attributes to enable")
);
}
w.emit();
} else {
feature_gate::emit_feature_err(
&ecx.parse_sess.span_diagnostic,
"custom_derive", sp, feature_gate::GateIssue::Language,
feature_gate::EXPLAIN_DERIVE_UNDERSCORE
);
return;
}
})
}
warn_if_deprecated(ecx, sp, $name);
$func(ecx, sp, mitem, annotatable, push);
}
}
env.insert(intern(concat!("derive_", $name)),
MultiDecorator(Box::new(DeriveExtension)));
})+
env.insert(intern("derive"),
MultiModifier(Box::new(expand_derive)));
env.insert(intern("derive"), MultiModifier(Box::new(expand_derive)));
}
fn is_builtin_trait(name: &str) -> bool {
pub fn is_builtin_trait(name: &str) -> bool {
match name {
$( $name )|+ => true,
_ => false,
}
}
fn expand_builtin(name: &str,
ecx: &mut ExtCtxt,
span: Span,
mitem: &MetaItem,
item: &Annotatable,
push: &mut FnMut(Annotatable)) {
match name {
$(
$name => {
warn_if_deprecated(ecx, span, $name);
$func(ecx, span, mitem, item, push);
}
)*
_ => panic!("not a builtin derive mode: {}", name),
}
}
}
}

View File

@ -19,6 +19,8 @@
html_root_url = "https://doc.rust-lang.org/nightly/")]
#![cfg_attr(not(stage0), deny(warnings))]
#![feature(rustc_macro_lib)]
#![feature(rustc_macro_internals)]
#![feature(rustc_private)]
#![feature(staged_api)]
@ -28,6 +30,7 @@ extern crate log;
#[macro_use]
extern crate syntax;
extern crate syntax_pos;
extern crate rustc_macro;
extern crate rustc_errors as errors;
use syntax::ext::base::{MacroExpanderFn, NormalTT};
@ -44,6 +47,8 @@ mod format;
mod log_syntax;
mod trace_macros;
pub mod rustc_macro_registrar;
// for custom_derive
pub mod deriving;

View File

@ -0,0 +1,280 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
use errors;
use syntax::ast::{self, Ident, NodeId};
use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute};
use syntax::ext::base::{ExtCtxt, DummyMacroLoader};
use syntax::ext::build::AstBuilder;
use syntax::ext::expand::ExpansionConfig;
use syntax::parse::ParseSess;
use syntax::parse::token::{self, InternedString};
use syntax::feature_gate::Features;
use syntax::ptr::P;
use syntax_pos::{Span, DUMMY_SP};
use syntax::visit::{self, Visitor};
use deriving;
struct CustomDerive {
trait_name: InternedString,
function_name: Ident,
span: Span,
}
struct CollectCustomDerives<'a> {
derives: Vec<CustomDerive>,
in_root: bool,
handler: &'a errors::Handler,
is_rustc_macro_crate: bool,
}
pub fn modify(sess: &ParseSess,
mut krate: ast::Crate,
is_rustc_macro_crate: bool,
num_crate_types: usize,
handler: &errors::Handler,
features: &Features) -> ast::Crate {
let mut loader = DummyMacroLoader;
let mut cx = ExtCtxt::new(sess,
Vec::new(),
ExpansionConfig::default("rustc_macro".to_string()),
&mut loader);
let mut collect = CollectCustomDerives {
derives: Vec::new(),
in_root: true,
handler: handler,
is_rustc_macro_crate: is_rustc_macro_crate,
};
visit::walk_crate(&mut collect, &krate);
if !is_rustc_macro_crate {
return krate
} else if !features.rustc_macro {
let mut err = handler.struct_err("the `rustc-macro` crate type is \
experimental");
err.help("add #![feature(rustc_macro)] to the crate attributes to \
enable");
err.emit();
}
if num_crate_types > 1 {
handler.err("cannot mix `rustc-macro` crate type with others");
}
krate.module.items.push(mk_registrar(&mut cx, &collect.derives));
if krate.exported_macros.len() > 0 {
handler.err("cannot export macro_rules! macros from a `rustc-macro` \
crate type currently");
}
return krate
}
impl<'a> CollectCustomDerives<'a> {
fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
if self.is_rustc_macro_crate &&
self.in_root &&
*vis == ast::Visibility::Public {
self.handler.span_err(sp,
"`rustc-macro` crate types cannot \
export any items other than functions \
tagged with `#[rustc_macro_derive]` \
currently");
}
}
}
impl<'a> Visitor for CollectCustomDerives<'a> {
fn visit_item(&mut self, item: &ast::Item) {
// First up, make sure we're checking a bare function. If we're not then
// we're just not interested in this item.
//
// If we find one, try to locate a `#[rustc_macro_derive]` attribute on
// it.
match item.node {
ast::ItemKind::Fn(..) => {}
_ => {
self.check_not_pub_in_root(&item.vis, item.span);
return visit::walk_item(self, item)
}
}
let mut attrs = item.attrs.iter()
.filter(|a| a.check_name("rustc_macro_derive"));
let attr = match attrs.next() {
Some(attr) => attr,
None => {
self.check_not_pub_in_root(&item.vis, item.span);
return visit::walk_item(self, item)
}
};
if let Some(a) = attrs.next() {
self.handler.span_err(a.span(), "multiple `#[rustc_macro_derive]` \
attributes found");
}
if !self.is_rustc_macro_crate {
self.handler.span_err(attr.span(),
"the `#[rustc_macro_derive]` attribute is \
only usable with crates of the `rustc-macro` \
crate type");
}
// Once we've located the `#[rustc_macro_derive]` attribute, verify
// that it's of the form `#[rustc_macro_derive(Foo)]`
let list = match attr.meta_item_list() {
Some(list) => list,
None => {
self.handler.span_err(attr.span(),
"attribute must be of form: \
#[rustc_macro_derive(TraitName)]");
return
}
};
if list.len() != 1 {
self.handler.span_err(attr.span(),
"attribute must only have one argument");
return
}
let attr = &list[0];
let trait_name = match attr.name() {
Some(name) => name,
_ => {
self.handler.span_err(attr.span(), "not a meta item");
return
}
};
if !attr.is_word() {
self.handler.span_err(attr.span(), "must only be one word");
}
if deriving::is_builtin_trait(&trait_name) {
self.handler.span_err(attr.span(),
"cannot override a built-in #[derive] mode");
}
if self.derives.iter().any(|d| d.trait_name == trait_name) {
self.handler.span_err(attr.span(),
"derive mode defined twice in this crate");
}
if self.in_root {
self.derives.push(CustomDerive {
span: item.span,
trait_name: trait_name,
function_name: item.ident,
});
} else {
let msg = "functions tagged with `#[rustc_macro_derive]` must \
currently reside in the root of the crate";
self.handler.span_err(item.span, msg);
}
visit::walk_item(self, item);
}
fn visit_mod(&mut self, m: &ast::Mod, _s: Span, id: NodeId) {
let mut prev_in_root = self.in_root;
if id != ast::CRATE_NODE_ID {
prev_in_root = mem::replace(&mut self.in_root, false);
}
visit::walk_mod(self, m);
self.in_root = prev_in_root;
}
fn visit_mac(&mut self, mac: &ast::Mac) {
visit::walk_mac(self, mac)
}
}
// Creates a new module which looks like:
//
// mod $gensym {
// extern crate rustc_macro;
//
// use rustc_macro::__internal::Registry;
//
// #[plugin_registrar]
// fn registrar(registrar: &mut Registry) {
// registrar.register_custom_derive($name_trait1, ::$name1);
// registrar.register_custom_derive($name_trait2, ::$name2);
// // ...
// }
// }
fn mk_registrar(cx: &mut ExtCtxt,
custom_derives: &[CustomDerive]) -> P<ast::Item> {
let eid = cx.codemap().record_expansion(ExpnInfo {
call_site: DUMMY_SP,
callee: NameAndSpan {
format: MacroAttribute(token::intern("rustc_macro")),
span: None,
allow_internal_unstable: true,
}
});
let span = Span { expn_id: eid, ..DUMMY_SP };
let rustc_macro = token::str_to_ident("rustc_macro");
let krate = cx.item(span,
rustc_macro,
Vec::new(),
ast::ItemKind::ExternCrate(None));
let __internal = token::str_to_ident("__internal");
let registry = token::str_to_ident("Registry");
let registrar = token::str_to_ident("registrar");
let register_custom_derive = token::str_to_ident("register_custom_derive");
let stmts = custom_derives.iter().map(|cd| {
let path = cx.path_global(cd.span, vec![cd.function_name]);
let trait_name = cx.expr_str(cd.span, cd.trait_name.clone());
(path, trait_name)
}).map(|(path, trait_name)| {
let registrar = cx.expr_ident(span, registrar);
let ufcs_path = cx.path(span, vec![rustc_macro, __internal, registry,
register_custom_derive]);
cx.expr_call(span,
cx.expr_path(ufcs_path),
vec![registrar, trait_name, cx.expr_path(path)])
}).map(|expr| {
cx.stmt_expr(expr)
}).collect::<Vec<_>>();
let path = cx.path(span, vec![rustc_macro, __internal, registry]);
let registrar_path = cx.ty_path(path);
let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable);
let func = cx.item_fn(span,
registrar,
vec![cx.arg(span, registrar, arg_ty)],
cx.ty(span, ast::TyKind::Tup(Vec::new())),
cx.block(span, stmts));
let derive_registrar = token::intern_and_get_ident("rustc_derive_registrar");
let derive_registrar = cx.meta_word(span, derive_registrar);
let derive_registrar = cx.attribute(span, derive_registrar);
let func = func.map(|mut i| {
i.attrs.push(derive_registrar);
i.vis = ast::Visibility::Public;
i
});
let module = cx.item_mod(span,
span,
ast::Ident::with_empty_ctxt(token::gensym("registrar")),
Vec::new(),
vec![krate, func]);
module.map(|mut i| {
i.vis = ast::Visibility::Public;
i
})
}

10
src/rustc/Cargo.lock generated
View File

@ -214,6 +214,13 @@ dependencies = [
"rustc_bitflags 0.0.0",
]
[[package]]
name = "rustc_macro"
version = "0.0.0"
dependencies = [
"syntax 0.0.0",
]
[[package]]
name = "rustc_metadata"
version = "0.0.0"
@ -228,8 +235,10 @@ dependencies = [
"rustc_data_structures 0.0.0",
"rustc_errors 0.0.0",
"rustc_llvm 0.0.0",
"rustc_macro 0.0.0",
"serialize 0.0.0",
"syntax 0.0.0",
"syntax_ext 0.0.0",
"syntax_pos 0.0.0",
]
@ -400,6 +409,7 @@ dependencies = [
"fmt_macros 0.0.0",
"log 0.0.0",
"rustc_errors 0.0.0",
"rustc_macro 0.0.0",
"syntax 0.0.0",
"syntax_pos 0.0.0",
]

View File

@ -0,0 +1,33 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:append-impl.rs
#![feature(rustc_macro)]
#![allow(warnings)]
#[macro_use]
extern crate append_impl;
trait Append {
fn foo(&self);
}
#[derive(PartialEq,
Append,
Eq)]
//~^^ ERROR: the semantics of constant patterns is not yet settled
struct A {
inner: u32,
}
fn main() {
A { inner: 3 }.foo();
}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
extern crate rustc_macro;
pub mod a { //~ `rustc-macro` crate types cannot export any items
use rustc_macro::TokenStream;
#[rustc_macro_derive(B)]
pub fn bar(a: TokenStream) -> TokenStream {
//~^ ERROR: must currently reside in the root of the crate
a
}
}

View File

@ -0,0 +1,46 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
extern crate rustc_macro;
#[rustc_macro_derive]
//~^ ERROR: attribute must be of form: #[rustc_macro_derive(TraitName)]
pub fn foo1(input: rustc_macro::TokenStream) -> rustc_macro::TokenStream {
input
}
#[rustc_macro_derive = "foo"]
//~^ ERROR: attribute must be of form: #[rustc_macro_derive(TraitName)]
pub fn foo2(input: rustc_macro::TokenStream) -> rustc_macro::TokenStream {
input
}
#[rustc_macro_derive(
a = "b"
)]
//~^^ ERROR: must only be one word
pub fn foo3(input: rustc_macro::TokenStream) -> rustc_macro::TokenStream {
input
}
#[rustc_macro_derive(b, c)]
//~^ ERROR: attribute must only have one argument
pub fn foo4(input: rustc_macro::TokenStream) -> rustc_macro::TokenStream {
input
}
#[rustc_macro_derive(d(e))]
//~^ ERROR: must only be one word
pub fn foo5(input: rustc_macro::TokenStream) -> rustc_macro::TokenStream {
input
}

View File

@ -0,0 +1,31 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// force-host
// no-prefer-dynamic
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(Append)]
pub fn derive_a(input: TokenStream) -> TokenStream {
let mut input = input.to_string();
input.push_str("
impl Append for A {
fn foo(&self) {}
}
");
input.parse().unwrap()
}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// force-host
// no-prefer-dynamic
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn derive_a(input: TokenStream) -> TokenStream {
input
}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// force-host
// no-prefer-dynamic
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn derive_a(input: TokenStream) -> TokenStream {
input
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
// force-host
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn derive_a(_input: TokenStream) -> TokenStream {
"struct A { inner }".parse().unwrap()
}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
// force-host
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn derive_a(_input: TokenStream) -> TokenStream {
panic!("nope!");
}

View File

@ -0,0 +1,29 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// force-host
// no-prefer-dynamic
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(Unstable)]
pub fn derive(_input: TokenStream) -> TokenStream {
"
#[rustc_foo]
fn foo() {}
".parse().unwrap()
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// force-host
// no-prefer-dynamic
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![crate_type = "rustc-macro"]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(Unstable)]
pub fn derive(_input: TokenStream) -> TokenStream {
"unsafe fn foo() -> u32 { ::std::intrinsics::init() }".parse().unwrap()
}

View File

@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-a.rs
extern crate derive_a;
//~^ ERROR: crates of the `rustc-macro` crate type cannot be linked at runtime
fn main() {}

View File

@ -0,0 +1,28 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn foo(input: TokenStream) -> TokenStream {
input
}
#[rustc_macro_derive(A)] //~ ERROR: derive mode defined twice in this crate
pub fn bar(input: TokenStream) -> TokenStream {
input
}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-bad.rs
#![feature(rustc_macro)]
#[macro_use]
extern crate derive_bad;
#[derive(
A
)]
//~^^ ERROR: custom derive attribute panicked
//~| HELP: called `Result::unwrap()` on an `Err` value: LexError
struct A;
fn main() {}

View File

@ -0,0 +1,22 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-a.rs
#![feature(rustc_macro)]
#![allow(warnings)]
#[macro_use]
extern crate derive_a;
#[derive_A] //~ ERROR: attributes of the form `#[derive_*]` are reserved for the compiler
struct A;
fn main() {}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-unstable-2.rs
#![feature(rustc_macro)]
#![allow(warnings)]
#[macro_use]
extern crate derive_unstable_2;
#[derive(Unstable)]
//~^ ERROR: reserved for internal compiler
struct A;
fn main() {
foo();
}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-unstable.rs
#![feature(rustc_macro)]
#![allow(warnings)]
#[macro_use]
extern crate derive_unstable;
#[derive(Unstable)]
//~^ ERROR: use of unstable library feature
struct A;
fn main() {
unsafe { foo(); }
}

View File

@ -0,0 +1,19 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: cannot export macro_rules! macros from a `rustc-macro` crate
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#[macro_export]
macro_rules! foo {
($e:expr) => ($e)
}

View File

@ -0,0 +1,22 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rustc-macro"]
#![allow(warnings)]
pub fn a() {} //~ ERROR: cannot export any items
pub struct B; //~ ERROR: cannot export any items
pub enum C {} //~ ERROR: cannot export any items
pub mod d {} //~ ERROR: cannot export any items
mod e {}
struct F;
enum G {}
fn h() {}

View File

@ -0,0 +1,13 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: the `rustc-macro` crate type is experimental
#![crate_type = "rustc-macro"]

View File

@ -0,0 +1,13 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern crate rustc_macro; //~ ERROR: use of unstable library feature
fn main() {}

View File

@ -1,4 +1,4 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -8,13 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pretty-expanded FIXME #23616
#![crate_type = "rustc-macro"]
#![feature(custom_derive)]
#[derive_Clone]
struct Test;
pub fn main() {
Test.clone();
#[rustc_macro_derive(Foo)] //~ ERROR: is an experimental feature
pub fn foo() {
}

View File

@ -0,0 +1,15 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-a.rs
#[macro_use]
extern crate derive_a;
//~^ ERROR: loading custom derive macro crates is experimentally supported

View File

@ -0,0 +1,12 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(rustc_macro)] //~ ERROR: experimental and subject to change
fn foo() {}

View File

@ -0,0 +1,22 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-a.rs
#![feature(rustc_macro)]
#![allow(warnings)]
#[macro_use]
extern crate derive_a;
use derive_a::derive_a;
//~^ ERROR: unresolved import `derive_a::derive_a`
fn main() {}

View File

@ -0,0 +1,23 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-panic.rs
#![feature(rustc_macro)]
#[macro_use]
extern crate derive_panic;
#[derive(A)]
//~^ ERROR: custom derive attribute panicked
//~| HELP: message: nope!
struct Foo;
fn main() {}

View File

@ -0,0 +1,21 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_macro)]
extern crate rustc_macro;
#[rustc_macro_derive(Foo)]
//~^ ERROR: only usable with crates of the `rustc-macro` crate type
pub fn foo(a: rustc_macro::TokenStream) -> rustc_macro::TokenStream {
a
}
fn main() {}

View File

@ -0,0 +1,22 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(PartialEq)]
//~^ ERROR: cannot override a built-in #[derive] mode
pub fn foo(input: TokenStream) -> TokenStream {
input
}

View File

@ -0,0 +1,21 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-a.rs
// aux-build:derive-a-2.rs
#![feature(rustc_macro)]
#[macro_use]
extern crate derive_a;
#[macro_use]
extern crate derive_a_2; //~ ERROR: cannot shadow existing derive mode `A`
fn main() {}

View File

@ -0,0 +1,24 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#![allow(warnings)]
extern crate rustc_macro;
#[rustc_macro_derive(A)]
unsafe extern fn foo(a: i32, b: u32) -> u32 {
//~^ ERROR: mismatched types
//~| NOTE: expected normal fn, found unsafe fn
//~| NOTE: expected type `fn(rustc_macro::TokenStream) -> rustc_macro::TokenStream`
//~| NOTE: found type `unsafe extern "C" fn(i32, u32) -> u32 {foo}`
loop {}
}

View File

@ -0,0 +1,14 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: cannot mix `rustc-macro` crate type with others
#![crate_type = "rustc-macro"]
#![crate_type = "rlib"]

View File

@ -0,0 +1,12 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern: cannot mix `rustc-macro` crate type with others
// compile-flags: --crate-type rlib --crate-type rustc-macro

View File

@ -13,7 +13,7 @@
macro_rules! foo (
() => (
#[derive_Clone] //~ WARN attributes of the form
#[derive_Clone] //~ ERROR attributes of the form
struct T;
);
);
@ -25,9 +25,8 @@ macro_rules! bar (
foo!();
bar!(
#[derive_Clone] //~ WARN attributes of the form
#[derive_Clone] //~ ERROR attributes of the form
struct S;
);
#[rustc_error]
fn main() {} //~ ERROR compilation successful
fn main() {}

View File

@ -0,0 +1,25 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:add-impl.rs
#![feature(rustc_macro)]
#[macro_use]
extern crate add_impl;
#[derive(AddImpl)]
struct B;
fn main() {
B.foo();
foo();
bar::foo();
}

View File

@ -0,0 +1,33 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(AddImpl)]
// #[cfg(rustc_macro)]
pub fn derive(input: TokenStream) -> TokenStream {
(input.to_string() + "
impl B {
fn foo(&self) {}
}
fn foo() {}
mod bar { pub fn foo() {} }
").parse().unwrap()
}

View File

@ -0,0 +1,27 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert!(input.contains("struct A;"));
assert!(input.contains("#[derive(Eq, Copy, Clone)]"));
"#[derive(Eq, Copy, Clone)] struct A;".parse().unwrap()
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(AToB)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert_eq!(input, "struct A;\n");
"struct B;".parse().unwrap()
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(CToD)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert_eq!(input, "struct C;\n");
"struct D;".parse().unwrap()
}

View File

@ -0,0 +1,32 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
// compile-flags:--crate-type rustc-macro
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(AToB)]
pub fn derive1(input: TokenStream) -> TokenStream {
println!("input1: {:?}", input.to_string());
assert_eq!(input.to_string(), "#[derive(BToC)]\nstruct A;\n");
"#[derive(BToC)] struct B;".parse().unwrap()
}
#[rustc_macro_derive(BToC)]
pub fn derive2(input: TokenStream) -> TokenStream {
assert_eq!(input.to_string(), "struct B;\n");
"struct C;".parse().unwrap()
}

View File

@ -0,0 +1,36 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// no-prefer-dynamic
#![crate_type = "rustc-macro"]
#![feature(rustc_macro)]
#![feature(rustc_macro_lib)]
#![deny(warnings)]
extern crate rustc_macro;
use rustc_macro::TokenStream;
#[rustc_macro_derive(A)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = input.to_string();
assert!(input.contains("struct A;"));
r#"
struct A;
impl A {
fn a(&self) {
panic!("hello");
}
}
"#.parse().unwrap()
}

View File

@ -0,0 +1,23 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-same-struct.rs
#![feature(rustc_macro)]
#[macro_use]
extern crate derive_same_struct;
#[derive(AToB, BToC)]
struct A;
fn main() {
C;
}

View File

@ -0,0 +1,30 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:expand-with-a-macro.rs
// ignore-stage1
#![feature(rustc_macro)]
#![deny(warnings)]
#[macro_use]
extern crate expand_with_a_macro;
use std::panic;
#[derive(A)]
struct A;
fn main() {
assert!(panic::catch_unwind(|| {
A.a();
}).is_err());
}

View File

@ -0,0 +1,30 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-atob.rs
// aux-build:derive-ctod.rs
#![feature(rustc_macro)]
#[macro_use]
extern crate derive_atob;
#[macro_use]
extern crate derive_ctod;
#[derive(AToB)]
struct A;
#[derive(CToD)]
struct C;
fn main() {
B;
D;
}

View File

@ -0,0 +1,29 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:derive-a.rs
// ignore-stage1
#![feature(rustc_macro)]
#[macro_use]
extern crate derive_a;
#[derive(Debug, PartialEq, A, Eq, Copy, Clone)]
struct A;
fn main() {
A;
assert_eq!(A, A);
A.clone();
let a = A;
let _c = a;
let _d = a;
}

View File

@ -11,9 +11,6 @@
// Regression test for issue #21010: Normalize associated types in
// various special paths in the `type_is_immediate` function.
// pretty-expanded FIXME #23616
pub trait OffsetState: Sized {}
pub trait Offset {
type State: OffsetState;

View File

@ -13,8 +13,6 @@
// Tests (correct) usage of trait super-builtin-kinds cross-crate.
// pretty-expanded FIXME #23616
extern crate trait_superkinds_in_metadata;
use trait_superkinds_in_metadata::{RequiresRequiresShareAndSend, RequiresShare};
use trait_superkinds_in_metadata::RequiresCopy;

View File

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pretty-expanded FIXME #23616
pub fn main() {
#[derive(Copy, Clone)]
enum x { foo }

View File

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pretty-expanded FIXME #23616
#[derive(Copy, Clone)]
struct Test;

View File

@ -10,8 +10,6 @@
// Regression test for #20797.
// pretty-expanded FIXME #23616
#![feature(question_mark)]
use std::default::Default;

View File

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pretty-expanded FIXME #23616
#![allow(unknown_features)]
#![feature(box_syntax)]

View File

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// pretty-expanded FIXME #23616
#![allow(warnings)]
#![feature(collections)]
#![feature(drain, enumset, collections_bound, btree_range, vecmap)]