Skip to content
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

expand: Resolve and expand inner attributes on out-of-line modules #82399

Merged
merged 1 commit into from
Mar 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4774,7 +4774,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser 0.10.1",
"semver-parser 0.10.2",
"serde",
]

Expand All @@ -4786,9 +4786,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"

[[package]]
name = "semver-parser"
version = "0.10.1"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2297,7 +2297,7 @@ impl FnRetTy {
}
}

#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
pub enum Inline {
Yes,
No,
Expand Down
20 changes: 13 additions & 7 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,16 +1282,13 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
let (file_path, dir_path, dir_ownership) = match mod_kind {
ModKind::Loaded(_, inline, _) => {
// Inline `mod foo { ... }`, but we still need to push directories.
assert!(
*inline == Inline::Yes,
"`mod` item is loaded from a file for the second time"
);
let (dir_path, dir_ownership) = mod_dir_path(
&self.cx.sess,
ident,
&attrs,
&self.cx.current_expansion.module,
self.cx.current_expansion.dir_ownership,
*inline,
);
item.attrs = attrs;
(None, dir_path, dir_ownership)
Expand Down Expand Up @@ -1322,10 +1319,19 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
item.attrs = attrs;
if item.attrs.len() > old_attrs_len {
// If we loaded an out-of-line module and added some inner attributes,
// then we need to re-configure it.
// FIXME: Attributes also need to be recollected
// for resolution and expansion.
// then we need to re-configure it and re-collect attributes for
// resolution and expansion.
item = configure!(self, item);

if let Some(attr) = self.take_first_attr(&mut item) {
return self
.collect_attr(
attr,
Annotatable::Item(item),
AstFragmentKind::Items,
)
.make_items();
}
}
(Some(file_path), dir_path, dir_ownership)
}
Expand Down
58 changes: 39 additions & 19 deletions compiler/rustc_expand/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::base::ModuleData;
use rustc_ast::ptr::P;
use rustc_ast::{token, Attribute, Item};
use rustc_ast::{token, Attribute, Inline, Item};
use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_parse::new_parser_from_file;
use rustc_session::parse::ParseSess;
Expand Down Expand Up @@ -83,29 +83,49 @@ crate fn mod_dir_path(
attrs: &[Attribute],
module: &ModuleData,
mut dir_ownership: DirOwnership,
inline: Inline,
) -> (PathBuf, DirOwnership) {
if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) {
// For inline modules file path from `#[path]` is actually the directory path
// for historical reasons, so we don't pop the last segment here.
return (file_path, DirOwnership::Owned { relative: None });
}
match inline {
Inline::Yes => {
if let Some(file_path) = mod_file_path_from_attr(sess, attrs, &module.dir_path) {
// For inline modules file path from `#[path]` is actually the directory path
// for historical reasons, so we don't pop the last segment here.
return (file_path, DirOwnership::Owned { relative: None });
}

// We have to push on the current module name in the case of relative
// paths in order to ensure that any additional module paths from inline
// `mod x { ... }` come after the relative extension.
//
// For example, a `mod z { ... }` inside `x/y.rs` should set the current
// directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
let mut dir_path = module.dir_path.clone();
if let DirOwnership::Owned { relative } = &mut dir_ownership {
if let Some(ident) = relative.take() {
// Remove the relative offset.
// We have to push on the current module name in the case of relative
// paths in order to ensure that any additional module paths from inline
// `mod x { ... }` come after the relative extension.
//
// For example, a `mod z { ... }` inside `x/y.rs` should set the current
// directory path to `/x/y/z`, not `/x/z` with a relative offset of `y`.
let mut dir_path = module.dir_path.clone();
if let DirOwnership::Owned { relative } = &mut dir_ownership {
if let Some(ident) = relative.take() {
// Remove the relative offset.
dir_path.push(&*ident.as_str());
}
}
dir_path.push(&*ident.as_str());

(dir_path, dir_ownership)
}
}
dir_path.push(&*ident.as_str());
Inline::No => {
// FIXME: This is a subset of `parse_external_mod` without actual parsing,
// check whether the logic for unloaded, loaded and inline modules can be unified.
let file_path = mod_file_path(sess, ident, &attrs, &module.dir_path, dir_ownership)
.map(|mp| {
dir_ownership = mp.dir_ownership;
mp.file_path
})
.unwrap_or_default();

// Extract the directory path for submodules of the module.
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();

(dir_path, dir_ownership)
(dir_path, dir_ownership)
}
}
}

fn mod_file_path<'a>(
Expand Down
27 changes: 25 additions & 2 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use crate::Namespace::*;
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy};
use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
use rustc_ast::{self as ast, NodeId};
use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId};
use rustc_ast_lowering::ResolverAstLowering;
use rustc_ast_pretty::pprust;
use rustc_attr::StabilityLevel;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::ptr_key::PtrKey;
use rustc_data_structures::sync::Lrc;
use rustc_errors::struct_span_err;
use rustc_expand::base::Annotatable;
use rustc_expand::base::{Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::compile_declarative_macro;
use rustc_expand::expand::{AstFragment, Invocation, InvocationKind};
Expand Down Expand Up @@ -153,6 +154,26 @@ crate fn registered_attrs_and_tools(
(registered_attrs, registered_tools)
}

// Some feature gates for inner attributes are reported as lints for backward compatibility.
fn soft_custom_inner_attributes_gate(path: &ast::Path, invoc: &Invocation) -> bool {
match &path.segments[..] {
// `#![test]`
[seg] if seg.ident.name == sym::test => return true,
// `#![rustfmt::skip]` on out-of-line modules
[seg1, seg2] if seg1.ident.name == sym::rustfmt && seg2.ident.name == sym::skip => {
if let InvocationKind::Attr { item, .. } = &invoc.kind {
if let Annotatable::Item(item) = item {
if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _)) = item.kind {
return true;
}
}
}
}
_ => {}
}
false
}

impl<'a> ResolverExpand for Resolver<'a> {
fn next_node_id(&mut self) -> NodeId {
self.next_node_id()
Expand Down Expand Up @@ -267,6 +288,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
parent_scope,
node_id,
force,
soft_custom_inner_attributes_gate(path, invoc),
)?;

let span = invoc.span();
Expand Down Expand Up @@ -440,6 +462,7 @@ impl<'a> Resolver<'a> {
parent_scope: &ParentScope<'a>,
node_id: NodeId,
force: bool,
soft_custom_inner_attributes_gate: bool,
) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> {
let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force)
{
Expand Down Expand Up @@ -507,7 +530,7 @@ impl<'a> Resolver<'a> {
Res::NonMacroAttr(..) => "custom inner attributes are unstable",
_ => unreachable!(),
};
if path == &sym::test {
if soft_custom_inner_attributes_gate {
self.session.parse_sess.buffer_lint(SOFT_UNSTABLE, path.span, node_id, msg);
} else {
feature_err(&self.session.parse_sess, sym::custom_inner_attributes, path.span, msg)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@ symbols! {
size_of,
size_of_val,
sized,
skip,
slice,
slice_alloc,
slice_patterns,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// check-pass

mod module_with_cfg;

mod module_with_cfg {} // Ok, the module above is configured away by an inner attribute.

fn main() {}
3 changes: 3 additions & 0 deletions src/test/ui/conditional-compilation/module_with_cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// ignore-test

#![cfg_attr(all(), cfg(FALSE))]
18 changes: 18 additions & 0 deletions src/test/ui/proc-macro/inner-attr-non-inline-mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// compile-flags: -Z span-debug
// error-pattern:custom inner attributes are unstable
// error-pattern:inner macro attributes are unstable
// error-pattern:this was previously accepted
// aux-build:test-macros.rs

#![no_std] // Don't load unnecessary hygiene information from std
extern crate std;

#[macro_use]
extern crate test_macros;

#[deny(unused_attributes)]
mod module_with_attrs;
//~^ ERROR non-inline modules in proc macro input are unstable
//~| ERROR custom inner attributes are unstable

fn main() {}
40 changes: 40 additions & 0 deletions src/test/ui/proc-macro/inner-attr-non-inline-mod.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
error[E0658]: inner macro attributes are unstable
--> $DIR/module_with_attrs.rs:4:4
|
LL | #![print_attr]
| ^^^^^^^^^^
|
= note: see issue #54726 <https://github.com/rust-lang/rust/issues/54726> for more information
= help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable

error[E0658]: non-inline modules in proc macro input are unstable
--> $DIR/inner-attr-non-inline-mod.rs:14:1
|
LL | mod module_with_attrs;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information
= help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable

error[E0658]: custom inner attributes are unstable
--> $DIR/inner-attr-non-inline-mod.rs:14:1
|
LL | mod module_with_attrs;
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #54726 <https://github.com/rust-lang/rust/issues/54726> for more information
= help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable

error: custom inner attributes are unstable
--> $DIR/module_with_attrs.rs:3:4
|
LL | #![rustfmt::skip]
| ^^^^^^^^^^^^^
|
= note: `#[deny(soft_unstable)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #64266 <https://github.com/rust-lang/rust/issues/64266>

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0658`.
76 changes: 76 additions & 0 deletions src/test/ui/proc-macro/inner-attr-non-inline-mod.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
PRINT-ATTR INPUT (DISPLAY): #[deny(unused_attributes)] mod module_with_attrs { # ! [rustfmt :: skip] }
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
spacing: Alone,
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "deny",
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Group {
delimiter: Parenthesis,
stream: TokenStream [
Ident {
ident: "unused_attributes",
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
],
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
],
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Ident {
ident: "mod",
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Ident {
ident: "module_with_attrs",
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [
Punct {
ch: '#',
spacing: Joint,
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Punct {
ch: '!',
spacing: Alone,
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Group {
delimiter: Bracket,
stream: TokenStream [
Ident {
ident: "rustfmt",
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Punct {
ch: ':',
spacing: Joint,
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Punct {
ch: ':',
spacing: Alone,
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
Ident {
ident: "skip",
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
],
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
],
span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0),
},
]
4 changes: 4 additions & 0 deletions src/test/ui/proc-macro/module_with_attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// ignore-test

#![rustfmt::skip]
#![print_attr]