diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index ac9e7d06c4e40..83b7e13905aee 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 3f80728a2606b..59239b49eddd8 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 + 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 c465f8c948a80..5c01b7ea70a13 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 5e13ee7b8a41d..c66fcdec7164c 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 e990c771bdf50..89fa5713b73ef 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>, + #[subdiagnostic] + pub sign_bit_sub: Option>, } 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 264a59c5585a0..ec9e7c7fdae83 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 f53dc8cb0ec13..ae32a54be3ded 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 e4b01ef2b1710..7284b33f09d8e 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::>() - .join("::"), - path.segments[position..] - .iter() - .map(|segment| path_segment_to_string(segment)) - .collect::>() - .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 { + 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 d0eaa9fd7ffac..0c65b25fe1389 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 c4134dbcd2507..45e5b76272e1d 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 b512135d92770..12a8b2b8db4b6 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 0000000000000..593428b8a70a5 --- /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 8ecf05f0e121f..046d018543f38 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` 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 3f60a90f87a22..dc902f8cb0242 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 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 0000000000000..b0c2254018048 --- /dev/null +++ b/tests/rustdoc/issue-109449-doc-hidden-reexports.rs @@ -0,0 +1,143 @@ +// Test to enforce rules over re-exports inlining from +// . + +#![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 62cb1f7f4aa37..e7c90dcc81bb2 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 0000000000000..15f380f7fb471 --- /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() { + 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 0000000000000..892697493b7a9 --- /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 { + phantom_decode: PhantomData, + phantom_encode: PhantomData, +} + +pub enum ParseError {} + +impl Codec where + DecodeLine: Debug + convert::TryFrom, + DecodeLine: convert::TryFrom, + //~^ ERROR expected trait, found enum `ParseError` + //~| HELP constrain the associated type to `ParseError` +{ +} + +impl Codec where + DecodeLine: Debug + TryFrom, + DecodeLine: TryFrom, + //~^ 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 0000000000000..2b2f5f1ad8d07 --- /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 { + phantom_decode: PhantomData, + phantom_encode: PhantomData, +} + +pub enum ParseError {} + +impl Codec where + DecodeLine: Debug + convert::TryFrom, + >::Error: ParseError, + //~^ ERROR expected trait, found enum `ParseError` + //~| HELP constrain the associated type to `ParseError` +{ +} + +impl Codec where + DecodeLine: Debug + TryFrom, + >::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 0000000000000..f463e2dad2cfc --- /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 | >::Error: ParseError, + | ^^^^^^^^^^ not a trait + | +help: constrain the associated type to `ParseError` + | +LL | DecodeLine: convert::TryFrom, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0404]: expected trait, found enum `ParseError` + --> $DIR/issue-112472-multi-generics-suggestion.rs:25:45 + | +LL | >::Error: ParseError, + | ^^^^^^^^^^ not a trait + | +help: constrain the associated type to `ParseError` + | +LL | DecodeLine: TryFrom, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0404`.