Skip to content

Commit 7dbdd72

Browse files
authored
Rollup merge of rust-lang#69838 - Centril:expand-module, r=petrochenkov,eddyb
Expansion-driven outline module parsing After this PR, the parser will not do any conditional compilation or loading of external module files when `mod foo;` is encountered. Instead, the parser only leaves `mod foo;` in place in the AST, with no items filled in. Expansion later kicks in and will load the actual files and do the parsing. This entails that the following is now valid: ```rust #[cfg(FALSE)] mod foo { mod bar { mod baz; // `foo/bar/baz.rs` doesn't exist, but no error! } } ``` Fixes rust-lang#64197. r? @petrochenkov
2 parents c64771e + 42ab820 commit 7dbdd72

32 files changed

+518
-557
lines changed

src/librustc_ast/ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2153,7 +2153,7 @@ impl FnRetTy {
21532153
/// Module declaration.
21542154
///
21552155
/// E.g., `mod foo;` or `mod foo { .. }`.
2156-
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
2156+
#[derive(Clone, RustcEncodable, RustcDecodable, Debug, Default)]
21572157
pub struct Mod {
21582158
/// A span from the first token past `{` to the last token until `}`.
21592159
/// For `mod foo;`, the inner span ranges from the first token

src/librustc_builtin_macros/source_util.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_ast::tokenstream::TokenStream;
55
use rustc_ast_pretty::pprust;
66
use rustc_expand::base::{self, *};
77
use rustc_expand::panictry;
8-
use rustc_parse::{self, new_sub_parser_from_file, parser::Parser, DirectoryOwnership};
8+
use rustc_parse::{self, new_sub_parser_from_file, parser::Parser};
99
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
1010
use rustc_span::symbol::Symbol;
1111
use rustc_span::{self, Pos, Span};
@@ -108,8 +108,7 @@ pub fn expand_include<'cx>(
108108
return DummyResult::any(sp);
109109
}
110110
};
111-
let directory_ownership = DirectoryOwnership::Owned { relative: None };
112-
let p = new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp);
111+
let p = new_sub_parser_from_file(cx.parse_sess(), &file, None, sp);
113112

114113
struct ExpandResult<'a> {
115114
p: Parser<'a>,

src/librustc_expand/base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::expand::{self, AstFragment, Invocation};
2+
use crate::module::DirectoryOwnership;
23

34
use rustc_ast::ast::{self, Attribute, Name, NodeId, PatKind};
45
use rustc_ast::mut_visit::{self, MutVisitor};
@@ -10,7 +11,7 @@ use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability};
1011
use rustc_data_structures::fx::FxHashMap;
1112
use rustc_data_structures::sync::{self, Lrc};
1213
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
13-
use rustc_parse::{self, parser, DirectoryOwnership, MACRO_ARGUMENTS};
14+
use rustc_parse::{self, parser, MACRO_ARGUMENTS};
1415
use rustc_session::parse::ParseSess;
1516
use rustc_span::edition::Edition;
1617
use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};

src/librustc_parse/config.rs src/librustc_expand/config.rs

+3-20
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
1-
//! Process the potential `cfg` attributes on a module.
2-
//! Also determine if the module should be included in this configuration.
3-
//!
4-
//! This module properly belongs in rustc_expand, but for now it's tied into
5-
//! parsing, so we leave it here to avoid complicated out-of-line dependencies.
6-
//!
7-
//! A principled solution to this wrong location would be to implement [#64197].
8-
//!
9-
//! [#64197]: https://github.com/rust-lang/rust/issues/64197
10-
11-
use crate::{parse_in, validate_attr};
1+
//! Conditional compilation stripping.
2+
123
use rustc_ast::ast::{self, AttrItem, Attribute, MetaItem};
134
use rustc_ast::attr::HasAttrs;
145
use rustc_ast::mut_visit::*;
@@ -21,6 +12,7 @@ use rustc_feature::{Feature, Features, State as FeatureState};
2112
use rustc_feature::{
2213
ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
2314
};
15+
use rustc_parse::{parse_in, validate_attr};
2416
use rustc_session::parse::{feature_err, ParseSess};
2517
use rustc_span::edition::{Edition, ALL_EDITIONS};
2618
use rustc_span::symbol::{sym, Symbol};
@@ -538,12 +530,3 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
538530
fn is_cfg(attr: &Attribute) -> bool {
539531
attr.check_name(sym::cfg)
540532
}
541-
542-
/// Process the potential `cfg` attributes on a module.
543-
/// Also determine if the module should be included in this configuration.
544-
pub fn process_configure_mod(sess: &ParseSess, cfg_mods: bool, attrs: &mut Vec<Attribute>) -> bool {
545-
// Don't perform gated feature checking.
546-
let mut strip_unconfigured = StripUnconfigured { sess, features: None };
547-
strip_unconfigured.process_cfg_attrs(attrs);
548-
!cfg_mods || strip_unconfigured.in_cfg(&attrs)
549-
}

src/librustc_expand/expand.rs

+48-35
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::base::*;
22
use crate::config::StripUnconfigured;
3+
use crate::configure;
34
use crate::hygiene::{ExpnData, ExpnId, ExpnKind, SyntaxContext};
45
use crate::mbe::macro_rules::annotate_err_with_kind;
6+
use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership};
57
use crate::placeholders::{placeholder, PlaceholderExpander};
68
use crate::proc_macro::collect_derives;
79

@@ -17,10 +19,8 @@ use rustc_ast_pretty::pprust;
1719
use rustc_attr::{self as attr, is_builtin_attr, HasAttrs};
1820
use rustc_errors::{Applicability, FatalError, PResult};
1921
use rustc_feature::Features;
20-
use rustc_parse::configure;
2122
use rustc_parse::parser::Parser;
2223
use rustc_parse::validate_attr;
23-
use rustc_parse::DirectoryOwnership;
2424
use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS;
2525
use rustc_session::lint::BuiltinLintDiagnostics;
2626
use rustc_session::parse::{feature_err, ParseSess};
@@ -1359,59 +1359,72 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
13591359
.make_items();
13601360
}
13611361

1362+
let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck.
1363+
let ident = item.ident;
1364+
let span = item.span;
1365+
13621366
match item.kind {
13631367
ast::ItemKind::Mac(..) => {
1368+
item.attrs = attrs;
13641369
self.check_attributes(&item.attrs);
13651370
item.and_then(|item| match item.kind {
13661371
ItemKind::Mac(mac) => self
1367-
.collect(
1368-
AstFragmentKind::Items,
1369-
InvocationKind::Bang { mac, span: item.span },
1370-
)
1372+
.collect(AstFragmentKind::Items, InvocationKind::Bang { mac, span })
13711373
.make_items(),
13721374
_ => unreachable!(),
13731375
})
13741376
}
1375-
ast::ItemKind::Mod(ast::Mod { inner, inline, .. })
1376-
if item.ident != Ident::invalid() =>
1377-
{
1378-
let orig_directory_ownership = self.cx.current_expansion.directory_ownership;
1377+
ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => {
1378+
let sess = self.cx.parse_sess;
1379+
let orig_ownership = self.cx.current_expansion.directory_ownership;
13791380
let mut module = (*self.cx.current_expansion.module).clone();
1380-
module.mod_path.push(item.ident);
13811381

1382-
if inline {
1383-
if let Some(path) = attr::first_attr_value_str_by_name(&item.attrs, sym::path) {
1384-
self.cx.current_expansion.directory_ownership =
1385-
DirectoryOwnership::Owned { relative: None };
1386-
module.directory.push(&*path.as_str());
1387-
} else {
1388-
module.directory.push(&*item.ident.as_str());
1389-
}
1382+
let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop.
1383+
let dir = Directory { ownership: orig_ownership, path: module.directory };
1384+
let Directory { ownership, path } = if old_mod.inline {
1385+
// Inline `mod foo { ... }`, but we still need to push directories.
1386+
item.attrs = attrs;
1387+
push_directory(ident, &item.attrs, dir)
13901388
} else {
1391-
let path = self.cx.parse_sess.source_map().span_to_unmapped_path(inner);
1392-
let mut path = match path {
1393-
FileName::Real(path) => path,
1394-
other => PathBuf::from(other.to_string()),
1395-
};
1396-
let directory_ownership = match path.file_name().unwrap().to_str() {
1397-
Some("mod.rs") => DirectoryOwnership::Owned { relative: None },
1398-
Some(_) => DirectoryOwnership::Owned { relative: Some(item.ident) },
1399-
None => DirectoryOwnership::UnownedViaMod,
1389+
// We have an outline `mod foo;` so we need to parse the file.
1390+
let (new_mod, dir) =
1391+
parse_external_mod(sess, ident, span, dir, &mut attrs, pushed);
1392+
*old_mod = new_mod;
1393+
item.attrs = attrs;
1394+
// File can have inline attributes, e.g., `#![cfg(...)]` & co. => Reconfigure.
1395+
item = match self.configure(item) {
1396+
Some(node) => node,
1397+
None => {
1398+
if *pushed {
1399+
sess.included_mod_stack.borrow_mut().pop();
1400+
}
1401+
return Default::default();
1402+
}
14001403
};
1401-
path.pop();
1402-
module.directory = path;
1403-
self.cx.current_expansion.directory_ownership = directory_ownership;
1404-
}
1404+
dir
1405+
};
14051406

1407+
// Set the module info before we flat map.
1408+
self.cx.current_expansion.directory_ownership = ownership;
1409+
module.directory = path;
1410+
module.mod_path.push(ident);
14061411
let orig_module =
14071412
mem::replace(&mut self.cx.current_expansion.module, Rc::new(module));
1413+
14081414
let result = noop_flat_map_item(item, self);
1415+
1416+
// Restore the module info.
14091417
self.cx.current_expansion.module = orig_module;
1410-
self.cx.current_expansion.directory_ownership = orig_directory_ownership;
1418+
self.cx.current_expansion.directory_ownership = orig_ownership;
1419+
if *pushed {
1420+
sess.included_mod_stack.borrow_mut().pop();
1421+
}
14111422
result
14121423
}
1413-
1414-
_ => noop_flat_map_item(item, self),
1424+
_ => {
1425+
item.attrs = attrs;
1426+
noop_flat_map_item(item, self)
1427+
}
14151428
}
14161429
}
14171430

src/librustc_expand/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
#![feature(bool_to_option)]
12
#![feature(cow_is_borrowed)]
23
#![feature(crate_visibility_modifier)]
34
#![feature(decl_macro)]
45
#![feature(proc_macro_diagnostic)]
56
#![feature(proc_macro_internals)]
67
#![feature(proc_macro_span)]
8+
#![feature(try_blocks)]
79

810
extern crate proc_macro as pm;
911

@@ -33,8 +35,10 @@ pub use mbe::macro_rules::compile_declarative_macro;
3335
crate use rustc_span::hygiene;
3436
pub mod base;
3537
pub mod build;
38+
#[macro_use]
39+
pub mod config;
3640
pub mod expand;
37-
pub use rustc_parse::config;
41+
pub mod module;
3842
pub mod proc_macro;
3943

4044
crate mod mbe;

src/librustc_expand/mbe/macro_rules.rs

+12-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::base::{DummyResult, ExpansionData, ExtCtxt, MacResult, TTMacroExpander};
1+
use crate::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
22
use crate::base::{SyntaxExtension, SyntaxExtensionKind};
33
use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind};
44
use crate::mbe;
@@ -18,7 +18,6 @@ use rustc_data_structures::sync::Lrc;
1818
use rustc_errors::{Applicability, DiagnosticBuilder, FatalError};
1919
use rustc_feature::Features;
2020
use rustc_parse::parser::Parser;
21-
use rustc_parse::Directory;
2221
use rustc_session::parse::ParseSess;
2322
use rustc_span::edition::Edition;
2423
use rustc_span::hygiene::Transparency;
@@ -182,6 +181,8 @@ fn generic_extension<'cx>(
182181
lhses: &[mbe::TokenTree],
183182
rhses: &[mbe::TokenTree],
184183
) -> Box<dyn MacResult + 'cx> {
184+
let sess = cx.parse_sess;
185+
185186
if cx.trace_macros() {
186187
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(arg.clone()));
187188
trace_macros_note(&mut cx.expansions, sp, msg);
@@ -209,7 +210,7 @@ fn generic_extension<'cx>(
209210
// hacky, but speeds up the `html5ever` benchmark significantly. (Issue
210211
// 68836 suggests a more comprehensive but more complex change to deal with
211212
// this situation.)
212-
let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
213+
let parser = parser_from_cx(sess, arg.clone());
213214

214215
for (i, lhs) in lhses.iter().enumerate() {
215216
// try each arm's matchers
@@ -222,14 +223,13 @@ fn generic_extension<'cx>(
222223
// This is used so that if a matcher is not `Success(..)`ful,
223224
// then the spans which became gated when parsing the unsuccessful matcher
224225
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
225-
let mut gated_spans_snapshot =
226-
mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut());
226+
let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut());
227227

228228
match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
229229
Success(named_matches) => {
230230
// The matcher was `Success(..)`ful.
231231
// Merge the gated spans from parsing the matcher with the pre-existing ones.
232-
cx.parse_sess.gated_spans.merge(gated_spans_snapshot);
232+
sess.gated_spans.merge(gated_spans_snapshot);
233233

234234
let rhs = match rhses[i] {
235235
// ignore delimiters
@@ -258,11 +258,7 @@ fn generic_extension<'cx>(
258258
trace_macros_note(&mut cx.expansions, sp, msg);
259259
}
260260

261-
let directory = Directory {
262-
path: cx.current_expansion.module.directory.clone(),
263-
ownership: cx.current_expansion.directory_ownership,
264-
};
265-
let mut p = Parser::new(cx.parse_sess(), tts, Some(directory), true, false, None);
261+
let mut p = Parser::new(sess, tts, false, None);
266262
p.root_module_name =
267263
cx.current_expansion.module.mod_path.last().map(|id| id.to_string());
268264
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
@@ -289,7 +285,7 @@ fn generic_extension<'cx>(
289285

290286
// The matcher was not `Success(..)`ful.
291287
// Restore to the state before snapshotting and maybe try again.
292-
mem::swap(&mut gated_spans_snapshot, &mut cx.parse_sess.gated_spans.spans.borrow_mut());
288+
mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut());
293289
}
294290
drop(parser);
295291

@@ -309,8 +305,7 @@ fn generic_extension<'cx>(
309305
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
310306
_ => continue,
311307
};
312-
let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
313-
match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
308+
match parse_tt(&mut Cow::Borrowed(&parser_from_cx(sess, arg.clone())), lhs_tt) {
314309
Success(_) => {
315310
if comma_span.is_dummy() {
316311
err.note("you might be missing a comma");
@@ -392,7 +387,7 @@ pub fn compile_declarative_macro(
392387
),
393388
];
394389

395-
let parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS);
390+
let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS);
396391
let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
397392
Success(m) => m,
398393
Failure(token, msg) => {
@@ -1209,16 +1204,8 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
12091204
}
12101205
}
12111206

1212-
fn parser_from_cx<'cx>(
1213-
current_expansion: &'cx ExpansionData,
1214-
sess: &'cx ParseSess,
1215-
tts: TokenStream,
1216-
) -> Parser<'cx> {
1217-
let directory = Directory {
1218-
path: current_expansion.module.directory.clone(),
1219-
ownership: current_expansion.directory_ownership,
1220-
};
1221-
Parser::new(sess, tts, Some(directory), true, true, rustc_parse::MACRO_ARGUMENTS)
1207+
fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
1208+
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
12221209
}
12231210

12241211
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For

0 commit comments

Comments
 (0)