diff --git a/RELEASES.md b/RELEASES.md
index 3205b02e5c452..e8c79c573f976 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -92,7 +92,6 @@ Cargo
-----
- [Allow named debuginfo options in `Cargo.toml`.](https://github.com/rust-lang/cargo/pull/11958/)
- [Add `workspace_default_members` to the output of `cargo metadata`.](https://github.com/rust-lang/cargo/pull/11978/)
-- [`cargo add` now considers `rust-version` when selecting packages.](https://github.com/rust-lang/cargo/pull/12078/)
- [Automatically inherit workspace fields when running `cargo new`/`cargo init`.](https://github.com/rust-lang/cargo/pull/12069/)
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 429e62c4a1c63..9e193402feb20 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1619,13 +1619,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug!(?hir_bounds);
let lifetime_mapping = if in_trait {
- self.arena.alloc_from_iter(
- collected_lifetime_mapping
- .iter()
- .map(|(lifetime, def_id)| (**lifetime, *def_id)),
+ Some(
+ &*self.arena.alloc_from_iter(
+ collected_lifetime_mapping
+ .iter()
+ .map(|(lifetime, def_id)| (**lifetime, *def_id)),
+ ),
)
} else {
- &mut []
+ None
};
let opaque_ty_item = hir::OpaqueTy {
@@ -2090,13 +2092,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
let lifetime_mapping = if in_trait {
- self.arena.alloc_from_iter(
- collected_lifetime_mapping
- .iter()
- .map(|(lifetime, def_id)| (**lifetime, *def_id)),
+ Some(
+ &*self.arena.alloc_from_iter(
+ collected_lifetime_mapping
+ .iter()
+ .map(|(lifetime, def_id)| (**lifetime, *def_id)),
+ ),
)
} else {
- &mut []
+ None
};
let opaque_ty_item = hir::OpaqueTy {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 35ef30114b712..68f1559ea220e 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2664,10 +2664,19 @@ pub struct OpaqueTy<'hir> {
pub generics: &'hir Generics<'hir>,
pub bounds: GenericBounds<'hir>,
pub origin: OpaqueTyOrigin,
- // Opaques have duplicated lifetimes, this mapping connects the original lifetime with the copy
- // so we can later generate bidirectional outlives predicates to enforce that these lifetimes
- // stay in sync.
- pub lifetime_mapping: &'hir [(Lifetime, LocalDefId)],
+ /// Return-position impl traits (and async futures) must "reify" any late-bound
+ /// lifetimes that are captured from the function signature they originate from.
+ ///
+ /// This is done by generating a new early-bound lifetime parameter local to the
+ /// opaque which is substituted in the function signature with the late-bound
+ /// lifetime.
+ ///
+ /// This mapping associated a captured lifetime (first parameter) with the new
+ /// early-bound lifetime that was generated for the opaque.
+ pub lifetime_mapping: Option<&'hir [(Lifetime, LocalDefId)]>,
+ /// Whether the opaque is a return-position impl trait (or async future)
+ /// originating from a trait method. This makes it so that the opaque is
+ /// lowered as an associated type.
pub in_trait: bool,
}
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 1c9070600dbe3..979b101e7fe2d 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -66,7 +66,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
let opaque_ty_node = tcx.hir().get(opaque_ty_id);
let Node::Item(&Item {
- kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }),
+ kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping: Some(lifetime_mapping), .. }),
..
}) = opaque_ty_node
else {
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 6522e449386c7..7c701fd4fe16e 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -956,11 +956,11 @@ pub trait LintContext: Sized {
db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace));
db.span_note(private_item_span, "but the private item here shadows it".to_owned());
}
- BuiltinLintDiagnostics::UnusedQualifications { path_span, unqualified_path } => {
+ BuiltinLintDiagnostics::UnusedQualifications { removal_span } => {
db.span_suggestion_verbose(
- path_span,
- "replace it with the unqualified path",
- unqualified_path,
+ removal_span,
+ "remove the unnecessary path segments",
+ "",
Applicability::MachineApplicable
);
}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index f6ffd46b1fe08..10ebe29dfce69 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -551,10 +551,8 @@ pub enum BuiltinLintDiagnostics {
private_item_span: Span,
},
UnusedQualifications {
- /// The span of the unnecessarily-qualified path.
- path_span: Span,
- /// The replacement unqualified path.
- unqualified_path: Ident,
+ /// The span of the unnecessarily-qualified path to remove.
+ removal_span: Span,
},
}
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 846a1ffe09b17..05128a51016a0 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3911,8 +3911,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
&& path[0].ident.name != kw::PathRoot
&& path[0].ident.name != kw::DollarCrate
{
+ let last_segment = *path.last().unwrap();
let unqualified_result = {
- match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) {
+ match self.resolve_path(&[last_segment], Some(ns), None) {
PathResult::NonModule(path_res) => path_res.expect_full_res(),
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
module.res().unwrap()
@@ -3928,8 +3929,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
finalize.path_span,
"unnecessary qualification",
lint::BuiltinLintDiagnostics::UnusedQualifications {
- path_span: finalize.path_span,
- unqualified_path: path.last().unwrap().ident
+ removal_span: finalize.path_span.until(last_segment.ident.span),
}
)
}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2d00c53951fef..d14953f1bb786 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -90,6 +90,19 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
}
v
}));
+ items.extend(doc.inlined_foreigns.iter().flat_map(|((_, renamed), (res, local_import_id))| {
+ let Some(def_id) = res.opt_def_id() else { return Vec::new() };
+ let name = renamed.unwrap_or_else(|| cx.tcx.item_name(def_id));
+ let import = cx.tcx.hir().expect_item(*local_import_id);
+ match import.kind {
+ hir::ItemKind::Use(path, kind) => {
+ let hir::UsePath { segments, span, .. } = *path;
+ let path = hir::Path { segments, res: *res, span };
+ clean_use_statement_inner(import, name, &path, kind, cx, &mut Default::default())
+ }
+ _ => unreachable!(),
+ }
+ }));
items.extend(doc.items.values().flat_map(|(item, renamed, _)| {
// Now we actually lower the imports, skipping everything else.
if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind {
@@ -2652,9 +2665,6 @@ fn clean_use_statement<'tcx>(
let mut items = Vec::new();
let hir::UsePath { segments, ref res, span } = *path;
for &res in res {
- if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
- continue;
- }
let path = hir::Path { segments, res, span };
items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
}
@@ -2669,6 +2679,9 @@ fn clean_use_statement_inner<'tcx>(
cx: &mut DocContext<'tcx>,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
) -> Vec- {
+ if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
+ return Vec::new();
+ }
// We need this comparison because some imports (for std types for example)
// are "inserted" as well but directly by the compiler and they should not be
// taken into account.
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 265123ddf6c8b..66737a0152158 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -35,6 +35,8 @@ pub(crate) struct Module<'hir> {
(LocalDefId, Option),
(&'hir hir::Item<'hir>, Option, Option),
>,
+ /// Same as for `items`.
+ pub(crate) inlined_foreigns: FxIndexMap<(DefId, Option), (Res, LocalDefId)>,
pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option)>,
}
@@ -54,6 +56,7 @@ impl Module<'_> {
import_id,
mods: Vec::new(),
items: FxIndexMap::default(),
+ inlined_foreigns: FxIndexMap::default(),
foreigns: Vec::new(),
}
}
@@ -272,21 +275,30 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
return false;
}
- // For cross-crate impl inlining we need to know whether items are
- // reachable in documentation -- a previously unreachable item can be
- // made reachable by cross-crate inlining which we're checking here.
- // (this is done here because we need to know this upfront).
- if !ori_res_did.is_local() && !is_no_inline {
- crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
- return false;
- }
-
+ let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did);
let Some(res_did) = ori_res_did.as_local() else {
- return false;
+ // For cross-crate impl inlining we need to know whether items are
+ // reachable in documentation -- a previously unreachable item can be
+ // made reachable by cross-crate inlining which we're checking here.
+ // (this is done here because we need to know this upfront).
+ crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
+ if is_hidden {
+ return false;
+ }
+ // We store inlined foreign items otherwise, it'd mean that the `use` item would be kept
+ // around. It's not a problem unless this `use` imports both a local AND a foreign item.
+ // If a local item is inlined, its `use` is not supposed to still be around in `clean`,
+ // which would make appear the `use` in the generated documentation like the local item
+ // was not inlined even though it actually was.
+ self.modules
+ .last_mut()
+ .unwrap()
+ .inlined_foreigns
+ .insert((ori_res_did, renamed), (res, def_id));
+ return true;
};
let is_private = !self.cx.cache.effective_visibilities.is_directly_public(tcx, ori_res_did);
- let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did);
let item = tcx.hir().get_by_def_id(res_did);
if !please_inline {
@@ -314,7 +326,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
return false;
}
- let inlined = match tcx.hir().get_by_def_id(res_did) {
+ let inlined = match item {
// Bang macros are handled a bit on their because of how they are handled by the
// compiler. If they have `#[doc(hidden)]` and the re-export doesn't have
// `#[doc(inline)]`, then we don't inline it.
@@ -346,7 +358,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
};
self.view_item_stack.remove(&res_did);
if inlined {
- self.cx.cache.inlined_items.insert(res_did.to_def_id());
+ self.cx.cache.inlined_items.insert(ori_res_did);
}
inlined
}
@@ -483,7 +495,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
continue;
}
}
-
self.add_to_current_mod(item, renamed, import_id);
}
}
diff --git a/tests/rustdoc/issue-105735-overlapping-reexport-2.rs b/tests/rustdoc/issue-105735-overlapping-reexport-2.rs
new file mode 100644
index 0000000000000..9108248394894
--- /dev/null
+++ b/tests/rustdoc/issue-105735-overlapping-reexport-2.rs
@@ -0,0 +1,25 @@
+// Regression test to ensure that both `AtomicU8` items are displayed but not the re-export.
+
+#![crate_name = "foo"]
+#![no_std]
+
+// @has 'foo/index.html'
+// @has - '//*[@class="item-name"]/a[@class="type"]' 'AtomicU8'
+// @has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
+// We also ensure we don't have another item displayed.
+// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 2
+// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Type Definitions'
+// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Constants'
+
+mod other {
+ pub type AtomicU8 = ();
+}
+
+mod thing {
+ pub use crate::other::AtomicU8;
+
+ #[allow(non_upper_case_globals)]
+ pub const AtomicU8: () = ();
+}
+
+pub use crate::thing::AtomicU8;
diff --git a/tests/rustdoc/issue-105735-overlapping-reexport.rs b/tests/rustdoc/issue-105735-overlapping-reexport.rs
new file mode 100644
index 0000000000000..50f2450b90a0b
--- /dev/null
+++ b/tests/rustdoc/issue-105735-overlapping-reexport.rs
@@ -0,0 +1,21 @@
+// Regression test to ensure that both `AtomicU8` items are displayed but not the re-export.
+
+#![crate_name = "foo"]
+#![no_std]
+
+// @has 'foo/index.html'
+// @has - '//*[@class="item-name"]/a[@class="struct"]' 'AtomicU8'
+// @has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8'
+// We also ensure we don't have another item displayed.
+// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 2
+// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs'
+// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Constants'
+
+mod thing {
+ pub use core::sync::atomic::AtomicU8;
+
+ #[allow(non_upper_case_globals)]
+ pub const AtomicU8: () = ();
+}
+
+pub use crate::thing::AtomicU8;
diff --git a/tests/ui/lint/lint-qualification.fixed b/tests/ui/lint/lint-qualification.fixed
new file mode 100644
index 0000000000000..c144930136234
--- /dev/null
+++ b/tests/ui/lint/lint-qualification.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![deny(unused_qualifications)]
+#![allow(deprecated)]
+
+mod foo {
+ pub fn bar() {}
+}
+
+fn main() {
+ use foo::bar;
+ bar(); //~ ERROR: unnecessary qualification
+ bar();
+
+ let _ = || -> Result<(), ()> { try!(Ok(())); Ok(()) }; // issue #37345
+
+ macro_rules! m { () => {
+ $crate::foo::bar(); // issue #37357
+ ::foo::bar(); // issue #38682
+ } }
+ m!();
+}
diff --git a/tests/ui/lint/lint-qualification.rs b/tests/ui/lint/lint-qualification.rs
index 0cace0ca0355a..80904303559d8 100644
--- a/tests/ui/lint/lint-qualification.rs
+++ b/tests/ui/lint/lint-qualification.rs
@@ -1,3 +1,4 @@
+// run-rustfix
#![deny(unused_qualifications)]
#![allow(deprecated)]
diff --git a/tests/ui/lint/lint-qualification.stderr b/tests/ui/lint/lint-qualification.stderr
index d09cb78c4f013..90a06bc6cbe5d 100644
--- a/tests/ui/lint/lint-qualification.stderr
+++ b/tests/ui/lint/lint-qualification.stderr
@@ -1,18 +1,19 @@
error: unnecessary qualification
- --> $DIR/lint-qualification.rs:10:5
+ --> $DIR/lint-qualification.rs:11:5
|
LL | foo::bar();
| ^^^^^^^^
|
note: the lint level is defined here
- --> $DIR/lint-qualification.rs:1:9
+ --> $DIR/lint-qualification.rs:2:9
|
LL | #![deny(unused_qualifications)]
| ^^^^^^^^^^^^^^^^^^^^^
-help: replace it with the unqualified path
+help: remove the unnecessary path segments
+ |
+LL - foo::bar();
+LL + bar();
|
-LL | bar();
- | ~~~
error: aborting due to previous error
diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed
new file mode 100644
index 0000000000000..e730f94660bbd
--- /dev/null
+++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed
@@ -0,0 +1,31 @@
+// run-rustfix
+
+#![deny(unused_qualifications)]
+#![feature(unsized_fn_params)]
+
+#[allow(unused_imports)]
+use std::ops;
+use std::ops::Index;
+
+pub struct A;
+
+impl Index for A {
+ //~^ ERROR unnecessary qualification
+ type Output = ();
+ fn index(&self, _: str) -> &Self::Output {
+ &()
+ }
+}
+
+mod inner {
+ pub trait Trait {}
+}
+
+// the import needs to be here for the lint to show up
+#[allow(unused_imports)]
+use inner::Trait;
+
+impl Trait for () {}
+//~^ ERROR unnecessary qualification
+
+fn main() {}
diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs
new file mode 100644
index 0000000000000..641c892e3de9a
--- /dev/null
+++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+
+#![deny(unused_qualifications)]
+#![feature(unsized_fn_params)]
+
+#[allow(unused_imports)]
+use std::ops;
+use std::ops::Index;
+
+pub struct A;
+
+impl ops::Index for A {
+ //~^ ERROR unnecessary qualification
+ type Output = ();
+ fn index(&self, _: str) -> &Self::Output {
+ &()
+ }
+}
+
+mod inner {
+ pub trait Trait {}
+}
+
+// the import needs to be here for the lint to show up
+#[allow(unused_imports)]
+use inner::Trait;
+
+impl inner::Trait for () {}
+//~^ ERROR unnecessary qualification
+
+fn main() {}
diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr
new file mode 100644
index 0000000000000..d9c7fd21871ba
--- /dev/null
+++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr
@@ -0,0 +1,31 @@
+error: unnecessary qualification
+ --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:12:6
+ |
+LL | impl ops::Index for A {
+ | ^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:3:9
+ |
+LL | #![deny(unused_qualifications)]
+ | ^^^^^^^^^^^^^^^^^^^^^
+help: remove the unnecessary path segments
+ |
+LL - impl ops::Index for A {
+LL + impl Index for A {
+ |
+
+error: unnecessary qualification
+ --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:28:6
+ |
+LL | impl inner::Trait for () {}
+ | ^^^^^^^^^^^^^^^^
+ |
+help: remove the unnecessary path segments
+ |
+LL - impl inner::Trait for () {}
+LL + impl Trait for () {}
+ |
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/resolve/unused-qualifications-suggestion.stderr b/tests/ui/resolve/unused-qualifications-suggestion.stderr
index c8e91e07295b6..e3dac37fc6e22 100644
--- a/tests/ui/resolve/unused-qualifications-suggestion.stderr
+++ b/tests/ui/resolve/unused-qualifications-suggestion.stderr
@@ -9,10 +9,11 @@ note: the lint level is defined here
|
LL | #![deny(unused_qualifications)]
| ^^^^^^^^^^^^^^^^^^^^^
-help: replace it with the unqualified path
+help: remove the unnecessary path segments
+ |
+LL - foo::bar();
+LL + bar();
|
-LL | bar();
- | ~~~
error: unnecessary qualification
--> $DIR/unused-qualifications-suggestion.rs:21:5
@@ -20,10 +21,11 @@ error: unnecessary qualification
LL | baz::qux::quux();
| ^^^^^^^^^^^^^^
|
-help: replace it with the unqualified path
+help: remove the unnecessary path segments
+ |
+LL - baz::qux::quux();
+LL + quux();
|
-LL | quux();
- | ~~~~
error: aborting due to 2 previous errors