Skip to content

Commit

Permalink
Rollup merge of #90202 - matthewjasper:xcrate-hygiene, r=petrochenkov
Browse files Browse the repository at this point in the history
Improve and test cross-crate hygiene

- Decode the parent expansion for traits and enums in `rustc_resolve`, this was already being used for resolution in typeck
- Avoid suggesting importing names with def-site hygiene, since it's often not useful
- Add more tests

r? `@petrochenkov`
  • Loading branch information
GuillaumeGomez authored Oct 30, 2021
2 parents 7349440 + a76a2d4 commit 1a1f525
Show file tree
Hide file tree
Showing 27 changed files with 525 additions and 32 deletions.
13 changes: 7 additions & 6 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,8 +1198,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
}
}

if let EntryKind::Mod(data) = kind {
for exp in data.decode((self, sess)).reexports.decode((self, sess)) {
if let EntryKind::Mod(exports) = kind {
for exp in exports.decode((self, sess)) {
match exp.res {
Res::Def(DefKind::Macro(..), _) => {}
_ if macros_only => continue,
Expand All @@ -1219,10 +1219,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
}

fn module_expansion(&self, id: DefIndex, sess: &Session) -> ExpnId {
if let EntryKind::Mod(m) = self.kind(id) {
m.decode((self, sess)).expansion
} else {
panic!("Expected module, found {:?}", self.local_def_id(id))
match self.kind(id) {
EntryKind::Mod(_) | EntryKind::Enum(_) | EntryKind::Trait(_) => {
self.get_expn_that_defined(id, sess)
}
_ => panic!("Expected module, found {:?}", self.local_def_id(id)),
}
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,11 +1086,11 @@ impl EncodeContext<'a, 'tcx> {
Lazy::empty()
};

let data = ModData { reexports, expansion: tcx.expn_that_defined(local_def_id) };

record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data)));
record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports));
if self.is_proc_macro {
record!(self.tables.children[def_id] <- &[]);
// Encode this here because we don't do it in encode_def_ids.
record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id));
} else {
record!(self.tables.children[def_id] <- md.item_ids.iter().map(|item_id| {
item_id.def_id.local_def_index
Expand Down
8 changes: 1 addition & 7 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ enum EntryKind {
Union(Lazy<VariantData>, ReprOptions),
Fn(Lazy<FnData>),
ForeignFn(Lazy<FnData>),
Mod(Lazy<ModData>),
Mod(Lazy<[Export]>),
MacroDef(Lazy<MacroDef>),
ProcMacro(MacroKind),
Closure,
Expand All @@ -364,12 +364,6 @@ enum EntryKind {
#[derive(Encodable, Decodable)]
struct RenderedConst(String);

#[derive(MetadataEncodable, MetadataDecodable)]
struct ModData {
reexports: Lazy<[Export]>,
expansion: ExpnId,
}

#[derive(MetadataEncodable, MetadataDecodable)]
struct FnData {
asyncness: hir::IsAsync,
Expand Down
8 changes: 1 addition & 7 deletions compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,11 @@ impl<'a> Resolver<'a> {
} else {
def_key.disambiguated_data.data.get_opt_name().expect("module without name")
};
let expn_id = if def_kind == DefKind::Mod {
self.cstore().module_expansion_untracked(def_id, &self.session)
} else {
// FIXME: Parent expansions for enums and traits are not kept in metadata.
ExpnId::root()
};

Some(self.new_module(
parent,
ModuleKind::Def(def_kind, def_id, name),
expn_id,
self.cstore().module_expansion_untracked(def_id, &self.session),
self.cstore().get_span_untracked(def_id, &self.session),
// FIXME: Account for `#[no_implicit_prelude]` attributes.
parent.map_or(false, |module| module.no_implicit_prelude),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,9 +842,11 @@ impl<'a> Resolver<'a> {

// collect results based on the filter function
// avoid suggesting anything from the same module in which we are resolving
// avoid suggesting anything with a hygienic name
if ident.name == lookup_ident.name
&& ns == namespace
&& !ptr::eq(in_module, parent_scope.module)
&& !ident.span.normalize_to_macros_2_0().from_expansion()
{
let res = name_binding.res();
if filter_fn(res) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_span/src/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ impl SyntaxContext {
/// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`.
/// pub fn $i() {} // `$i`'s `SyntaxContext` is empty.
/// }
/// n(f);
/// n!(f);
/// macro n($j:ident) {
/// use foo::*;
/// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n`
Expand Down
73 changes: 73 additions & 0 deletions src/test/ui/hygiene/auxiliary/fields.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#![feature(decl_macro)]

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Field {
RootCtxt,
MacroCtxt,
}

#[rustfmt::skip]
macro x(
$macro_name:ident,
$macro2_name:ident,
$type_name:ident,
$field_name:ident,
$const_name:ident
) {
#[derive(Copy, Clone)]
pub struct $type_name {
pub field: Field,
pub $field_name: Field,
}

pub const $const_name: $type_name =
$type_name { field: Field::MacroCtxt, $field_name: Field::RootCtxt };

#[macro_export]
macro_rules! $macro_name {
(check_fields_of $e:expr) => {{
let e = $e;
assert_eq!(e.field, Field::MacroCtxt);
assert_eq!(e.$field_name, Field::RootCtxt);
}};
(check_fields) => {{
assert_eq!($const_name.field, Field::MacroCtxt);
assert_eq!($const_name.$field_name, Field::RootCtxt);
}};
(construct) => {
$type_name { field: Field::MacroCtxt, $field_name: Field::RootCtxt }
};
}

pub macro $macro2_name {
(check_fields_of $e:expr) => {{
let e = $e;
assert_eq!(e.field, Field::MacroCtxt);
assert_eq!(e.$field_name, Field::RootCtxt);
}},
(check_fields) => {{
assert_eq!($const_name.field, Field::MacroCtxt);
assert_eq!($const_name.$field_name, Field::RootCtxt);
}},
(construct) => {
$type_name { field: Field::MacroCtxt, $field_name: Field::RootCtxt }
}
}
}

x!(test_fields, test_fields2, MyStruct, field, MY_CONST);

pub fn check_fields(s: MyStruct) {
test_fields!(check_fields_of s);
}

pub fn check_fields_local() {
test_fields!(check_fields);
test_fields2!(check_fields);

let s1 = test_fields!(construct);
test_fields!(check_fields_of s1);

let s2 = test_fields2!(construct);
test_fields2!(check_fields_of s2);
}
160 changes: 160 additions & 0 deletions src/test/ui/hygiene/auxiliary/methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#![feature(decl_macro)]

#[derive(PartialEq, Eq, Debug)]
pub enum Method {
DefaultMacroCtxt,
DefaultRootCtxt,
OverrideMacroCtxt,
OverrideRootCtxt,
}

#[rustfmt::skip]
macro x($macro_name:ident, $macro2_name:ident, $trait_name:ident, $method_name:ident) {
pub trait $trait_name {
fn method(&self) -> Method {
Method::DefaultMacroCtxt
}

fn $method_name(&self) -> Method {
Method::DefaultRootCtxt
}
}

impl $trait_name for () {}
impl $trait_name for bool {
fn method(&self) -> Method {
Method::OverrideMacroCtxt
}

fn $method_name(&self) -> Method {
Method::OverrideRootCtxt
}
}

#[macro_export]
macro_rules! $macro_name {
(check_resolutions) => {
assert_eq!(().method(), Method::DefaultMacroCtxt);
assert_eq!($trait_name::method(&()), Method::DefaultMacroCtxt);
assert_eq!(().$method_name(), Method::DefaultRootCtxt);
assert_eq!($trait_name::$method_name(&()), Method::DefaultRootCtxt);

assert_eq!(false.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&false), Method::OverrideMacroCtxt);
assert_eq!(false.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&false), Method::OverrideRootCtxt);

assert_eq!('a'.method(), Method::DefaultMacroCtxt);
assert_eq!($trait_name::method(&'a'), Method::DefaultMacroCtxt);
assert_eq!('a'.$method_name(), Method::DefaultRootCtxt);
assert_eq!($trait_name::$method_name(&'a'), Method::DefaultRootCtxt);

assert_eq!(1i32.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&1i32), Method::OverrideMacroCtxt);
assert_eq!(1i32.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&1i32), Method::OverrideRootCtxt);

assert_eq!(1i64.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&1i64), Method::OverrideMacroCtxt);
assert_eq!(1i64.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&1i64), Method::OverrideRootCtxt);
};
(assert_no_override $v:expr) => {
assert_eq!($v.method(), Method::DefaultMacroCtxt);
assert_eq!($trait_name::method(&$v), Method::DefaultMacroCtxt);
assert_eq!($v.$method_name(), Method::DefaultRootCtxt);
assert_eq!($trait_name::$method_name(&$v), Method::DefaultRootCtxt);
};
(assert_override $v:expr) => {
assert_eq!($v.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&$v), Method::OverrideMacroCtxt);
assert_eq!($v.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&$v), Method::OverrideRootCtxt);
};
(impl for $t:ty) => {
impl $trait_name for $t {
fn method(&self) -> Method {
Method::OverrideMacroCtxt
}

fn $method_name(&self) -> Method {
Method::OverrideRootCtxt
}
}
};
}

pub macro $macro2_name {
(check_resolutions) => {
assert_eq!(().method(), Method::DefaultMacroCtxt);
assert_eq!($trait_name::method(&()), Method::DefaultMacroCtxt);
assert_eq!(().$method_name(), Method::DefaultRootCtxt);
assert_eq!($trait_name::$method_name(&()), Method::DefaultRootCtxt);

assert_eq!(false.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&false), Method::OverrideMacroCtxt);
assert_eq!(false.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&false), Method::OverrideRootCtxt);

assert_eq!('a'.method(), Method::DefaultMacroCtxt);
assert_eq!($trait_name::method(&'a'), Method::DefaultMacroCtxt);
assert_eq!('a'.$method_name(), Method::DefaultRootCtxt);
assert_eq!($trait_name::$method_name(&'a'), Method::DefaultRootCtxt);

assert_eq!(1i32.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&1i32), Method::OverrideMacroCtxt);
assert_eq!(1i32.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&1i32), Method::OverrideRootCtxt);

assert_eq!(1i64.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&1i64), Method::OverrideMacroCtxt);
assert_eq!(1i64.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&1i64), Method::OverrideRootCtxt);
},
(assert_no_override $v:expr) => {
assert_eq!($v.method(), Method::DefaultMacroCtxt);
assert_eq!($trait_name::method(&$v), Method::DefaultMacroCtxt);
assert_eq!($v.$method_name(), Method::DefaultRootCtxt);
assert_eq!($trait_name::$method_name(&$v), Method::DefaultRootCtxt);
},
(assert_override $v:expr) => {
assert_eq!($v.method(), Method::OverrideMacroCtxt);
assert_eq!($trait_name::method(&$v), Method::OverrideMacroCtxt);
assert_eq!($v.$method_name(), Method::OverrideRootCtxt);
assert_eq!($trait_name::$method_name(&$v), Method::OverrideRootCtxt);
},
(impl for $t:ty) => {
impl $trait_name for $t {
fn method(&self) -> Method {
Method::OverrideMacroCtxt
}

fn $method_name(&self) -> Method {
Method::OverrideRootCtxt
}
}
}
}
}

x!(test_trait, test_trait2, MyTrait, method);

impl MyTrait for char {}
test_trait!(impl for i32);
test_trait2!(impl for i64);

pub fn check_crate_local() {
test_trait!(check_resolutions);
test_trait2!(check_resolutions);
}

// Check that any comparison of idents at monomorphization time is correct
pub fn check_crate_local_generic<T: MyTrait, U: MyTrait>(t: T, u: U) {
test_trait!(check_resolutions);
test_trait2!(check_resolutions);

test_trait!(assert_no_override t);
test_trait2!(assert_no_override t);
test_trait!(assert_override u);
test_trait2!(assert_override u);
}
7 changes: 7 additions & 0 deletions src/test/ui/hygiene/auxiliary/pub_hygiene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![feature(decl_macro)]

macro x() {
pub struct MyStruct;
}

x!();
15 changes: 15 additions & 0 deletions src/test/ui/hygiene/auxiliary/use_by_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(decl_macro)]

macro x($macro_name:ident) {
#[macro_export]
macro_rules! $macro_name {
(define) => {
pub struct MyStruct;
};
(create) => {
MyStruct {}
};
}
}

x!(my_struct);
36 changes: 36 additions & 0 deletions src/test/ui/hygiene/auxiliary/variants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#![feature(decl_macro)]

#[rustfmt::skip]
macro x($macro_name:ident, $macro2_name:ident, $type_name:ident, $variant_name:ident) {
#[repr(u8)]
pub enum $type_name {
Variant = 0,
$variant_name = 1,
}

#[macro_export]
macro_rules! $macro_name {
() => {{
assert_eq!($type_name::Variant as u8, 0);
assert_eq!($type_name::$variant_name as u8, 1);
assert_eq!(<$type_name>::Variant as u8, 0);
assert_eq!(<$type_name>::$variant_name as u8, 1);
}};
}

pub macro $macro2_name {
() => {{
assert_eq!($type_name::Variant as u8, 0);
assert_eq!($type_name::$variant_name as u8, 1);
assert_eq!(<$type_name>::Variant as u8, 0);
assert_eq!(<$type_name>::$variant_name as u8, 1);
}},
}
}

x!(test_variants, test_variants2, MyEnum, Variant);

pub fn check_variants() {
test_variants!();
test_variants2!();
}
Loading

0 comments on commit 1a1f525

Please sign in to comment.