about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/mod.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs7
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs28
-rw-r--r--compiler/rustc_lint/messages.ftl1
-rw-r--r--compiler/rustc_lint/src/lints.rs17
-rw-r--r--compiler/rustc_lint/src/types.rs49
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs5
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs92
-rw-r--r--config.example.toml7
-rw-r--r--library/core/src/macros/mod.rs2
-rw-r--r--src/doc/rustdoc/src/SUMMARY.md1
-rw-r--r--src/doc/rustdoc/src/write-documentation/re-exports.md172
-rw-r--r--src/doc/rustdoc/src/write-documentation/the-doc-attribute.md8
-rw-r--r--src/tools/rustdoc-gui-test/src/main.rs16
-rw-r--r--tests/rustdoc/issue-109449-doc-hidden-reexports.rs143
-rw-r--r--tests/ui/lint/type-overflow.stderr28
-rw-r--r--tests/ui/match/issue-112438.rs11
-rw-r--r--tests/ui/resolve/issue-112472-multi-generics-suggestion.fixed31
-rw-r--r--tests/ui/resolve/issue-112472-multi-generics-suggestion.rs31
-rw-r--r--tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr25
20 files changed, 629 insertions, 49 deletions
diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs
index ac9e7d06c4e..83b7e13905a 100644
--- a/compiler/rustc_ast_pretty/src/pprust/mod.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -32,6 +32,10 @@ pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
     State::new().bounds_to_string(bounds)
 }
 
+pub fn where_bound_predicate_to_string(where_bound_predicate: &ast::WhereBoundPredicate) -> String {
+    State::new().where_bound_predicate_to_string(where_bound_predicate)
+}
+
 pub fn pat_to_string(pat: &ast::Pat) -> String {
     State::new().pat_to_string(pat)
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 3f80728a260..59239b49edd 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -824,6 +824,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         Self::to_string(|s| s.print_type_bounds(bounds))
     }
 
+    fn where_bound_predicate_to_string(
+        &self,
+        where_bound_predicate: &ast::WhereBoundPredicate,
+    ) -> String {
+        Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
+    }
+
     fn pat_to_string(&self, pat: &ast::Pat) -> String {
         Self::to_string(|s| s.print_pat(pat))
     }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index c465f8c948a..5c01b7ea70a 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -623,19 +623,8 @@ impl<'a> State<'a> {
 
     pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
         match predicate {
-            ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
-                bound_generic_params,
-                bounded_ty,
-                bounds,
-                ..
-            }) => {
-                self.print_formal_generic_params(bound_generic_params);
-                self.print_type(bounded_ty);
-                self.word(":");
-                if !bounds.is_empty() {
-                    self.nbsp();
-                    self.print_type_bounds(bounds);
-                }
+            ast::WherePredicate::BoundPredicate(where_bound_predicate) => {
+                self.print_where_bound_predicate(where_bound_predicate);
             }
             ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
                 lifetime,
@@ -658,6 +647,19 @@ impl<'a> State<'a> {
         }
     }
 
+    pub fn print_where_bound_predicate(
+        &mut self,
+        where_bound_predicate: &ast::WhereBoundPredicate,
+    ) {
+        self.print_formal_generic_params(&where_bound_predicate.bound_generic_params);
+        self.print_type(&where_bound_predicate.bounded_ty);
+        self.word(":");
+        if !where_bound_predicate.bounds.is_empty() {
+            self.nbsp();
+            self.print_type_bounds(&where_bound_predicate.bounds);
+        }
+    }
+
     fn print_use_tree(&mut self, tree: &ast::UseTree) {
         match &tree.kind {
             ast::UseTreeKind::Simple(rename) => {
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 5e13ee7b8a4..c66fcdec716 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -425,6 +425,7 @@ lint_overflowing_bin_hex = literal out of range for `{$ty}`
     .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}`
     .positive_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}`
     .suggestion = consider using the type `{$suggestion_ty}` instead
+    .sign_bit_suggestion = to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`
     .help = consider using the type `{$suggestion_ty}` instead
 
 lint_overflowing_int = literal out of range for `{$ty}`
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index e990c771bdf..89fa5713b73 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1342,6 +1342,8 @@ pub struct OverflowingBinHex<'a> {
     pub sign: OverflowingBinHexSign,
     #[subdiagnostic]
     pub sub: Option<OverflowingBinHexSub<'a>>,
+    #[subdiagnostic]
+    pub sign_bit_sub: Option<OverflowingBinHexSignBitSub<'a>>,
 }
 
 pub enum OverflowingBinHexSign {
@@ -1386,6 +1388,21 @@ pub enum OverflowingBinHexSub<'a> {
     Help { suggestion_ty: &'a str },
 }
 
+#[derive(Subdiagnostic)]
+#[suggestion(
+    lint_sign_bit_suggestion,
+    code = "{lit_no_suffix}{uint_ty} as {int_ty}",
+    applicability = "maybe-incorrect"
+)]
+pub struct OverflowingBinHexSignBitSub<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub lit_no_suffix: &'a str,
+    pub negative_val: String,
+    pub uint_ty: &'a str,
+    pub int_ty: &'a str,
+}
+
 #[derive(LintDiagnostic)]
 #[diag(lint_overflowing_int)]
 #[note]
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 264a59c5585..ec9e7c7fdae 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -3,9 +3,10 @@ use crate::{
     lints::{
         AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
         InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
-        OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSub,
-        OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
-        RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag,
+        OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
+        OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
+        OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
+        VariantSizeDifferencesDiag,
     },
 };
 use crate::{LateContext, LateLintPass, LintContext};
@@ -297,10 +298,50 @@ fn report_bin_hex_error(
             }
         },
     );
+    let sign_bit_sub = (!negative)
+        .then(|| {
+            let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else {
+                return None;
+            };
+
+            let Some(bit_width) = int_ty.bit_width() else {
+                return None; // isize case
+            };
+
+            // Skip if sign bit is not set
+            if (val & (1 << (bit_width - 1))) == 0 {
+                return None;
+            }
+
+            let lit_no_suffix =
+                if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+                    repr_str.split_at(pos).0
+                } else {
+                    &repr_str
+                };
+
+            Some(OverflowingBinHexSignBitSub {
+                span: expr.span,
+                lit_no_suffix,
+                negative_val: actually.clone(),
+                int_ty: int_ty.name_str(),
+                uint_ty: int_ty.to_unsigned().name_str(),
+            })
+        })
+        .flatten();
+
     cx.emit_spanned_lint(
         OVERFLOWING_LITERALS,
         expr.span,
-        OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub },
+        OverflowingBinHex {
+            ty: t,
+            lit: repr_str.clone(),
+            dec: val,
+            actually,
+            sign,
+            sub,
+            sign_bit_sub,
+        },
     )
 }
 
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index f53dc8cb0ec..ae32a54be3d 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -95,7 +95,10 @@ impl<'tcx> TyCtxt<'tcx> {
                     // used generic parameters is a bug of evaluation, so checking for it
                     // here does feel somewhat sensible.
                     if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
-                        assert!(matches!(self.def_kind(ct.def), DefKind::AnonConst));
+                        assert!(matches!(
+                            self.def_kind(ct.def),
+                            DefKind::InlineConst | DefKind::AnonConst
+                        ));
                         let mir_body = self.mir_for_ctfe(ct.def);
                         if mir_body.is_polymorphic {
                             let Some(local_def_id) = ct.def.as_local() else { return };
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index e4b01ef2b17..7284b33f09d 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -10,7 +10,7 @@ use rustc_ast::{
     self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
     MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
 };
-use rustc_ast_pretty::pprust::path_segment_to_string;
+use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -1050,7 +1050,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             };
 
         // Confirm that the target is an associated type.
-        let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
+        let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
             // use this to verify that ident is a type param.
             let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
                 return false;
@@ -1079,7 +1079,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 return false;
             }
             if let (
-                [ast::PathSegment { ident: constrain_ident, args: None, .. }],
+                [ast::PathSegment { args: None, .. }],
                 [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
             ) = (&type_param_path.segments[..], &bounds[..])
             {
@@ -1087,29 +1087,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                     &poly_trait_ref.trait_ref.path.segments[..]
                 {
                     if ident.span == span {
+                        let Some(new_where_bound_predicate) = mk_where_bound_predicate(path, poly_trait_ref, ty) else { return false; };
                         err.span_suggestion_verbose(
                             *where_span,
                             format!("constrain the associated type to `{}`", ident),
-                            format!(
-                                "{}: {}<{} = {}>",
-                                self.r
-                                    .tcx
-                                    .sess
-                                    .source_map()
-                                    .span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`.
-                                    .unwrap_or_else(|_| constrain_ident.to_string()),
-                                path.segments[..position]
-                                    .iter()
-                                    .map(|segment| path_segment_to_string(segment))
-                                    .collect::<Vec<_>>()
-                                    .join("::"),
-                                path.segments[position..]
-                                    .iter()
-                                    .map(|segment| path_segment_to_string(segment))
-                                    .collect::<Vec<_>>()
-                                    .join("::"),
-                                ident,
-                            ),
+                            where_bound_predicate_to_string(&new_where_bound_predicate),
                             Applicability::MaybeIncorrect,
                         );
                     }
@@ -2605,6 +2587,70 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     }
 }
 
+fn mk_where_bound_predicate(
+    path: &Path,
+    poly_trait_ref: &ast::PolyTraitRef,
+    ty: &ast::Ty,
+) -> Option<ast::WhereBoundPredicate> {
+    use rustc_span::DUMMY_SP;
+    let modified_segments = {
+        let mut segments = path.segments.clone();
+        let [preceding @ .., second_last, last] = segments.as_mut_slice() else { return None; };
+        let mut segments = ThinVec::from(preceding);
+
+        let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint {
+            id: DUMMY_NODE_ID,
+            ident: last.ident,
+            gen_args: None,
+            kind: ast::AssocConstraintKind::Equality {
+                term: ast::Term::Ty(ast::ptr::P(ast::Ty {
+                    kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()),
+                    id: DUMMY_NODE_ID,
+                    span: DUMMY_SP,
+                    tokens: None,
+                })),
+            },
+            span: DUMMY_SP,
+        });
+
+        match second_last.args.as_deref_mut() {
+            Some(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { args, .. })) => {
+                args.push(added_constraint);
+            }
+            Some(_) => return None,
+            None => {
+                second_last.args =
+                    Some(ast::ptr::P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs {
+                        args: ThinVec::from([added_constraint]),
+                        span: DUMMY_SP,
+                    })));
+            }
+        }
+
+        segments.push(second_last.clone());
+        segments
+    };
+
+    let new_where_bound_predicate = ast::WhereBoundPredicate {
+        span: DUMMY_SP,
+        bound_generic_params: ThinVec::new(),
+        bounded_ty: ast::ptr::P(ty.clone()),
+        bounds: vec![ast::GenericBound::Trait(
+            ast::PolyTraitRef {
+                bound_generic_params: ThinVec::new(),
+                trait_ref: ast::TraitRef {
+                    path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
+                    ref_id: DUMMY_NODE_ID,
+                },
+                span: DUMMY_SP,
+            },
+            ast::TraitBoundModifier::None,
+        )],
+    };
+
+    Some(new_where_bound_predicate)
+}
+
 /// Report lifetime/lifetime shadowing as an error.
 pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
     let mut err = struct_span_err!(
diff --git a/config.example.toml b/config.example.toml
index d0eaa9fd7ff..0c65b25fe13 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -250,6 +250,13 @@ changelog-seen = 2
 # target when running tests, otherwise this can be omitted.
 #nodejs = "node"
 
+# The npm executable to use. Note that this is used for rustdoc-gui tests,
+# otherwise this can be omitted.
+#
+# Under Windows this should be `npm.cmd` or path to it (verified on nodejs v18.06), or
+# error will be emitted.
+#npm = "npm"
+
 # Python interpreter to use for various tasks throughout the build, notably
 # rustdoc tests, the lldb python interpreter, and some dist bits and pieces.
 #
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index c4134dbcd25..45e5b76272e 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -960,6 +960,8 @@ pub(crate) mod builtin {
     ///
     /// A compile time error is never emitted when using this macro regardless
     /// of whether the environment variable is present or not.
+    /// To emit a compile error if the environment variable is not present,
+    /// use the [`env!`] macro instead.
     ///
     /// # Examples
     ///
diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md
index b512135d927..12a8b2b8db4 100644
--- a/src/doc/rustdoc/src/SUMMARY.md
+++ b/src/doc/rustdoc/src/SUMMARY.md
@@ -7,6 +7,7 @@
 - [How to write documentation](how-to-write-documentation.md)
     - [What to include (and exclude)](write-documentation/what-to-include.md)
     - [The `#[doc]` attribute](write-documentation/the-doc-attribute.md)
+    - [Re-exports](write-documentation/re-exports.md)
     - [Linking to items by name](write-documentation/linking-to-items-by-name.md)
     - [Documentation tests](write-documentation/documentation-tests.md)
 - [Rustdoc-specific lints](lints.md)
diff --git a/src/doc/rustdoc/src/write-documentation/re-exports.md b/src/doc/rustdoc/src/write-documentation/re-exports.md
new file mode 100644
index 00000000000..593428b8a70
--- /dev/null
+++ b/src/doc/rustdoc/src/write-documentation/re-exports.md
@@ -0,0 +1,172 @@
+# Re-exports
+
+Let's start by explaining what are re-exports. To do so, we will use an example where we are
+writing a library (named `lib`) with some types dispatched in sub-modules:
+
+```rust
+pub mod sub_module1 {
+    pub struct Foo;
+}
+pub mod sub_module2 {
+    pub struct AnotherFoo;
+}
+```
+
+Users can import them like this:
+
+```rust,ignore (inline)
+use lib::sub_module1::Foo;
+use lib::sub_module2::AnotherFoo;
+```
+
+But what if you want the types to be available directly at the crate root or if we don't want the
+modules to be visible for users? That's where re-exports come in:
+
+```rust,ignore (inline)
+// `sub_module1` and `sub_module2` are not visible outside.
+mod sub_module1 {
+    pub struct Foo;
+}
+mod sub_module2 {
+    pub struct AnotherFoo;
+}
+// We re-export both types:
+pub use crate::sub_module1::Foo;
+pub use crate::sub_module2::AnotherFoo;
+```
+
+And now users will be able to do:
+
+```rust,ignore (inline)
+use lib::{Foo, AnotherFoo};
+```
+
+And since both `sub_module1` and `sub_module2` are private, users won't be able to import them.
+
+Now what's interesting is that the generated documentation for this crate will show both `Foo` and
+`AnotherFoo` directly at the crate root, meaning they have been inlined. There are a few rules to
+know whether or not a re-exported item will be inlined.
+
+## Inlining rules
+
+If a public item comes from a private module, it will be inlined:
+
+```rust,ignore (inline)
+mod private_module {
+    pub struct Public;
+}
+pub mod public_mod {
+    // `Public` will inlined here since `private_module` is private.
+    pub use super::private_module::Public;
+}
+// `Public` will not be inlined here since `public_mod` is public.
+pub use self::public_mod::Public;
+```
+
+Likewise, if an item inherits `#[doc(hidden)]` from any of its ancestors, it will be inlined:
+
+```rust,ignore (inline)
+#[doc(hidden)]
+pub mod public_mod {
+    pub struct Public;
+}
+// `Public` be inlined since its parent (`public_mod`) has `#[doc(hidden)]`.
+pub use self::public_mod::Public;
+```
+
+If an item has `#[doc(hidden)]`, it won't be inlined (nor visible in the generated documentation):
+
+```rust,ignore (inline)
+// This struct won't be visible.
+#[doc(hidden)]
+pub struct Hidden;
+
+// This re-export won't be visible.
+pub use self::Hidden as InlinedHidden;
+```
+
+The same applies on re-exports themselves: if you have multiple re-exports and some of them have
+`#[doc(hidden)]`, then these ones (and only these) own't appear in the documentation:
+
+```rust,ignore (inline)
+mod private_mod {
+    /// First
+    pub struct InPrivate;
+}
+
+/// Second
+#[doc(hidden)]
+pub use self::private_mod::InPrivate as Hidden;
+/// Third
+pub use self::Hidden as Visible;
+```
+
+In this case, `InPrivate` will be inlined as `Visible`. However, its documentation will be
+`First Third` and not `First Second Third` because the re-export with `Second` as documentation has
+`#[doc(hidden)]`, therefore, all its attributes are ignored.
+
+## Inlining with `#[doc(inline)]`
+
+You can use the `#[doc(inline)]` attribute if you want to force an item to be inlined:
+
+```rust,ignore (inline)
+pub mod public_mod {
+    pub struct Public;
+}
+#[doc(inline)]
+pub use self::public_mod::Public;
+```
+
+With this code, even though `public_mod::Public` is public and present in the documentation, the
+`Public` type will be present both at the crate root and in the `public_mod` module.
+
+## Preventing inlining with `#[doc(no_inline)]`
+
+On the opposite of the `#[doc(inline)]` attribute, if you want to prevent an item from being
+inlined, you can use `#[doc(no_inline)]`:
+
+```rust,ignore (inline)
+mod private_mod {
+    pub struct Public;
+}
+#[doc(no_inline)]
+pub use self::private_mod::Public;
+```
+
+In the generated documentation, you will see a re-export at the crate root and not the type
+directly.
+
+## Attributes
+
+When an item is inlined, its doc comments and most of its attributes will be inlined along with it:
+
+```rust,ignore (inline)
+mod private_mod {
+    /// First
+    #[cfg(a)]
+    pub struct InPrivate;
+    /// Second
+    #[cfg(b)]
+    pub use self::InPrivate as Second;
+}
+
+/// Third
+#[doc(inline)]
+#[cfg(c)]
+pub use self::private_mod::Second as Visible;
+```
+
+In this case, `Visible` will have as documentation `First Second Third` and will also have as `cfg`:
+`#[cfg(a, b, c)]`.
+
+[Intra-doc links](./linking-to-items-by-name.md) are resolved relative to where the doc comment is
+defined.
+
+There are a few attributes which are not inlined though:
+ * `#[doc(alias="")]`
+ * `#[doc(inline)]`
+ * `#[doc(no_inline)]`
+ * `#[doc(hidden)]` (because the re-export itself and its attributes are ignored).
+
+All other attributes are inherited when inlined, so that the documentation matches the behavior if
+the inlined item was directly defined at the spot where it's shown.
diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
index 8ecf05f0e12..046d018543f 100644
--- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
+++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md
@@ -223,12 +223,18 @@ Now we'll have a `Re-exports` line, and `Bar` will not link to anywhere.
 One special case: In Rust 2018 and later, if you `pub use` one of your dependencies, `rustdoc` will
 not eagerly inline it as a module unless you add `#[doc(inline)]`.
 
+If you want to know more about inlining rules, take a look at the
+[`re-exports` chapter](./re-exports.md).
+
 ### `hidden`
 
 <span id="dochidden"></span>
 
 Any item annotated with `#[doc(hidden)]` will not appear in the documentation, unless
-the `strip-hidden` pass is removed.
+the `strip-hidden` pass is removed. Re-exported items where one of its ancestors has
+`#[doc(hidden)]` will be considered the same as private.
+
+You can find more information in the [`re-exports` chapter](./re-exports.md).
 
 ### `alias`
 
diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs
index 3f60a90f87a..dc902f8cb02 100644
--- a/src/tools/rustdoc-gui-test/src/main.rs
+++ b/src/tools/rustdoc-gui-test/src/main.rs
@@ -14,13 +14,19 @@ fn get_browser_ui_test_version_inner(npm: &Path, global: bool) -> Option<String>
     if global {
         command.arg("--global");
     }
-    let lines = command
-        .output()
-        .map(|output| String::from_utf8_lossy(&output.stdout).into_owned())
-        .unwrap_or(String::new());
+    let lines = match command.output() {
+        Ok(output) => String::from_utf8_lossy(&output.stdout).into_owned(),
+        Err(e) => {
+            eprintln!(
+                "path to npm can be wrong, provided path: {npm:?}. Try to set npm path \
+            in config.toml in [build.npm]",
+            );
+            panic!("{:?}", e)
+        }
+    };
     lines
         .lines()
-        .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
+        .find_map(|l| l.rsplit(':').next()?.strip_prefix("browser-ui-test@"))
         .map(|v| v.to_owned())
 }
 
diff --git a/tests/rustdoc/issue-109449-doc-hidden-reexports.rs b/tests/rustdoc/issue-109449-doc-hidden-reexports.rs
new file mode 100644
index 00000000000..b0c22540180
--- /dev/null
+++ b/tests/rustdoc/issue-109449-doc-hidden-reexports.rs
@@ -0,0 +1,143 @@
+// Test to enforce rules over re-exports inlining from
+// <https://github.com/rust-lang/rust/issues/109449>.
+
+#![crate_name = "foo"]
+
+mod private_module {
+    #[doc(hidden)]
+    pub struct Public;
+    #[doc(hidden)]
+    pub type Bar = ();
+}
+
+#[doc(hidden)]
+mod module {
+    pub struct Public2;
+    pub type Bar2 = ();
+}
+
+#[doc(hidden)]
+pub type Bar3 = ();
+#[doc(hidden)]
+pub struct FooFoo;
+
+// Checking that re-exporting a `#[doc(hidden)]` item will NOT inline it.
+pub mod single_reexport {
+    // @has 'foo/single_reexport/index.html'
+
+    // First we check that we have 4 type aliases.
+    // @count - '//*[@id="main-content"]/*[@class="item-table"]//code' 4
+
+    // Then we check that we have the correct link for each re-export.
+
+    // @!has - '//*[@href="struct.Foo.html"]' 'Foo'
+    // @has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;'
+    pub use crate::private_module::Public as Foo;
+    // @!has - '//*[@href="type.Foo2.html"]' 'Foo2'
+    // @has - '//*[@id="reexport.Foo2"]/code' 'pub use crate::private_module::Bar as Foo2;'
+    pub use crate::private_module::Bar as Foo2;
+    // @!has - '//*[@href="type.Yo.html"]' 'Yo'
+    // @has - '//*[@id="reexport.Yo"]/code' 'pub use crate::Bar3 as Yo;'
+    pub use crate::Bar3 as Yo;
+    // @!has - '//*[@href="struct.Yo2.html"]' 'Yo2'
+    // @has - '//*[@id="reexport.Yo2"]/code' 'pub use crate::FooFoo as Yo2;'
+    pub use crate::FooFoo as Yo2;
+
+    // Checking that each file is also created as expected.
+    // @!has 'foo/single_reexport/struct.Foo.html'
+    // @!has 'foo/single_reexport/type.Foo2.html'
+    // @!has 'foo/single_reexport/type.Yo.html'
+    // @!has 'foo/single_reexport/struct.Yo2.html'
+}
+
+// However, re-exporting an item inheriting `#[doc(hidden)]` will inline it.
+pub mod single_reexport_inherit_hidden {
+    // @has 'foo/single_reexport_inherit_hidden/index.html'
+
+    // @has - '//*[@href="struct.Foo3.html"]' 'Foo3'
+    pub use crate::module::Public2 as Foo3;
+    // @has - '//*[@href="type.Foo4.html"]' 'Foo4'
+    pub use crate::module::Bar2 as Foo4;
+
+    // @has 'foo/single_reexport_inherit_hidden/struct.Foo3.html'
+    // @has 'foo/single_reexport_inherit_hidden/type.Foo4.html'
+}
+
+pub mod single_reexport_no_inline {
+    // First we ensure that we only have re-exports and no inlined items.
+    // @has 'foo/single_reexport_no_inline/index.html'
+    // @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 1
+    // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Re-exports'
+
+    // Now we check that we don't have links to the items, just `pub use`.
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Public as XFoo;'
+    // @!has - '//*[@id="main-content"]//a' 'XFoo'
+    #[doc(no_inline)]
+    pub use crate::private_module::Public as XFoo;
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::private_module::Bar as Foo2;'
+    // @!has - '//*[@id="main-content"]//a' 'Foo2'
+    #[doc(no_inline)]
+    pub use crate::private_module::Bar as Foo2;
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::Bar3 as Yo;'
+    // @!has - '//*[@id="main-content"]//a' 'Yo'
+    #[doc(no_inline)]
+    pub use crate::Bar3 as Yo;
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::FooFoo as Yo2;'
+    // @!has - '//*[@id="main-content"]//a' 'Yo2'
+    #[doc(no_inline)]
+    pub use crate::FooFoo as Yo2;
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::module::Public2 as Foo3;'
+    // @!has - '//*[@id="main-content"]//a' 'Foo3'
+    #[doc(no_inline)]
+    pub use crate::module::Public2 as Foo3;
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::module::Bar2 as Foo4;'
+    // @!has - '//*[@id="main-content"]//a' 'Foo4'
+    #[doc(no_inline)]
+    pub use crate::module::Bar2 as Foo4;
+}
+
+// Checking that glob re-exports don't inline `#[doc(hidden)]` items.
+pub mod glob_reexport {
+    // With glob re-exports, we don't inline `#[doc(hidden)]` items so only `module` items
+    // should be inlined.
+    // @has 'foo/glob_reexport/index.html'
+    // @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 3
+    // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Re-exports'
+    // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs'
+    // @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Type Definitions'
+
+    // Now we check we have 1 re-export and 2 inlined items.
+    // If not item from a glob re-export is visible, we don't show the re-export.
+    // @!has - '//*[@id="main-content"]//*' 'pub use crate::private_module::*;'
+    pub use crate::private_module::*;
+    // @has - '//*[@id="main-content"]//*' 'pub use crate::*;'
+    pub use crate::*;
+    // This one should be inlined.
+    // @!has - '//*[@id="main-content"]//*' 'pub use crate::module::*;'
+    // @has - '//*[@id="main-content"]//a[@href="struct.Public2.html"]' 'Public2'
+    // @has - '//*[@id="main-content"]//a[@href="type.Bar2.html"]' 'Bar2'
+    // And we check that the two files were created too.
+    // @has 'foo/glob_reexport/struct.Public2.html'
+    // @has 'foo/glob_reexport/type.Bar2.html'
+    pub use crate::module::*;
+}
+
+mod private {
+    /// Original.
+    pub struct Bar3;
+}
+
+// Checking that `#[doc(hidden)]` re-exports documentation isn't generated.
+pub mod doc_hidden_reexport {
+    // @has 'foo/doc_hidden_reexport/index.html'
+    // Ensure there is only one item in this page and that it's a struct.
+    // @count - '//*[@class="item-name"]' 1
+    // @has - '//a[@class="struct"]' 'Reexport'
+    // Check that the `#[doc(hidden)]` re-export's attributes are not taken into account.
+    // @has - '//*[@class="desc docblock-short"]' 'Visible. Original.'
+    /// Hidden.
+    #[doc(hidden)]
+    pub use crate::private::Bar3;
+    /// Visible.
+    pub use self::Bar3 as Reexport;
+}
diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr
index 62cb1f7f4aa..e7c90dcc81b 100644
--- a/tests/ui/lint/type-overflow.stderr
+++ b/tests/ui/lint/type-overflow.stderr
@@ -16,17 +16,33 @@ warning: literal out of range for `i8`
   --> $DIR/type-overflow.rs:10:16
    |
 LL |     let fail = 0b1000_0001i8;
-   |                ^^^^^^^^^^^^^ help: consider using the type `u8` instead: `0b1000_0001u8`
+   |                ^^^^^^^^^^^^^
    |
    = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into the type `i8` and will become `-127i8`
+help: consider using the type `u8` instead
+   |
+LL |     let fail = 0b1000_0001u8;
+   |                ~~~~~~~~~~~~~
+help: to use as a negative number (decimal `-127`), consider using the type `u8` for the literal and cast it to `i8`
+   |
+LL |     let fail = 0b1000_0001u8 as i8;
+   |                ~~~~~~~~~~~~~~~~~~~
 
 warning: literal out of range for `i64`
   --> $DIR/type-overflow.rs:12:16
    |
 LL |     let fail = 0x8000_0000_0000_0000i64;
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the type `u64` instead: `0x8000_0000_0000_0000u64`
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into the type `i64` and will become `-9223372036854775808i64`
+help: consider using the type `u64` instead
+   |
+LL |     let fail = 0x8000_0000_0000_0000u64;
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~
+help: to use as a negative number (decimal `-9223372036854775808`), consider using the type `u64` for the literal and cast it to `i64`
+   |
+LL |     let fail = 0x8000_0000_0000_0000u64 as i64;
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 warning: literal out of range for `u32`
   --> $DIR/type-overflow.rs:14:16
@@ -44,6 +60,10 @@ LL |     let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
    |
    = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into the type `i128` and will become `-170141183460469231731687303715884105728i128`
    = help: consider using the type `u128` instead
+help: to use as a negative number (decimal `-170141183460469231731687303715884105728`), consider using the type `u128` for the literal and cast it to `i128`
+   |
+LL |     let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128 as i128;
+   |                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 warning: literal out of range for `i32`
   --> $DIR/type-overflow.rs:19:16
@@ -53,6 +73,10 @@ LL |     let fail = 0x8FFF_FFFF_FFFF_FFFE;
    |
    = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into the type `i32` and will become `-2i32`
    = help: consider using the type `i128` instead
+help: to use as a negative number (decimal `-2`), consider using the type `u32` for the literal and cast it to `i32`
+   |
+LL |     let fail = 0x8FFF_FFFF_FFFF_FFFEu32 as i32;
+   |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 warning: literal out of range for `i8`
   --> $DIR/type-overflow.rs:21:17
diff --git a/tests/ui/match/issue-112438.rs b/tests/ui/match/issue-112438.rs
new file mode 100644
index 00000000000..15f380f7fb4
--- /dev/null
+++ b/tests/ui/match/issue-112438.rs
@@ -0,0 +1,11 @@
+// run-pass
+#![feature(inline_const_pat)]
+#![allow(dead_code)]
+#![allow(incomplete_features)]
+fn foo<const V: usize>() {
+    match 0 {
+        const { 1 << 5 } | _ => {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/resolve/issue-112472-multi-generics-suggestion.fixed b/tests/ui/resolve/issue-112472-multi-generics-suggestion.fixed
new file mode 100644
index 00000000000..892697493b7
--- /dev/null
+++ b/tests/ui/resolve/issue-112472-multi-generics-suggestion.fixed
@@ -0,0 +1,31 @@
+// run-rustfix
+
+use std::fmt::Debug;
+use std::marker::PhantomData;
+use std::convert::{self, TryFrom};
+
+#[allow(unused)]
+struct Codec<EncodeLine, DecodeLine> {
+    phantom_decode: PhantomData<DecodeLine>,
+    phantom_encode: PhantomData<EncodeLine>,
+}
+
+pub enum ParseError {}
+
+impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
+    DecodeLine: Debug + convert::TryFrom<String>,
+    DecodeLine: convert::TryFrom<String, Error = ParseError>,
+    //~^ ERROR expected trait, found enum `ParseError`
+    //~| HELP constrain the associated type to `ParseError`
+{
+}
+
+impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
+    DecodeLine: Debug + TryFrom<String>,
+    DecodeLine: TryFrom<String, Error = ParseError>,
+    //~^ ERROR expected trait, found enum `ParseError`
+    //~| HELP constrain the associated type to `ParseError`
+{
+}
+
+fn main() {}
diff --git a/tests/ui/resolve/issue-112472-multi-generics-suggestion.rs b/tests/ui/resolve/issue-112472-multi-generics-suggestion.rs
new file mode 100644
index 00000000000..2b2f5f1ad8d
--- /dev/null
+++ b/tests/ui/resolve/issue-112472-multi-generics-suggestion.rs
@@ -0,0 +1,31 @@
+// run-rustfix
+
+use std::fmt::Debug;
+use std::marker::PhantomData;
+use std::convert::{self, TryFrom};
+
+#[allow(unused)]
+struct Codec<EncodeLine, DecodeLine> {
+    phantom_decode: PhantomData<DecodeLine>,
+    phantom_encode: PhantomData<EncodeLine>,
+}
+
+pub enum ParseError {}
+
+impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
+    DecodeLine: Debug + convert::TryFrom<String>,
+    <DecodeLine as convert::TryFrom<String>>::Error: ParseError,
+    //~^ ERROR expected trait, found enum `ParseError`
+    //~| HELP constrain the associated type to `ParseError`
+{
+}
+
+impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
+    DecodeLine: Debug + TryFrom<String>,
+    <DecodeLine as TryFrom<String>>::Error: ParseError,
+    //~^ ERROR expected trait, found enum `ParseError`
+    //~| HELP constrain the associated type to `ParseError`
+{
+}
+
+fn main() {}
diff --git a/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr b/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr
new file mode 100644
index 00000000000..f463e2dad2c
--- /dev/null
+++ b/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr
@@ -0,0 +1,25 @@
+error[E0404]: expected trait, found enum `ParseError`
+  --> $DIR/issue-112472-multi-generics-suggestion.rs:17:54
+   |
+LL |     <DecodeLine as convert::TryFrom<String>>::Error: ParseError,
+   |                                                      ^^^^^^^^^^ not a trait
+   |
+help: constrain the associated type to `ParseError`
+   |
+LL |     DecodeLine: convert::TryFrom<String, Error = ParseError>,
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0404]: expected trait, found enum `ParseError`
+  --> $DIR/issue-112472-multi-generics-suggestion.rs:25:45
+   |
+LL |     <DecodeLine as TryFrom<String>>::Error: ParseError,
+   |                                             ^^^^^^^^^^ not a trait
+   |
+help: constrain the associated type to `ParseError`
+   |
+LL |     DecodeLine: TryFrom<String, Error = ParseError>,
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0404`.