Skip to content

Commit 1ea10c2

Browse files
committed
resolve: Report more early resolution ambiguities for imports
The new ambiguities are reported when the import's visibility is ambiguous and may depend on the resolution/expansion order.
1 parent 29e035e commit 1ea10c2

16 files changed

+276
-25
lines changed

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ declare_lint_pass! {
2121
AMBIGUOUS_ASSOCIATED_ITEMS,
2222
AMBIGUOUS_GLOB_IMPORTS,
2323
AMBIGUOUS_GLOB_REEXPORTS,
24+
AMBIGUOUS_IMPORT_VISIBILITIES,
2425
ARITHMETIC_OVERFLOW,
2526
ASM_SUB_REGISTER,
2627
BAD_ASM_STYLE,
@@ -4488,6 +4489,55 @@ declare_lint! {
44884489
};
44894490
}
44904491

4492+
declare_lint! {
4493+
/// The `ambiguous_import_visibilities` lint detects imports that should report ambiguity
4494+
/// errors, but previously didn't do that due to rustc bugs.
4495+
///
4496+
/// ### Example
4497+
///
4498+
/// ```rust,compile_fail
4499+
/// #![deny(ambiguous_import_visibilities)]
4500+
/// mod reexport {
4501+
/// mod m {
4502+
/// pub struct S {}
4503+
/// }
4504+
///
4505+
/// macro_rules! mac {
4506+
/// () => { use m::S; }
4507+
/// }
4508+
///
4509+
/// pub use m::*;
4510+
/// mac!();
4511+
///
4512+
/// pub use S as Z;
4513+
/// }
4514+
///
4515+
/// fn main() {
4516+
/// reexport::Z {}; // ok, no privacy error
4517+
/// }
4518+
/// ```
4519+
///
4520+
/// {{produces}}
4521+
///
4522+
/// ### Explanation
4523+
///
4524+
/// Previous versions of Rust compile it successfully because it
4525+
/// fetched the glob import's visibility for `pub use S as Z` import, and ignored the private
4526+
/// `use m::S` import that appeared later.
4527+
///
4528+
/// This is a [future-incompatible] lint to transition this to a
4529+
/// hard error in the future.
4530+
///
4531+
/// [future-incompatible]: ../index.md#future-incompatible-lints
4532+
pub AMBIGUOUS_IMPORT_VISIBILITIES,
4533+
Deny,
4534+
"detects certain glob imports that require reporting an ambiguity error",
4535+
@future_incompatible = FutureIncompatibleInfo {
4536+
reason: FutureIncompatibilityReason::FutureReleaseError,
4537+
reference: "issue #149145 <https://github.com/rust-lang/rust/issues/149145>",
4538+
};
4539+
}
4540+
44914541
declare_lint! {
44924542
/// The `refining_impl_trait_reachable` lint detects `impl Trait` return
44934543
/// types in method signatures that are refined by a publically reachable

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,12 @@ impl<Id: Into<DefId>> Visibility<Id> {
380380
}
381381
}
382382

383+
impl<Id: Into<DefId> + Copy> Visibility<Id> {
384+
pub fn min(self, vis: Visibility<Id>, tcx: TyCtxt<'_>) -> Visibility<Id> {
385+
if self.is_at_least(vis, tcx) { vis } else { self }
386+
}
387+
}
388+
383389
impl Visibility<DefId> {
384390
pub fn expect_local(self) -> Visibility {
385391
self.map_id(|id| id.expect_local())

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use rustc_middle::ty::TyCtxt;
2424
use rustc_session::Session;
2525
use rustc_session::lint::BuiltinLintDiag;
2626
use rustc_session::lint::builtin::{
27-
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS,
27+
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES,
2828
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
2929
};
3030
use rustc_session::utils::was_invoked_from_cargo;
@@ -148,12 +148,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
148148
let diag = self.ambiguity_diagnostic(ambiguity_error);
149149

150150
if ambiguity_error.warning {
151-
let NameBindingKind::Import { import, .. } = ambiguity_error.b1.0.kind else {
152-
unreachable!()
151+
let node_id = match ambiguity_error.b1.0.kind {
152+
NameBindingKind::Import { import, .. } => import.root_id,
153+
NameBindingKind::Res(_) => CRATE_NODE_ID,
153154
};
154155
self.lint_buffer.buffer_lint(
155-
AMBIGUOUS_GLOB_IMPORTS,
156-
import.root_id,
156+
if ambiguity_error.ambig_vis.is_some() {
157+
AMBIGUOUS_IMPORT_VISIBILITIES
158+
} else {
159+
AMBIGUOUS_GLOB_IMPORTS
160+
},
161+
node_id,
157162
diag.ident.span,
158163
diag,
159164
);
@@ -1995,7 +2000,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
19952000
}
19962001

19972002
fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors::Ambiguity {
1998-
let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error;
2003+
let AmbiguityError { kind, ambig_vis, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error;
19992004
let extern_prelude_ambiguity = || {
20002005
self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)).is_some_and(|entry| {
20012006
entry.item_binding.map(|(b, _)| b) == Some(b1)
@@ -2051,8 +2056,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
20512056
let (b1_note, b1_help_msgs) = could_refer_to(b1, misc1, "");
20522057
let (b2_note, b2_help_msgs) = could_refer_to(b2, misc2, " also");
20532058

2059+
let ambig_vis = ambig_vis.map(|(vis1, vis2)| {
2060+
format!(
2061+
"{} or {}",
2062+
vis1.to_string(CRATE_DEF_ID, self.tcx),
2063+
vis2.to_string(CRATE_DEF_ID, self.tcx)
2064+
)
2065+
});
2066+
20542067
errors::Ambiguity {
20552068
ident,
2069+
ambig_vis,
20562070
kind: kind.descr(),
20572071
b1_note,
20582072
b1_help_msgs,

compiler/rustc_resolve/src/errors.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,7 @@ pub(crate) struct UnknownDiagnosticAttributeTypoSugg {
14501450
// FIXME: Make this properly translatable.
14511451
pub(crate) struct Ambiguity {
14521452
pub ident: Ident,
1453+
pub ambig_vis: Option<String>,
14531454
pub kind: &'static str,
14541455
pub b1_note: Spanned<String>,
14551456
pub b1_help_msgs: Vec<String>,
@@ -1459,8 +1460,12 @@ pub(crate) struct Ambiguity {
14591460

14601461
impl Ambiguity {
14611462
fn decorate<'a>(self, diag: &mut Diag<'a, impl EmissionGuarantee>) {
1462-
diag.primary_message(format!("`{}` is ambiguous", self.ident));
1463-
diag.span_label(self.ident.span, "ambiguous name");
1463+
if let Some(ambig_vis) = self.ambig_vis {
1464+
diag.primary_message(format!("ambiguous import visibility: {ambig_vis}"));
1465+
} else {
1466+
diag.primary_message(format!("`{}` is ambiguous", self.ident));
1467+
diag.span_label(self.ident.span, "ambiguous name");
1468+
}
14641469
diag.note(format!("ambiguous because of {}", self.kind));
14651470
diag.span_note(self.b1_note.span, self.b1_note.node);
14661471
for help_msg in self.b1_help_msgs {

compiler/rustc_resolve/src/ident.rs

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use Namespace::*;
55
use rustc_ast::{self as ast, NodeId};
66
use rustc_errors::ErrorGuaranteed;
77
use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS};
8+
use rustc_middle::ty::Visibility;
89
use rustc_middle::{bug, span_bug};
910
use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
1011
use rustc_session::parse::feature_err;
@@ -469,9 +470,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
469470
// We do not need to report them if we are either in speculative resolution,
470471
// or in late resolution when everything is already imported and expanded
471472
// and no ambiguities exist.
472-
if matches!(finalize, None | Some(Finalize { stage: Stage::Late, .. })) {
473-
return ControlFlow::Break(Ok(binding));
474-
}
473+
let import_vis = match finalize {
474+
None | Some(Finalize { stage: Stage::Late, .. }) => {
475+
return ControlFlow::Break(Ok(binding));
476+
}
477+
Some(Finalize { import_vis, .. }) => import_vis,
478+
};
475479

476480
if let Some((innermost_binding, innermost_flags)) = innermost_result {
477481
// Found another solution, if the first one was "weak", report an error.
@@ -482,6 +486,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
482486
innermost_binding,
483487
flags,
484488
innermost_flags,
489+
import_vis,
485490
extern_prelude_item_binding,
486491
extern_prelude_flag_binding,
487492
) {
@@ -730,11 +735,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
730735
innermost_binding: NameBinding<'ra>,
731736
flags: Flags,
732737
innermost_flags: Flags,
738+
import_vis: Option<Visibility>,
733739
extern_prelude_item_binding: Option<NameBinding<'ra>>,
734740
extern_prelude_flag_binding: Option<NameBinding<'ra>>,
735741
) -> bool {
736742
let (res, innermost_res) = (binding.res(), innermost_binding.res());
737-
if res == innermost_res {
743+
let ambig_vis = self.ambig_vis(import_vis, binding, innermost_binding);
744+
if res == innermost_res && ambig_vis.is_none() {
738745
return false;
739746
}
740747

@@ -793,10 +800,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
793800
};
794801
self.ambiguity_errors.push(AmbiguityError {
795802
kind,
803+
ambig_vis,
796804
ident: orig_ident,
797805
b1: innermost_binding,
798806
b2: binding,
799-
warning: false,
807+
warning: ambig_vis.is_some(),
800808
misc1: misc(innermost_flags),
801809
misc2: misc(flags),
802810
});
@@ -1207,17 +1215,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
12071215
&& shadowing == Shadowing::Restricted
12081216
&& finalize.stage == Stage::Early
12091217
&& binding.expansion != LocalExpnId::ROOT
1210-
&& binding.res() != shadowed_glob.res()
12111218
{
1212-
self.ambiguity_errors.push(AmbiguityError {
1213-
kind: AmbiguityKind::GlobVsExpanded,
1214-
ident,
1215-
b1: binding,
1216-
b2: shadowed_glob,
1217-
warning: false,
1218-
misc1: AmbiguityErrorMisc::None,
1219-
misc2: AmbiguityErrorMisc::None,
1220-
});
1219+
let ambig_vis = self.ambig_vis(finalize.import_vis, shadowed_glob, binding);
1220+
if shadowed_glob.res() != binding.res() || ambig_vis.is_some() {
1221+
self.ambiguity_errors.push(AmbiguityError {
1222+
kind: AmbiguityKind::GlobVsExpanded,
1223+
ambig_vis,
1224+
ident,
1225+
b1: binding,
1226+
b2: shadowed_glob,
1227+
warning: ambig_vis.is_some(),
1228+
misc1: AmbiguityErrorMisc::None,
1229+
misc2: AmbiguityErrorMisc::None,
1230+
});
1231+
}
12211232
}
12221233

12231234
if shadowing == Shadowing::Unrestricted
@@ -1324,6 +1335,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
13241335
false
13251336
}
13261337

1338+
fn ambig_vis(
1339+
&self,
1340+
import_vis: Option<Visibility>,
1341+
binding: NameBinding<'ra>,
1342+
innermost_binding: NameBinding<'ra>,
1343+
) -> Option<(Visibility, Visibility)> {
1344+
match import_vis {
1345+
Some(import_vis) if binding.res() == innermost_binding.res() => {
1346+
let min =
1347+
|b: NameBinding<'_>| b.vis.min(import_vis.to_def_id(), self.tcx).expect_local();
1348+
let (min1, min2) = (min(binding), min(innermost_binding));
1349+
(min1 != min2).then_some((min1, min2))
1350+
}
1351+
_ => None,
1352+
}
1353+
}
1354+
13271355
/// Validate a local resolution (from ribs).
13281356
#[instrument(level = "debug", skip(self, all_ribs))]
13291357
fn validate_res_from_ribs(

compiler/rustc_resolve/src/imports.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
11361136
ident,
11371137
ns,
11381138
&import.parent_scope,
1139-
Some(Finalize { report_private: false, ..finalize }),
1139+
Some(Finalize {
1140+
report_private: false,
1141+
import_vis: Some(import.vis),
1142+
..finalize
1143+
}),
11401144
bindings[ns].get().binding(),
11411145
Some(import),
11421146
);

compiler/rustc_resolve/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ enum AmbiguityErrorMisc {
904904

905905
struct AmbiguityError<'ra> {
906906
kind: AmbiguityKind,
907+
ambig_vis: Option<(Visibility, Visibility)>,
907908
ident: Ident,
908909
b1: NameBinding<'ra>,
909910
b2: NameBinding<'ra>,
@@ -2032,6 +2033,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
20322033
if let Some((b2, kind)) = used_binding.ambiguity {
20332034
let ambiguity_error = AmbiguityError {
20342035
kind,
2036+
ambig_vis: None,
20352037
ident,
20362038
b1: used_binding,
20372039
b2,
@@ -2502,6 +2504,8 @@ struct Finalize {
25022504
used: Used = Used::Other,
25032505
/// Finalizing early or late resolution.
25042506
stage: Stage = Stage::Early,
2507+
/// Nominal visibility of the import item, in case we are resolving and import's final segment.
2508+
import_vis: Option<Visibility> = None,
25052509
}
25062510

25072511
impl Finalize {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ edition:2018
2+
//@ proc-macro: same-res-ambigious-extern-macro.rs
3+
4+
macro_rules! globbing{
5+
() => {
6+
pub use same_res_ambigious_extern_macro::*;
7+
}
8+
}
9+
10+
#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility
11+
extern crate same_res_ambigious_extern_macro;
12+
globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility
13+
14+
pub trait RustEmbed {}
15+
16+
pub use RustEmbed as Embed; //~ ERROR ambiguous import visibility
17+
//~| WARN this was previously accepted
18+
fn main() {}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error: ambiguous import visibility: pub(crate) or pub
2+
--> $DIR/ambiguous-import-visibility-macro.rs:16:9
3+
|
4+
LL | pub use RustEmbed as Embed;
5+
| ^^^^^^^^^
6+
|
7+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8+
= note: for more information, see issue #149145 <https://github.com/rust-lang/rust/issues/149145>
9+
= note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution
10+
note: `RustEmbed` could refer to the derive macro imported here
11+
--> $DIR/ambiguous-import-visibility-macro.rs:6:17
12+
|
13+
LL | pub use same_res_ambigious_extern_macro::*;
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
...
16+
LL | globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility
17+
| ------------ in this macro invocation
18+
= help: consider adding an explicit import of `RustEmbed` to disambiguate
19+
= help: or use `crate::RustEmbed` to refer to this derive macro unambiguously
20+
note: `RustEmbed` could also refer to the derive macro imported here
21+
--> $DIR/ambiguous-import-visibility-macro.rs:10:1
22+
|
23+
LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility
24+
| ^^^^^^^^^^^^
25+
= note: `#[deny(ambiguous_import_visibilities)]` (part of `#[deny(future_incompatible)]`) on by default
26+
= note: this error originates in the macro `globbing` (in Nightly builds, run with -Z macro-backtrace for more info)
27+
28+
error: aborting due to 1 previous error
29+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ edition:2018..
2+
3+
mod reexport {
4+
mod m {
5+
pub struct S {}
6+
}
7+
8+
macro_rules! mac {
9+
() => {
10+
use m::S;
11+
};
12+
}
13+
14+
pub use m::*;
15+
mac!();
16+
17+
pub use S as Z; //~ ERROR ambiguous import visibility
18+
//~| WARN this was previously accepted
19+
}
20+
21+
fn main() {
22+
reexport::Z {};
23+
}

0 commit comments

Comments
 (0)