Skip to content

Commit 1fe2eb8

Browse files
committed
expand: Introduce enum for module loading errors and make module loading speculative
1 parent 1e1d574 commit 1fe2eb8

File tree

6 files changed

+90
-91
lines changed

6 files changed

+90
-91
lines changed

compiler/rustc_expand/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![feature(bool_to_option)]
12
#![feature(crate_visibility_modifier)]
23
#![feature(decl_macro)]
34
#![feature(destructuring_assignment)]

compiler/rustc_expand/src/module.rs

+85-87
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::base::ModuleData;
22
use rustc_ast::ptr::P;
33
use rustc_ast::{token, Attribute, Item};
4-
use rustc_errors::{struct_span_err, PResult};
4+
use rustc_errors::{struct_span_err, DiagnosticBuilder};
55
use rustc_parse::new_parser_from_file;
66
use rustc_session::parse::ParseSess;
77
use rustc_session::Session;
@@ -19,14 +19,6 @@ pub enum DirOwnership {
1919
UnownedViaBlock,
2020
}
2121

22-
/// Information about the path to a module.
23-
// Public for rustfmt usage.
24-
pub struct ModulePath<'a> {
25-
name: String,
26-
path_exists: bool,
27-
pub result: PResult<'a, ModulePathSuccess>,
28-
}
29-
3022
// Public for rustfmt usage.
3123
pub struct ModulePathSuccess {
3224
pub file_path: PathBuf,
@@ -41,6 +33,14 @@ crate struct ParsedExternalMod {
4133
pub dir_ownership: DirOwnership,
4234
}
4335

36+
pub enum ModError<'a> {
37+
CircularInclusion(Vec<PathBuf>),
38+
ModInBlock(Option<Ident>),
39+
FileNotFound(Ident, PathBuf),
40+
MultipleCandidates(Ident, String, String),
41+
ParserError(DiagnosticBuilder<'a>),
42+
}
43+
4444
crate fn parse_external_mod(
4545
sess: &Session,
4646
ident: Ident,
@@ -50,47 +50,33 @@ crate fn parse_external_mod(
5050
attrs: &mut Vec<Attribute>,
5151
) -> ParsedExternalMod {
5252
// We bail on the first error, but that error does not cause a fatal error... (1)
53-
let result: PResult<'_, _> = try {
53+
let result: Result<_, ModError<'_>> = try {
5454
// Extract the file path and the new ownership.
55-
let mp = mod_file_path(sess, ident, span, &attrs, &module.dir_path, dir_ownership)?;
55+
let mp = mod_file_path(sess, ident, &attrs, &module.dir_path, dir_ownership)?;
5656
dir_ownership = mp.dir_ownership;
5757

5858
// Ensure file paths are acyclic.
59-
error_on_circular_module(&sess.parse_sess, span, &mp.file_path, &module.file_path_stack)?;
59+
if let Some(pos) = module.file_path_stack.iter().position(|p| p == &mp.file_path) {
60+
Err(ModError::CircularInclusion(module.file_path_stack[pos..].to_vec()))?;
61+
}
6062

6163
// Actually parse the external file as a module.
6264
let mut parser = new_parser_from_file(&sess.parse_sess, &mp.file_path, Some(span));
63-
let (mut inner_attrs, items, inner_span) = parser.parse_mod(&token::Eof)?;
65+
let (mut inner_attrs, items, inner_span) =
66+
parser.parse_mod(&token::Eof).map_err(|err| ModError::ParserError(err))?;
6467
attrs.append(&mut inner_attrs);
6568
(items, inner_span, mp.file_path)
6669
};
6770
// (1) ...instead, we return a dummy module.
68-
let (items, inner_span, file_path) = result.map_err(|mut err| err.emit()).unwrap_or_default();
71+
let (items, inner_span, file_path) =
72+
result.map_err(|err| err.report(sess, span)).unwrap_or_default();
6973

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

7377
ParsedExternalMod { items, inner_span, file_path, dir_path, dir_ownership }
7478
}
7579

76-
fn error_on_circular_module<'a>(
77-
sess: &'a ParseSess,
78-
span: Span,
79-
file_path: &Path,
80-
file_path_stack: &[PathBuf],
81-
) -> PResult<'a, ()> {
82-
if let Some(i) = file_path_stack.iter().position(|p| *p == file_path) {
83-
let mut err = String::from("circular modules: ");
84-
for p in &file_path_stack[i..] {
85-
err.push_str(&p.to_string_lossy());
86-
err.push_str(" -> ");
87-
}
88-
err.push_str(&file_path.to_string_lossy());
89-
return Err(sess.span_diagnostic.struct_span_err(span, &err[..]));
90-
}
91-
Ok(())
92-
}
93-
9480
crate fn mod_dir_path(
9581
sess: &Session,
9682
ident: Ident,
@@ -125,11 +111,10 @@ crate fn mod_dir_path(
125111
fn mod_file_path<'a>(
126112
sess: &'a Session,
127113
ident: Ident,
128-
span: Span,
129114
attrs: &[Attribute],
130115
dir_path: &Path,
131116
dir_ownership: DirOwnership,
132-
) -> PResult<'a, ModulePathSuccess> {
117+
) -> Result<ModulePathSuccess, ModError<'a>> {
133118
if let Some(file_path) = mod_file_path_from_attr(sess, attrs, dir_path) {
134119
// All `#[path]` files are treated as though they are a `mod.rs` file.
135120
// This means that `mod foo;` declarations inside `#[path]`-included
@@ -146,30 +131,14 @@ fn mod_file_path<'a>(
146131
DirOwnership::Owned { relative } => relative,
147132
DirOwnership::UnownedViaBlock => None,
148133
};
149-
let ModulePath { path_exists, name, result } =
150-
default_submod_path(&sess.parse_sess, ident, span, relative, dir_path);
134+
let result = default_submod_path(&sess.parse_sess, ident, relative, dir_path);
151135
match dir_ownership {
152-
DirOwnership::Owned { .. } => Ok(result?),
153-
DirOwnership::UnownedViaBlock => {
154-
let _ = result.map_err(|mut err| err.cancel());
155-
error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name)
156-
}
157-
}
158-
}
159-
160-
fn error_decl_mod_in_block<'a, T>(
161-
sess: &'a ParseSess,
162-
span: Span,
163-
path_exists: bool,
164-
name: &str,
165-
) -> PResult<'a, T> {
166-
let msg = "Cannot declare a non-inline module inside a block unless it has a path attribute";
167-
let mut err = sess.span_diagnostic.struct_span_err(span, msg);
168-
if path_exists {
169-
let msg = format!("Maybe `use` the module `{}` instead of redeclaring it", name);
170-
err.span_note(span, &msg);
136+
DirOwnership::Owned { .. } => result,
137+
DirOwnership::UnownedViaBlock => Err(ModError::ModInBlock(match result {
138+
Ok(_) | Err(ModError::MultipleCandidates(..)) => Some(ident),
139+
_ => None,
140+
})),
171141
}
172-
Err(err)
173142
}
174143

175144
/// Derive a submodule path from the first found `#[path = "path_string"]`.
@@ -197,10 +166,9 @@ fn mod_file_path_from_attr(
197166
pub fn default_submod_path<'a>(
198167
sess: &'a ParseSess,
199168
ident: Ident,
200-
span: Span,
201169
relative: Option<Ident>,
202170
dir_path: &Path,
203-
) -> ModulePath<'a> {
171+
) -> Result<ModulePathSuccess, ModError<'a>> {
204172
// If we're in a foo.rs file instead of a mod.rs file,
205173
// we need to look for submodules in
206174
// `./foo/<ident>.rs` and `./foo/<ident>/mod.rs` rather than
@@ -222,7 +190,7 @@ pub fn default_submod_path<'a>(
222190
let default_exists = sess.source_map().file_exists(&default_path);
223191
let secondary_exists = sess.source_map().file_exists(&secondary_path);
224192

225-
let result = match (default_exists, secondary_exists) {
193+
match (default_exists, secondary_exists) {
226194
(true, false) => Ok(ModulePathSuccess {
227195
file_path: default_path,
228196
dir_ownership: DirOwnership::Owned { relative: Some(ident) },
@@ -231,35 +199,65 @@ pub fn default_submod_path<'a>(
231199
file_path: secondary_path,
232200
dir_ownership: DirOwnership::Owned { relative: None },
233201
}),
234-
(false, false) => {
235-
let mut err = struct_span_err!(
236-
sess.span_diagnostic,
237-
span,
238-
E0583,
239-
"file not found for module `{}`",
240-
mod_name,
241-
);
242-
err.help(&format!(
243-
"to create the module `{}`, create file \"{}\"",
244-
mod_name,
245-
default_path.display(),
246-
));
247-
Err(err)
248-
}
202+
(false, false) => Err(ModError::FileNotFound(ident, default_path)),
249203
(true, true) => {
250-
let mut err = struct_span_err!(
251-
sess.span_diagnostic,
252-
span,
253-
E0761,
254-
"file for module `{}` found at both {} and {}",
255-
mod_name,
256-
default_path_str,
257-
secondary_path_str,
258-
);
259-
err.help("delete or rename one of them to remove the ambiguity");
260-
Err(err)
204+
Err(ModError::MultipleCandidates(ident, default_path_str, secondary_path_str))
261205
}
262-
};
206+
}
207+
}
263208

264-
ModulePath { name: mod_name, path_exists: default_exists || secondary_exists, result }
209+
impl ModError<'_> {
210+
fn report(self, sess: &Session, span: Span) {
211+
let diag = &sess.parse_sess.span_diagnostic;
212+
match self {
213+
ModError::CircularInclusion(file_paths) => {
214+
let mut msg = String::from("circular modules: ");
215+
for file_path in &file_paths {
216+
msg.push_str(&file_path.display().to_string());
217+
msg.push_str(" -> ");
218+
}
219+
msg.push_str(&file_paths[0].display().to_string());
220+
diag.struct_span_err(span, &msg)
221+
}
222+
ModError::ModInBlock(ident) => {
223+
let msg = "cannot declare a non-inline module inside a block unless it has a path attribute";
224+
let mut err = diag.struct_span_err(span, msg);
225+
if let Some(ident) = ident {
226+
let note =
227+
format!("maybe `use` the module `{}` instead of redeclaring it", ident);
228+
err.span_note(span, &note);
229+
}
230+
err
231+
}
232+
ModError::FileNotFound(ident, default_path) => {
233+
let mut err = struct_span_err!(
234+
diag,
235+
span,
236+
E0583,
237+
"file not found for module `{}`",
238+
ident,
239+
);
240+
err.help(&format!(
241+
"to create the module `{}`, create file \"{}\"",
242+
ident,
243+
default_path.display(),
244+
));
245+
err
246+
}
247+
ModError::MultipleCandidates(ident, default_path_short, secondary_path_short) => {
248+
let mut err = struct_span_err!(
249+
diag,
250+
span,
251+
E0761,
252+
"file for module `{}` found at both {} and {}",
253+
ident,
254+
default_path_short,
255+
secondary_path_short,
256+
);
257+
err.help("delete or rename one of them to remove the ambiguity");
258+
err
259+
}
260+
ModError::ParserError(err) => err,
261+
}.emit()
262+
}
265263
}

src/test/ui/directory_ownership/macro-expanded-mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
macro_rules! mod_decl {
44
($i:ident) => {
5-
mod $i; //~ ERROR Cannot declare a non-inline module inside a block
5+
mod $i; //~ ERROR cannot declare a non-inline module inside a block
66
};
77
}
88

src/test/ui/directory_ownership/macro-expanded-mod.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: Cannot declare a non-inline module inside a block unless it has a path attribute
1+
error: cannot declare a non-inline module inside a block unless it has a path attribute
22
--> $DIR/macro-expanded-mod.rs:5:9
33
|
44
LL | mod $i;
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Test that non-inline modules are not allowed inside blocks.
22

33
fn main() {
4-
mod foo; //~ ERROR Cannot declare a non-inline module inside a block
4+
mod foo; //~ ERROR cannot declare a non-inline module inside a block
55
}

src/test/ui/directory_ownership/non-inline-mod-restriction.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: Cannot declare a non-inline module inside a block unless it has a path attribute
1+
error: cannot declare a non-inline module inside a block unless it has a path attribute
22
--> $DIR/non-inline-mod-restriction.rs:4:5
33
|
44
LL | mod foo;

0 commit comments

Comments
 (0)