Implements a FCW lint to detect uses of ambiguously glob imported traits.

This commit is contained in:
LorrensP-2158466 2025-10-22 18:59:22 +03:00
parent 8c52f735ab
commit 3369a17e51
9 changed files with 473 additions and 19 deletions

View file

@ -4595,6 +4595,11 @@ pub struct Upvar {
pub struct TraitCandidate {
pub def_id: DefId,
pub import_ids: SmallVec<[LocalDefId; 1]>,
// Indicates whether this trait candidate is ambiguously glob imported
// in it's scope. Related to the AMBIGUOUS_GLOB_IMPORTED_TRAITS lint.
// If this is set to true and the trait is used as a result of method lookup, this
// lint is thrown.
pub lint_ambiguous: bool,
}
#[derive(Copy, Clone, Debug, HashStable_Generic)]

View file

@ -1,3 +1,4 @@
use std::fmt::Debug;
use std::ops::Deref;
use rustc_hir as hir;
@ -12,7 +13,9 @@ use rustc_hir_analysis::hir_ty_lowering::{
use rustc_infer::infer::{
BoundRegionConversionTime, DefineOpaqueTypes, InferOk, RegionVariableOrigin,
};
use rustc_lint::builtin::RESOLVING_TO_ITEMS_SHADOWING_SUPERTRAIT_ITEMS;
use rustc_lint::builtin::{
AMBIGUOUS_GLOB_IMPORTED_TRAITS, RESOLVING_TO_ITEMS_SHADOWING_SUPERTRAIT_ITEMS,
};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
@ -149,6 +152,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// Lint when an item is shadowing a supertrait item.
self.lint_shadowed_supertrait_items(pick, segment);
// Lint when a trait is ambiguously imported
self.lint_ambiguously_glob_imported_traits(pick, segment);
// Add any trait/regions obligations specified on the method's type parameters.
// We won't add these if we encountered an illegal sized bound, so that we can use
// a custom error in that case.
@ -322,7 +328,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
})
}
probe::TraitPick => {
probe::TraitPick(_) => {
let trait_def_id = pick.item.container_id(self.tcx);
// Make a trait reference `$0 : Trait<$1...$n>`
@ -716,6 +722,25 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
);
}
fn lint_ambiguously_glob_imported_traits(
&self,
pick: &probe::Pick<'_>,
segment: &hir::PathSegment<'tcx>,
) {
if pick.kind != probe::PickKind::TraitPick(true) {
return;
}
let trait_name = self.tcx.item_name(pick.item.container_id(self.tcx));
let import_span = self.tcx.hir_span_if_local(pick.import_ids[0].to_def_id()).unwrap();
self.tcx.node_lint(AMBIGUOUS_GLOB_IMPORTED_TRAITS, segment.hir_id, |diag| {
diag.primary_message(format!("Use of ambiguously glob imported trait `{trait_name}`"))
.span(segment.ident.span)
.span_label(import_span, format!("`{trait_name}` imported ambiguously here"))
.help(format!("Import `{trait_name}` explicitly"));
});
}
fn upcast(
&mut self,
source_trait_ref: ty::PolyTraitRef<'tcx>,

View file

@ -106,7 +106,7 @@ pub(crate) struct Candidate<'tcx> {
pub(crate) enum CandidateKind<'tcx> {
InherentImplCandidate { impl_def_id: DefId, receiver_steps: usize },
ObjectCandidate(ty::PolyTraitRef<'tcx>),
TraitCandidate(ty::PolyTraitRef<'tcx>),
TraitCandidate(ty::PolyTraitRef<'tcx>, bool /* lint_ambiguous */),
WhereClauseCandidate(ty::PolyTraitRef<'tcx>),
}
@ -235,7 +235,10 @@ pub(crate) struct Pick<'tcx> {
pub(crate) enum PickKind<'tcx> {
InherentImplPick,
ObjectPick,
TraitPick,
TraitPick(
// Is Ambiguously Imported
bool,
),
WhereClausePick(
// Trait
ty::PolyTraitRef<'tcx>,
@ -560,7 +563,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
probe_cx.push_candidate(
Candidate {
item,
kind: CandidateKind::TraitCandidate(ty::Binder::dummy(trait_ref)),
kind: CandidateKind::TraitCandidate(
ty::Binder::dummy(trait_ref),
false,
),
import_ids: smallvec![],
},
false,
@ -1018,6 +1024,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.assemble_extension_candidates_for_trait(
&trait_candidate.import_ids,
trait_did,
trait_candidate.lint_ambiguous,
);
}
}
@ -1029,7 +1036,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let mut duplicates = FxHashSet::default();
for trait_info in suggest::all_traits(self.tcx) {
if duplicates.insert(trait_info.def_id) {
self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id);
self.assemble_extension_candidates_for_trait(
&smallvec![],
trait_info.def_id,
false,
);
}
}
}
@ -1055,6 +1066,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&mut self,
import_ids: &SmallVec<[LocalDefId; 1]>,
trait_def_id: DefId,
lint_ambiguous: bool,
) {
let trait_args = self.fresh_args_for_item(self.span, trait_def_id);
let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, trait_args);
@ -1076,7 +1088,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
Candidate {
item,
import_ids: import_ids.clone(),
kind: TraitCandidate(bound_trait_ref),
kind: TraitCandidate(bound_trait_ref, lint_ambiguous),
},
false,
);
@ -1099,7 +1111,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
Candidate {
item,
import_ids: import_ids.clone(),
kind: TraitCandidate(ty::Binder::dummy(trait_ref)),
kind: TraitCandidate(ty::Binder::dummy(trait_ref), lint_ambiguous),
},
false,
);
@ -1842,7 +1854,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
ObjectCandidate(_) | WhereClauseCandidate(_) => {
CandidateSource::Trait(candidate.item.container_id(self.tcx))
}
TraitCandidate(trait_ref) => self.probe(|_| {
TraitCandidate(trait_ref, _) => self.probe(|_| {
let trait_ref = self.instantiate_binder_with_fresh_vars(
self.span,
BoundRegionConversionTime::FnCall,
@ -1872,7 +1884,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
fn candidate_source_from_pick(&self, pick: &Pick<'tcx>) -> CandidateSource {
match pick.kind {
InherentImplPick => CandidateSource::Impl(pick.item.container_id(self.tcx)),
ObjectPick | WhereClausePick(_) | TraitPick => {
ObjectPick | WhereClausePick(_) | TraitPick(_) => {
CandidateSource::Trait(pick.item.container_id(self.tcx))
}
}
@ -1948,7 +1960,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
impl_bounds,
));
}
TraitCandidate(poly_trait_ref) => {
TraitCandidate(poly_trait_ref, _) => {
// Some trait methods are excluded for arrays before 2021.
// (`array.into_iter()` wants a slice iterator for compatibility.)
if let Some(method_name) = self.method_name {
@ -2274,11 +2286,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
let lint_ambiguous = match probes[0].0.kind {
TraitCandidate(_, lint) => lint,
_ => false,
};
// FIXME: check the return type here somehow.
// If so, just use this trait and call it a day.
Some(Pick {
item: probes[0].0.item,
kind: TraitPick,
kind: TraitPick(lint_ambiguous),
import_ids: probes[0].0.import_ids.clone(),
autoderefs: 0,
autoref_or_ptr_adjustment: None,
@ -2348,9 +2365,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
let lint_ambiguous = match probes[0].0.kind {
TraitCandidate(_, lint) => lint,
_ => false,
};
Some(Pick {
item: child_candidate.item,
kind: TraitPick,
kind: TraitPick(lint_ambiguous),
import_ids: child_candidate.import_ids.clone(),
autoderefs: 0,
autoref_or_ptr_adjustment: None,
@ -2613,7 +2635,7 @@ impl<'tcx> Candidate<'tcx> {
kind: match self.kind {
InherentImplCandidate { .. } => InherentImplPick,
ObjectCandidate(_) => ObjectPick,
TraitCandidate(_) => TraitPick,
TraitCandidate(_, lint_ambiguous) => TraitPick(lint_ambiguous),
WhereClauseCandidate(trait_ref) => {
// Only trait derived from where-clauses should
// appear here, so they should not contain any

View file

@ -17,6 +17,7 @@ declare_lint_pass! {
AARCH64_SOFTFLOAT_NEON,
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
AMBIGUOUS_ASSOCIATED_ITEMS,
AMBIGUOUS_GLOB_IMPORTED_TRAITS,
AMBIGUOUS_GLOB_IMPORTS,
AMBIGUOUS_GLOB_REEXPORTS,
AMBIGUOUS_PANIC_IMPORTS,
@ -4473,6 +4474,60 @@ declare_lint! {
};
}
declare_lint! {
/// The `ambiguous_glob_imported_traits` lint reports uses of traits that are
/// imported ambiguously via glob imports. Previously, this was not enforced
/// due to a bug in rustc.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(ambiguous_glob_imported_traits)]
/// mod m1 {
/// pub trait Trait {
/// fn method1(&self) {}
/// }
/// impl Trait for u8 {}
/// }
/// mod m2 {
/// pub trait Trait {
/// fn method2(&self) {}
/// }
/// impl Trait for u8 {}
/// }
///
/// fn main() {
/// use m1::*;
/// use m2::*;
/// 0u8.method1();
/// 0u8.method2();
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// When multiple traits with the same name are brought into scope through glob imports,
/// one trait becomes the "primary" one while the others are shadowed. Methods from the
/// shadowed traits (e.g. `method2`) become inaccessible, while methods from the "primary"
/// trait (e.g. `method1`) still resolve. Ideally, none of the ambiguous traits would be in scope,
/// but we have to allow this for now because of backwards compatibility.
/// This lint reports uses of these "primary" traits that are ambiguous.
///
/// This is a [future-incompatible] lint to transition this to a
/// hard error in the future.
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub AMBIGUOUS_GLOB_IMPORTED_TRAITS,
Warn,
"detects uses of ambiguously glob imported traits",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #147992),
report_in_deps: false,
};
}
declare_lint! {
/// The `ambiguous_panic_imports` lint detects ambiguous core and std panic imports, but
/// previously didn't do that due to `#[macro_use]` prelude macro import.

View file

@ -622,7 +622,18 @@ struct ModuleData<'ra> {
globs: CmRefCell<Vec<Import<'ra>>>,
/// Used to memoize the traits in this module for faster searches through all traits in scope.
traits: CmRefCell<Option<Box<[(Macros20NormalizedIdent, Decl<'ra>, Option<Module<'ra>>)]>>>,
traits: CmRefCell<
Option<
Box<
[(
Macros20NormalizedIdent,
Decl<'ra>,
Option<Module<'ra>>,
bool, /* lint ambiguous */
)],
>,
>,
>,
/// Span of the module itself. Used for error reporting.
span: Span,
@ -719,7 +730,12 @@ impl<'ra> Module<'ra> {
return;
}
if let Res::Def(DefKind::Trait | DefKind::TraitAlias, def_id) = binding.res() {
collected_traits.push((name, binding, r.as_ref().get_module(def_id)))
collected_traits.push((
name,
binding,
r.as_ref().get_module(def_id),
binding.is_ambiguity_recursive(),
));
}
});
*traits = Some(collected_traits.into_boxed_slice());
@ -1925,7 +1941,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if let Some(module) = current_trait {
if self.trait_may_have_item(Some(module), assoc_item) {
let def_id = module.def_id();
found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] });
found_traits.push(TraitCandidate {
def_id,
import_ids: smallvec![],
lint_ambiguous: false,
});
}
}
@ -1963,11 +1983,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
) {
module.ensure_traits(self);
let traits = module.traits.borrow();
for &(trait_name, trait_binding, trait_module) in traits.as_ref().unwrap().iter() {
for &(trait_name, trait_binding, trait_module, lint_ambiguous) in
traits.as_ref().unwrap().iter()
{
if self.trait_may_have_item(trait_module, assoc_item) {
let def_id = trait_binding.res().def_id();
let import_ids = self.find_transitive_imports(&trait_binding.kind, trait_name.0);
found_traits.push(TraitCandidate { def_id, import_ids });
found_traits.push(TraitCandidate { def_id, import_ids, lint_ambiguous });
}
}
}

View file

@ -0,0 +1,84 @@
//@ edition:2018
//@ aux-crate:external=ambiguous-trait-reexport.rs
mod m1 {
pub trait Trait {
fn method1(&self) {}
}
impl Trait for u8 {}
}
mod m2 {
pub trait Trait {
fn method2(&self) {}
}
impl Trait for u8 {}
}
mod m1_reexport {
pub use crate::m1::Trait;
}
mod m2_reexport {
pub use crate::m2::Trait;
}
mod ambig_reexport {
pub use crate::m1::*;
pub use crate::m2::*;
}
fn test1() {
// Create an ambiguous import for `Trait` in one order
use m1::*;
use m2::*;
0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope
}
fn test2() {
// Create an ambiguous import for `Trait` in another order
use m2::*;
use m1::*;
0u8.method1(); //~ ERROR: no method named `method1` found for type `u8` in the current scope
0u8.method2(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
}
fn test_indirect_reexport() {
use m1_reexport::*;
use m2_reexport::*;
0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope
}
fn test_ambig_reexport() {
use ambig_reexport::*;
0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope
}
fn test_external() {
use external::m1::*;
use external::m2::*;
0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope
}
fn test_external_indirect_reexport() {
use external::m1_reexport::*;
use external::m2_reexport::*;
0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope
}
fn test_external_ambig_reexport() {
use external::ambig_reexport::*;
0u8.method1(); //~ WARNING Use of ambiguously glob imported trait `Trait` [ambiguous_glob_imported_traits]
//~| WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
0u8.method2(); //~ ERROR: no method named `method2` found for type `u8` in the current scope
}
fn main() {}

View file

@ -0,0 +1,191 @@
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:32:9
|
LL | use m1::*;
| -- `Trait` imported ambiguously here
LL | use m2::*;
LL | 0u8.method1();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
= note: `#[warn(ambiguous_glob_imported_traits)]` (part of `#[warn(future_incompatible)]`) on by default
error[E0599]: no method named `method2` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:34:9
|
LL | 0u8.method2();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m2::Trait;
|
LL + use crate::m2::Trait;
|
error[E0599]: no method named `method1` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:41:9
|
LL | 0u8.method1();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method1` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m1::Trait;
|
LL + use crate::m1::Trait;
|
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:42:9
|
LL | use m2::*;
| -- `Trait` imported ambiguously here
...
LL | 0u8.method2();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:49:9
|
LL | use m1_reexport::*;
| ----------- `Trait` imported ambiguously here
LL | use m2_reexport::*;
LL | 0u8.method1();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
error[E0599]: no method named `method2` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:51:9
|
LL | 0u8.method2();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m2::Trait;
|
LL + use crate::m2::Trait;
|
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:56:9
|
LL | use ambig_reexport::*;
| -------------- `Trait` imported ambiguously here
LL | 0u8.method1();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
error[E0599]: no method named `method2` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:58:9
|
LL | 0u8.method2();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m2::Trait;
|
LL + use crate::m2::Trait;
|
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:64:9
|
LL | use external::m1::*;
| ------------ `Trait` imported ambiguously here
LL | use external::m2::*;
LL | 0u8.method1();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
error[E0599]: no method named `method2` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:66:9
|
LL | 0u8.method2();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m2::Trait;
|
LL + use crate::m2::Trait;
|
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:72:9
|
LL | use external::m1_reexport::*;
| --------------------- `Trait` imported ambiguously here
LL | use external::m2_reexport::*;
LL | 0u8.method1();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
error[E0599]: no method named `method2` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:74:9
|
LL | 0u8.method2();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m2::Trait;
|
LL + use crate::m2::Trait;
|
warning: Use of ambiguously glob imported trait `Trait`
--> $DIR/ambiguous-trait-in-scope.rs:79:9
|
LL | use external::ambig_reexport::*;
| ------------------------ `Trait` imported ambiguously here
LL | 0u8.method1();
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #147992 <https://github.com/rust-lang/rust/issues/147992>
= help: Import `Trait` explicitly
error[E0599]: no method named `method2` found for type `u8` in the current scope
--> $DIR/ambiguous-trait-in-scope.rs:81:9
|
LL | 0u8.method2();
| ^^^^^^^ method not found in `u8`
|
= help: items from traits can only be used if the trait is in scope
help: the following traits which provide `method2` are implemented but not in scope; perhaps you want to import one of them
|
LL + use ambiguous_trait_reexport::m2::Trait;
|
LL + use crate::m2::Trait;
|
error: aborting due to 7 previous errors; 7 warnings emitted
For more information about this error, try `rustc --explain E0599`.

View file

@ -0,0 +1,23 @@
pub mod m1 {
pub trait Trait {
fn method1(&self) {}
}
impl Trait for u8 {}
}
pub mod m2 {
pub trait Trait {
fn method2(&self) {}
}
impl Trait for u8 {}
}
pub mod m1_reexport {
pub use crate::m1::Trait;
}
pub mod m2_reexport {
pub use crate::m2::Trait;
}
pub mod ambig_reexport {
pub use crate::m1::*;
pub use crate::m2::*;
}

View file

@ -0,0 +1,27 @@
//@ check-pass
// The AMBIGUOUS_GLOB_IMPORTED_TRAITS lint is reported on uses of traits that are
// ambiguously glob imported. This test checks that we don't report this lint
// when the same trait is glob imported multiple times.
mod t {
pub trait Trait {
fn method(&self) {}
}
impl Trait for i8 {}
}
mod m1 {
pub use t::Trait;
}
mod m2 {
pub use t::Trait;
}
use m1::*;
use m2::*;
fn main() {
0i8.method();
}