-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Use hygiene for AST passes #63919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use hygiene for AST passes #63919
Changes from all commits
0133941
4082cd9
6fcdb36
2a82aec
beb2f5b
846df20
0b86782
c8cf9f5
e552840
3f3fc52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,86 @@ | ||
use syntax::{ast, attr}; | ||
use syntax::edition::Edition; | ||
use syntax::ext::hygiene::MacroKind; | ||
use syntax::ext::expand::ExpansionConfig; | ||
use syntax::ext::hygiene::AstPass; | ||
use syntax::ext::base::{ExtCtxt, Resolver}; | ||
use syntax::parse::ParseSess; | ||
use syntax::ptr::P; | ||
use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned, respan}; | ||
use syntax::symbol::{Ident, Symbol, kw, sym}; | ||
use syntax_pos::DUMMY_SP; | ||
|
||
use std::iter; | ||
|
||
pub fn inject( | ||
mut krate: ast::Crate, alt_std_name: Option<&str>, edition: Edition | ||
mut krate: ast::Crate, | ||
resolver: &mut dyn Resolver, | ||
sess: &ParseSess, | ||
alt_std_name: Option<Symbol>, | ||
) -> (ast::Crate, Option<Symbol>) { | ||
let rust_2018 = edition >= Edition::Edition2018; | ||
let rust_2018 = sess.edition >= Edition::Edition2018; | ||
|
||
// the first name in this list is the crate name of the crate with the prelude | ||
let names: &[&str] = if attr::contains_name(&krate.attrs, sym::no_core) { | ||
let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) { | ||
return (krate, None); | ||
} else if attr::contains_name(&krate.attrs, sym::no_std) { | ||
if attr::contains_name(&krate.attrs, sym::compiler_builtins) { | ||
&["core"] | ||
&[sym::core] | ||
} else { | ||
&["core", "compiler_builtins"] | ||
&[sym::core, sym::compiler_builtins] | ||
} | ||
} else { | ||
&["std"] | ||
&[sym::std] | ||
}; | ||
|
||
let expn_id = resolver.expansion_for_ast_pass( | ||
DUMMY_SP, | ||
AstPass::StdImports, | ||
&[sym::prelude_import], | ||
None, | ||
); | ||
let span = DUMMY_SP.with_def_site_ctxt(expn_id); | ||
let call_site = DUMMY_SP.with_call_site_ctxt(expn_id); | ||
|
||
let ecfg = ExpansionConfig::default("std_lib_injection".to_string()); | ||
let cx = ExtCtxt::new(sess, ecfg, resolver); | ||
|
||
|
||
// .rev() to preserve ordering above in combination with insert(0, ...) | ||
let alt_std_name = alt_std_name.map(Symbol::intern); | ||
for orig_name_str in names.iter().rev() { | ||
// HACK(eddyb) gensym the injected crates on the Rust 2018 edition, | ||
// so they don't accidentally interfere with the new import paths. | ||
let orig_name_sym = Symbol::intern(orig_name_str); | ||
let orig_name_ident = Ident::with_dummy_span(orig_name_sym); | ||
let (rename, orig_name) = if rust_2018 { | ||
(orig_name_ident.gensym(), Some(orig_name_sym)) | ||
for &name in names.iter().rev() { | ||
let ident = if rust_2018 { | ||
Ident::new(name, span) | ||
} else { | ||
(orig_name_ident, None) | ||
Ident::new(name, call_site) | ||
}; | ||
krate.module.items.insert(0, P(ast::Item { | ||
attrs: vec![attr::mk_attr_outer( | ||
attr::mk_word_item(ast::Ident::with_dummy_span(sym::macro_use)) | ||
)], | ||
vis: dummy_spanned(ast::VisibilityKind::Inherited), | ||
node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), | ||
ident: rename, | ||
id: ast::DUMMY_NODE_ID, | ||
span: DUMMY_SP, | ||
tokens: None, | ||
})); | ||
krate.module.items.insert(0, cx.item( | ||
span, | ||
ident, | ||
vec![cx.attribute(cx.meta_word(span, sym::macro_use))], | ||
ast::ItemKind::ExternCrate(alt_std_name), | ||
)); | ||
} | ||
|
||
// the crates have been injected, the assumption is that the first one is the one with | ||
// the prelude. | ||
// The crates have been injected, the assumption is that the first one is | ||
// the one with the prelude. | ||
let name = names[0]; | ||
|
||
let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable( | ||
ExpnKind::Macro(MacroKind::Attr, sym::std_inject), DUMMY_SP, edition, | ||
[sym::prelude_import][..].into(), | ||
)); | ||
let import_path = if rust_2018 { | ||
[name, sym::prelude, sym::v1].iter() | ||
.map(|symbol| ast::Ident::new(*symbol, span)).collect() | ||
} else { | ||
[kw::PathRoot, name, sym::prelude, sym::v1].iter() | ||
.map(|symbol| ast::Ident::new(*symbol, span)).collect() | ||
}; | ||
|
||
krate.module.items.insert(0, P(ast::Item { | ||
attrs: vec![attr::mk_attr_outer( | ||
attr::mk_word_item(ast::Ident::new(sym::prelude_import, span)))], | ||
vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), | ||
node: ast::ItemKind::Use(P(ast::UseTree { | ||
prefix: ast::Path { | ||
segments: iter::once(ast::Ident::with_dummy_span(kw::PathRoot)) | ||
.chain( | ||
[name, "prelude", "v1"].iter().cloned() | ||
.map(ast::Ident::from_str) | ||
).map(ast::PathSegment::from_ident).collect(), | ||
span, | ||
}, | ||
let use_item = cx.item( | ||
span, | ||
ast::Ident::invalid(), | ||
vec![cx.attribute(cx.meta_word(span, sym::prelude_import))], | ||
ast::ItemKind::Use(P(ast::UseTree { | ||
prefix: cx.path(span, import_path), | ||
kind: ast::UseTreeKind::Glob, | ||
span, | ||
})), | ||
id: ast::DUMMY_NODE_ID, | ||
ident: ast::Ident::invalid(), | ||
span, | ||
tokens: None, | ||
})); | ||
); | ||
|
||
krate.module.items.insert(0, use_item); | ||
|
||
(krate, Some(Symbol::intern(name))) | ||
(krate, Some(name)) | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub fn not_in_lib_std() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Make sure that attribute used when injecting the prelude are resolved | ||
// hygienically. | ||
|
||
// check-pass | ||
// aux-build:not-libstd.rs | ||
|
||
//revisions: rust2015 rust2018 | ||
//[rust2018] edition:2018 | ||
|
||
// The prelude import shouldn't see these as candidates for when it's trying to | ||
// use the built-in macros. | ||
extern crate core; | ||
use core::prelude::v1::test as prelude_import; | ||
use core::prelude::v1::test as macro_use; | ||
|
||
// Should not be used for the prelude import - not a concern in the 2015 edition | ||
// because `std` is already declared in the crate root. | ||
#[cfg(rust2018)] | ||
extern crate not_libstd as std; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, this is kinda arguable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see it the other way Looking at the linked issue, the crate in question isn't |
||
|
||
#[cfg(rust2018)] | ||
mod x { | ||
// The extern crate item should override `std` in the extern prelude. | ||
fn f() { | ||
std::not_in_lib_std(); | ||
} | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
// build-pass (FIXME(62277): could be check-pass?) | ||
// check-pass | ||
// edition:2018 | ||
// aux-build:gensymed.rs | ||
|
||
extern crate gensymed; | ||
|
||
use gensymed::*; | ||
|
||
fn main() {} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Check that declarative macros can declare tests | ||
|
||
// check-pass | ||
// compile-flags: --test | ||
|
||
#![feature(decl_macro)] | ||
|
||
macro create_test() { | ||
#[test] | ||
fn test() {} | ||
} | ||
|
||
macro create_module_test() { | ||
mod x { | ||
#[test] | ||
fn test() {} | ||
} | ||
} | ||
|
||
create_test!(); | ||
create_test!(); | ||
create_module_test!(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
error[E0432]: unresolved import `main` | ||
--> $DIR/inaccessible-test-modules.rs:5:5 | ||
| | ||
LL | use main as x; | ||
| ----^^^^^ | ||
| | | ||
| no `main` in the root | ||
| help: a similar name exists in the module: `main` | ||
|
||
error[E0432]: unresolved import `test` | ||
--> $DIR/inaccessible-test-modules.rs:6:5 | ||
| | ||
LL | use test as y; | ||
| ----^^^^^ | ||
| | | ||
| no `test` in the root | ||
| help: a similar name exists in the module: `test` | ||
|
||
error: aborting due to 2 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0432`. |
Uh oh!
There was an error while loading. Please reload this page.