Skip to content
Open
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
1 change: 1 addition & 0 deletions compiler/rustc_builtin_macros/src/compile_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(crate) fn expand_compile_error<'cx>(
#[expect(rustc::diagnostic_outside_of_impl, reason = "diagnostic message is specified by user")]
#[expect(rustc::untranslatable_diagnostic, reason = "diagnostic message is specified by user")]
let guar = cx.dcx().span_err(sp, var.to_string());
cx.resolver.mark_scope_with_compile_error(cx.current_expansion.lint_node_id);

ExpandResult::Ready(DummyResult::any(sp, guar))
}
4 changes: 4 additions & 0 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,10 @@ pub trait ResolverExpand {
/// Record the name of an opaque `Ty::ImplTrait` pre-expansion so that it can be used
/// to generate an item name later that does not reference placeholder macros.
fn insert_impl_trait_name(&mut self, id: NodeId, name: Symbol);

/// Mark the scope as having a compile error so that error for lookup in this scope
/// should be suppressed
fn mark_scope_with_compile_error(&mut self, parent_node: NodeId);
}

pub trait LintStoreExpand {
Expand Down
28 changes: 23 additions & 5 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1718,7 +1718,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
diag_metadata: Option<&DiagMetadata<'_>>,
) -> PathResult<'ra> {
let mut module = None;
let mut module_had_parse_errors = false;
let mut module_had_parse_errors = !self.mods_with_parse_errors.is_empty()
&& self.mods_with_parse_errors.contains(&parent_scope.module.nearest_parent_mod());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary?
The nearest_parent_mod() part doesn't look right, and I'm not sure what effect this has in general, because a similar check already exists below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when looking for foo::bar::baz() the test bellow only starts when checking for bar in foo, but without this test here, we would still get the errors if foo is not found and that the current module contains a compile_error!

Copy link
Contributor

@petrochenkov petrochenkov Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I dislike random diagnostic changes like this, the added logic partially repeats a piece of logic that normally happens in an entirely different function (resolve_ident_in_lexical_scope/resolve_ident_in_scope_set).

Also, foo may be "not found" in multiple other locations, not just the current module (local variables, unnamed blocks, various preludes).
I guess it's ok to suppress the error if a poisoned module was found in at least one of those location during the search.
And parent_scope.module.nearest_parent_mod() will always be in the list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your review.

Indeed, the function scope are not added to the poisoned modules. (If I use invalid symbols in error_in_this_function, the error is not suppressed as it should)

I am not very familiar with the rustc codebase, so let me know if there is a way to solve that. (Adding non-module NodeId in the set?)

I was just re-using the existing module_had_parse_errors set, but maybe there is a better option?

I tried to change a bit the logic to be more robust, but I'm not sure what logic you mean is repeated.

let mut allow_super = true;
let mut second_binding = None;

Expand Down Expand Up @@ -1834,6 +1835,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}

let binding = if let Some(module) = module {
if !self.mods_with_parse_errors.is_empty()
&& let ModuleOrUniformRoot::Module(m) = module
&& m.res()
.and_then(|r| r.module_like_def_id())
.is_some_and(|def_id| self.mods_with_parse_errors.contains(&def_id))
{
module_had_parse_errors = true;
}
self.reborrow().resolve_ident_in_module(
module,
ident,
Expand Down Expand Up @@ -1866,7 +1875,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
path.len() - 1,
));
}
_ => Err(Determinacy::determined(finalize.is_some())),
_ => {
for rib in &ribs[ns] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just keep the previous version, it's better to have something simpler, than to copy even more logic from resolve_ident_in_(scope_set,lexical_scope) to here.

if let RibKind::Module(module) = rib.kind
&& module.res().and_then(|r| r.module_like_def_id()).is_some_and(
|def_id| self.mods_with_parse_errors.contains(&def_id),
)
{
module_had_parse_errors = true;
break;
}
}
Err(Determinacy::determined(finalize.is_some()))
}
}
} else {
self.reborrow().resolve_ident_in_scope_set(
Expand Down Expand Up @@ -1903,9 +1924,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {

let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
if let Some(def_id) = binding.res().module_like_def_id() {
if self.mods_with_parse_errors.contains(&def_id) {
module_had_parse_errors = true;
}
module = Some(ModuleOrUniformRoot::Module(self.expect_module(def_id)));
record_segment_res(self.reborrow(), finalize, res, id);
} else if res == Res::ToolMod && !is_last && opt_ns.is_some() {
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
self.invocation_parents[&id].parent_def
}

fn mark_scope_with_compile_error(&mut self, id: NodeId) {
if let Some(id) = self.opt_local_def_id(id)
&& self.tcx.def_kind(id).is_module_like()
{
self.mods_with_parse_errors.insert(id.to_def_id());
}
}

fn resolve_dollar_crates(&self) {
hygiene::update_dollar_crate_names(|ctxt| {
let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
Expand Down
40 changes: 40 additions & 0 deletions tests/ui/macros/compile_error_macro-suppress-errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
pub mod some_module {
compile_error!("Error in a module"); //~ ERROR: Error in a module

fn abc() -> Hello {
let _: self::SomeType = self::Hello::new();
let _: SomeType = Hello::new();
}

mod inner_module {
use super::Hello;
use crate::another_module::NotExist; //~ ERROR: unresolved import `crate::another_module::NotExist`
use crate::some_module::World;
struct Foo {
bar: super::Xyz,
error: self::MissingType, //~ ERROR: cannot find type `MissingType` in module `self`
}
}
}

pub mod another_module {
use crate::some_module::NotExist;
fn error_in_this_function() {
compile_error!("Error in a function"); //~ ERROR: Error in a function
}
}

fn main() {
// these errors are suppressed because of the compile_error! macro

let _ = some_module::some_function();
let _: some_module::SomeType = some_module::Hello::new();

// these errors are not suppressed

let _ = another_module::some_function();
//~^ ERROR: cannot find function `some_function` in module `another_module`
let _: another_module::SomeType = another_module::Hello::new();
//~^ ERROR: cannot find type `SomeType` in module `another_module`
//~^^ ERROR: failed to resolve: could not find `Hello` in `another_module`
}
46 changes: 46 additions & 0 deletions tests/ui/macros/compile_error_macro-suppress-errors.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
error: Error in a module
--> $DIR/compile_error_macro-suppress-errors.rs:2:5
|
LL | compile_error!("Error in a module");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: Error in a function
--> $DIR/compile_error_macro-suppress-errors.rs:23:9
|
LL | compile_error!("Error in a function");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0432]: unresolved import `crate::another_module::NotExist`
--> $DIR/compile_error_macro-suppress-errors.rs:11:13
|
LL | use crate::another_module::NotExist;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `NotExist` in `another_module`

error[E0433]: failed to resolve: could not find `Hello` in `another_module`
--> $DIR/compile_error_macro-suppress-errors.rs:37:55
|
LL | let _: another_module::SomeType = another_module::Hello::new();
| ^^^^^ could not find `Hello` in `another_module`

error[E0425]: cannot find type `MissingType` in module `self`
--> $DIR/compile_error_macro-suppress-errors.rs:15:26
|
LL | error: self::MissingType,
| ^^^^^^^^^^^ not found in `self`

error[E0425]: cannot find function `some_function` in module `another_module`
--> $DIR/compile_error_macro-suppress-errors.rs:35:29
|
LL | let _ = another_module::some_function();
| ^^^^^^^^^^^^^ not found in `another_module`

error[E0425]: cannot find type `SomeType` in module `another_module`
--> $DIR/compile_error_macro-suppress-errors.rs:37:28
|
LL | let _: another_module::SomeType = another_module::Hello::new();
| ^^^^^^^^ not found in `another_module`

error: aborting due to 7 previous errors

Some errors have detailed explanations: E0425, E0432, E0433.
For more information about an error, try `rustc --explain E0425`.
Loading