Skip to content

Commit c181026

Browse files
authored
Rollup merge of rust-lang#133937 - estebank:silence-resolve-errors-from-mod-with-parse-errors, r=davidtwco
Keep track of parse errors in `mod`s and don't emit resolve errors for paths involving them When we expand a `mod foo;` and parse `foo.rs`, we now track whether that file had an unrecovered parse error that reached the end of the file. If so, we keep that information around in the HIR and mark its `DefId` in the `Resolver`. When resolving a path like `foo::bar`, we do not emit any errors for "`bar` not found in `foo`", as we know that the parse error might have caused `bar` to not be parsed and accounted for. When this happens in an existing project, every path referencing `foo` would be an irrelevant compile error. Instead, we now skip emitting anything until `foo.rs` is fixed. Tellingly enough, we didn't have any test for errors caused by expansion of `mod`s with parse errors. Fix rust-lang#97734.
2 parents ab0d792 + 27420c6 commit c181026

File tree

28 files changed

+178
-85
lines changed

28 files changed

+178
-85
lines changed

compiler/rustc_ast/src/ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2877,7 +2877,7 @@ pub enum ModKind {
28772877
/// or with definition outlined to a separate file `mod foo;` and already loaded from it.
28782878
/// The inner span is from the first token past `{` to the last token until `}`,
28792879
/// or from the first to the last token in the loaded file.
2880-
Loaded(ThinVec<P<Item>>, Inline, ModSpans),
2880+
Loaded(ThinVec<P<Item>>, Inline, ModSpans, Result<(), ErrorGuaranteed>),
28812881
/// Module with definition outlined to a separate file `mod foo;` but not yet loaded from it.
28822882
Unloaded,
28832883
}

compiler/rustc_ast/src/mut_visit.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,12 @@ impl WalkItemKind for ItemKind {
12121212
ItemKind::Mod(safety, mod_kind) => {
12131213
visit_safety(vis, safety);
12141214
match mod_kind {
1215-
ModKind::Loaded(items, _inline, ModSpans { inner_span, inject_use_span }) => {
1215+
ModKind::Loaded(
1216+
items,
1217+
_inline,
1218+
ModSpans { inner_span, inject_use_span },
1219+
_,
1220+
) => {
12161221
items.flat_map_in_place(|item| vis.flat_map_item(item));
12171222
vis.visit_span(inner_span);
12181223
vis.visit_span(inject_use_span);

compiler/rustc_ast/src/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ impl WalkItemKind for ItemKind {
380380
try_visit!(visitor.visit_fn(kind, span, id));
381381
}
382382
ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
383-
ModKind::Loaded(items, _inline, _inner_span) => {
383+
ModKind::Loaded(items, _inline, _inner_span, _) => {
384384
walk_list!(visitor, visit_item, items);
385385
}
386386
ModKind::Unloaded => {}

compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
238238
})
239239
}
240240
ItemKind::Mod(_, mod_kind) => match mod_kind {
241-
ModKind::Loaded(items, _, spans) => {
241+
ModKind::Loaded(items, _, spans, _) => {
242242
hir::ItemKind::Mod(self.lower_mod(items, spans))
243243
}
244244
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),

compiler/rustc_ast_passes/src/ast_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10291029
self.dcx().emit_err(errors::UnsafeItem { span, kind: "module" });
10301030
}
10311031
// Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
1032-
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _))
1032+
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _))
10331033
&& !attr::contains_name(&item.attrs, sym::path)
10341034
{
10351035
self.check_mod_file_item_asciionly(item.ident);

compiler/rustc_builtin_macros/src/test_harness.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,10 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
141141

142142
// We don't want to recurse into anything other than mods, since
143143
// mods or tests inside of functions will break things
144-
if let ast::ItemKind::Mod(_, ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. })) =
145-
item.kind
144+
if let ast::ItemKind::Mod(
145+
_,
146+
ModKind::Loaded(.., ast::ModSpans { inner_span: span, .. }, _),
147+
) = item.kind
146148
{
147149
let prev_tests = mem::take(&mut self.tests);
148150
walk_item_kind(

compiler/rustc_expand/src/expand.rs

+19-13
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
723723
item_inner.kind,
724724
ItemKind::Mod(
725725
_,
726-
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _),
726+
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _),
727727
)
728728
) =>
729729
{
@@ -889,7 +889,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
889889
fn visit_item(&mut self, item: &'ast ast::Item) {
890890
match &item.kind {
891891
ItemKind::Mod(_, mod_kind)
892-
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) =>
892+
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) =>
893893
{
894894
feature_err(
895895
self.sess,
@@ -1195,7 +1195,7 @@ impl InvocationCollectorNode for P<ast::Item> {
11951195

11961196
let ecx = &mut collector.cx;
11971197
let (file_path, dir_path, dir_ownership) = match mod_kind {
1198-
ModKind::Loaded(_, inline, _) => {
1198+
ModKind::Loaded(_, inline, _, _) => {
11991199
// Inline `mod foo { ... }`, but we still need to push directories.
12001200
let (dir_path, dir_ownership) = mod_dir_path(
12011201
ecx.sess,
@@ -1217,15 +1217,21 @@ impl InvocationCollectorNode for P<ast::Item> {
12171217
ModKind::Unloaded => {
12181218
// We have an outline `mod foo;` so we need to parse the file.
12191219
let old_attrs_len = attrs.len();
1220-
let ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership } =
1221-
parse_external_mod(
1222-
ecx.sess,
1223-
ident,
1224-
span,
1225-
&ecx.current_expansion.module,
1226-
ecx.current_expansion.dir_ownership,
1227-
&mut attrs,
1228-
);
1220+
let ParsedExternalMod {
1221+
items,
1222+
spans,
1223+
file_path,
1224+
dir_path,
1225+
dir_ownership,
1226+
had_parse_error,
1227+
} = parse_external_mod(
1228+
ecx.sess,
1229+
ident,
1230+
span,
1231+
&ecx.current_expansion.module,
1232+
ecx.current_expansion.dir_ownership,
1233+
&mut attrs,
1234+
);
12291235

12301236
if let Some(lint_store) = ecx.lint_store {
12311237
lint_store.pre_expansion_lint(
@@ -1239,7 +1245,7 @@ impl InvocationCollectorNode for P<ast::Item> {
12391245
);
12401246
}
12411247

1242-
*mod_kind = ModKind::Loaded(items, Inline::No, spans);
1248+
*mod_kind = ModKind::Loaded(items, Inline::No, spans, had_parse_error);
12431249
node.attrs = attrs;
12441250
if node.attrs.len() > old_attrs_len {
12451251
// If we loaded an out-of-line module and added some inner attributes,

compiler/rustc_expand/src/module.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub(crate) struct ParsedExternalMod {
3737
pub file_path: PathBuf,
3838
pub dir_path: PathBuf,
3939
pub dir_ownership: DirOwnership,
40+
pub had_parse_error: Result<(), ErrorGuaranteed>,
4041
}
4142

4243
pub enum ModError<'a> {
@@ -74,14 +75,17 @@ pub(crate) fn parse_external_mod(
7475
attrs.extend(inner_attrs);
7576
(items, inner_span, mp.file_path)
7677
};
78+
7779
// (1) ...instead, we return a dummy module.
78-
let (items, spans, file_path) =
79-
result.map_err(|err| err.report(sess, span)).unwrap_or_default();
80+
let ((items, spans, file_path), had_parse_error) = match result {
81+
Err(err) => (Default::default(), Err(err.report(sess, span))),
82+
Ok(result) => (result, Ok(())),
83+
};
8084

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

84-
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership }
88+
ParsedExternalMod { items, spans, file_path, dir_path, dir_ownership, had_parse_error }
8589
}
8690

8791
pub(crate) fn mod_dir_path(

compiler/rustc_lint/src/builtin.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3038,7 +3038,7 @@ impl EarlyLintPass for SpecialModuleName {
30383038
for item in &krate.items {
30393039
if let ast::ItemKind::Mod(
30403040
_,
3041-
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
3041+
ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _),
30423042
) = item.kind
30433043
{
30443044
if item.attrs.iter().any(|a| a.has_name(sym::path)) {

compiler/rustc_parse/src/parser/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<'a> Parser<'a> {
4545
let (inner_attrs, items, inner_span) =
4646
self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
4747
attrs.extend(inner_attrs);
48-
ModKind::Loaded(items, Inline::Yes, inner_span)
48+
ModKind::Loaded(items, Inline::Yes, inner_span, Ok(()))
4949
};
5050
Ok((id, ItemKind::Mod(safety, mod_kind)))
5151
}

compiler/rustc_resolve/src/build_reduced_graph.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
770770
);
771771
}
772772

773-
ItemKind::Mod(..) => {
773+
ItemKind::Mod(.., ref mod_kind) => {
774774
let module = self.r.new_module(
775775
Some(parent),
776776
ModuleKind::Def(def_kind, def_id, ident.name),
@@ -781,6 +781,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
781781
);
782782
self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
783783

784+
if let ast::ModKind::Loaded(_, _, _, Err(_)) = mod_kind {
785+
self.r.mods_with_parse_errors.insert(def_id);
786+
}
787+
784788
// Descend into the module.
785789
self.parent_scope.module = module;
786790
}

compiler/rustc_resolve/src/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3056,7 +3056,7 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
30563056

30573057
fn visit_item(&mut self, item: &'tcx ast::Item) {
30583058
if self.target_module == item.id {
3059-
if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind {
3059+
if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans, _)) = &item.kind {
30603060
let inject = mod_spans.inject_use_span;
30613061
if is_span_suitable_for_use_injection(inject) {
30623062
self.first_legal_span = Some(inject);

compiler/rustc_resolve/src/ident.rs

+53-29
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14281428
ignore_import: Option<Import<'ra>>,
14291429
) -> PathResult<'ra> {
14301430
let mut module = None;
1431+
let mut module_had_parse_errors = false;
14311432
let mut allow_super = true;
14321433
let mut second_binding = None;
14331434

@@ -1471,9 +1472,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14711472
continue;
14721473
}
14731474
}
1474-
return PathResult::failed(ident, false, finalize.is_some(), module, || {
1475-
("there are too many leading `super` keywords".to_string(), None)
1476-
});
1475+
return PathResult::failed(
1476+
ident,
1477+
false,
1478+
finalize.is_some(),
1479+
module_had_parse_errors,
1480+
module,
1481+
|| ("there are too many leading `super` keywords".to_string(), None),
1482+
);
14771483
}
14781484
if segment_idx == 0 {
14791485
if name == kw::SelfLower {
@@ -1511,19 +1517,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15111517

15121518
// Report special messages for path segment keywords in wrong positions.
15131519
if ident.is_path_segment_keyword() && segment_idx != 0 {
1514-
return PathResult::failed(ident, false, finalize.is_some(), module, || {
1515-
let name_str = if name == kw::PathRoot {
1516-
"crate root".to_string()
1517-
} else {
1518-
format!("`{name}`")
1519-
};
1520-
let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot {
1521-
format!("global paths cannot start with {name_str}")
1522-
} else {
1523-
format!("{name_str} in paths can only be used in start position")
1524-
};
1525-
(label, None)
1526-
});
1520+
return PathResult::failed(
1521+
ident,
1522+
false,
1523+
finalize.is_some(),
1524+
module_had_parse_errors,
1525+
module,
1526+
|| {
1527+
let name_str = if name == kw::PathRoot {
1528+
"crate root".to_string()
1529+
} else {
1530+
format!("`{name}`")
1531+
};
1532+
let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot {
1533+
format!("global paths cannot start with {name_str}")
1534+
} else {
1535+
format!("{name_str} in paths can only be used in start position")
1536+
};
1537+
(label, None)
1538+
},
1539+
);
15271540
}
15281541

15291542
let binding = if let Some(module) = module {
@@ -1589,6 +1602,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
15891602

15901603
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
15911604
if let Some(next_module) = binding.module() {
1605+
if self.mods_with_parse_errors.contains(&next_module.def_id()) {
1606+
module_had_parse_errors = true;
1607+
}
15921608
module = Some(ModuleOrUniformRoot::Module(next_module));
15931609
record_segment_res(self, res);
15941610
} else if res == Res::ToolMod && !is_last && opt_ns.is_some() {
@@ -1614,6 +1630,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
16141630
ident,
16151631
is_last,
16161632
finalize.is_some(),
1633+
module_had_parse_errors,
16171634
module,
16181635
|| {
16191636
let label = format!(
@@ -1637,19 +1654,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
16371654
}
16381655
}
16391656

1640-
return PathResult::failed(ident, is_last, finalize.is_some(), module, || {
1641-
self.report_path_resolution_error(
1642-
path,
1643-
opt_ns,
1644-
parent_scope,
1645-
ribs,
1646-
ignore_binding,
1647-
ignore_import,
1648-
module,
1649-
segment_idx,
1650-
ident,
1651-
)
1652-
});
1657+
return PathResult::failed(
1658+
ident,
1659+
is_last,
1660+
finalize.is_some(),
1661+
module_had_parse_errors,
1662+
module,
1663+
|| {
1664+
self.report_path_resolution_error(
1665+
path,
1666+
opt_ns,
1667+
parent_scope,
1668+
ribs,
1669+
ignore_binding,
1670+
ignore_import,
1671+
module,
1672+
segment_idx,
1673+
ident,
1674+
)
1675+
},
1676+
);
16531677
}
16541678
}
16551679
}

compiler/rustc_resolve/src/imports.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
670670

671671
fn throw_unresolved_import_error(
672672
&mut self,
673-
errors: Vec<(Import<'_>, UnresolvedImportError)>,
673+
mut errors: Vec<(Import<'_>, UnresolvedImportError)>,
674674
glob_error: bool,
675675
) {
676+
errors.retain(|(_import, err)| match err.module {
677+
// Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors.
678+
Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false,
679+
_ => true,
680+
});
676681
if errors.is_empty() {
677682
return;
678683
}
@@ -898,6 +903,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
898903
label,
899904
suggestion,
900905
module,
906+
error_implied_by_parse_error: _,
901907
} => {
902908
if no_ambiguity {
903909
assert!(import.imported_module.get().is_none());

compiler/rustc_resolve/src/late.rs

+7
Original file line numberDiff line numberDiff line change
@@ -4395,6 +4395,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
43954395
PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
43964396
PartialRes::new(module.res().unwrap())
43974397
}
4398+
// A part of this path references a `mod` that had a parse error. To avoid resolution
4399+
// errors for each reference to that module, we don't emit an error for them until the
4400+
// `mod` is fixed. this can have a significant cascade effect.
4401+
PathResult::Failed { error_implied_by_parse_error: true, .. } => {
4402+
PartialRes::new(Res::Err)
4403+
}
43984404
// In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
43994405
// don't report an error right away, but try to fallback to a primitive type.
44004406
// So, we are still able to successfully resolve something like
@@ -4443,6 +4449,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
44434449
suggestion,
44444450
module,
44454451
segment_name,
4452+
error_implied_by_parse_error: _,
44464453
} => {
44474454
return Err(respan(span, ResolutionError::FailedToResolve {
44484455
segment: Some(segment_name),

0 commit comments

Comments
 (0)