Skip to content

Commit dead3a8

Browse files
fix: backport mod resolution error handling
1 parent bc9a0b2 commit dead3a8

File tree

8 files changed

+85
-36
lines changed

8 files changed

+85
-36
lines changed

src/bin/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub enum OperationError {
7272
#[error("The `--print-config=minimal` option doesn't work with standard input.")]
7373
MinimalPathWithStdin,
7474
/// An io error during reading or writing.
75-
#[error("io error: {0}")]
75+
#[error("{0}")]
7676
IoError(IoError),
7777
/// Attempt to use --check with stdin, which isn't currently
7878
/// supported.

src/format_report_formatter.rs

+1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ fn error_kind_to_snippet_annotation_type(error_kind: &ErrorKind) -> AnnotationTy
162162
ErrorKind::LineOverflow(..)
163163
| ErrorKind::TrailingWhitespace
164164
| ErrorKind::IoError(_)
165+
| ErrorKind::ModuleResolutionError(_)
165166
| ErrorKind::ParseError
166167
| ErrorKind::LostComment
167168
| ErrorKind::LicenseCheck

src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::comment::LineClasses;
2626
use crate::emitter::Emitter;
2727
use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile};
2828
use crate::issues::Issue;
29+
use crate::modules::ModuleResolutionError;
2930
use crate::shape::Indent;
3031
use crate::syntux::parser::DirectoryOwnership;
3132
use crate::utils::indent_next_line;
@@ -110,6 +111,9 @@ pub enum ErrorKind {
110111
/// An io error during reading or writing.
111112
#[error("io error: {0}")]
112113
IoError(io::Error),
114+
/// Error during module resolution.
115+
#[error("{0}")]
116+
ModuleResolutionError(#[from] ModuleResolutionError),
113117
/// Parse error occurred when parsing the input.
114118
#[error("parse error")]
115119
ParseError,

src/modules.rs

+63-32
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ use std::path::{Path, PathBuf};
55
use rustc_ast::ast;
66
use rustc_ast::visit::Visitor;
77
use rustc_span::symbol::{self, sym, Symbol};
8+
use thiserror::Error;
89

910
use crate::attr::MetaVisitor;
1011
use crate::config::FileName;
1112
use crate::items::is_mod_decl;
12-
use crate::syntux::parser::{Directory, DirectoryOwnership, ModulePathSuccess, Parser};
13+
use crate::syntux::parser::{
14+
Directory, DirectoryOwnership, ModulePathSuccess, Parser, ParserError,
15+
};
1316
use crate::syntux::session::ParseSess;
1417
use crate::utils::contains_skip;
1518

@@ -29,6 +32,24 @@ pub(crate) struct ModResolver<'ast, 'sess> {
2932
recursive: bool,
3033
}
3134

35+
/// Represents errors while trying to resolve modules.
36+
#[error("failed to resolve mod `{module}`: {kind}")]
37+
#[derive(Debug, Error)]
38+
pub struct ModuleResolutionError {
39+
module: String,
40+
kind: ModuleResolutionErrorKind,
41+
}
42+
43+
#[derive(Debug, Error)]
44+
pub(crate) enum ModuleResolutionErrorKind {
45+
/// Find a file that cannot be parsed.
46+
#[error("cannot parse {file}")]
47+
ParseError { file: PathBuf },
48+
/// File cannot be found.
49+
#[error("{file} does not exist")]
50+
NotFound { file: PathBuf },
51+
}
52+
3253
#[derive(Clone)]
3354
enum SubModKind<'a, 'ast> {
3455
/// `mod foo;`
@@ -63,7 +84,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
6384
pub(crate) fn visit_crate(
6485
mut self,
6586
krate: &'ast ast::Crate,
66-
) -> Result<FileModMap<'ast>, String> {
87+
) -> Result<FileModMap<'ast>, ModuleResolutionError> {
6788
let root_filename = self.parse_sess.span_to_filename(krate.span);
6889
self.directory.path = match root_filename {
6990
FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(),
@@ -81,7 +102,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
81102
}
82103

83104
/// Visit `cfg_if` macro and look for module declarations.
84-
fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), String> {
105+
fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), ModuleResolutionError> {
85106
let mut visitor = visitor::CfgIfVisitor::new(self.parse_sess);
86107
visitor.visit_item(&item);
87108
for module_item in visitor.mods() {
@@ -93,7 +114,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
93114
}
94115

95116
/// Visit modules defined inside macro calls.
96-
fn visit_mod_outside_ast(&mut self, module: ast::Mod) -> Result<(), String> {
117+
fn visit_mod_outside_ast(&mut self, module: ast::Mod) -> Result<(), ModuleResolutionError> {
97118
for item in module.items {
98119
if is_cfg_if(&item) {
99120
self.visit_cfg_if(Cow::Owned(item.into_inner()))?;
@@ -108,7 +129,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
108129
}
109130

110131
/// Visit modules from AST.
111-
fn visit_mod_from_ast(&mut self, module: &'ast ast::Mod) -> Result<(), String> {
132+
fn visit_mod_from_ast(&mut self, module: &'ast ast::Mod) -> Result<(), ModuleResolutionError> {
112133
for item in &module.items {
113134
if is_cfg_if(item) {
114135
self.visit_cfg_if(Cow::Borrowed(item))?;
@@ -125,7 +146,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
125146
&mut self,
126147
item: &'c ast::Item,
127148
sub_mod: Cow<'ast, ast::Mod>,
128-
) -> Result<(), String> {
149+
) -> Result<(), ModuleResolutionError> {
129150
let old_directory = self.directory.clone();
130151
let sub_mod_kind = self.peek_sub_mod(item, &sub_mod)?;
131152
if let Some(sub_mod_kind) = sub_mod_kind {
@@ -141,7 +162,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
141162
&self,
142163
item: &'c ast::Item,
143164
sub_mod: &Cow<'ast, ast::Mod>,
144-
) -> Result<Option<SubModKind<'c, 'ast>>, String> {
165+
) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> {
145166
if contains_skip(&item.attrs) {
146167
return Ok(None);
147168
}
@@ -165,7 +186,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
165186
&mut self,
166187
sub_mod_kind: SubModKind<'c, 'ast>,
167188
_sub_mod: Cow<'ast, ast::Mod>,
168-
) -> Result<(), String> {
189+
) -> Result<(), ModuleResolutionError> {
169190
match sub_mod_kind {
170191
SubModKind::External(mod_path, _, sub_mod) => {
171192
self.file_map
@@ -188,7 +209,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
188209
&mut self,
189210
sub_mod: Cow<'ast, ast::Mod>,
190211
sub_mod_kind: SubModKind<'c, 'ast>,
191-
) -> Result<(), String> {
212+
) -> Result<(), ModuleResolutionError> {
192213
match sub_mod_kind {
193214
SubModKind::External(mod_path, directory_ownership, sub_mod) => {
194215
let directory = Directory {
@@ -226,7 +247,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
226247
&mut self,
227248
sub_mod: Cow<'ast, ast::Mod>,
228249
directory: Option<Directory>,
229-
) -> Result<(), String> {
250+
) -> Result<(), ModuleResolutionError> {
230251
if let Some(directory) = directory {
231252
self.directory = directory;
232253
}
@@ -242,7 +263,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
242263
mod_name: symbol::Ident,
243264
attrs: &[ast::Attribute],
244265
sub_mod: &Cow<'ast, ast::Mod>,
245-
) -> Result<Option<SubModKind<'c, 'ast>>, String> {
266+
) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> {
246267
let relative = match self.directory.ownership {
247268
DirectoryOwnership::Owned { relative } => relative,
248269
DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
@@ -252,16 +273,20 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
252273
return Ok(None);
253274
}
254275
return match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.inner) {
255-
Some((_, ref attrs)) if contains_skip(attrs) => Ok(None),
256-
Some((m, _)) => Ok(Some(SubModKind::External(
276+
Ok((_, ref attrs)) if contains_skip(attrs) => Ok(None),
277+
Ok((m, _)) => Ok(Some(SubModKind::External(
257278
path,
258279
DirectoryOwnership::Owned { relative: None },
259280
Cow::Owned(m),
260281
))),
261-
None => Err(format!(
262-
"Failed to find module {} in {:?} {:?}",
263-
mod_name, self.directory.path, relative,
264-
)),
282+
Err(ParserError::ParseError) => Err(ModuleResolutionError {
283+
module: mod_name.to_string(),
284+
kind: ModuleResolutionErrorKind::ParseError { file: path },
285+
}),
286+
Err(..) => Err(ModuleResolutionError {
287+
module: mod_name.to_string(),
288+
kind: ModuleResolutionErrorKind::NotFound { file: path },
289+
}),
265290
};
266291
}
267292

@@ -291,22 +316,26 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
291316
}
292317
}
293318
match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.inner) {
294-
Some((_, ref attrs)) if contains_skip(attrs) => Ok(None),
295-
Some((m, _)) if outside_mods_empty => {
319+
Ok((_, ref attrs)) if contains_skip(attrs) => Ok(None),
320+
Ok((m, _)) if outside_mods_empty => {
296321
Ok(Some(SubModKind::External(path, ownership, Cow::Owned(m))))
297322
}
298-
Some((m, _)) => {
323+
Ok((m, _)) => {
299324
mods_outside_ast.push((path.clone(), ownership, Cow::Owned(m)));
300325
if should_insert {
301326
mods_outside_ast.push((path, ownership, sub_mod.clone()));
302327
}
303328
Ok(Some(SubModKind::MultiExternal(mods_outside_ast)))
304329
}
305-
None if outside_mods_empty => Err(format!(
306-
"Failed to find module {} in {:?} {:?}",
307-
mod_name, self.directory.path, relative,
308-
)),
309-
None => {
330+
Err(ParserError::ParseError) => Err(ModuleResolutionError {
331+
module: mod_name.to_string(),
332+
kind: ModuleResolutionErrorKind::ParseError { file: path },
333+
}),
334+
Err(..) if outside_mods_empty => Err(ModuleResolutionError {
335+
module: mod_name.to_string(),
336+
kind: ModuleResolutionErrorKind::NotFound { file: path },
337+
}),
338+
Err(..) => {
310339
if should_insert {
311340
mods_outside_ast.push((path, ownership, sub_mod.clone()));
312341
}
@@ -320,10 +349,12 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
320349
}
321350
Err(mut e) => {
322351
e.cancel();
323-
Err(format!(
324-
"Failed to find module {} in {:?} {:?}",
325-
mod_name, self.directory.path, relative,
326-
))
352+
Err(ModuleResolutionError {
353+
module: mod_name.to_string(),
354+
kind: ModuleResolutionErrorKind::NotFound {
355+
file: self.directory.path.clone(),
356+
},
357+
})
327358
}
328359
}
329360
}
@@ -379,9 +410,9 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
379410
}
380411
let m = match Parser::parse_file_as_module(self.parse_sess, &actual_path, sub_mod.inner)
381412
{
382-
Some((_, ref attrs)) if contains_skip(attrs) => continue,
383-
Some((m, _)) => m,
384-
None => continue,
413+
Ok((_, ref attrs)) if contains_skip(attrs) => continue,
414+
Ok((m, _)) => m,
415+
Err(..) => continue,
385416
};
386417

387418
result.push((

src/syntux/parser.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ impl<'a> Parser<'a> {
106106
sess: &'a ParseSess,
107107
path: &Path,
108108
span: Span,
109-
) -> Option<(ast::Mod, Vec<ast::Attribute>)> {
109+
) -> Result<(ast::Mod, Vec<ast::Attribute>), ParserError> {
110110
let result = catch_unwind(AssertUnwindSafe(|| {
111111
let mut parser = new_parser_from_file(sess.inner(), &path, Some(span));
112112
match parser.parse_mod(&TokenKind::Eof) {
@@ -119,8 +119,10 @@ impl<'a> Parser<'a> {
119119
}
120120
}));
121121
match result {
122-
Ok(Some(m)) => Some(m),
123-
_ => None,
122+
Ok(Some(m)) => Ok(m),
123+
Ok(None) => Err(ParserError::ParseError),
124+
Err(..) if path.exists() => Err(ParserError::ParseError),
125+
Err(_) => Err(ParserError::ParsePanicError),
124126
}
125127
}
126128

src/test/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const SKIP_FILE_WHITE_LIST: &[&str] = &[
4040
"cfg_mod/bar.rs",
4141
"cfg_mod/foo.rs",
4242
"cfg_mod/wasm32.rs",
43+
"skip/foo.rs",
4344
];
4445

4546
fn init_log() {

tests/target/skip/foo.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![rustfmt::skip]
2+
3+
fn
4+
foo()
5+
{}

tests/target/skip/main.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod foo;
2+
3+
fn main() {
4+
println!("Hello, world!");
5+
}

0 commit comments

Comments
 (0)