Auto merge of #112661 - matthiaskrgr:rollup-9u5i2zy, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #111212 (Add casting suggestion when assigning negative 2's complement bin or hex literal to a size compatible signed integer) - #112304 (Add chapter in rustdoc book for re-exports and add a regression test for `#[doc(hidden)]` behaviour) - #112486 (Fix suggestion for E0404 not dealing with multiple generics) - #112562 (rustdoc-gui: allow running on Windows) - #112621 (Mention `env!` in `option_env!`'s docs) - #112634 (add InlineConst check) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
331249a949
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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}`
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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!(
|
||||
|
@ -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.
|
||||
#
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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)
|
||||
|
172
src/doc/rustdoc/src/write-documentation/re-exports.md
Normal file
172
src/doc/rustdoc/src/write-documentation/re-exports.md
Normal file
@ -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.
|
@ -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`
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
143
tests/rustdoc/issue-109449-doc-hidden-reexports.rs
Normal file
143
tests/rustdoc/issue-109449-doc-hidden-reexports.rs
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
11
tests/ui/match/issue-112438.rs
Normal file
11
tests/ui/match/issue-112438.rs
Normal file
@ -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() {}
|
@ -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() {}
|
31
tests/ui/resolve/issue-112472-multi-generics-suggestion.rs
Normal file
31
tests/ui/resolve/issue-112472-multi-generics-suggestion.rs
Normal file
@ -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() {}
|
@ -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`.
|
Loading…
x
Reference in New Issue
Block a user