diff options
| author | Ben Kimock <kimockb@gmail.com> | 2023-03-05 12:27:42 -0500 |
|---|---|---|
| committer | Ben Kimock <kimockb@gmail.com> | 2023-03-05 12:27:42 -0500 |
| commit | d56332ece806af134fbbb017d5e989a68bb022e0 (patch) | |
| tree | 284242248144c20d716fdafea65858586dabbafe | |
| parent | e397cf42ee93a6d00c6247af76be98e66a4f621f (diff) | |
| parent | 740d476bbf9122b27c3aac18b5e4d2c8162cb576 (diff) | |
| download | rust-d56332ece806af134fbbb017d5e989a68bb022e0.tar.gz rust-d56332ece806af134fbbb017d5e989a68bb022e0.zip | |
Merge from rustc
761 files changed, 14359 insertions, 8627 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c17dfd8c8e..44f789c592b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -322,7 +322,7 @@ jobs: NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 - os: macos-latest + os: macos-12-xl - name: dist-apple-various env: SCRIPT: "./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim" @@ -333,7 +333,7 @@ jobs: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - os: macos-latest + os: macos-12-xl - name: dist-x86_64-apple-alt env: SCRIPT: "./x.py dist bootstrap --include-default-paths" @@ -344,7 +344,7 @@ jobs: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - os: macos-latest + os: macos-12-xl - name: x86_64-apple-1 env: SCRIPT: "./x.py --stage 2 test --exclude tests/ui --exclude tests/rustdoc --exclude tests/run-make-fulldeps" @@ -355,7 +355,7 @@ jobs: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - os: macos-latest + os: macos-12-xl - name: x86_64-apple-2 env: SCRIPT: "./x.py --stage 2 test tests/ui tests/rustdoc tests/run-make-fulldeps" @@ -366,7 +366,7 @@ jobs: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - os: macos-latest + os: macos-12-xl - name: dist-aarch64-apple env: SCRIPT: "./x.py dist bootstrap --include-default-paths --stage 2" @@ -381,7 +381,7 @@ jobs: NO_OVERFLOW_CHECKS: 1 DIST_REQUIRE_ALL_TOOLS: 1 JEMALLOC_SYS_WITH_LG_PAGE: 14 - os: macos-latest + os: macos-12-xl - name: x86_64-msvc-1 env: RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --enable-profiler" diff --git a/.mailmap b/.mailmap index 726d4c3d1d2..715bc4d3085 100644 --- a/.mailmap +++ b/.mailmap @@ -15,7 +15,7 @@ Adrien Tétar <adri-from-59@hotmail.fr> Ahmed Charles <ahmedcharles@gmail.com> <acharles@outlook.com> Alan Egerton <eggyal@gmail.com> Alan Stoate <alan.stoate@gmail.com> -Albert Larsan <albert.larsan@gmail.com> Albert Larsan <74931857+albertlarsan68@users.noreply.github.com> +Albert Larsan <albert.larsan@gmail.com> <74931857+albertlarsan68@users.noreply.github.com> Alessandro Decina <alessandro.d@gmail.com> Alex Burka <durka42+github@gmail.com> Alex Burka <aburka@seas.upenn.edu> Alex Hansen <ahansen2@trinity.edu> diff --git a/Cargo.lock b/Cargo.lock index f8268cbc46f..5530541cdd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1373,9 +1373,9 @@ dependencies = [ [[package]] name = "ena" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +checksum = "b2e5d13ca2353ab7d0230988629def93914a8c4015f621f9b13ed2955614731d" dependencies = [ "log", ] @@ -1924,15 +1924,6 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" @@ -2222,14 +2213,14 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.0", "io-lifetimes", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2260,9 +2251,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -2328,9 +2319,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" dependencies = [ "rustc-std-workspace-core", ] @@ -3810,6 +3801,8 @@ dependencies = [ "rustc_span", "rustc_symbol_mangling", "rustc_target", + "serde", + "serde_json", "smallvec", "tempfile", "tracing", diff --git a/README.md b/README.md index 0eb7c4b266a..c424bd12ffd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # The Rust Programming Language +[](https://www.rust-lang.org/community) + This is the main source code repository for [Rust]. It contains the compiler, standard library, and documentation. diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index aa3a666b0b2..39574ca558f 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1505,14 +1505,6 @@ pub struct PointeeInfo { pub safe: Option<PointerKind>, } -/// Used in `might_permit_raw_init` to indicate the kind of initialisation -/// that is checked to be valid -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum InitKind { - Zero, - UninitMitigated0x01Fill, -} - impl LayoutS { /// Returns `true` if the layout corresponds to an unsized type. pub fn is_unsized(&self) -> bool { diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 81efdaa44b3..3a0af04f9eb 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -225,7 +225,7 @@ impl AssocOp { AssignOp(_) | // `{ 42 } +=` As | // `{ 42 } as usize` // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect - // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. + // NotEqual | // `{ 42 } != { 42 }` struct literals parser recovery. Colon, // `{ 42 }: usize` ) } diff --git a/compiler/rustc_ast_lowering/locales/en-US.ftl b/compiler/rustc_ast_lowering/locales/en-US.ftl index a2837deafde..3ccd84398ec 100644 --- a/compiler/rustc_ast_lowering/locales/en-US.ftl +++ b/compiler/rustc_ast_lowering/locales/en-US.ftl @@ -22,9 +22,6 @@ ast_lowering_misplaced_impl_trait = ast_lowering_misplaced_assoc_ty_binding = associated type bounds are only allowed in where clauses and function signatures, not in {$position} -ast_lowering_rustc_box_attribute_error = - #[rustc_box] requires precisely one argument and no other attributes are allowed - ast_lowering_underscore_expr_lhs_assign = in expressions, `_` can only be used on the left-hand side of an assignment .label = `_` not allowed here diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index def74c2adee..5e6b6050bc0 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -88,13 +88,6 @@ pub struct MisplacedAssocTyBinding<'a> { } #[derive(Diagnostic, Clone, Copy)] -#[diag(ast_lowering_rustc_box_attribute_error)] -pub struct RustcBoxAttributeError { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic, Clone, Copy)] #[diag(ast_lowering_underscore_expr_lhs_assign)] pub struct UnderscoreExprLhsAssign { #[primary_span] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index d4fafe38638..ffb30b1b391 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -2,7 +2,7 @@ use super::errors::{ AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt, GeneratorTooManyParameters, InclusiveRangeWithNoEnd, NotSupportedForLifetimeBinderAsyncClosure, - RustcBoxAttributeError, UnderscoreExprLhsAssign, + UnderscoreExprLhsAssign, }; use super::ResolverAstLoweringExt; use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; @@ -83,15 +83,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::Tup(elts) => hir::ExprKind::Tup(self.lower_exprs(elts)), ExprKind::Call(f, args) => { - if e.attrs.get(0).map_or(false, |a| a.has_name(sym::rustc_box)) { - if let [inner] = &args[..] && e.attrs.len() == 1 { - let kind = hir::ExprKind::Box(self.lower_expr(&inner)); - return hir::Expr { hir_id, kind, span: self.lower_span(e.span) }; - } else { - let guar = self.tcx.sess.emit_err(RustcBoxAttributeError { span: e.span }); - hir::ExprKind::Err(guar) - } - } else if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) { + if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) { self.lower_legacy_const_generics((**f).clone(), args.clone(), &legacy_args) } else { let f = self.lower_expr(f); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7325bce6055..debe0acb04e 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -13,7 +13,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::PredicateOrigin; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::ty::{DefIdTree, ResolverAstLowering, TyCtxt}; +use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; @@ -1339,13 +1339,19 @@ impl<'hir> LoweringContext<'_, 'hir> { .map(|predicate| self.lower_where_predicate(predicate)), ); - let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> = - self.lower_generic_params_mut(&generics.params).collect(); + let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> = self + .lower_generic_params_mut(&generics.params, hir::GenericParamSource::Generics) + .collect(); // Introduce extra lifetimes if late resolution tells us to. let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id); params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { - self.lifetime_res_to_generic_param(ident, node_id, res) + self.lifetime_res_to_generic_param( + ident, + node_id, + res, + hir::GenericParamSource::Generics, + ) })); let has_where_clause_predicates = !generics.where_clause.predicates.is_empty(); @@ -1449,7 +1455,8 @@ impl<'hir> LoweringContext<'_, 'hir> { span, }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { hir_id: self.next_id(), - bound_generic_params: self.lower_generic_params(bound_generic_params), + bound_generic_params: self + .lower_generic_params(bound_generic_params, hir::GenericParamSource::Binder), bounded_ty: self .lower_ty(bounded_ty, &ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 5d78d914b6d..b20157f2c7c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -804,6 +804,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ident: Ident, node_id: NodeId, res: LifetimeRes, + source: hir::GenericParamSource, ) -> Option<hir::GenericParam<'hir>> { let (name, kind) = match res { LifetimeRes::Param { .. } => { @@ -837,6 +838,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, colon_span: None, + source, }) } @@ -852,11 +854,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { binder: NodeId, generic_params: &[GenericParam], ) -> &'hir [hir::GenericParam<'hir>] { - let mut generic_params: Vec<_> = self.lower_generic_params_mut(generic_params).collect(); + let mut generic_params: Vec<_> = self + .lower_generic_params_mut(generic_params, hir::GenericParamSource::Binder) + .collect(); let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder); debug!(?extra_lifetimes); generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { - self.lifetime_res_to_generic_param(ident, node_id, res) + self.lifetime_res_to_generic_param(ident, node_id, res, hir::GenericParamSource::Binder) })); let generic_params = self.arena.alloc_from_iter(generic_params); debug!(?generic_params); @@ -1375,8 +1379,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, ); let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span); - let (param, bounds, path) = - self.lower_generic_and_bounds(*def_node_id, span, ident, bounds); + let (param, bounds, path) = self.lower_universal_param_and_bounds( + *def_node_id, + span, + ident, + bounds, + ); self.impl_trait_defs.push(param); if let Some(bounds) = bounds { self.impl_trait_bounds.push(bounds); @@ -1530,6 +1538,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, colon_span: None, + source: hir::GenericParamSource::Generics, } }, )); @@ -1598,7 +1607,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::OwnerNode::Item(self.arena.alloc(opaque_ty_item)) } - /// Given a `parent_def_id`, a list of `lifetimes_in_bounds and a `remapping` hash to be + /// Given a `parent_def_id`, a list of `lifetimes_in_bounds` and a `remapping` hash to be /// filled, this function creates new definitions for `Param` and `Fresh` lifetimes, inserts the /// new definition, adds it to the remapping with the definition of the given lifetime and /// returns a list of lifetimes to be lowered afterwards. @@ -1987,6 +1996,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, colon_span: None, + source: hir::GenericParamSource::Generics, } }, )); @@ -2152,16 +2162,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_generic_params_mut<'s>( &'s mut self, params: &'s [GenericParam], + source: hir::GenericParamSource, ) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> { - params.iter().map(move |param| self.lower_generic_param(param)) + params.iter().map(move |param| self.lower_generic_param(param, source)) } - fn lower_generic_params(&mut self, params: &[GenericParam]) -> &'hir [hir::GenericParam<'hir>] { - self.arena.alloc_from_iter(self.lower_generic_params_mut(params)) + fn lower_generic_params( + &mut self, + params: &[GenericParam], + source: hir::GenericParamSource, + ) -> &'hir [hir::GenericParam<'hir>] { + self.arena.alloc_from_iter(self.lower_generic_params_mut(params, source)) } #[instrument(level = "trace", skip(self))] - fn lower_generic_param(&mut self, param: &GenericParam) -> hir::GenericParam<'hir> { + fn lower_generic_param( + &mut self, + param: &GenericParam, + source: hir::GenericParamSource, + ) -> hir::GenericParam<'hir> { let (name, kind) = self.lower_generic_param_kind(param); let hir_id = self.lower_node_id(param.id); @@ -2174,6 +2193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pure_wrt_drop: self.tcx.sess.contains_name(¶m.attrs, sym::may_dangle), kind, colon_span: param.colon_span.map(|s| self.lower_span(s)), + source, } } @@ -2266,7 +2286,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } #[instrument(level = "debug", skip(self), ret)] - fn lower_generic_and_bounds( + fn lower_universal_param_and_bounds( &mut self, node_id: NodeId, span: Span, @@ -2286,6 +2306,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, kind: hir::GenericParamKind::Type { default: None, synthetic: true }, colon_span: None, + source: hir::GenericParamSource::Generics, }; let preds = self.lower_generic_bound_predicate( diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1c561375626..539c822ea09 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -12,7 +12,7 @@ use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}; use rustc_ast::walk_list; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_macros::Subdiagnostic; use rustc_parse::validate_attr; use rustc_session::lint::builtin::{ @@ -192,7 +192,7 @@ impl<'a> AstValidator<'a> { // We allow these: // - `Option<impl Trait>` // - `option::Option<impl Trait>` - // - `option::Option<T>::Foo<impl Trait> + // - `option::Option<T>::Foo<impl Trait>` // // But not these: // - `<impl Trait>::Foo` @@ -643,7 +643,7 @@ fn validate_generic_param_order( span: Span, ) { let mut max_param: Option<ParamKindOrd> = None; - let mut out_of_order = FxHashMap::default(); + let mut out_of_order = FxIndexMap::default(); let mut param_idents = Vec::with_capacity(generics.len()); for (idx, param) in generics.iter().enumerate() { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 926b0da2ec6..96042ea3078 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -386,7 +386,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ).span_suggestion_verbose( lhs.span.shrink_to_lo(), "you might have meant to introduce a new binding", - "let ".to_string(), + "let ", Applicability::MachineApplicable, ).emit(); } diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index b9dcaee2373..6207a32b28d 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,7 +4,6 @@ //! //! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. -#![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_is_partitioned)] diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7a1066f6b58..a622a215a2e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,7 +6,7 @@ use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; use rustc_infer::infer::TyCtxtInferExt; @@ -236,10 +236,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = used_place.ty(self.body, self.infcx.tcx).ty; let needs_note = match ty.kind() { ty::Closure(id, _) => { - let tables = self.infcx.tcx.typeck(id.expect_local()); - let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(id.expect_local()); - - tables.closure_kind_origins().get(hir_id).is_none() + self.infcx.tcx.closure_kind_origin(id.expect_local()).is_none() } _ => true, }; @@ -1470,6 +1467,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`. /// + /// Depending on the origin of the StorageDeadOrDrop, this may be + /// reported as either a drop or an illegal mutation of a borrowed value. + /// The latter is preferred when the this is a drop triggered by a + /// reassignment, as it's more user friendly to report a problem with the + /// explicit assignment than the implicit drop. + #[instrument(level = "debug", skip(self))] + pub(crate) fn report_storage_dead_or_drop_of_borrowed( + &mut self, + location: Location, + place_span: (Place<'tcx>, Span), + borrow: &BorrowData<'tcx>, + ) { + // It's sufficient to check the last desugaring as Replace is the last + // one to be applied. + if let Some(DesugaringKind::Replace) = place_span.1.desugaring_kind() { + self.report_illegal_mutation_of_borrowed(location, place_span, borrow) + } else { + self.report_borrowed_value_does_not_live_long_enough( + location, + borrow, + place_span, + Some(WriteKind::StorageDeadOrDrop), + ) + } + } + /// This means that some data referenced by `borrow` needs to live /// past the point where the StorageDeadOrDrop of `place` occurs. /// This is usually interpreted as meaning that `place` has too @@ -1670,7 +1693,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("`{}` would have to be valid for `{}`...", name, region_name), ); - let fn_hir_id = self.mir_hir_id(); err.span_label( drop_span, format!( @@ -1678,19 +1700,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { name, self.infcx .tcx - .hir() - .opt_name(fn_hir_id) + .opt_item_name(self.mir_def_id().to_def_id()) .map(|name| format!("function `{}`", name)) .unwrap_or_else(|| { - match &self - .infcx - .tcx - .typeck(self.mir_def_id()) - .node_type(fn_hir_id) - .kind() - { - ty::Closure(..) => "enclosing closure", - ty::Generator(..) => "enclosing generator", + match &self.infcx.tcx.def_kind(self.mir_def_id()) { + DefKind::Closure => "enclosing closure", + DefKind::Generator => "enclosing generator", kind => bug!("expected closure or generator, found {:?}", kind), } .to_string() diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index f5bd99f15ab..af705e6a80f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::{ Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::print::Print; -use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; @@ -115,11 +115,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { let did = did.expect_local(); - let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - - if let Some((span, hir_place)) = - self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) - { + if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { diag.span_note( *span, &format!( @@ -139,11 +135,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(target) = target { if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { let did = did.expect_local(); - let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(did); - - if let Some((span, hir_place)) = - self.infcx.tcx.typeck(did).closure_kind_origins().get(hir_id) - { + if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { diag.span_note( *span, &format!( @@ -373,14 +365,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // // We know the field exists so it's safe to call operator[] and `unwrap` here. let def_id = def_id.expect_local(); - let var_id = self - .infcx - .tcx - .typeck(def_id) - .closure_min_captures_flattened(def_id) - .nth(field.index()) - .unwrap() - .get_root_variable(); + let var_id = + self.infcx.tcx.closure_captures(def_id)[field.index()].get_root_variable(); Some(self.infcx.tcx.hir().name(var_id).to_string()) } @@ -939,7 +925,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return OtherUse(use_span); } - for stmt in &self.body[location.block].statements[location.statement_index + 1..] { + // drop and replace might have moved the assignment to the next block + let maybe_additional_statement = + if let TerminatorKind::Drop { target: drop_target, .. } = + self.body[location.block].terminator().kind + { + self.body[drop_target].statements.first() + } else { + None + }; + + let statements = + self.body[location.block].statements[location.statement_index + 1..].iter(); + + for stmt in statements.chain(maybe_additional_statement) { if let StatementKind::Assign(box (_, Rvalue::Aggregate(kind, places))) = &stmt.kind { let (&def_id, is_generator) = match kind { box AggregateKind::Closure(def_id, _) => (def_id, false), @@ -987,7 +986,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr); if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = expr { for (captured_place, place) in - self.infcx.tcx.typeck(def_id).closure_min_captures_flattened(def_id).zip(places) + self.infcx.tcx.closure_captures(def_id).iter().zip(places) { match place { Operand::Copy(place) | Operand::Move(place) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index ea58ad5ae3e..5e4c7292e59 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -467,7 +467,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_suggestion_verbose( span.shrink_to_lo(), "consider borrowing here", - "&".to_string(), + '&', Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 9f37b915b77..eded913ae96 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -385,7 +385,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_suggestion_verbose( local_decl.source_info.span.shrink_to_lo(), "consider changing this to be mutable", - "mut ".to_string(), + "mut ", Applicability::MachineApplicable, ); let tcx = self.infcx.tcx; @@ -828,7 +828,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let Some(hir::Node::Item(item)) = node else { return; }; let hir::ItemKind::Fn(.., body_id) = item.kind else { return; }; let body = self.infcx.tcx.hir().body(body_id); - let mut v = V { assign_span: span, err, ty, suggested: false }; + let mut assign_span = span; + // Drop desugaring is done at MIR build so it's not in the HIR + if let Some(DesugaringKind::Replace) = span.desugaring_kind() { + assign_span.remove_mark(); + } + + let mut v = V { assign_span, err, ty, suggested: false }; v.visit_body(body); if !v.suggested { err.help(&format!( @@ -901,10 +907,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err: &mut Diagnostic, ) { let tables = tcx.typeck(closure_local_def_id); - let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id); - if let Some((span, closure_kind_origin)) = - &tables.closure_kind_origins().get(closure_hir_id) - { + if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) { let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base { let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin); let root_hir_id = upvar_id.var_path.hir_id; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 4baf1b6aa87..ffe82b46cfd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -415,7 +415,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x } /// ``` /// - /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`. + /// Here we would be invoked with `fr = 'a` and `outlived_fr = 'b`. pub(crate) fn report_region_error( &mut self, fr: RegionVid, @@ -949,7 +949,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); multi_span.push_span_label( ident.span, - "calling this method introduces the `impl`'s 'static` requirement", + "calling this method introduces the `impl`'s `'static` requirement", ); err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span }); err.span_suggestion_verbose( diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index f6881a2e5bc..cc5a1f5ab12 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, RegionVid, Ty}; +use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 18d7bde60d7..115e512a974 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -202,14 +202,14 @@ fn do_mir_borrowck<'tcx>( let mut errors = error::BorrowckErrors::new(infcx.tcx); // Gather the upvars of a closure, if any. - let tables = tcx.typeck_opt_const_arg(def); - if let Some(e) = tables.tainted_by_errors { + if let Some(e) = input_body.tainted_by_errors { infcx.set_tainted_by_errors(e); errors.set_tainted_by_errors(e); } - let upvars: Vec<_> = tables - .closure_min_captures_flattened(def.did) - .map(|captured_place| { + let upvars: Vec<_> = tcx + .closure_captures(def.did) + .iter() + .map(|&captured_place| { let capture = captured_place.info.capture_kind; let by_ref = match capture { ty::UpvarCapture::ByValue => false, @@ -1185,12 +1185,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { this.buffer_error(err); } WriteKind::StorageDeadOrDrop => this - .report_borrowed_value_does_not_live_long_enough( - location, - borrow, - place_span, - Some(kind), - ), + .report_storage_dead_or_drop_of_borrowed(location, place_span, borrow), WriteKind::Mutate => { this.report_illegal_mutation_of_borrowed(location, place_span, borrow) } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 941da2dd3b5..21b5bd7cb94 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -889,7 +889,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// from a universe it can't name; at present, the only way for /// this to be true is if `scc` outlives `'static`. This is /// actually stricter than necessary: ideally, we'd support bounds - /// like `for<'a: 'b`>` that might then allow us to approximate + /// like `for<'a: 'b>` that might then allow us to approximate /// `'a` with `'b` and not `'static`. But it will have to do for /// now. fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index c361357ca21..8132800f107 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -235,7 +235,7 @@ pub(crate) struct RegionValues<N: Idx> { free_regions: SparseBitMatrix<N, RegionVid>, /// Placeholders represent bound regions -- so something like `'a` - /// in for<'a> fn(&'a u32)`. + /// in `for<'a> fn(&'a u32)`. placeholders: SparseBitMatrix<N, PlaceholderIndex>, } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index c6b78df9a5f..717020ea5b8 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -26,11 +26,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if !self.tcx().is_closure(mir_def_id.to_def_id()) { return; } - let Some(user_provided_poly_sig) = - self.tcx().typeck(mir_def_id).user_provided_sigs.get(&mir_def_id) - else { - return; - }; + let user_provided_poly_sig = self.tcx().closure_user_provided_sig(mir_def_id); // Instantiate the canonicalized variables from user-provided signature // (e.g., the `_` in the code above) with fresh variables. diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index e058fe0db22..76d0b875424 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -18,13 +18,11 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_hir::{BodyOwnerKind, HirId}; +use rustc_hir::BodyOwnerKind; use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::{ - self, DefIdTree, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt, -}; +use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_span::Symbol; use std::iter; @@ -231,9 +229,7 @@ impl<'tcx> UniversalRegions<'tcx> { mir_def: ty::WithOptConstParam<LocalDefId>, param_env: ty::ParamEnv<'tcx>, ) -> Self { - let tcx = infcx.tcx; - let mir_hir_id = tcx.hir().local_def_id_to_hir_id(mir_def.did); - UniversalRegionsBuilder { infcx, mir_def, mir_hir_id, param_env }.build() + UniversalRegionsBuilder { infcx, mir_def, param_env }.build() } /// Given a reference to a closure type, extracts all the values @@ -390,7 +386,6 @@ impl<'tcx> UniversalRegions<'tcx> { struct UniversalRegionsBuilder<'cx, 'tcx> { infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>, mir_def: ty::WithOptConstParam<LocalDefId>, - mir_hir_id: HirId, param_env: ty::ParamEnv<'tcx>, } @@ -560,12 +555,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { match tcx.hir().body_owner_kind(self.mir_def.did) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - let defining_ty = if self.mir_def.did.to_def_id() == typeck_root_def_id { - tcx.type_of(typeck_root_def_id).subst_identity() - } else { - let tables = tcx.typeck(self.mir_def.did); - tables.node_type(self.mir_hir_id) - }; + let defining_ty = tcx.type_of(self.mir_def.def_id_for_type_of()).subst_identity(); debug!("defining_ty (pre-replacement): {:?}", defining_ty); @@ -594,7 +584,18 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { self.infcx.replace_free_regions_with_nll_infer_vars(FR, identity_substs); DefiningTy::Const(self.mir_def.did.to_def_id(), substs) } else { - let ty = tcx.typeck(self.mir_def.did).node_type(self.mir_hir_id); + // FIXME this line creates a dependency between borrowck and typeck. + // + // This is required for `AscribeUserType` canonical query, which will call + // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes + // into borrowck, which is ICE #78174. + // + // As a workaround, inline consts have an additional generic param (`ty` + // below), so that `type_of(inline_const_def_id).substs(substs)` uses the + // proper type with NLL infer vars. + let ty = tcx + .typeck(self.mir_def.did) + .node_type(tcx.local_def_id_to_hir_id(self.mir_def.did)); let substs = InlineConstSubsts::new( tcx, InlineConstSubstsParts { parent_substs: identity_substs, ty }, diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 6aa900f0201..f011cb754cb 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -53,7 +53,7 @@ pub fn expand_env<'cx>( tts: TokenStream, ) -> Box<dyn base::MacResult + 'cx> { let mut exprs = match get_exprs_from_tts(cx, tts) { - Some(exprs) if exprs.is_empty() => { + Some(exprs) if exprs.is_empty() || exprs.len() > 2 => { cx.span_err(sp, "env! takes 1 or 2 arguments"); return DummyResult::any(sp); } @@ -64,28 +64,48 @@ pub fn expand_env<'cx>( let Some((var, _style)) = expr_to_string(cx, exprs.next().unwrap(), "expected string literal") else { return DummyResult::any(sp); }; - let msg = match exprs.next() { - None => Symbol::intern(&format!("environment variable `{}` not defined", var)), + + let custom_msg = match exprs.next() { + None => None, Some(second) => match expr_to_string(cx, second, "expected string literal") { None => return DummyResult::any(sp), - Some((s, _style)) => s, + Some((s, _style)) => Some(s), }, }; - if exprs.next().is_some() { - cx.span_err(sp, "env! takes 1 or 2 arguments"); - return DummyResult::any(sp); - } - let sp = cx.with_def_site_ctxt(sp); let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { - cx.span_err(sp, msg.as_str()); + let (msg, help) = match custom_msg { + None => ( + format!("environment variable `{var}` not defined at compile time"), + Some(help_for_missing_env_var(var.as_str())), + ), + Some(s) => (s.to_string(), None), + }; + let mut diag = cx.struct_span_err(sp, &msg); + if let Some(help) = help { + diag.help(help); + } + diag.emit(); return DummyResult::any(sp); } Some(value) => cx.expr_str(sp, value), }; MacEager::expr(e) } + +fn help_for_missing_env_var(var: &str) -> String { + if var.starts_with("CARGO_") + || var.starts_with("DEP_") + || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") + { + format!( + "Cargo sets build script variables at run time. Use `std::env::var(\"{var}\")` instead" + ) + } else { + format!("Use `std::env::var(\"{var}\")` to read the variable at run time") + } +} diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 6feb3a7732e..e74aabf2fcb 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -21,7 +21,8 @@ mod simd; pub(crate) use cpuid::codegen_cpuid_call; pub(crate) use llvm::codegen_llvm_intrinsic_call; -use rustc_middle::ty::layout::HasParamEnv; +use rustc_middle::ty; +use rustc_middle::ty::layout::{HasParamEnv, ValidityRequirement}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::SubstsRef; use rustc_span::symbol::{kw, sym, Symbol}; @@ -627,54 +628,39 @@ fn codegen_regular_intrinsic_call<'tcx>( intrinsic_args!(fx, args => (); intrinsic); let ty = substs.type_at(0); - let layout = fx.layout_of(ty); - if layout.abi.is_uninhabited() { - with_no_trimmed_paths!({ - crate::base::codegen_panic_nounwind( - fx, - &format!("attempted to instantiate uninhabited type `{}`", layout.ty), - source_info, - ) - }); - return; - } - if intrinsic == sym::assert_zero_valid - && !fx - .tcx - .permits_zero_init(fx.param_env().and(ty)) - .expect("expected to have layout during codegen") - { - with_no_trimmed_paths!({ - crate::base::codegen_panic_nounwind( - fx, - &format!( - "attempted to zero-initialize type `{}`, which is invalid", - layout.ty - ), - source_info, - ); - }); - return; - } + let requirement = ValidityRequirement::from_intrinsic(intrinsic); - if intrinsic == sym::assert_mem_uninitialized_valid - && !fx + if let Some(requirement) = requirement { + let do_panic = !fx .tcx - .permits_uninit_init(fx.param_env().and(ty)) - .expect("expected to have layout during codegen") - { - with_no_trimmed_paths!({ - crate::base::codegen_panic_nounwind( - fx, - &format!( - "attempted to leave type `{}` uninitialized, which is invalid", - layout.ty - ), - source_info, - ) - }); - return; + .check_validity_requirement((requirement, fx.param_env().and(ty))) + .expect("expect to have layout during codegen"); + + if do_panic { + let layout = fx.layout_of(ty); + + with_no_trimmed_paths!({ + crate::base::codegen_panic_nounwind( + fx, + &if layout.abi.is_uninhabited() { + format!("attempted to instantiate uninhabited type `{}`", layout.ty) + } else if requirement == ValidityRequirement::Zero { + format!( + "attempted to zero-initialize type `{}`, which is invalid", + layout.ty + ) + } else { + format!( + "attempted to leave type `{}` uninitialized, which is invalid", + layout.ty + ) + }, + source_info, + ) + }); + return; + } } } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 773c0ebbe59..a7ba2f8b695 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -36,3 +36,5 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } tempfile = "3.2.0" +serde = { version = "1", features = [ "derive" ]} +serde_json = "1" diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 40f0594b40d..a4ae1b01e86 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -761,6 +761,7 @@ pub(crate) unsafe fn codegen( EmitObj::None => {} } + record_llvm_cgu_instructions_stats(&cgcx.prof, llmod); drop(handlers); } @@ -974,3 +975,23 @@ fn record_artifact_size( self_profiler_ref.artifact_size(artifact_kind, artifact_name.to_string_lossy(), file_size); } } + +fn record_llvm_cgu_instructions_stats(prof: &SelfProfilerRef, llmod: &llvm::Module) { + if !prof.enabled() { + return; + } + + let raw_stats = + llvm::build_string(|s| unsafe { llvm::LLVMRustModuleInstructionStats(&llmod, s) }) + .expect("cannot get module instruction stats"); + + #[derive(serde::Deserialize)] + struct InstructionsStats { + module: String, + total: u64, + } + + let InstructionsStats { module, total } = + serde_json::from_str(&raw_stats).expect("cannot parse llvm cgu instructions stats"); + prof.artifact_size("cgu_instructions", module, total); +} diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index edb1c160626..b0a9a30ab46 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -10,6 +10,7 @@ use crate::value::Value; use rustc_ast::Mutability; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; @@ -252,8 +253,13 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), _ => self.static_addr_of(init, alloc.align, None), }; - if !self.sess().fewer_names() { - llvm::set_value_name(value, format!("{:?}", alloc_id).as_bytes()); + if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() { + let hash = self.tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + alloc.hash_stable(&mut hcx, &mut hasher); + hasher.finish::<u128>() + }); + llvm::set_value_name(value, format!("alloc_{hash:032x}").as_bytes()); } (value, AddressSpace::DATA) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 5cd0e1cb63a..6bcd3e5bf58 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -5,7 +5,7 @@ use super::CodegenUnitDebugContext; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; -use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_middle::ty::{self, Ty}; use trace; use crate::common::CodegenCx; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7aab666fc5e..253c2ca7c76 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1814,8 +1814,6 @@ extern "C" { /// Creates a legacy pass manager -- only used for final codegen. pub fn LLVMCreatePassManager<'a>() -> &'a mut PassManager<'a>; - pub fn LLVMInitializePasses(); - pub fn LLVMTimeTraceProfilerInitialize(); pub fn LLVMTimeTraceProfilerFinishThread(); @@ -2410,6 +2408,8 @@ extern "C" { pub fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); pub fn LLVMRustModuleCost(M: &Module) -> u64; + #[allow(improper_ctypes)] + pub fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); pub fn LLVMRustThinLTOBufferCreate(M: &Module, is_thin: bool) -> &'static mut ThinLTOBuffer; pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 20b1dd94153..ba58a2e68e9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -120,8 +120,6 @@ unsafe fn configure_llvm(sess: &Session) { llvm::LLVMTimeTraceProfilerInitialize(); } - llvm::LLVMInitializePasses(); - rustc_llvm::initialize_available_targets(); llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 019ec0758d6..4ab56699922 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -306,7 +306,13 @@ pub fn create_compressed_metadata_file( symbol_name: &str, ) -> Vec<u8> { let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); + // Our length will be backfilled once we're done writing + compressed.write_all(&[0; 4]).unwrap(); FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap(); + let meta_len = rustc_metadata::METADATA_HEADER.len(); + let data_len = (compressed.len() - meta_len - 4) as u32; + compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes()); + let Some(mut file) = create_object_file(sess) else { return compressed.to_vec(); }; diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 067a3e167fe..fd81b1c6fe1 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -11,7 +11,7 @@ use rustc_middle::middle::exported_symbols::{ use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::Instance; -use rustc_middle::ty::{self, DefIdTree, SymbolName, TyCtxt}; +use rustc_middle::ty::{self, SymbolName, TyCtxt}; use rustc_session::config::{CrateType, OomStrategy}; use rustc_target::spec::SanitizerSet; diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 7d5c0048626..c62968e5354 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -8,7 +8,7 @@ use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self as ty, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::{lint, parse::feature_err}; use rustc_span::{sym, Span}; use rustc_target::spec::{abi, SanitizerSet}; diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f6c1b7a98aa..57a19a4ab1e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -14,7 +14,7 @@ use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::lang_items::LangItem; use rustc_index::vec::Idx; use rustc_middle::mir::{self, AssertKind, SwitchTargets}; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; use rustc_session::config::OptLevel; @@ -655,41 +655,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Emit a panic or a no-op for `assert_*` intrinsics. // These are intrinsics that compile to panics so that we can get a message // which mentions the offending type, even from a const context. - #[derive(Debug, PartialEq)] - enum AssertIntrinsic { - Inhabited, - ZeroValid, - MemUninitializedValid, - } - let panic_intrinsic = intrinsic.and_then(|i| match i { - sym::assert_inhabited => Some(AssertIntrinsic::Inhabited), - sym::assert_zero_valid => Some(AssertIntrinsic::ZeroValid), - sym::assert_mem_uninitialized_valid => Some(AssertIntrinsic::MemUninitializedValid), - _ => None, - }); - if let Some(intrinsic) = panic_intrinsic { - use AssertIntrinsic::*; - + let panic_intrinsic = intrinsic.and_then(|s| ValidityRequirement::from_intrinsic(s)); + if let Some(requirement) = panic_intrinsic { let ty = instance.unwrap().substs.type_at(0); + + let do_panic = !bx + .tcx() + .check_validity_requirement((requirement, bx.param_env().and(ty))) + .expect("expect to have layout during codegen"); + let layout = bx.layout_of(ty); - let do_panic = match intrinsic { - Inhabited => layout.abi.is_uninhabited(), - ZeroValid => !bx - .tcx() - .permits_zero_init(bx.param_env().and(ty)) - .expect("expected to have layout during codegen"), - MemUninitializedValid => !bx - .tcx() - .permits_uninit_init(bx.param_env().and(ty)) - .expect("expected to have layout during codegen"), - }; + Some(if do_panic { let msg_str = with_no_visible_paths!({ with_no_trimmed_paths!({ if layout.abi.is_uninhabited() { // Use this error even for the other intrinsics as it is more precise. format!("attempted to instantiate uninhabited type `{}`", ty) - } else if intrinsic == ZeroValid { + } else if requirement == ValidityRequirement::Zero { format!("attempted to zero-initialize type `{}`, which is invalid", ty) } else { format!( diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 0432a9c5a12..4301e4fe69b 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::sym; @@ -185,7 +185,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ ("avx512vpopcntdq", Some(sym::avx512_target_feature)), ("bmi1", None), ("bmi2", None), - ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)), + ("cmpxchg16b", None), ("ermsb", Some(sym::ermsb_target_feature)), ("f16c", None), ("fma", None), @@ -394,7 +394,6 @@ pub fn from_target_feature( Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature, Some(sym::tbm_target_feature) => rust_features.tbm_target_feature, Some(sym::wasm_target_feature) => rust_features.wasm_target_feature, - Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 9eaab1f47a7..04c90f28551 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -2,7 +2,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 6e47646caed..a29cdade023 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::{ BinOp, NonDivergingIntrinsic, }; use rustc_middle::ty; -use rustc_middle::ty::layout::LayoutOf as _; +use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; @@ -418,54 +418,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => { let ty = instance.substs.type_at(0); - let layout = self.layout_of(ty)?; - - // For *all* intrinsics we first check `is_uninhabited` to give a more specific - // error message. - if layout.abi.is_uninhabited() { - // The run-time intrinsic panics just to get a good backtrace; here we abort - // since there is no problem showing a backtrace even for aborts. - M::abort( - self, - format!( + let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap(); + + let should_panic = !self + .tcx + .check_validity_requirement((requirement, self.param_env.and(ty))) + .map_err(|_| err_inval!(TooGeneric))?; + + if should_panic { + let layout = self.layout_of(ty)?; + + let msg = match requirement { + // For *all* intrinsics we first check `is_uninhabited` to give a more specific + // error message. + _ if layout.abi.is_uninhabited() => format!( "aborted execution: attempted to instantiate uninhabited type `{}`", ty ), - )?; - } - - if intrinsic_name == sym::assert_zero_valid { - let should_panic = !self - .tcx - .permits_zero_init(self.param_env.and(ty)) - .map_err(|_| err_inval!(TooGeneric))?; - - if should_panic { - M::abort( - self, - format!( - "aborted execution: attempted to zero-initialize type `{}`, which is invalid", - ty - ), - )?; - } - } + ValidityRequirement::Inhabited => bug!("handled earlier"), + ValidityRequirement::Zero => format!( + "aborted execution: attempted to zero-initialize type `{}`, which is invalid", + ty + ), + ValidityRequirement::UninitMitigated0x01Fill => format!( + "aborted execution: attempted to leave type `{}` uninitialized, which is invalid", + ty + ), + ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"), + }; - if intrinsic_name == sym::assert_mem_uninitialized_valid { - let should_panic = !self - .tcx - .permits_uninit_init(self.param_env.and(ty)) - .map_err(|_| err_inval!(TooGeneric))?; - - if should_panic { - M::abort( - self, - format!( - "aborted execution: attempted to leave type `{}` uninitialized, which is invalid", - ty - ), - )?; - } + M::abort(self, msg)?; } } sym::simd_insert => { @@ -476,7 +458,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(input_len, dest_len, "Return vector length must match input length"); assert!( index < dest_len, - "Index `{}` must be in bounds of vector with length {}`", + "Index `{}` must be in bounds of vector with length {}", index, dest_len ); @@ -496,7 +478,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (input, input_len) = self.operand_to_simd(&args[0])?; assert!( index < input_len, - "index `{}` must be in bounds of vector with length `{}`", + "index `{}` must be in bounds of vector with length {}", index, input_len ); diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 450488315ef..92fa59aec6e 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -16,8 +16,8 @@ use rustc_target::spec::abi::Abi as CallAbi; use crate::const_eval::CheckAlignment; use super::{ - AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, - MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind, + AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, + InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind, }; /// Data returned by Machine::stack_pop, @@ -105,10 +105,16 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Extra data stored in every allocation. type AllocExtra: Debug + Clone + 'static; + /// Type for the bytes of the allocation. + type Bytes: AllocBytes + 'static; + /// Memory's allocation map type MemoryMap: AllocMap< AllocId, - (MemoryKind<Self::MemoryKind>, Allocation<Self::Provenance, Self::AllocExtra>), + ( + MemoryKind<Self::MemoryKind>, + Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>, + ), > + Default + Clone; @@ -338,7 +344,7 @@ pub trait Machine<'mir, 'tcx>: Sized { id: AllocId, alloc: Cow<'b, Allocation>, kind: Option<MemoryKind<Self::MemoryKind>>, - ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>; + ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>; fn eval_inline_asm( _ecx: &mut InterpCx<'mir, 'tcx, Self>, @@ -459,6 +465,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { type AllocExtra = (); type FrameExtra = (); + type Bytes = Box<[u8]>; #[inline(always)] fn use_addr_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index f4e03ad8c59..a3764a7d142 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -21,8 +21,9 @@ use rustc_target::abi::{Align, HasDataLayout, Size}; use crate::const_eval::CheckAlignment; use super::{ - alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, InterpCx, - InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar, + alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, + GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, + Scalar, }; #[derive(Debug, PartialEq, Copy, Clone)] @@ -114,16 +115,16 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// A reference to some allocation that was already bounds-checked for the given region /// and had the on-access machine hooks run. #[derive(Copy, Clone)] -pub struct AllocRef<'a, 'tcx, Prov: Provenance, Extra> { - alloc: &'a Allocation<Prov, Extra>, +pub struct AllocRef<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes = Box<[u8]>> { + alloc: &'a Allocation<Prov, Extra, Bytes>, range: AllocRange, tcx: TyCtxt<'tcx>, alloc_id: AllocId, } /// A reference to some allocation that was already bounds-checked for the given region /// and had the on-access machine hooks run. -pub struct AllocRefMut<'a, 'tcx, Prov: Provenance, Extra> { - alloc: &'a mut Allocation<Prov, Extra>, +pub struct AllocRefMut<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes = Box<[u8]>> { + alloc: &'a mut Allocation<Prov, Extra, Bytes>, range: AllocRange, tcx: TyCtxt<'tcx>, alloc_id: AllocId, @@ -483,7 +484,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, id: AllocId, is_write: bool, - ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra, M::Bytes>>> { let (alloc, def_id) = match self.tcx.try_get_global_alloc(id) { Some(GlobalAlloc::Memory(mem)) => { // Memory of a constant or promoted or anonymous memory referenced by a static. @@ -526,6 +527,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } + /// Get the base address for the bytes in an `Allocation` specified by the + /// `AllocID` passed in; error if no such allocation exists. + /// + /// It is up to the caller to take sufficient care when using this address: + /// there could be provenance or uninit memory in there, and other memory + /// accesses could invalidate the exposed pointer. + pub fn alloc_base_addr(&self, id: AllocId) -> InterpResult<'tcx, *const u8> { + let alloc = self.get_alloc_raw(id)?; + Ok(alloc.base_addr()) + } + /// Gives raw access to the `Allocation`, without bounds or alignment checks. /// The caller is responsible for calling the access hooks! /// @@ -533,7 +545,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn get_alloc_raw( &self, id: AllocId, - ) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> { + ) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra, M::Bytes>> { // The error type of the inner closure here is somewhat funny. We have two // ways of "erroring": An actual error, or because we got a reference from // `get_global_alloc` that we can actually use directly without inserting anything anywhere. @@ -569,7 +581,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> + { let ptr_and_alloc = self.check_and_deref_ptr( ptr, size, @@ -612,7 +625,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn get_alloc_raw_mut( &mut self, id: AllocId, - ) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra>, &mut M)> { + ) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra, M::Bytes>, &mut M)> { // We have "NLL problem case #3" here, which cannot be worked around without loss of // efficiency even for the common case where the key is in the map. // <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions> @@ -641,7 +654,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ptr: Pointer<Option<M::Provenance>>, size: Size, align: Align, - ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> + { let parts = self.get_ptr_access(ptr, size, align)?; if let Some((alloc_id, offset, prov)) = parts { let tcx = *self.tcx; @@ -840,11 +854,11 @@ pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Cannot be a closure because it is generic in `Prov`, `Extra`. - fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra>( + fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( fmt: &mut std::fmt::Formatter<'_>, tcx: TyCtxt<'tcx>, allocs_to_print: &mut VecDeque<AllocId>, - alloc: &Allocation<Prov, Extra>, + alloc: &Allocation<Prov, Extra, Bytes>, ) -> std::fmt::Result { for alloc_id in alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id()) { @@ -912,7 +926,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, } /// Reading and writing. -impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> { +impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> + AllocRefMut<'a, 'tcx, Prov, Extra, Bytes> +{ /// `range` is relative to this allocation reference, not the base of the allocation. pub fn write_scalar(&mut self, range: AllocRange, val: Scalar<Prov>) -> InterpResult<'tcx> { let range = self.range.subrange(range); @@ -937,7 +953,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> { } } -impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> { +impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Prov, Extra, Bytes> { /// `range` is relative to this allocation reference, not the base of the allocation. pub fn read_scalar( &self, diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 244fa8030af..3c463500a60 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -353,7 +353,8 @@ where pub(super) fn get_place_alloc( &self, place: &MPlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> + { assert!(place.layout.is_sized()); assert!(!place.meta.has_meta()); let size = place.layout.size; @@ -364,7 +365,8 @@ where pub(super) fn get_place_alloc_mut( &mut self, place: &MPlaceTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> { + ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> + { assert!(place.layout.is_sized()); assert!(!place.meta.has_meta()); let size = place.layout.size; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index e76d4c1728e..f7881c50960 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -240,10 +240,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // FIXME this should be more descriptive i.e. CapturePlace instead of CapturedVar // https://github.com/rust-lang/project-rfc-2229/issues/46 if let Some(local_def_id) = def_id.as_local() { - let tables = self.ecx.tcx.typeck(local_def_id); - if let Some(captured_place) = - tables.closure_min_captures_flattened(local_def_id).nth(field) - { + let captures = self.ecx.tcx.closure_captures(local_def_id); + if let Some(captured_place) = captures.get(field) { // Sometimes the index is beyond the number of upvars (seen // for a generator). let var_hir_id = captured_place.get_root_variable(); diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index fc6d61c79c2..ed9efe568fb 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -38,7 +38,6 @@ use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; use rustc_macros::fluent_messages; use rustc_middle::ty; use rustc_middle::ty::query::Providers; -use rustc_target::abi::InitKind; fluent_messages! { "../locales/en-US.ftl" } @@ -62,9 +61,7 @@ pub fn provide(providers: &mut Providers) { let (param_env, value) = param_env_and_value.into_parts(); const_eval::deref_mir_constant(tcx, param_env, value) }; - providers.permits_uninit_init = |tcx, param_env_and_ty| { - util::might_permit_raw_init(tcx, param_env_and_ty, InitKind::UninitMitigated0x01Fill) + providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| { + util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; - providers.permits_zero_init = - |tcx, param_env_and_ty| util::might_permit_raw_init(tcx, param_env_and_ty, InitKind::Zero); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index e4366f655e4..aa24d9053b9 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -332,7 +332,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { fn check_static(&mut self, def_id: DefId, span: Span) { if self.tcx.is_thread_local_static(def_id) { - self.tcx.sess.delay_span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef"); + self.tcx.sess.delay_span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`"); } self.check_op_spanned(ops::StaticAccess, span) } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 3e416b89ca6..e586720a0d0 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -12,9 +12,7 @@ use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{ - suggest_constraining_type_param, Adt, Closure, DefIdTree, FnDef, FnPtr, Param, Ty, -}; +use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty}; use rustc_middle::ty::{Binder, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 068491646f4..fb37eb79a33 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -755,8 +755,26 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("explicit `{:?}` is forbidden", kind)); } } - StatementKind::StorageLive(..) - | StatementKind::StorageDead(..) + StatementKind::StorageLive(local) => { + // We check that the local is not live when entering a `StorageLive` for it. + // Technically, violating this restriction is only UB and not actually indicative + // of not well-formed MIR. This means that an optimization which turns MIR that + // already has UB into MIR that fails this check is not necessarily wrong. However, + // we have no such optimizations at the moment, and so we include this check anyway + // to help us catch bugs. If you happen to write an optimization that might cause + // this to incorrectly fire, feel free to remove this check. + if self.reachable_blocks.contains(location.block) { + self.storage_liveness.seek_before_primary_effect(location); + let locals_with_storage = self.storage_liveness.get(); + if locals_with_storage.contains(*local) { + self.fail( + location, + format!("StorageLive({local:?}) which already has storage here"), + ); + } + } + } + StatementKind::StorageDead(_) | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter | StatementKind::Nop => {} diff --git a/compiler/rustc_const_eval/src/util/might_permit_raw_init.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 2eba1e11466..23fcd22c52b 100644 --- a/compiler/rustc_const_eval/src/util/might_permit_raw_init.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,7 +1,7 @@ -use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_session::Limit; -use rustc_target::abi::{Abi, FieldsShape, InitKind, Scalar, Variants}; +use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants}; use crate::const_eval::{CheckAlignment, CompileTimeInterpreter}; use crate::interpret::{InterpCx, MemoryKind, OpTy}; @@ -18,16 +18,23 @@ use crate::interpret::{InterpCx, MemoryKind, OpTy}; /// Rust UB as long as there is no risk of miscompilations. The `strict_init_checks` can be set to /// do a full check against Rust UB instead (in which case we will also ignore the 0x01-filling and /// to the full uninit check). -pub fn might_permit_raw_init<'tcx>( +pub fn check_validity_requirement<'tcx>( tcx: TyCtxt<'tcx>, + kind: ValidityRequirement, param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>, - kind: InitKind, ) -> Result<bool, LayoutError<'tcx>> { - if tcx.sess.opts.unstable_opts.strict_init_checks { - might_permit_raw_init_strict(tcx.layout_of(param_env_and_ty)?, tcx, kind) + let layout = tcx.layout_of(param_env_and_ty)?; + + // There is nothing strict or lax about inhabitedness. + if kind == ValidityRequirement::Inhabited { + return Ok(!layout.abi.is_uninhabited()); + } + + if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { + might_permit_raw_init_strict(layout, tcx, kind) } else { let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env }; - might_permit_raw_init_lax(tcx.layout_of(param_env_and_ty)?, &layout_cx, kind) + might_permit_raw_init_lax(layout, &layout_cx, kind) } } @@ -36,7 +43,7 @@ pub fn might_permit_raw_init<'tcx>( fn might_permit_raw_init_strict<'tcx>( ty: TyAndLayout<'tcx>, tcx: TyCtxt<'tcx>, - kind: InitKind, + kind: ValidityRequirement, ) -> Result<bool, LayoutError<'tcx>> { let machine = CompileTimeInterpreter::new( Limit::new(0), @@ -50,7 +57,7 @@ fn might_permit_raw_init_strict<'tcx>( .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) .expect("OOM: failed to allocate for uninit check"); - if kind == InitKind::Zero { + if kind == ValidityRequirement::Zero { cx.write_bytes_ptr( allocated.ptr, std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()), @@ -72,15 +79,18 @@ fn might_permit_raw_init_strict<'tcx>( fn might_permit_raw_init_lax<'tcx>( this: TyAndLayout<'tcx>, cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, - init_kind: InitKind, + init_kind: ValidityRequirement, ) -> Result<bool, LayoutError<'tcx>> { let scalar_allows_raw_init = move |s: Scalar| -> bool { match init_kind { - InitKind::Zero => { + ValidityRequirement::Inhabited => { + bug!("ValidityRequirement::Inhabited should have been handled above") + } + ValidityRequirement::Zero => { // The range must contain 0. s.valid_range(cx).contains(0) } - InitKind::UninitMitigated0x01Fill => { + ValidityRequirement::UninitMitigated0x01Fill => { // The range must include an 0x01-filled buffer. let mut val: u128 = 0x01; for _ in 1..s.size(cx).bytes() { @@ -89,6 +99,9 @@ fn might_permit_raw_init_lax<'tcx>( } s.valid_range(cx).contains(val) } + ValidityRequirement::Uninit => { + bug!("ValidityRequirement::Uninit should have been handled above") + } } }; diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 51735e33e0f..c0aabd77cee 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -1,14 +1,14 @@ mod alignment; mod call_kind; +mod check_validity_requirement; pub mod collect_writes; mod compare_types; mod find_self_call; -mod might_permit_raw_init; mod type_name; pub use self::alignment::is_disaligned; pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind}; +pub use self::check_validity_requirement::check_validity_requirement; pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype}; pub use self::find_self_call::find_self_call; -pub use self::might_permit_raw_init::might_permit_raw_init; pub use self::type_name::type_name; diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index decbb6519ba..29cb2c0a33e 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" arrayvec = { version = "0.7", default-features = false } bitflags = "1.2.1" cfg-if = "1.0" -ena = "0.14" +ena = "0.14.1" indexmap = { version = "1.9.1" } jobserver_crate = { version = "0.1.13", package = "jobserver" } libc = "0.2" diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 16f401f2057..91abdaadabd 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -426,6 +426,7 @@ impl<O: ForestObligation> ObligationForest<O> { // nodes. Therefore we use a `while` loop. let mut index = 0; while let Some(node) = self.nodes.get_mut(index) { + // This test is extremely hot. if node.state.get() != NodeState::Pending || !processor.needs_process_obligation(&node.obligation) { @@ -439,6 +440,7 @@ impl<O: ForestObligation> ObligationForest<O> { // out of sync with `nodes`. It's not very common, but it does // happen, and code in `compress` has to allow for it. + // This code is much less hot. match processor.process_obligation(&mut node.obligation) { ProcessResult::Unchanged => { // No change in state. diff --git a/compiler/rustc_data_structures/src/sorted_map/index_map.rs b/compiler/rustc_data_structures/src/sorted_map/index_map.rs index 814e7c7fb9b..7d23ff51948 100644 --- a/compiler/rustc_data_structures/src/sorted_map/index_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map/index_map.rs @@ -100,6 +100,11 @@ impl<I: Idx, K: Ord, V> SortedIndexMultiMap<I, K, V> { (k == &key).then_some((i, v)) }) } + + #[inline] + pub fn contains_key(&self, key: K) -> bool { + self.get_by_key(key).next().is_some() + } } impl<I: Idx, K: Eq, V: Eq> Eq for SortedIndexMultiMap<I, K, V> {} diff --git a/compiler/rustc_data_structures/src/sorted_map/tests.rs b/compiler/rustc_data_structures/src/sorted_map/tests.rs index 3cc250862df..def7a7112fb 100644 --- a/compiler/rustc_data_structures/src/sorted_map/tests.rs +++ b/compiler/rustc_data_structures/src/sorted_map/tests.rs @@ -17,6 +17,10 @@ fn test_sorted_index_multi_map() { assert_eq!(set.get_by_key(3).copied().collect::<Vec<_>>(), vec![0]); assert!(set.get_by_key(4).next().is_none()); + // `contains_key` works + assert!(set.contains_key(3)); + assert!(!set.contains_key(4)); + // `get_by_key` returns items in insertion order. let twos: Vec<_> = set.get_by_key_enumerated(2).collect(); let idxs: Vec<usize> = twos.iter().map(|(i, _)| *i).collect(); diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs index b0d66c32a07..724be5888dd 100644 --- a/compiler/rustc_data_structures/src/stable_hasher/tests.rs +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -150,7 +150,7 @@ fn test_isize_compression() { let hash_b = hash(&(b as isize, a as isize)); assert_ne!( hash_a, hash_b, - "The hash stayed the same when permuting values `{a}` and `{b}!", + "The hash stayed the same when permuting values `{a}` and `{b}`!", ); } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 211bbf4f50e..1b2e7b7e083 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1895,7 +1895,7 @@ impl EmitterWriter { self.draw_code_line( &mut buffer, &mut row_num, - &Vec::new(), + &[], p + line_start, l, show_code_change, @@ -1919,7 +1919,7 @@ impl EmitterWriter { self.draw_code_line( &mut buffer, &mut row_num, - &Vec::new(), + &[], p + line_start, l, show_code_change, @@ -1936,7 +1936,7 @@ impl EmitterWriter { self.draw_code_line( &mut buffer, &mut row_num, - &Vec::new(), + &[], p + line_start, l, show_code_change, @@ -1951,7 +1951,7 @@ impl EmitterWriter { self.draw_code_line( &mut buffer, &mut row_num, - highlight_parts, + &highlight_parts, line_pos + line_start, line, show_code_change, @@ -2176,7 +2176,7 @@ impl EmitterWriter { &self, buffer: &mut StyledBuffer, row_num: &mut usize, - highlight_parts: &Vec<SubstitutionHighlight>, + highlight_parts: &[SubstitutionHighlight], line_num: usize, line_to_add: &str, show_code_change: DisplaySuggestion, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index cbf595089cc..99af872f07f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -331,7 +331,7 @@ impl CodeSuggestion { }); buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); - if prev_hi.line == cur_lo.line && cur_hi.line == cur_lo.line { + if cur_hi.line == cur_lo.line { // Account for the difference between the width of the current code and the // snippet being suggested, so that the *later* suggestions are correctly // aligned on the screen. diff --git a/compiler/rustc_expand/locales/en-US.ftl b/compiler/rustc_expand/locales/en-US.ftl index b475d285f6b..cfae781bdee 100644 --- a/compiler/rustc_expand/locales/en-US.ftl +++ b/compiler/rustc_expand/locales/en-US.ftl @@ -133,3 +133,6 @@ expand_trace_macro = trace_macro expand_proc_macro_panicked = proc macro panicked .help = message: {$message} + +expand_proc_macro_derive_tokens = + proc-macro derive produced unparseable tokens diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 70ab222b484..e5102a952e7 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -390,3 +390,10 @@ pub(crate) struct ProcMacroPanicked { pub(crate) struct ProcMacroPanickedHelp { pub message: String, } + +#[derive(Diagnostic)] +#[diag(expand_proc_macro_derive_tokens)] +pub struct ProcMacroDeriveTokens { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index b79835be73a..47a8b4bc488 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -282,7 +282,7 @@ pub(super) fn transcribe<'a>( } // There should be no meta-var declarations in the invocation of a macro. - mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"), + mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl`"), } } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index cef64a10479..ddba1441719 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -176,7 +176,7 @@ impl MultiItemModifier for DeriveProcMacro { // fail if there have been errors emitted if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before { - ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit(); + ecx.sess.emit_err(errors::ProcMacroDeriveTokens { span }); } ExpandResult::Ready(items) diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 14918d3c190..480d95b77e9 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -43,7 +43,6 @@ pub(crate) fn string_to_stream(source_str: String) -> TokenStream { ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str), None, ) - .0 } /// Parses a string, returns a crate. diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e064e87a59a..e7b2df34ccc 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -90,6 +90,8 @@ declare_features! ( (accepted, clone_closures, "1.26.0", Some(44490), None), /// Allows coercing non capturing closures to function pointers. (accepted, closure_to_fn_coercion, "1.19.0", Some(39817), None), + /// Allows using `cmpxchg16b` from `core::arch::x86_64`. + (accepted, cmpxchg16b_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None), /// Allows usage of the `compile_error!` macro. (accepted, compile_error, "1.20.0", Some(40872), None), /// Allows `impl Trait` in function return types. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 6d8f7e4a0f6..a01914f969e 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -256,7 +256,6 @@ declare_features! ( (active, arm_target_feature, "1.27.0", Some(44839), None), (active, avx512_target_feature, "1.27.0", Some(44839), None), (active, bpf_target_feature, "1.54.0", Some(44839), None), - (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None), (active, ermsb_target_feature, "1.49.0", Some(44839), None), (active, hexagon_target_feature, "1.27.0", Some(44839), None), (active, mips_target_feature, "1.27.0", Some(44839), None), @@ -317,8 +316,6 @@ declare_features! ( (active, c_unwind, "1.52.0", Some(74990), None), /// Allows using C-variadics. (active, c_variadic, "1.34.0", Some(44930), None), - /// Allows capturing disjoint fields in a closure/generator (RFC 2229). - (incomplete, capture_disjoint_fields, "1.49.0", Some(53488), None), /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. (active, cfg_sanitize, "1.41.0", Some(39699), None), /// Allows `cfg(target_abi = "...")`. diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 79a12801de2..04d4f6cb14e 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -52,6 +52,8 @@ declare_features! ( (removed, allow_fail, "1.19.0", Some(46488), None, Some("removed due to no clear use cases")), (removed, await_macro, "1.38.0", Some(50547), None, Some("subsumed by `.await` syntax")), + /// Allows capturing disjoint fields in a closure/generator (RFC 2229). + (removed, capture_disjoint_fields, "1.49.0", Some(53488), None, Some("stabilized in Rust 2021")), /// Allows comparing raw pointers during const eval. (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None, Some("cannot be allowed in const eval in any meaningful way")), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 72e9f7c1343..19d3d41c984 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -498,6 +498,7 @@ pub struct GenericParam<'hir> { pub pure_wrt_drop: bool, pub kind: GenericParamKind<'hir>, pub colon_span: Option<Span>, + pub source: GenericParamSource, } impl<'hir> GenericParam<'hir> { @@ -516,6 +517,20 @@ impl<'hir> GenericParam<'hir> { } } +/// Records where the generic parameter originated from. +/// +/// This can either be from an item's generics, in which case it's typically +/// early-bound (but can be a late-bound lifetime in functions, for example), +/// or from a `for<...>` binder, in which case it's late-bound (and notably, +/// does not show up in the parent item's generics). +#[derive(Debug, HashStable_Generic, PartialEq, Eq, Copy, Clone)] +pub enum GenericParamSource { + // Early or late-bound parameters defined on an item + Generics, + // Late-bound parameters defined via a `for<...>` + Binder, +} + #[derive(Default)] pub struct GenericParamCount { pub lifetimes: usize, diff --git a/compiler/rustc_hir_analysis/locales/en-US.ftl b/compiler/rustc_hir_analysis/locales/en-US.ftl index 50b0816889b..1d313945b52 100644 --- a/compiler/rustc_hir_analysis/locales/en-US.ftl +++ b/compiler/rustc_hir_analysis/locales/en-US.ftl @@ -62,14 +62,6 @@ hir_analysis_manual_implementation = hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl -hir_analysis_unused_extern_crate = - unused extern crate - .suggestion = remove it - -hir_analysis_extern_crate_not_idiomatic = - `extern crate` is not idiomatic in the new edition - .suggestion = convert it to a `{$msg_code}` - hir_analysis_trait_object_declared_with_no_traits = at least one trait is required for an object type .alias_span = this alias does not contain a trait @@ -155,3 +147,11 @@ hir_analysis_main_function_generic_parameters = `main` function is not allowed t hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like {$conventions} .label = C-variadic function must have a compatible calling convention + +hir_analysis_cannot_capture_late_bound_ty_in_anon_const = + cannot capture late-bound type parameter in a constant + .label = parameter defined here + +hir_analysis_cannot_capture_late_bound_const_in_anon_const = + cannot capture late-bound const parameter in a constant + .label = parameter defined here diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index a15cf454df7..899029d98e0 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -35,7 +35,7 @@ use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::DynKind; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, Const, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; @@ -50,6 +50,7 @@ use rustc_trait_selection::traits::{self, astconv_object_safety_violations, Obli use smallvec::{smallvec, SmallVec}; use std::collections::BTreeSet; +use std::fmt::Display; use std::slice; #[derive(Debug)] @@ -1095,11 +1096,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // those that do. self.one_bound_for_assoc_type( || traits::supertraits(tcx, trait_ref), - || trait_ref.print_only_trait_path().to_string(), + trait_ref.print_only_trait_path(), binding.item_name, path_span, - || match binding.kind { - ConvertedBindingKind::Equality(ty) => Some(ty.to_string()), + match binding.kind { + ConvertedBindingKind::Equality(term) => Some(term), _ => None, }, )? @@ -1789,10 +1790,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assoc_name, ) }, - || param_name.to_string(), + param_name, assoc_name, span, - || None, + None, ) } @@ -1802,10 +1803,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn one_bound_for_assoc_type<I>( &self, all_candidates: impl Fn() -> I, - ty_param_name: impl Fn() -> String, + ty_param_name: impl Display, assoc_name: Ident, span: Span, - is_equality: impl Fn() -> Option<String>, + is_equality: Option<ty::Term<'tcx>>, ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> where I: Iterator<Item = ty::PolyTraitRef<'tcx>>, @@ -1821,7 +1822,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (None, None) => { let reported = self.complain_about_assoc_type_not_found( all_candidates, - &ty_param_name(), + &ty_param_name.to_string(), assoc_name, span, ); @@ -1833,7 +1834,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if let Some(bound2) = next_cand { debug!(?bound2); - let is_equality = is_equality(); let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates); let mut err = if is_equality.is_some() { // More specific Error Index entry. @@ -1843,7 +1843,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { E0222, "ambiguous associated type `{}` in bounds of `{}`", assoc_name, - ty_param_name() + ty_param_name ) } else { struct_span_err!( @@ -1852,7 +1852,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { E0221, "ambiguous associated type `{}` in bounds of `{}`", assoc_name, - ty_param_name() + ty_param_name ) }; err.span_label(span, format!("ambiguous associated type `{}`", assoc_name)); @@ -1886,18 +1886,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.span_suggestion_verbose( span.with_hi(assoc_name.span.lo()), "use fully qualified syntax to disambiguate", - format!( - "<{} as {}>::", - ty_param_name(), - bound.print_only_trait_path(), - ), + format!("<{} as {}>::", ty_param_name, bound.print_only_trait_path()), Applicability::MaybeIncorrect, ); } } else { err.note(&format!( "associated type `{}` could derive from `{}`", - ty_param_name(), + ty_param_name, bound.print_only_trait_path(), )); } @@ -1906,7 +1902,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.help(&format!( "consider introducing a new type parameter `T` and adding `where` constraints:\ \n where\n T: {},\n{}", - ty_param_name(), + ty_param_name, where_bounds.join(",\n"), )); } @@ -2070,10 +2066,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.one_bound_for_assoc_type( || traits::supertraits(tcx, ty::Binder::dummy(trait_ref.subst_identity())), - || "Self".to_string(), + kw::SelfUpper, assoc_ident, span, - || None, + None, )? } ( @@ -2403,8 +2399,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { infcx .can_eq( ty::ParamEnv::empty(), - tcx.erase_regions(impl_.self_ty()), - tcx.erase_regions(qself_ty), + impl_.self_ty(), + // Must fold past escaping bound vars too, + // since we have those at this point in astconv. + tcx.fold_regions(qself_ty, |_, _| tcx.lifetimes.re_erased), ) }) && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4ea471f8f05..9693a59defb 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -22,8 +22,7 @@ use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ - self, AdtDef, DefIdTree, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, + self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_span::symbol::sym; @@ -792,8 +791,10 @@ fn check_impl_items_against_trait<'tcx>( trait_def.must_implement_one_of.as_deref(); for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) { - let is_implemented = ancestors - .leaf_def(tcx, trait_item_id) + let leaf_def = ancestors.leaf_def(tcx, trait_item_id); + + let is_implemented = leaf_def + .as_ref() .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value()); if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { @@ -801,8 +802,8 @@ fn check_impl_items_against_trait<'tcx>( } // true if this item is specifically implemented in this impl - let is_implemented_here = ancestors - .leaf_def(tcx, trait_item_id) + let is_implemented_here = leaf_def + .as_ref() .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); if !is_implemented_here { @@ -831,6 +832,36 @@ fn check_impl_items_against_trait<'tcx>( } } } + + if let Some(leaf_def) = &leaf_def + && !leaf_def.is_final() + && let def_id = leaf_def.item.def_id + && tcx.impl_method_has_trait_impl_trait_tys(def_id) + { + let def_kind = tcx.def_kind(def_id); + let descr = tcx.def_kind_descr(def_kind, def_id); + let (msg, feature) = if tcx.asyncness(def_id).is_async() { + ( + format!("async {descr} in trait cannot be specialized"), + sym::async_fn_in_trait, + ) + } else { + ( + format!( + "{descr} with return-position `impl Trait` in trait cannot be specialized" + ), + sym::return_position_impl_trait_in_trait, + ) + }; + tcx.sess + .struct_span_err(tcx.def_span(def_id), msg) + .note(format!( + "specialization behaves in inconsistent and \ + surprising ways with `#![feature({feature})]`, \ + and for now is disallowed" + )) + .emit(); + } } if !missing_items.is_empty() { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 89b4e6227bd..5adc7a87323 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -16,8 +16,7 @@ use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::util::ExplicitSelf; use rustc_middle::ty::{ - self, DefIdTree, InternalSubsts, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, + self, InternalSubsts, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; @@ -648,6 +647,13 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), ) .fold_with(&mut collector); + + debug_assert_ne!( + collector.types.len(), + 0, + "expect >1 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`" + ); + let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig); trait_sig.error_reported()?; let trait_return_ty = trait_sig.output(); @@ -1866,7 +1872,7 @@ pub(super) fn check_type_bounds<'tcx>( // type Bar<C> =... // } // - // - `impl_trait_ref` would be `<(A, B) as Foo<u32>> + // - `impl_trait_ref` would be `<(A, B) as Foo<u32>>` // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0) // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from // the *trait* with the generic associated type parameters (as bound vars). diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 5716be4f1a9..f3f5851d8f9 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -1,12 +1,8 @@ -use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate}; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; -use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; use rustc_session::lint; -use rustc_span::{Span, Symbol}; pub fn check_crate(tcx: TyCtxt<'_>) { let mut used_trait_imports: UnordSet<LocalDefId> = Default::default(); @@ -43,131 +39,4 @@ pub fn check_crate(tcx: TyCtxt<'_>) { |lint| lint, ); } - - unused_crates_lint(tcx); -} - -fn unused_crates_lint(tcx: TyCtxt<'_>) { - let lint = lint::builtin::UNUSED_EXTERN_CRATES; - - // Collect first the crates that are completely unused. These we - // can always suggest removing (no matter which edition we are - // in). - let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx - .maybe_unused_extern_crates(()) - .iter() - .filter(|&&(def_id, _)| { - tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { - !tcx.is_compiler_builtins(cnum) - && !tcx.is_panic_runtime(cnum) - && !tcx.has_global_allocator(cnum) - && !tcx.has_panic_handler(cnum) - }) - }) - .cloned() - .collect(); - - // Collect all the extern crates (in a reliable order). - let mut crates_to_lint = vec![]; - - for id in tcx.hir().items() { - if matches!(tcx.def_kind(id.owner_id), DefKind::ExternCrate) { - let item = tcx.hir().item(id); - if let hir::ItemKind::ExternCrate(orig_name) = item.kind { - crates_to_lint.push(ExternCrateToLint { - def_id: item.owner_id.to_def_id(), - span: item.span, - orig_name, - warn_if_unused: !item.ident.as_str().starts_with('_'), - }); - } - } - } - - let extern_prelude = &tcx.resolutions(()).extern_prelude; - - for extern_crate in &crates_to_lint { - let def_id = extern_crate.def_id.expect_local(); - let item = tcx.hir().expect_item(def_id); - - // If the crate is fully unused, we suggest removing it altogether. - // We do this in any edition. - if extern_crate.warn_if_unused { - if let Some(&span) = unused_extern_crates.get(&def_id) { - // Removal suggestion span needs to include attributes (Issue #54400) - let id = tcx.hir().local_def_id_to_hir_id(def_id); - let span_with_attrs = tcx - .hir() - .attrs(id) - .iter() - .map(|attr| attr.span) - .fold(span, |acc, attr_span| acc.to(attr_span)); - - tcx.emit_spanned_lint(lint, id, span, UnusedExternCrate { span: span_with_attrs }); - continue; - } - } - - // If we are not in Rust 2018 edition, then we don't make any further - // suggestions. - if !tcx.sess.rust_2018() { - continue; - } - - // If the extern crate isn't in the extern prelude, - // there is no way it can be written as a `use`. - let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); - if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { - continue; - } - - // If the extern crate is renamed, then we cannot suggest replacing it with a use as this - // would not insert the new name into the prelude, where other imports in the crate may be - // expecting it. - if extern_crate.orig_name.is_some() { - continue; - } - - let id = tcx.hir().local_def_id_to_hir_id(def_id); - // If the extern crate has any attributes, they may have funky - // semantics we can't faithfully represent using `use` (most - // notably `#[macro_use]`). Ignore it. - if !tcx.hir().attrs(id).is_empty() { - continue; - } - - let base_replacement = match extern_crate.orig_name { - Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), - None => format!("use {};", item.ident.name), - }; - let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default(); - let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; - tcx.emit_spanned_lint( - lint, - id, - extern_crate.span, - ExternCrateNotIdiomatic { - span: extern_crate.span, - msg_code: add_vis("use".to_string()), - suggestion_code: add_vis(base_replacement), - }, - ); - } -} - -struct ExternCrateToLint { - /// `DefId` of the extern crate - def_id: DefId, - - /// span from the item - span: Span, - - /// if `Some`, then this is renamed (`extern crate orig_name as - /// crate_name`), and -- perhaps surprisingly -- this stores the - /// *original* name (`item.name` will contain the new name) - orig_name: Option<Symbol>, - - /// if `false`, the original name started with `_`, so we shouldn't lint - /// about it going unused (but we should still emit idiom lints). - warn_if_unused: bool, } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 875c5f1fd00..ffb68abf978 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -437,7 +437,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn } // Here we are considering a case of converting - // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`, + // `S<P0...Pn>` to `S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`, // which acts like a pointer to `U`, but carries along some extra data of type `T`: // // struct Foo<T, U> { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 9cf3ff65a91..6f6f993f727 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -3,7 +3,7 @@ use crate::astconv::AstConv; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::Span; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 6c00b8ff7bd..e9963e67741 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -17,13 +17,15 @@ use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeNa use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::*; -use rustc_middle::ty::{self, DefIdTree, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use std::fmt; +use crate::errors; + trait RegionExt { fn early(param: &GenericParam<'_>) -> (LocalDefId, ResolvedArg); @@ -161,6 +163,15 @@ enum Scope<'a> { s: ScopeRef<'a>, }, + /// Disallows capturing non-lifetime binders from parent scopes. + /// + /// This is necessary for something like `for<T> [(); { /* references T */ }]:`, + /// since we don't do something more correct like replacing any captured + /// late-bound vars with early-bound params in the const's own generics. + AnonConstBoundary { + s: ScopeRef<'a>, + }, + Root { opt_parent_item: Option<LocalDefId>, }, @@ -211,6 +222,7 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("s", &"..") .finish(), Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), + Scope::AnonConstBoundary { s: _ } => f.debug_struct("AnonConstBoundary").finish(), Scope::Root { opt_parent_item } => { f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() } @@ -312,7 +324,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { break (vec![], BinderScopeType::Normal); } - Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => { + Scope::Elision { s, .. } + | Scope::ObjectLifetimeDefault { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } @@ -1029,50 +1043,64 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) { self.visit_poly_trait_ref_inner(trait_ref, NonLifetimeBinderAllowed::Allow); } + + fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { + self.with(Scope::AnonConstBoundary { s: self.scope }, |this| { + intravisit::walk_anon_const(this, c); + }); + } } fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: DefId) -> ObjectLifetimeDefault { debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam); let param_def_id = param_def_id.expect_local(); - let parent_def_id = tcx.local_parent(param_def_id); - let generics = tcx.hir().get_generics(parent_def_id).unwrap(); - let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); - let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); - - // Scan the bounds and where-clauses on parameters to extract bounds - // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` - // for each type parameter. - match param.kind { - GenericParamKind::Type { .. } => { - let mut set = Set1::Empty; - - // Look for `type: ...` where clauses. - for bound in generics.bounds_for_param(param_def_id) { - // Ignore `for<'a> type: ...` as they can change what - // lifetimes mean (although we could "just" handle it). - if !bound.bound_generic_params.is_empty() { - continue; - } + let hir::Node::GenericParam(param) = tcx.hir().get_by_def_id(param_def_id) else { + bug!("expected GenericParam for object_lifetime_default"); + }; + match param.source { + hir::GenericParamSource::Generics => { + let parent_def_id = tcx.local_parent(param_def_id); + let generics = tcx.hir().get_generics(parent_def_id).unwrap(); + let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id); + let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap(); + + // Scan the bounds and where-clauses on parameters to extract bounds + // of the form `T:'a` so as to determine the `ObjectLifetimeDefault` + // for each type parameter. + match param.kind { + GenericParamKind::Type { .. } => { + let mut set = Set1::Empty; + + // Look for `type: ...` where clauses. + for bound in generics.bounds_for_param(param_def_id) { + // Ignore `for<'a> type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if !bound.bound_generic_params.is_empty() { + continue; + } - for bound in bound.bounds { - if let hir::GenericBound::Outlives(lifetime) = bound { - set.insert(lifetime.res); + for bound in bound.bounds { + if let hir::GenericBound::Outlives(lifetime) = bound { + set.insert(lifetime.res); + } + } } - } - } - match set { - Set1::Empty => ObjectLifetimeDefault::Empty, - Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, - Set1::One(hir::LifetimeName::Param(param_def_id)) => { - ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + match set { + Set1::Empty => ObjectLifetimeDefault::Empty, + Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static, + Set1::One(hir::LifetimeName::Param(param_def_id)) => { + ObjectLifetimeDefault::Param(param_def_id.to_def_id()) + } + _ => ObjectLifetimeDefault::Ambiguous, + } + } + _ => { + bug!("object_lifetime_default_raw must only be called on a type parameter") } - _ => ObjectLifetimeDefault::Ambiguous, } } - _ => { - bug!("object_lifetime_default_raw must only be called on a type parameter") - } + hir::GenericParamSource::Binder => ObjectLifetimeDefault::Empty, } } @@ -1267,7 +1295,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } @@ -1332,7 +1361,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } @@ -1351,6 +1381,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // search. let mut late_depth = 0; let mut scope = self.scope; + let mut crossed_anon_const = false; let result = loop { match *scope { Scope::Body { s, .. } => { @@ -1384,17 +1415,44 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { | Scope::TraitRefBoundary { s, .. } => { scope = s; } + + Scope::AnonConstBoundary { s } => { + crossed_anon_const = true; + scope = s; + } } }; if let Some(def) = result { + if let ResolvedArg::LateBound(..) = def && crossed_anon_const { + let use_span = self.tcx.hir().span(hir_id); + let def_span = self.tcx.def_span(param_def_id); + match self.tcx.def_kind(param_def_id) { + DefKind::ConstParam => { + self.tcx.sess.emit_err(errors::CannotCaptureLateBoundInAnonConst::Const { + use_span, + def_span, + }); + } + DefKind::TyParam => { + self.tcx.sess.emit_err(errors::CannotCaptureLateBoundInAnonConst::Type { + use_span, + def_span, + }); + } + _ => unreachable!(), + } + return; + } + self.map.defs.insert(hir_id, def); return; } - self.tcx - .sess - .delay_span_bug(self.tcx.hir().span(hir_id), "could not resolve {param_def_id:?}"); + self.tcx.sess.delay_span_bug( + self.tcx.hir().span(hir_id), + format!("could not resolve {param_def_id:?}"), + ); } #[instrument(level = "debug", skip(self))] @@ -1465,7 +1523,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } - | Scope::TraitRefBoundary { s, .. } => { + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } @@ -1701,7 +1760,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l, - Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { + Scope::Supertrait { s, .. } + | Scope::TraitRefBoundary { s, .. } + | Scope::AnonConstBoundary { s } => { scope = s; } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 50073d94ea5..acd9f8a5c8e 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{ - self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a566e73912e..3e069275775 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -5,7 +5,7 @@ use rustc_errors::{ error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, MultiSpan, }; -use rustc_macros::{Diagnostic, LintDiagnostic}; +use rustc_macros::Diagnostic; use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -247,26 +247,6 @@ pub struct SubstsOnOverriddenImpl { pub span: Span, } -#[derive(LintDiagnostic)] -#[diag(hir_analysis_unused_extern_crate)] -pub struct UnusedExternCrate { - #[suggestion(applicability = "machine-applicable", code = "")] - pub span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(hir_analysis_extern_crate_not_idiomatic)] -pub struct ExternCrateNotIdiomatic { - #[suggestion( - style = "short", - applicability = "machine-applicable", - code = "{suggestion_code}" - )] - pub span: Span, - pub msg_code: String, - pub suggestion_code: String, -} - #[derive(Diagnostic)] #[diag(hir_analysis_const_impl_for_non_const_trait)] pub struct ConstImplForNonConstTrait { @@ -401,3 +381,21 @@ pub(crate) struct VariadicFunctionCompatibleConvention<'a> { pub span: Span, pub conventions: &'a str, } + +#[derive(Diagnostic)] +pub(crate) enum CannotCaptureLateBoundInAnonConst { + #[diag(hir_analysis_cannot_capture_late_bound_ty_in_anon_const)] + Type { + #[primary_span] + use_span: Span, + #[label] + def_span: Span, + }, + #[diag(hir_analysis_cannot_capture_late_bound_const_in_anon_const)] + Const { + #[primary_span] + use_span: Span, + #[label] + def_span: Span, + }, +} diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index a8b33c74bc1..d53c429ca15 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -1,7 +1,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::Span; diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 5d5c8ca604a..a8b7699b667 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -8,7 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt}; -use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable}; use std::ops::ControlFlow; /// Defines the `TermsContext` basically houses an arena where we can diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 34d62987c3b..7ba57b3b7a2 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -315,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { probe::ProbeScope::TraitsInScope, None, ) { - Ok(pick) => pick.self_ty, + Ok(pick) => eraser.fold_ty(pick.self_ty), Err(_) => rcvr_ty, }; // Remove one layer of references to account for `&mut self` and diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 60e55c7b0cf..a8f8121153f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, TyCtxt, UserType, + self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType, }; use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index d64492e503d..ec14bd3c6f4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -3,9 +3,7 @@ use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::ty::{ - self, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, -}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::{self, Span}; use rustc_trait_selection::traits; @@ -714,12 +712,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.parent(expr_ctor_def_id) } hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => { - // If this is a variant, its parent is the type definition. - if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) { + // For a typical enum like + // `enum Blah<T> { Variant(T) }` + // we get the following resolutions: + // - expr_ctor_def_id ::: DefId(0:29 ~ source_file[b442]::Blah::Variant::{constructor#0}) + // - self.tcx.parent(expr_ctor_def_id) ::: DefId(0:28 ~ source_file[b442]::Blah::Variant) + // - self.tcx.parent(self.tcx.parent(expr_ctor_def_id)) ::: DefId(0:26 ~ source_file[b442]::Blah) + + // Therefore, we need to go up once to obtain the variant and up twice to obtain the type. + // Note that this pattern still holds even when we `use` a variant or `use` an enum type to rename it, or chain `use` expressions + // together; this resolution is handled automatically by `qpath_res`. + + // FIXME: Deal with type aliases? + if in_ty_adt.did() == self.tcx.parent(self.tcx.parent(expr_ctor_def_id)) { + // The constructor definition refers to the "constructor" of the variant: + // For example, `Some(5)` triggers this case. + self.tcx.parent(expr_ctor_def_id) + } else { // FIXME: Deal with type aliases? return Err(expr); } - expr_ctor_def_id } _ => { return Err(expr); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2e62e13648c..ea54b76bdec 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -28,7 +28,7 @@ use rustc_infer::infer::InferOk; use rustc_infer::infer::TypeTrace; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; +use rustc_middle::ty::{self, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; use rustc_span::{self, sym, Span}; @@ -36,7 +36,6 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; -use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { @@ -1507,11 +1506,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let coerce = if blk.targeted_by_break { CoerceMany::new(coerce_to_ty) } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) }; let prev_diverges = self.diverges.get(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c49621b7c24..c77251a156f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -16,7 +16,7 @@ use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty, + self, suggest_constraining_type_params, Binder, IsSuggestable, ToPredicate, Ty, TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 91fd8fad73c..e397dfd4570 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -5,6 +5,7 @@ #![feature(min_specialization)] #![feature(control_flow_enum)] #![feature(drain_filter)] +#![feature(option_as_slice)] #![allow(rustc::potential_query_instability)] #![recursion_limit = "256"] diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index bcfc61bffb2..4d3969d28aa 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -636,7 +636,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // `&&Some(x,)` `place_foo` // `&Some(x,)` `deref { place_foo}` // `Some(x,)` `deref { deref { place_foo }}` - // (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place + // `(x,)` `field0 { deref { deref { place_foo }}}` <- resulting place // // The above example has no adjustments. If the code were instead the (after adjustments, // equivalent) version diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 47a4d4e72df..4f4779d1543 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -27,7 +27,7 @@ use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; -use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; @@ -333,6 +333,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty.prefix_string(self.tcx), ty_str_reported, ); + if tcx.sess.source_map().is_multiline(sugg_span) { + err.span_label(sugg_span.with_hi(span.lo()), ""); + } let ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { short_ty_str } else { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index a4b325a9b79..eecded557a5 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -749,14 +749,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let opname = Ident::with_dummy_span(opname); - let input_types = - opt_rhs.as_ref().map(|(_, ty)| std::slice::from_ref(ty)).unwrap_or_default(); + let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip(); + let input_types = opt_rhs_ty.as_slice(); let cause = self.cause( span, traits::BinOp { - rhs_span: opt_rhs.map(|(expr, _)| expr.span), - is_lit: opt_rhs - .map_or(false, |(expr, _)| matches!(expr.kind, hir::ExprKind::Lit(_))), + rhs_span: opt_rhs_expr.map(|expr| expr.span), + is_lit: opt_rhs_expr + .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), output_ty: expected.only_has_type(self), }, ); diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 8fcec3363c0..2cca45de5e9 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -11,7 +11,6 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already. @@ -393,11 +392,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1)) } }; - let arg_tys = match arg_ty { - None => &[], - Some(ref ty) => slice::from_ref(ty), - }; - + let arg_tys = arg_ty.as_slice(); let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); let method = match method { Some(ok) => self.register_infer_ok_obligations(ok), diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e94915c754e..4a432328c4d 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We now fake capture information for all variables that are mentioned within the closure // We do this after handling migrations so that min_captures computes before - if !enable_precise_capture(self.tcx, span) { + if !enable_precise_capture(span) { let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { @@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have an origin, store it. if let Some(origin) = origin { - let origin = if enable_precise_capture(self.tcx, span) { + let origin = if enable_precise_capture(span) { (origin.0, origin.1) } else { (origin.0, Place { projections: vec![], ..origin.1 }) @@ -526,10 +526,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), }; + let var_ident = self.tcx.hir().ident(var_hir_id); let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { let mutability = self.determine_capture_mutability(&typeck_results, &place); let min_cap_list = vec![ty::CapturedPlace { + var_ident, place, info: capture_info, mutability, @@ -628,6 +630,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ancestor_found { let mutability = self.determine_capture_mutability(&typeck_results, &place); let captured_place = ty::CapturedPlace { + var_ident, place, info: updated_capture_info, mutability, @@ -1240,8 +1243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// This will make more sense with an example: /// - /// ```rust - /// #![feature(capture_disjoint_fields)] + /// ```rust,edition2021 /// /// struct FancyInteger(i32); // This implements Drop /// @@ -2221,7 +2223,7 @@ fn determine_place_ancestry_relation<'tcx>( /// || drop(&*m.a.field_of_a) /// // Here we really do want to capture `*m.a` because that outlives `'static` /// -/// // If we capture `m`, then the closure no longer outlives `'static' +/// // If we capture `m`, then the closure no longer outlives `'static` /// // it is constrained to `'a` /// } /// ``` @@ -2247,12 +2249,10 @@ fn truncate_capture_for_optimization( (place, curr_mode) } -/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if -/// user is using Rust Edition 2021 or higher. -/// +/// Precise capture is enabled if user is using Rust Edition 2021 or higher. /// `span` is the span of the closure. -fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool { +fn enable_precise_capture(span: Span) -> bool { // We use span here to ensure that if the closure was generated by a macro with a different // edition. - tcx.features().capture_disjoint_fields || span.rust_2021() + span.rust_2021() } diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index b4b0ea00c50..c550e553bb0 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -18,7 +18,7 @@ //! the HIR doesn't change as a result of the annotations, which might //! perturb the reuse results. //! -//! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] +//! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]` //! allows for doing a more fine-grained check to see if pre- or post-lto data //! was re-used. diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 15179392c88..cbf169afb18 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1870,7 +1870,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> { } } - /// Subtracts `set from `row`. `set` can be either `BitSet` or + /// Subtracts `set` from `row`. `set` can be either `BitSet` or /// `HybridBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. diff --git a/compiler/rustc_infer/locales/en-US.ftl b/compiler/rustc_infer/locales/en-US.ftl index c5b2b6c2d73..15780898dc6 100644 --- a/compiler/rustc_infer/locales/en-US.ftl +++ b/compiler/rustc_infer/locales/en-US.ftl @@ -79,7 +79,7 @@ infer_subtype = ...so that the {$requirement -> [if_else_different] `if` and `else` have incompatible types [no_else] `if` missing an `else` returns `()` [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] #[start]` function has the correct type + [fn_start_correct_type] `#[start]` function has the correct type [intristic_correct_type] intrinsic has the correct type [method_correct_type] method receiver has the correct type *[other] types are compatible @@ -92,7 +92,7 @@ infer_subtype_2 = ...so that {$requirement -> [if_else_different] `if` and `else` have incompatible types [no_else] `if` missing an `else` returns `()` [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] #[start]` function has the correct type + [fn_start_correct_type] `#[start]` function has the correct type [intristic_correct_type] intrinsic has the correct type [method_correct_type] method receiver has the correct type *[other] types are compatible @@ -277,7 +277,7 @@ infer_tid_consider_borrowing = consider borrowing this type parameter in the tra infer_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` infer_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement -infer_dtcs_introduces_requirement = calling this method introduces the `impl`'s 'static` requirement +infer_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement infer_dtcs_has_req_note = the used `impl` has a `'static` requirement infer_dtcs_suggestion = consider relaxing the implicit `'static` requirement @@ -313,7 +313,7 @@ infer_but_needs_to_satisfy = {$has_param_name -> [false] ...and is required to live as long as `'static` here } .used_here = ...is used here... - .introduced_by_bound = 'static` lifetime requirement introduced by this bound + .introduced_by_bound = `'static` lifetime requirement introduced by this bound infer_more_targeted = {$has_param_name -> [true] `{$param_name}` @@ -345,3 +345,6 @@ infer_prlf_defined_without_sub = the lifetime defined here... infer_prlf_must_oultive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here infer_prlf_must_oultive_without_sup = ...must outlive the lifetime defined here infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information) + +infer_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds + .label = opaque type defined here diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 7dccd0bb930..6bbd3fd3e6e 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1147,3 +1147,13 @@ pub enum PlaceholderRelationLfNotSatisfied { note: (), }, } + +#[derive(Diagnostic)] +#[diag(infer_opaque_captures_lifetime, code = "E0700")] +pub struct OpaqueCapturesLifetime<'tcx> { + #[primary_span] + pub span: Span, + #[label] + pub opaque_ty_span: Span, + pub opaque_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index c952ddc827a..7d9bae735e5 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -369,6 +369,34 @@ impl<'tcx> ToTrace<'tcx> for Const<'tcx> { } } +impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + use GenericArgKind::*; + TypeTrace { + cause: cause.clone(), + values: match (a.unpack(), b.unpack()) { + (Lifetime(a), Lifetime(b)) => Regions(ExpectedFound::new(a_is_expected, a, b)), + (Type(a), Type(b)) => Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + (Const(a), Const(b)) => { + Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())) + } + + (Lifetime(_), Type(_) | Const(_)) + | (Type(_), Lifetime(_) | Const(_)) + | (Const(_), Lifetime(_) | Type(_)) => { + bug!("relating different kinds: {a:?} {b:?}") + } + }, + } + } +} + impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> { fn to_trace( _: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 7ffd39de781..678c4a0beb6 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -50,7 +50,7 @@ impl<'tcx> InferCtxt<'tcx> { /// Like [Self::canonicalize_query], but preserves distinct universes. For /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and - /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1` + /// `'?1` is in `U3` would be canonicalized to have `?0` in `U1` and `'?1` /// in `U2`. /// /// This is used for Chalk integration. diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 8c782a933a5..ce230afdab3 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -30,7 +30,7 @@ use rustc_middle::ty::{self, List, TyCtxt}; use rustc_span::source_map::Span; pub use rustc_middle::infer::canonical::*; -use substitute::CanonicalExt; +pub use substitute::CanonicalExt; mod canonicalizer; pub mod query_response; @@ -100,7 +100,11 @@ impl<'tcx> InferCtxt<'tcx> { /// variable for it. If this is an existentially quantified /// variable, then you'll get a new inference variable; if it is a /// universally quantified variable, you get a placeholder. - fn instantiate_canonical_var( + /// + /// FIXME(-Ztrait-solver=next): This is public because it's used by the + /// new trait solver which has a different canonicalization routine. + /// We should somehow deduplicate all of this. + pub fn instantiate_canonical_var( &self, span: Span, cv_info: CanonicalVarInfo<'tcx>, diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 832af91a431..436d29c2449 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -151,11 +151,21 @@ impl<'tcx> InferCtxt<'tcx> { }) } - /// FIXME: This method should only be used for canonical queries and therefore be private. - /// - /// As the new solver does canonicalization slightly differently, this is also used there - /// for now. This should hopefully change fairly soon. - pub fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + /// Used by the new solver as that one takes the opaque types at the end of a probe + /// to deal with multiple candidates without having to recompute them. + pub fn clone_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + self.inner + .borrow() + .opaque_type_storage + .opaque_types + .iter() + .map(|&(k, ref v)| { + (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty) + }) + .collect() + } + + fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) .into_iter() .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs index c5c6fc41b9e..cac3b407251 100644 --- a/compiler/rustc_infer/src/infer/canonical/substitute.rs +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -11,7 +11,9 @@ use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, TyCtxt}; -pub(super) trait CanonicalExt<'tcx, V> { +/// FIXME(-Ztrait-solver=next): This or public because it is shared with the +/// new trait solver implementation. We should deduplicate canonicalization. +pub trait CanonicalExt<'tcx, V> { /// Instantiate the wrapped value, replacing each canonical value /// with the value given in `var_values`. fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 79efc1ce7bf..8a2b800af0e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -49,6 +49,7 @@ use super::lexical_region_resolve::RegionResolutionError; use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; +use crate::errors; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; @@ -281,15 +282,13 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( span: Span, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, - opaque_ty: ty::OpaqueTypeKey<'tcx>, + opaque_ty_key: ty::OpaqueTypeKey<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let opaque_ty = tcx.mk_opaque(opaque_ty.def_id.to_def_id(), opaque_ty.substs); - let mut err = struct_span_err!( - tcx.sess, + let mut err = tcx.sess.create_err(errors::OpaqueCapturesLifetime { span, - E0700, - "hidden type for `{opaque_ty}` captures lifetime that does not appear in bounds", - ); + opaque_ty: tcx.mk_opaque(opaque_ty_key.def_id.to_def_id(), opaque_ty_key.substs), + opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), + }); // Explain the region we are capturing. match *hidden_region { @@ -1690,7 +1689,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!("{name} is defined in the current crate") } else { let crate_name = self.tcx.crate_name(defid.krate); - format!("{name} is defined in crate `{crate_name}") + format!("{name} is defined in crate `{crate_name}`") }; diagnostic.span_note(def_span, msg); }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index e242900fd23..a3151d2d365 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -17,7 +17,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; -use rustc_middle::ty::{self, DefIdTree, InferConst}; +use rustc_middle::ty::{self, InferConst}; use rustc_middle::ty::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; use rustc_span::symbol::{kw, sym, Ident}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index db4b8af4683..c5ef48fe3da 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -5,7 +5,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::TyCtxt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Binder, Region, Ty, TypeVisitable}; use rustc_span::Span; /// Information about the anonymous region we are searching for. diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 335eb4c5406..2c480355085 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -70,7 +70,7 @@ pub enum RegionResolutionError<'tcx> { /// `o` requires that `a <= b`, but this does not hold ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), - /// `GenericBoundFailure(p, s, a) + /// `GenericBoundFailure(p, s, a)`: /// /// The parameter/associated-type `p` must be known to outlive the lifetime /// `a` (but none of the known bounds are sufficient). @@ -438,7 +438,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Value(a), VarValue::Empty(_)) => { match *a { - ReLateBound(..) | ReErased | ReError(_) => { + // this is always on an error path, + // so it doesn't really matter if it's shorter or longer than an empty region + ReError(_) => false, + + ReLateBound(..) | ReErased => { bug!("cannot relate region: {:?}", a); } @@ -467,7 +471,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Empty(a_ui), VarValue::Value(b)) => { match *b { - ReLateBound(..) | ReErased | ReError(_) => { + // this is always on an error path, + // so it doesn't really matter if it's shorter or longer than an empty region + ReError(_) => false, + + ReLateBound(..) | ReErased => { bug!("cannot relate region: {:?}", b); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index cf8007c964d..387843ee693 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -94,10 +94,10 @@ pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< /// call to `start_snapshot` and `rollback_to`. #[derive(Clone)] pub struct InferCtxtInner<'tcx> { - /// Cache for projections. This cache is snapshotted along with the infcx. + /// Cache for projections. /// - /// Public so that `traits::project` can use it. - pub projection_cache: traits::ProjectionCacheStorage<'tcx>, + /// This cache is snapshotted along with the infcx. + projection_cache: traits::ProjectionCacheStorage<'tcx>, /// We instantiate `UnificationTable` with `bounds<Ty>` because the types /// that might instantiate a general type variable have an order, @@ -156,7 +156,7 @@ pub struct InferCtxtInner<'tcx> { undo_log: InferCtxtUndoLogs<'tcx>, /// Caches for opaque type inference. - pub opaque_type_storage: OpaqueTypeStorage<'tcx>, + opaque_type_storage: OpaqueTypeStorage<'tcx>, } impl<'tcx> InferCtxtInner<'tcx> { @@ -195,41 +195,17 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - fn int_unification_table( - &mut self, - ) -> ut::UnificationTable< - ut::InPlace< - ty::IntVid, - &mut ut::UnificationStorage<ty::IntVid>, - &mut InferCtxtUndoLogs<'tcx>, - >, - > { + fn int_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::IntVid> { self.int_unification_storage.with_log(&mut self.undo_log) } #[inline] - fn float_unification_table( - &mut self, - ) -> ut::UnificationTable< - ut::InPlace< - ty::FloatVid, - &mut ut::UnificationStorage<ty::FloatVid>, - &mut InferCtxtUndoLogs<'tcx>, - >, - > { + fn float_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::FloatVid> { self.float_unification_storage.with_log(&mut self.undo_log) } #[inline] - fn const_unification_table( - &mut self, - ) -> ut::UnificationTable< - ut::InPlace< - ty::ConstVid<'tcx>, - &mut ut::UnificationStorage<ty::ConstVid<'tcx>>, - &mut InferCtxtUndoLogs<'tcx>, - >, - > { + fn const_unification_table(&mut self) -> UnificationTable<'_, 'tcx, ty::ConstVid<'tcx>> { self.const_unification_storage.with_log(&mut self.undo_log) } @@ -1429,17 +1405,14 @@ impl<'tcx> InferCtxt<'tcx> { } } + /// Attempts to resolve all type/region/const variables in + /// `value`. Region inference must have been run already (e.g., + /// by calling `resolve_regions_and_report_errors`). If some + /// variable was never unified, an `Err` results. + /// + /// This method is idempotent, but it not typically not invoked + /// except during the writeback phase. pub fn fully_resolve<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> FixupResult<'tcx, T> { - /*! - * Attempts to resolve all type/region/const variables in - * `value`. Region inference must have been run already (e.g., - * by calling `resolve_regions_and_report_errors`). If some - * variable was never unified, an `Err` results. - * - * This method is idempotent, but it not typically not invoked - * except during the writeback phase. - */ - let value = resolve::fully_resolve(self, value); assert!( value.as_ref().map_or(true, |value| !value.needs_infer()), @@ -1754,7 +1727,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // in this case. The typechecker should only ever report type errors involving mismatched // types using one of these methods, and should not call span_err directly for such // errors. - pub fn type_error_struct_with_diag<M>( &self, sp: Span, diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 6e413a7f412..573cd91a2a2 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -50,7 +50,7 @@ where /// /// - Covariant means `a <: b`. /// - Contravariant means `b <: a`. - /// - Invariant means `a == b. + /// - Invariant means `a == b`. /// - Bivariant means that it doesn't matter. ambient_variance: ty::Variance, diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 33514eedfc3..872f617474c 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -249,7 +249,7 @@ pub enum VerifyBound<'tcx> { /// in that case we can show `'b: 'c`. But if `'?x` winds up being something /// else, the bound isn't relevant. /// -/// In the [`VerifyBound`], this struct is enclosed in `Binder to account +/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account /// for cases like /// /// ```rust diff --git a/compiler/rustc_interface/locales/en-US.ftl b/compiler/rustc_interface/locales/en-US.ftl index da58492ccf2..37994899a20 100644 --- a/compiler/rustc_interface/locales/en-US.ftl +++ b/compiler/rustc_interface/locales/en-US.ftl @@ -33,7 +33,7 @@ interface_rustc_error_fatal = fatal error triggered by #[rustc_error] interface_rustc_error_unexpected_annotation = - unexpected annotation used with `#[rustc_error(...)]! + unexpected annotation used with `#[rustc_error(...)]`! interface_failed_writing_file = failed to write file {$path}: {$error}" diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index e6f04fe0aaa..322ec31fb2c 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -166,15 +166,17 @@ pub enum DocStyle { Inner, } -// Note that the suffix is *not* considered when deciding the `LiteralKind` in -// this type. This means that float literals like `1f32` are classified by this -// type as `Int`. (Compare against `rustc_ast::token::LitKind` and -// `rustc_ast::ast::LitKind.) +/// Enum representing the literal types supported by the lexer. +/// +/// Note that the suffix is *not* considered when deciding the `LiteralKind` in +/// this type. This means that float literals like `1f32` are classified by this +/// type as `Int`. (Compare against `rustc_ast::token::LitKind` and +/// `rustc_ast::ast::LitKind`). #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum LiteralKind { /// "12_u8", "0o100", "0b120i99", "1f32". Int { base: Base, empty_int: bool }, - /// "12.34f32", "1e3", but not "1f32`. + /// "12.34f32", "1e3", but not "1f32". Float { base: Base, empty_exponent: bool }, /// "'a'", "'\\'", "'''", "';" Char { terminated: bool }, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 59540aaf18f..5b2100b5da9 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1288,7 +1288,7 @@ declare_lint! { } declare_lint_pass!( - /// Explains corresponding feature flag must be enabled for the `#[track_caller] attribute to + /// Explains corresponding feature flag must be enabled for the `#[track_caller]` attribute to /// do anything UngatedAsyncFnTrackCaller => [UNGATED_ASYNC_FN_TRACK_CALLER] ); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index aace4974cc9..f5a711315ea 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -893,6 +893,23 @@ pub trait LintContext: Sized { BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => { db.help("consider implementing the trait by hand, or remove the `packed` attribute"); } + BuiltinLintDiagnostics::UnusedExternCrate { removal_span }=> { + db.span_suggestion( + removal_span, + "remove it", + "", + Applicability::MachineApplicable, + ); + } + BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span }=> { + let suggestion_span = vis_span.between(ident_span); + db.span_suggestion_verbose( + suggestion_span, + "convert it to a `use`", + if vis_span.is_empty() { "use " } else { " use " }, + Applicability::MachineApplicable, + ); + } } // Rewrap `db`, and pass control to the user. decorate(db) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 85958c41705..7ca50f5a2db 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -17,7 +17,7 @@ use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{ - self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_span::def_id::LocalDefId; use rustc_span::source_map; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3a92f5806c9..c43162f6325 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -14,7 +14,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_infer::traits::util::elaborate_predicates_with_span; use rustc_middle::ty::adjustment; -use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span}; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6efbf5ce9ee..534aff7fb62 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -522,6 +522,13 @@ pub enum BuiltinLintDiagnostics { is_formatting_arg: bool, }, ByteSliceInPackedStructWithDerive, + UnusedExternCrate { + removal_span: Span, + }, + ExternCrateNotIdiomatic { + vis_span: Span, + ident_span: Span, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 9146a3739b2..05890628378 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -14,6 +14,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/Host.h" #include "llvm/Support/Memory.h" #include "llvm/Support/SourceMgr.h" diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 0a42265a6ba..4761ce83fab 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -9,7 +9,6 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" -#include "llvm/InitializePasses.h" #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/IntrinsicInst.h" @@ -58,22 +57,6 @@ typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef) DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) -extern "C" void LLVMInitializePasses() { - PassRegistry &Registry = *PassRegistry::getPassRegistry(); - initializeCore(Registry); - initializeCodeGen(Registry); - initializeScalarOpts(Registry); - initializeVectorization(Registry); - initializeIPO(Registry); - initializeAnalysis(Registry); - initializeTransformUtils(Registry); - initializeInstCombine(Registry); -#if LLVM_VERSION_LT(16, 0) - initializeInstrumentation(Registry); -#endif - initializeTarget(Registry); -} - extern "C" void LLVMTimeTraceProfilerInitialize() { timeTraceProfilerInitialize( /* TimeTraceGranularity */ 0, @@ -1004,23 +987,8 @@ LLVMRustPrintModule(LLVMModuleRef M, const char *Path, DemangleFn Demangle) { } extern "C" void LLVMRustPrintPasses() { - LLVMInitializePasses(); - struct MyListener : PassRegistrationListener { - void passEnumerate(const PassInfo *Info) { - StringRef PassArg = Info->getPassArgument(); - StringRef PassName = Info->getPassName(); - if (!PassArg.empty()) { - // These unsigned->signed casts could theoretically overflow, but - // realistically never will (and even if, the result is implementation - // defined rather plain UB). - printf("%15.*s - %.*s\n", (int)PassArg.size(), PassArg.data(), - (int)PassName.size(), PassName.data()); - } - } - } Listener; - - PassRegistry *PR = PassRegistry::getPassRegistry(); - PR->enumerateWith(&Listener); + PassBuilder PB; + PB.printPassNames(outs()); } extern "C" void LLVMRustRunRestrictionPass(LLVMModuleRef M, char **Symbols, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b1e6534944d..e3493caaaf7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1751,6 +1751,19 @@ LLVMRustModuleCost(LLVMModuleRef M) { return std::distance(std::begin(f), std::end(f)); } +extern "C" void +LLVMRustModuleInstructionStats(LLVMModuleRef M, RustStringRef Str) +{ + RawRustStringOstream OS(Str); + llvm::json::OStream JOS(OS); + auto Module = unwrap(M); + + JOS.object([&] { + JOS.attribute("module", Module->getName()); + JOS.attribute("total", Module->getInstructionCount()); + }); +} + // Vector reductions: extern "C" LLVMValueRef LLVMRustBuildVectorReduceFAdd(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Src) { diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 82e6972d027..8d017d149f6 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -42,6 +42,12 @@ fn decodable_body( } let ty_name = s.ast().ident.to_string(); let decode_body = match s.variants() { + [] => { + let message = format!("`{}` has no variants to decode", ty_name); + quote! { + panic!(#message) + } + } [vi] => vi.construct(|field, _index| decode_field(field)), variants => { let match_inner: TokenStream = variants @@ -139,6 +145,11 @@ fn encodable_body( }); let encode_body = match s.variants() { + [] => { + quote! { + match *self {} + } + } [_] => { let encode_inner = s.each_variant(|vi| { vi.bindings() @@ -160,6 +171,23 @@ fn encodable_body( } } _ => { + let disc = { + let mut variant_idx = 0usize; + let encode_inner = s.each_variant(|_| { + let result = quote! { + #variant_idx + }; + variant_idx += 1; + result + }); + quote! { + let disc = match *self { + #encode_inner + }; + ::rustc_serialize::Encoder::emit_usize(__encoder, disc); + } + }; + let mut variant_idx = 0usize; let encode_inner = s.each_variant(|vi| { let encode_fields: TokenStream = vi @@ -176,26 +204,11 @@ fn encodable_body( result }) .collect(); - - let result = if !vi.bindings().is_empty() { - quote! { - ::rustc_serialize::Encoder::emit_enum_variant( - __encoder, - #variant_idx, - |__encoder| { #encode_fields } - ) - } - } else { - quote! { - ::rustc_serialize::Encoder::emit_fieldless_enum_variant::<#variant_idx>( - __encoder, - ) - } - }; variant_idx += 1; - result + encode_fields }); quote! { + #disc match *self { #encode_inner } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 755a2425350..c48e681eb94 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -789,6 +789,9 @@ fn get_metadata_section<'p>( loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?; // The header is uncompressed let header_len = METADATA_HEADER.len(); + // header + u32 length of data + let data_start = header_len + 4; + debug!("checking {} bytes of metadata-version stamp", header_len); let header = &buf[..cmp::min(header_len, buf.len())]; if header != METADATA_HEADER { @@ -798,8 +801,14 @@ fn get_metadata_section<'p>( ))); } + // Length of the compressed stream - this allows linkers to pad the section if they want + let Ok(len_bytes) = <[u8; 4]>::try_from(&buf[header_len..cmp::min(data_start, buf.len())]) else { + return Err(MetadataError::LoadFailure("invalid metadata length found".to_string())); + }; + let compressed_len = u32::from_be_bytes(len_bytes) as usize; + // Header is okay -> inflate the actual metadata - let compressed_bytes = &buf[header_len..]; + let compressed_bytes = &buf[data_start..(data_start + compressed_len)]; debug!("inflating {} bytes of compressed metadata", compressed_bytes.len()); // Assume the decompressed data will be at least the size of the compressed data, so we // don't have to grow the buffer as much. diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f0dafe73c00..3ab01f7809b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1101,37 +1101,18 @@ fn should_encode_const(def_kind: DefKind) -> bool { } } -fn should_encode_trait_impl_trait_tys(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if tcx.def_kind(def_id) != DefKind::AssocFn { - return false; - } - - let Some(item) = tcx.opt_associated_item(def_id) else { return false; }; - if item.container != ty::AssocItemContainer::ImplContainer { - return false; +// We only encode impl trait in trait when using `lower-impl-trait-in-trait-to-assoc-ty` unstable +// option. +fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty + && let Some(assoc_item) = tcx.opt_associated_item(def_id) + && assoc_item.container == ty::AssocItemContainer::TraitContainer + && assoc_item.kind == ty::AssocKind::Fn + { + true + } else { + false } - - let Some(trait_item_def_id) = item.trait_item_def_id else { return false; }; - - // FIXME(RPITIT): This does a somewhat manual walk through the signature - // of the trait fn to look for any RPITITs, but that's kinda doing a lot - // of work. We can probably remove this when we refactor RPITITs to be - // associated types. - tcx.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, data) = ty.kind() - && tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder - { - true - } else { - false - } - }) -} - -// Return `false` to avoid encoding impl trait in trait, while we don't use the query. -fn should_encode_fn_impl_trait_in_trait<'tcx>(_tcx: TyCtxt<'tcx>, _def_id: DefId) -> bool { - false } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { @@ -1211,7 +1192,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind { self.encode_info_for_adt(def_id); } - if should_encode_trait_impl_trait_tys(tcx, def_id) + if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { record!(self.tables.trait_impl_trait_tys[def_id] <- table); @@ -1640,7 +1621,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn encode_info_for_closure(&mut self, def_id: LocalDefId) { // NOTE(eddyb) `tcx.type_of(def_id)` isn't used because it's fully generic, - // including on the signature, which is inferred in `typeck. + // including on the signature, which is inferred in `typeck`. let typeck_result: &'tcx ty::TypeckResults<'tcx> = self.tcx.typeck(def_id); let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let ty = typeck_result.node_type(hir_id); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a7ec2d790b7..1d2541a6788 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -55,13 +55,13 @@ pub(crate) fn rustc_version() -> String { /// Metadata encoding version. /// N.B., increment this if you change the format of metadata such that /// the rustc version can't be found to compare with `rustc_version()`. -const METADATA_VERSION: u8 = 6; +const METADATA_VERSION: u8 = 7; /// Metadata header which includes `METADATA_VERSION`. /// -/// This header is followed by the position of the `CrateRoot`, -/// which is encoded as a 32-bit big-endian unsigned integer, -/// and further followed by the rustc version string. +/// This header is followed by the length of the compressed data, then +/// the position of the `CrateRoot`, which is encoded as a 32-bit big-endian +/// unsigned integer, and further followed by the rustc version string. pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; /// A value of type T referred to by its absolute position diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index d4019b5bf17..62e44b6298b 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -116,6 +116,7 @@ macro_rules! arena_types { [] bit_set_u32: rustc_index::bit_set::BitSet<u32>, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, + [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), ]); ) } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 2df851a7857..43eef1c770c 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1,5 +1,5 @@ use crate::hir::{ModuleItems, Owner}; -use crate::ty::{DefIdTree, TyCtxt}; +use crate::ty::TyCtxt; use rustc_ast as ast; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -903,6 +903,11 @@ impl<'hir> Map<'hir> { } #[inline] + pub fn ident(self, id: HirId) -> Ident { + self.opt_ident(id).unwrap() + } + + #[inline] pub fn opt_name(self, id: HirId) -> Option<Symbol> { self.opt_ident(id).map(|ident| ident.name) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 2e2ca6a2788..6706b9db3f5 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -7,7 +7,7 @@ pub mod nested_filter; pub mod place; use crate::ty::query::Providers; -use crate::ty::{DefIdTree, ImplSubject, TyCtxt}; +use crate::ty::{ImplSubject, TyCtxt}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{par_for_each_in, Send, Sync}; @@ -64,13 +64,17 @@ impl ModuleItems { self.foreign_items.iter().copied() } - pub fn definitions(&self) -> impl Iterator<Item = LocalDefId> + '_ { + pub fn owners(&self) -> impl Iterator<Item = OwnerId> + '_ { self.items .iter() - .map(|id| id.owner_id.def_id) - .chain(self.trait_items.iter().map(|id| id.owner_id.def_id)) - .chain(self.impl_items.iter().map(|id| id.owner_id.def_id)) - .chain(self.foreign_items.iter().map(|id| id.owner_id.def_id)) + .map(|id| id.owner_id) + .chain(self.trait_items.iter().map(|id| id.owner_id)) + .chain(self.impl_items.iter().map(|id| id.owner_id)) + .chain(self.foreign_items.iter().map(|id| id.owner_id)) + } + + pub fn definitions(&self) -> impl Iterator<Item = LocalDefId> + '_ { + self.owners().map(|id| id.def_id) } pub fn par_items(&self, f: impl Fn(ItemId) + Send + Sync) { @@ -173,6 +177,7 @@ pub fn provide(providers: &mut Providers) { } }; providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id.expect_local()); + providers.opt_rpitit_info = |_, _| None; providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls; providers.expn_that_defined = |tcx, id| { let id = id.expect_local(); diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 8712514a384..7f8fc17744d 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -123,6 +123,11 @@ impl<'tcx> CanonicalVarInfo<'tcx> { self.kind.universe() } + #[must_use] + pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarInfo<'tcx> { + CanonicalVarInfo { kind: self.kind.with_updated_universe(ui) } + } + pub fn is_existential(&self) -> bool { match self.kind { CanonicalVarKind::Ty(_) => true, @@ -133,6 +138,28 @@ impl<'tcx> CanonicalVarInfo<'tcx> { CanonicalVarKind::PlaceholderConst(_, _) => false, } } + + pub fn is_region(&self) -> bool { + match self.kind { + CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, + CanonicalVarKind::Ty(_) + | CanonicalVarKind::PlaceholderTy(_) + | CanonicalVarKind::Const(_, _) + | CanonicalVarKind::PlaceholderConst(_, _) => false, + } + } + + pub fn expect_anon_placeholder(self) -> u32 { + match self.kind { + CanonicalVarKind::Ty(_) + | CanonicalVarKind::Region(_) + | CanonicalVarKind::Const(_, _) => bug!("expected placeholder: {self:?}"), + + CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.name.expect_anon(), + CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.name.expect_anon(), + CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.name.as_u32(), + } + } } /// Describes the "kind" of the canonical variable. This is a "kind" @@ -177,6 +204,38 @@ impl<'tcx> CanonicalVarKind<'tcx> { CanonicalVarKind::PlaceholderConst(placeholder, _) => placeholder.universe, } } + + /// Replaces the universe of this canonical variable with `ui`. + /// + /// In case this is a float or int variable, this causes an ICE if + /// the updated universe is not the root. + pub fn with_updated_universe(self, ui: ty::UniverseIndex) -> CanonicalVarKind<'tcx> { + match self { + CanonicalVarKind::Ty(kind) => match kind { + CanonicalTyVarKind::General(_) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) + } + CanonicalTyVarKind::Int | CanonicalTyVarKind::Float => { + assert_eq!(ui, ty::UniverseIndex::ROOT); + CanonicalVarKind::Ty(kind) + } + }, + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(ty::Placeholder { universe: ui, ..placeholder }) + } + CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui), + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { universe: ui, ..placeholder }) + } + CanonicalVarKind::Const(_, ty) => CanonicalVarKind::Const(ui, ty), + CanonicalVarKind::PlaceholderConst(placeholder, ty) => { + CanonicalVarKind::PlaceholderConst( + ty::Placeholder { universe: ui, ..placeholder }, + ty, + ) + } + } + } } /// Rust actually has more than one category of type variables; @@ -213,7 +272,8 @@ pub struct QueryResponse<'tcx, R> { pub value: R, } -#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec<QueryOutlivesConstraint<'tcx>>, pub member_constraints: Vec<MemberConstraint<'tcx>>, diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 38868c21049..2db59f37f40 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -12,7 +12,8 @@ use rustc_span::Span; /// ```text /// R0 member of [O1..On] /// ``` -#[derive(Debug, Clone, HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct MemberConstraint<'tcx> { /// The `DefId` and substs of the opaque type causing this constraint. /// Used for error reporting. diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 893bf54b866..171cf1c1ab1 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -1,7 +1,7 @@ //! A pass that checks to make sure private fields and methods aren't used //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. -use crate::ty::{DefIdTree, TyCtxt, Visibility}; +use crate::ty::{TyCtxt, Visibility}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable; @@ -112,7 +112,7 @@ impl EffectiveVisibilities { &mut self, def_id: LocalDefId, eff_vis: &EffectiveVisibility, - tree: impl DefIdTree, + tcx: TyCtxt<'_>, ) { use std::collections::hash_map::Entry; match self.map.entry(def_id) { @@ -122,7 +122,7 @@ impl EffectiveVisibilities { let vis_at_level = eff_vis.at_level(l); let old_vis_at_level = old_eff_vis.at_level_mut(l); if vis_at_level != old_vis_at_level - && vis_at_level.is_at_least(*old_vis_at_level, tree) + && vis_at_level.is_at_least(*old_vis_at_level, tcx) { *old_vis_at_level = *vis_at_level } @@ -219,7 +219,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> { lazy_private_vis: impl FnOnce() -> Visibility, inherited_effective_vis: EffectiveVisibility, level: Level, - tree: impl DefIdTree, + tcx: TyCtxt<'_>, ) -> bool { let mut changed = false; let mut current_effective_vis = self @@ -240,7 +240,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> { && level != l) { calculated_effective_vis = - if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) { + if nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) { inherited_effective_vis_at_level } else { nominal_vis @@ -249,7 +249,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> { // effective visibility can't be decreased at next update call for the // same id if *current_effective_vis_at_level != calculated_effective_vis - && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tree) + && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tcx) { changed = true; *current_effective_vis_at_level = calculated_effective_vis; diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 354c84e2209..b61f7806b7a 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -3,7 +3,7 @@ pub use self::StabilityLevel::*; -use crate::ty::{self, DefIdTree, TyCtxt}; +use crate::ty::{self, TyCtxt}; use rustc_ast::NodeId; use rustc_attr::{self as attr, ConstStability, DefaultBodyStability, Deprecation, Stability}; use rustc_data_structures::fx::FxHashMap; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 221105ac48f..48375ed301d 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -8,7 +8,8 @@ mod tests; use std::borrow::Cow; use std::fmt; use std::hash; -use std::ops::Range; +use std::hash::Hash; +use std::ops::{Deref, DerefMut, Range}; use std::ptr; use either::{Left, Right}; @@ -29,6 +30,39 @@ use provenance_map::*; pub use init_mask::{InitChunk, InitChunkIter}; +/// Functionality required for the bytes of an `Allocation`. +pub trait AllocBytes: + Clone + fmt::Debug + Eq + PartialEq + Hash + Deref<Target = [u8]> + DerefMut<Target = [u8]> +{ + /// Adjust the bytes to the specified alignment -- by default, this is a no-op. + fn adjust_to_align(self, _align: Align) -> Self; + + /// Create an `AllocBytes` from a slice of `u8`. + fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, _align: Align) -> Self; + + /// Create a zeroed `AllocBytes` of the specified size and alignment; + /// call the callback error handler if there is an error in allocating the memory. + fn zeroed(size: Size, _align: Align) -> Option<Self>; +} + +// Default `bytes` for `Allocation` is a `Box<[u8]>`. +impl AllocBytes for Box<[u8]> { + fn adjust_to_align(self, _align: Align) -> Self { + self + } + + fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, _align: Align) -> Self { + Box::<[u8]>::from(slice.into()) + } + + fn zeroed(size: Size, _align: Align) -> Option<Self> { + let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).ok()?; + // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]> + let bytes = unsafe { bytes.assume_init() }; + Some(bytes) + } +} + /// This type represents an Allocation in the Miri/CTFE core engine. /// /// Its public API is rather low-level, working directly with allocation offsets and a custom error @@ -38,10 +72,10 @@ pub use init_mask::{InitChunk, InitChunkIter}; // hashed. (see the `Hash` impl below for more details), so the impl is not derived. #[derive(Clone, Eq, PartialEq, TyEncodable, TyDecodable)] #[derive(HashStable)] -pub struct Allocation<Prov: Provenance = AllocId, Extra = ()> { +pub struct Allocation<Prov: Provenance = AllocId, Extra = (), Bytes = Box<[u8]>> { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer. - bytes: Box<[u8]>, + bytes: Bytes, /// Maps from byte addresses to extra provenance data for each pointer. /// Only the first byte of a pointer is inserted into the map; i.e., /// every entry in this map applies to `pointer_size` consecutive bytes starting @@ -220,14 +254,27 @@ impl AllocRange { } // The constructors are all without extra; the extra gets added by a machine hook later. -impl<Prov: Provenance> Allocation<Prov> { +impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> { + /// Creates an allocation from an existing `Bytes` value - this is needed for miri FFI support + pub fn from_raw_bytes(bytes: Bytes, align: Align, mutability: Mutability) -> Self { + let size = Size::from_bytes(bytes.len()); + Self { + bytes, + provenance: ProvenanceMap::new(), + init_mask: InitMask::new(size, true), + align, + mutability, + extra: (), + } + } + /// Creates an allocation initialized by the given bytes pub fn from_bytes<'a>( slice: impl Into<Cow<'a, [u8]>>, align: Align, mutability: Mutability, ) -> Self { - let bytes = Box::<[u8]>::from(slice.into()); + let bytes = Bytes::from_bytes(slice, align); let size = Size::from_bytes(bytes.len()); Self { bytes, @@ -248,7 +295,7 @@ impl<Prov: Provenance> Allocation<Prov> { /// /// If `panic_on_fail` is true, this will never return `Err`. pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> { - let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).map_err(|_| { + let bytes = Bytes::zeroed(size, align).ok_or_else(|| { // This results in an error that can happen non-deterministically, since the memory // available to the compiler can change between runs. Normally queries are always // deterministic. However, we can be non-deterministic here because all uses of const @@ -262,8 +309,7 @@ impl<Prov: Provenance> Allocation<Prov> { }); InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) })?; - // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]> - let bytes = unsafe { bytes.assume_init() }; + Ok(Allocation { bytes, provenance: ProvenanceMap::new(), @@ -275,7 +321,7 @@ impl<Prov: Provenance> Allocation<Prov> { } } -impl Allocation { +impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> { /// Adjust allocation from the ones in tcx to a custom Machine instance /// with a different Provenance and Extra type. pub fn adjust_from_tcx<Prov: Provenance, Extra, Err>( @@ -283,9 +329,11 @@ impl Allocation { cx: &impl HasDataLayout, extra: Extra, mut adjust_ptr: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Prov>, Err>, - ) -> Result<Allocation<Prov, Extra>, Err> { - // Compute new pointer provenance, which also adjusts the bytes. - let mut bytes = self.bytes; + ) -> Result<Allocation<Prov, Extra, Bytes>, Err> { + // Compute new pointer provenance, which also adjusts the bytes, and realign the pointer if + // necessary. + let mut bytes = self.bytes.adjust_to_align(self.align); + let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len()); let ptr_size = cx.data_layout().pointer_size.bytes_usize(); let endian = cx.data_layout().endian; @@ -311,7 +359,7 @@ impl Allocation { } /// Raw accessors. Provide access to otherwise private bytes. -impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { +impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> { pub fn len(&self) -> usize { self.bytes.len() } @@ -340,7 +388,11 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { } /// Byte accessors. -impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { +impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> { + pub fn base_addr(&self) -> *const u8 { + self.bytes.as_ptr() + } + /// This is the entirely abstraction-violating way to just grab the raw bytes without /// caring about provenance or initialization. /// @@ -412,7 +464,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { } /// Reading and writing. -impl<Prov: Provenance, Extra> Allocation<Prov, Extra> { +impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> { /// Sets the init bit for the given range. fn mark_init(&mut self, range: AllocRange, is_init: bool) { if range.size.bytes() == 0 { diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b0975616b61..1766d7a6698 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -127,8 +127,8 @@ pub use self::error::{ pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar}; pub use self::allocation::{ - alloc_range, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk, - InitChunkIter, + alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, + InitChunk, InitChunkIter, }; pub use self::pointer::{Pointer, PointerArithmetic, Provenance}; diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 0d78c6135b3..6e6bb8ce95e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -10,7 +10,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; -use crate::ty::{self, DefIdTree, List, Ty, TyCtxt}; +use crate::ty::{self, List, Ty, TyCtxt}; use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex}; use crate::ty::{GenericArg, InternalSubsts, SubstsRef}; @@ -2908,7 +2908,7 @@ fn pretty_print_const_value<'tcx>( // the `destructure_const` query with an empty `ty::ParamEnv` without // introducing ICEs (e.g. via `layout_of`) from missing bounds. // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` - // to be able to destructure the tuple into `(0u8, *mut T) + // to be able to destructure the tuple into `(0u8, *mut T)` // // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the // correct `ty::ParamEnv` to allow printing *all* constant values. diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 16daf63b82d..d8829e3e782 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -12,8 +12,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; use rustc_middle::mir::interpret::{ - alloc_range, read_target_uint, AllocId, Allocation, ConstAllocation, ConstValue, GlobalAlloc, - Pointer, Provenance, + alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, ConstAllocation, ConstValue, + GlobalAlloc, Pointer, Provenance, }; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -787,21 +787,21 @@ pub fn write_allocations<'tcx>( /// After the hex dump, an ascii dump follows, replacing all unprintable characters (control /// characters or characters whose value is larger than 127) with a `.` /// This also prints provenance adequately. -pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra>( +pub fn display_allocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( tcx: TyCtxt<'tcx>, - alloc: &'a Allocation<Prov, Extra>, -) -> RenderAllocation<'a, 'tcx, Prov, Extra> { + alloc: &'a Allocation<Prov, Extra, Bytes>, +) -> RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> { RenderAllocation { tcx, alloc } } #[doc(hidden)] -pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra> { +pub struct RenderAllocation<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> { tcx: TyCtxt<'tcx>, - alloc: &'a Allocation<Prov, Extra>, + alloc: &'a Allocation<Prov, Extra, Bytes>, } -impl<'a, 'tcx, Prov: Provenance, Extra> std::fmt::Display - for RenderAllocation<'a, 'tcx, Prov, Extra> +impl<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes> std::fmt::Display + for RenderAllocation<'a, 'tcx, Prov, Extra, Bytes> { fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let RenderAllocation { tcx, alloc } = *self; @@ -845,9 +845,9 @@ fn write_allocation_newline( /// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there /// is only one line). Note that your prefix should contain a trailing space as the lines are /// printed directly after it. -fn write_allocation_bytes<'tcx, Prov: Provenance, Extra>( +fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( tcx: TyCtxt<'tcx>, - alloc: &Allocation<Prov, Extra>, + alloc: &Allocation<Prov, Extra, Bytes>, w: &mut dyn std::fmt::Write, prefix: &str, ) -> std::fmt::Result { diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 1610ae1ce14..28a3b51b7fc 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -11,7 +11,7 @@ use std::io::{self, Write}; pub const TOOLTIP_INDENT: &str = " "; const CARET: char = '\u{2038}'; // Unicode `CARET` -const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET +const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET` const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET` const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">"; const HEADER: &str = r#"<!DOCTYPE html> diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index dc02fd53ed0..78ee8a6a8fd 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -4,8 +4,9 @@ use crate::infer::canonical::Canonical; use crate::mir; use crate::traits; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::{TyAndLayout, ValidityRequirement}; use crate::ty::subst::{GenericArg, SubstsRef}; -use crate::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::hir_id::{HirId, OwnerId}; use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector}; @@ -696,3 +697,24 @@ impl Key for HirId { None } } + +impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) { + type CacheSelector = DefaultCacheSelector<Self>; + + // Just forward to `Ty<'tcx>` + #[inline(always)] + fn query_crate_is_local(&self) -> bool { + true + } + + fn default_span(&self, _: TyCtxt<'_>) -> Span { + DUMMY_SP + } + + fn ty_adt_id(&self) -> Option<DefId> { + match self.1.value.kind() { + ty::Adt(adt, _) => Some(adt.did()), + _ => None, + } + } +} diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 6a34e5ede19..5133da3429a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -90,6 +90,7 @@ rustc_queries! { /// Definitions that were generated with no HIR, would be feeded to return `None`. query opt_local_def_id_to_hir_id(key: LocalDefId) -> Option<hir::HirId>{ desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key.to_def_id()) } + feedable } /// Gives access to the HIR node's parent for the HIR owner `key`. @@ -166,6 +167,7 @@ rustc_queries! { } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } query collect_return_position_impl_trait_in_trait_tys(key: DefId) @@ -222,6 +224,7 @@ rustc_queries! { arena_cache cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// Maps from the `DefId` of an item (trait/struct/enum/fn) to the @@ -264,6 +267,7 @@ rustc_queries! { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// Elaborated version of the predicates from `explicit_item_bounds`. @@ -475,14 +479,10 @@ rustc_queries! { } } - query symbols_for_closure_captures( - key: (LocalDefId, LocalDefId) - ) -> &'tcx Vec<rustc_span::Symbol> { - arena_cache + query closure_typeinfo(key: LocalDefId) -> ty::ClosureTypeInfo<'tcx> { desc { - |tcx| "finding symbols for captures of closure `{}` in `{}`", - tcx.def_path_str(key.1.to_def_id()), - tcx.def_path_str(key.0.to_def_id()) + |tcx| "finding symbols for captures of closure `{}`", + tcx.def_path_str(key.to_def_id()) } } @@ -592,6 +592,7 @@ rustc_queries! { desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// Returns the inferred outlives predicates (e.g., for `struct @@ -600,6 +601,7 @@ rustc_queries! { desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// Maps from the `DefId` of a trait to the list of @@ -732,6 +734,7 @@ rustc_queries! { desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern + feedable } /// Collects the associated items defined on a trait or impl. @@ -1146,6 +1149,15 @@ rustc_queries! { desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern + feedable + } + + /// The `opt_rpitit_info` query returns the pair of the def id of the function where the RPIT + /// is defined and the opaque def id if any. + query opt_rpitit_info(def_id: DefId) -> Option<ty::ImplTraitInTraitData> { + desc { |tcx| "opt_rpitit_info `{}`", tcx.def_path_str(def_id) } + cache_on_disk_if { def_id.is_local() } + feedable } /// Gets the span for the definition. @@ -1161,6 +1173,7 @@ rustc_queries! { desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern + feedable } query lookup_stability(def_id: DefId) -> Option<attr::Stability> { @@ -1502,6 +1515,7 @@ rustc_queries! { desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern + feedable } query check_well_formed(key: hir::OwnerId) -> () { @@ -1658,7 +1672,7 @@ rustc_queries! { /// Does lifetime resolution on items. Importantly, we can't resolve /// lifetimes directly on things like trait methods, because of trait params. - /// See `rustc_resolve::late::lifetimes for details. + /// See `rustc_resolve::late::lifetimes` for details. query resolve_bound_vars(_: hir::OwnerId) -> &'tcx ResolveBoundVars { arena_cache desc { "resolving lifetimes" } @@ -1699,6 +1713,7 @@ rustc_queries! { query visibility(def_id: DefId) -> ty::Visibility<DefId> { desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } separate_provide_extern + feedable } query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> { @@ -1830,9 +1845,6 @@ rustc_queries! { query maybe_unused_trait_imports(_: ()) -> &'tcx FxIndexSet<LocalDefId> { desc { "fetching potentially unused trait imports" } } - query maybe_unused_extern_crates(_: ()) -> &'tcx [(LocalDefId, Span)] { - desc { "looking up all possibly unused extern crates" } - } query names_imported_by_glob_use(def_id: LocalDefId) -> &'tcx FxHashSet<Symbol> { desc { |tcx| "finding names imported by glob use for `{}`", tcx.def_path_str(def_id.to_def_id()) } } @@ -2173,12 +2185,8 @@ rustc_queries! { separate_provide_extern } - query permits_uninit_init(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<bool, ty::layout::LayoutError<'tcx>> { - desc { "checking to see if `{}` permits being left uninit", key.value } - } - - query permits_zero_init(key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<bool, ty::layout::LayoutError<'tcx>> { - desc { "checking to see if `{}` permits being left zeroed", key.value } + query check_validity_requirement(key: (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>)) -> Result<bool, ty::layout::LayoutError<'tcx>> { + desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 } } query compare_impl_const( diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 6f2dac46753..3b11fab8cdf 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -20,7 +20,7 @@ use rustc_middle::mir::interpret::AllocId; use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp}; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtDef, Ty, UpvarSubsts}; +use rustc_middle::ty::{self, AdtDef, FnSig, Ty, UpvarSubsts}; use rustc_middle::ty::{CanonicalUserType, CanonicalUserTypeAnnotation}; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -29,11 +29,15 @@ use rustc_target::asm::InlineAsmRegOrRegClass; use std::fmt; use std::ops::Index; -pub mod print; pub mod visit; macro_rules! thir_with_elements { - ($($name:ident: $id:ty => $value:ty => $format:literal,)*) => { + ( + $($field_name:ident: $field_ty:ty,)* + + @elements: + $($name:ident: $id:ty => $value:ty => $format:literal,)* + ) => { $( newtype_index! { #[derive(HashStable)] @@ -48,14 +52,20 @@ macro_rules! thir_with_elements { #[derive(Debug, HashStable, Clone)] pub struct Thir<'tcx> { $( + pub $field_name: $field_ty, + )* + $( pub $name: IndexVec<$id, $value>, )* } impl<'tcx> Thir<'tcx> { - pub fn new() -> Thir<'tcx> { + pub fn new($($field_name: $field_ty,)*) -> Thir<'tcx> { Thir { $( + $field_name, + )* + $( $name: IndexVec::new(), )* } @@ -76,6 +86,9 @@ macro_rules! thir_with_elements { pub const UPVAR_ENV_PARAM: ParamId = ParamId::from_u32(0); thir_with_elements! { + body_type: BodyTy<'tcx>, + +@elements: arms: ArmId => Arm<'tcx> => "a{}", blocks: BlockId => Block => "b{}", exprs: ExprId => Expr<'tcx> => "e{}", @@ -83,6 +96,12 @@ thir_with_elements! { params: ParamId => Param<'tcx> => "p{}", } +#[derive(Debug, HashStable, Clone)] +pub enum BodyTy<'tcx> { + Const(Ty<'tcx>), + Fn(FnSig<'tcx>), +} + /// Description of a type-checked function parameter. #[derive(Clone, Debug, HashStable)] pub struct Param<'tcx> { @@ -218,6 +237,9 @@ pub struct LocalVarId(pub hir::HirId); /// A THIR expression. #[derive(Clone, Debug, HashStable)] pub struct Expr<'tcx> { + /// kind of expression + pub kind: ExprKind<'tcx>, + /// The type of this expression pub ty: Ty<'tcx>, @@ -227,9 +249,6 @@ pub struct Expr<'tcx> { /// span of the expression in the source pub span: Span, - - /// kind of expression - pub kind: ExprKind<'tcx>, } #[derive(Clone, Debug, HashStable)] diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index c5bf9717f03..bd43867a3da 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -2,6 +2,7 @@ use std::ops::ControlFlow; use rustc_data_structures::intern::Interned; +use crate::infer::canonical::QueryRegionConstraints; use crate::ty::{ FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, }; @@ -18,20 +19,25 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { } /// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, TypeFoldable, TypeVisitable)] pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. - pub regions: (), + pub region_constraints: QueryRegionConstraints<'tcx>, pub opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>, } +// FIXME: Having to clone `region_constraints` for folding feels bad and +// probably isn't great wrt performance. +// +// Not sure how to fix this, maybe we should also intern `opaque_types` and +// `region_constraints` here or something. impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( self, folder: &mut F, ) -> Result<Self, F::Error> { Ok(FallibleTypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { - regions: (), + region_constraints: self.region_constraints.clone().try_fold_with(folder)?, opaque_types: self .opaque_types .iter() @@ -42,7 +48,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self { TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { - regions: (), + region_constraints: self.region_constraints.clone().fold_with(folder), opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), }) } @@ -53,7 +59,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { &self, visitor: &mut V, ) -> std::ops::ControlFlow<V::BreakTy> { - self.regions.visit_with(visitor)?; + self.region_constraints.visit_with(visitor)?; self.opaque_types.visit_with(visitor)?; ControlFlow::Continue(()) } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index f1a9e50a4f0..dfe23cf991f 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -1,6 +1,6 @@ pub use self::AssocItemContainer::*; -use crate::ty::{self, DefIdTree}; +use crate::ty; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 6ade8935fc8..dc2bd54b7fe 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -5,10 +5,10 @@ use crate::{mir, ty}; use std::fmt::Write; -use hir::LangItem; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, LangItem}; +use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; use super::{Ty, TyCtxt}; @@ -129,6 +129,9 @@ impl<'tcx> ClosureKind { #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct CapturedPlace<'tcx> { + /// Name and span where the binding happens. + pub var_ident: Ident, + /// The `Place` that is captured. pub place: HirPlace<'tcx>, @@ -148,12 +151,8 @@ impl<'tcx> CapturedPlace<'tcx> { } /// Returns a symbol of the captured upvar, which looks like `name__field1__field2`. - fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol { - let hir_id = match self.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - base => bug!("Expected an upvar, found {:?}", base), - }; - let mut symbol = tcx.hir().name(hir_id).as_str().to_string(); + pub fn to_symbol(&self) -> Symbol { + let mut symbol = self.var_ident.to_string(); let mut ty = self.place.base_ty; for proj in self.place.projections.iter() { @@ -169,11 +168,7 @@ impl<'tcx> CapturedPlace<'tcx> { .unwrap(); } ty => { - span_bug!( - self.get_capture_kind_span(tcx), - "Unexpected type {:?} for `Field` projection", - ty - ) + bug!("Unexpected type {:?} for `Field` projection", ty) } }, @@ -238,10 +233,39 @@ impl<'tcx> CapturedPlace<'tcx> { } } -fn symbols_for_closure_captures(tcx: TyCtxt<'_>, def_id: (LocalDefId, LocalDefId)) -> Vec<Symbol> { - let typeck_results = tcx.typeck(def_id.0); - let captures = typeck_results.closure_min_captures_flattened(def_id.1); - captures.into_iter().map(|captured_place| captured_place.to_symbol(tcx)).collect() +#[derive(Copy, Clone, Debug, HashStable)] +pub struct ClosureTypeInfo<'tcx> { + user_provided_sig: ty::CanonicalPolyFnSig<'tcx>, + captures: &'tcx [&'tcx ty::CapturedPlace<'tcx>], + kind_origin: Option<&'tcx (Span, HirPlace<'tcx>)>, +} + +fn closure_typeinfo<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ClosureTypeInfo<'tcx> { + debug_assert!(tcx.is_closure(def.to_def_id())); + let typeck_results = tcx.typeck(def); + let user_provided_sig = typeck_results.user_provided_sigs[&def]; + let captures = typeck_results.closure_min_captures_flattened(def); + let captures = tcx.arena.alloc_from_iter(captures); + let hir_id = tcx.hir().local_def_id_to_hir_id(def); + let kind_origin = typeck_results.closure_kind_origins().get(hir_id); + ClosureTypeInfo { user_provided_sig, captures, kind_origin } +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn closure_kind_origin(self, def_id: LocalDefId) -> Option<&'tcx (Span, HirPlace<'tcx>)> { + self.closure_typeinfo(def_id).kind_origin + } + + pub fn closure_user_provided_sig(self, def_id: LocalDefId) -> ty::CanonicalPolyFnSig<'tcx> { + self.closure_typeinfo(def_id).user_provided_sig + } + + pub fn closure_captures(self, def_id: LocalDefId) -> &'tcx [&'tcx ty::CapturedPlace<'tcx>] { + if !self.is_closure(def_id.to_def_id()) { + return &[]; + }; + self.closure_typeinfo(def_id).captures + } } /// Return true if the `proj_possible_ancestor` represents an ancestor path @@ -434,5 +458,5 @@ impl BorrowKind { } pub fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { symbols_for_closure_captures, ..*providers } + *providers = ty::query::Providers { closure_typeinfo, ..*providers } } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 884ae7f5da2..5d0cf23280b 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -1,6 +1,6 @@ use crate::middle::resolve_bound_vars as rbv; use crate::mir::interpret::LitToConstInput; -use crate::ty::{self, DefIdTree, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; +use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::intern::Interned; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -16,7 +16,7 @@ pub use int::*; pub use kind::*; pub use valtree::*; -/// Use this rather than `ConstData, whenever possible. +/// Use this rather than `ConstData`, whenever possible. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)] #[rustc_pass_by_value] pub struct Const<'tcx>(pub(super) Interned<'tcx, ConstData<'tcx>>); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 433c9ab9aa2..70091477e39 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -20,11 +20,10 @@ use crate::traits; use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData}; use crate::ty::query::{self, TyCtxtAt}; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, DefIdTree, FloatTy, FloatVar, - FloatVid, GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, - ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, Region, RegionKind, - ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVar, TyVid, TypeAndMut, TypeckResults, UintTy, - Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, FloatTy, FloatVar, FloatVid, + GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, + PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, Region, RegionKind, ReprOptions, + TraitObjectVisitor, Ty, TyKind, TyVar, TyVid, TypeAndMut, TypeckResults, UintTy, Visibility, }; use crate::ty::{GenericArg, InternalSubsts, SubstsRef}; use rustc_ast as ast; @@ -310,7 +309,7 @@ pub struct CommonLifetimes<'tcx> { pub re_vars: Vec<Region<'tcx>>, /// Pre-interned values of the form: - /// `ReLateBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon(v, None) }) + /// `ReLateBound(DebruijnIndex(i), BoundRegion { var: v, kind: BrAnon(v, None) })` /// for small values of `i` and `v`. pub re_late_bounds: Vec<Vec<Region<'tcx>>>, } @@ -794,8 +793,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool { - let cname = self.crate_name(LOCAL_CRATE); - self.sess.consider_optimizing(cname.as_str(), msg) + self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg) } /// Obtain all lang items of this crate and all dependencies (recursively) @@ -2187,7 +2185,7 @@ impl<'tcx> TyCtxt<'tcx> { // Actually intern type lists as lists of `GenericArg`s. // // Transmuting from `Ty<'tcx>` to `GenericArg<'tcx>` is sound - // as explained in ty_slice_as_generic_arg`. With this, + // as explained in `ty_slice_as_generic_arg`. With this, // we guarantee that even when transmuting between `List<Ty<'tcx>>` // and `List<GenericArg<'tcx>>`, the uniqueness requirement for // lists is upheld. @@ -2450,7 +2448,7 @@ impl<'tcx> TyCtxtAt<'tcx> { self.tcx.ty_error_with_message(self.span, "TyKind::Error constructed but no error reported") } - /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to /// ensure it gets used. #[track_caller] pub fn ty_error_with_message(self, msg: &str) -> Ty<'tcx> { @@ -2487,8 +2485,6 @@ pub fn provide(providers: &mut ty::query::Providers) { |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]); providers.maybe_unused_trait_imports = |tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports; - providers.maybe_unused_extern_crates = - |tcx, ()| &tcx.resolutions(()).maybe_unused_extern_crates[..]; providers.names_imported_by_glob_use = |tcx, id| { tcx.arena.alloc(tcx.resolutions(()).glob_map.get(&id).cloned().unwrap_or_default()) }; diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index e894e1aaf36..3ca17e7273e 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,9 +3,9 @@ use std::ops::ControlFlow; use crate::ty::{ - AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst, InferTy, Opaque, - PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitor, + AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate, + Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs index e268553f826..ac42d6e0510 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs @@ -1,5 +1,5 @@ use crate::ty::context::TyCtxt; -use crate::ty::{self, DefId, DefIdTree, ParamEnv, Ty}; +use crate::ty::{self, DefId, ParamEnv, Ty}; /// Represents whether some type is inhabited in a given context. /// Examples of uninhabited types are `!`, `enum Void {}`, or a struct diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 355b8d8b431..92a040068dd 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -105,7 +105,7 @@ impl<'tcx> VariantDef { impl<'tcx> Ty<'tcx> { pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> { match self.kind() { - // For now, union`s are always considered inhabited + // For now, unions are always considered inhabited Adt(adt, _) if adt.is_union() => InhabitedPredicate::True, // Non-exhaustive ADTs from other crates are always considered inhabited Adt(adt, _) if adt.is_variant_list_non_exhaustive() && !adt.did().is_local() => { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6c59cde86e3..254ffc33c96 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -7,6 +7,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::vec::Idx; use rustc_session::config::OptLevel; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; @@ -92,7 +93,7 @@ impl IntegerExt for Integer { if discr < fit { bug!( "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum `{}", + discriminant range of enum `{}`", ty ) } @@ -169,6 +170,41 @@ pub const FAT_PTR_EXTRA: usize = 1; /// * Cranelift stores the base-2 log of the lane count in a 4 bit integer. pub const MAX_SIMD_LANES: u64 = 1 << 0xF; +/// Used in `check_validity_requirement` to indicate the kind of initialization +/// that is checked to be valid +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum ValidityRequirement { + Inhabited, + Zero, + /// The return value of mem::uninitialized, 0x01 + /// (unless -Zstrict-init-checks is on, in which case it's the same as Uninit). + UninitMitigated0x01Fill, + /// True uninitialized memory. + Uninit, +} + +impl ValidityRequirement { + pub fn from_intrinsic(intrinsic: Symbol) -> Option<Self> { + match intrinsic { + sym::assert_inhabited => Some(Self::Inhabited), + sym::assert_zero_valid => Some(Self::Zero), + sym::assert_mem_uninitialized_valid => Some(Self::UninitMitigated0x01Fill), + _ => None, + } + } +} + +impl fmt::Display for ValidityRequirement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Inhabited => f.write_str("is inhabited"), + Self::Zero => f.write_str("allows being left zeroed"), + Self::UninitMitigated0x01Fill => f.write_str("allows being filled with 0x01"), + Self::Uninit => f.write_str("allows being left uninitialized"), + } + } +} + #[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 17262a0be24..8cc8a0573bb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -73,7 +73,7 @@ pub use self::binding::BindingMode; pub use self::binding::BindingMode::*; pub use self::closure::{ is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, - CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList, + CapturedPlace, ClosureKind, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList, RootVariableMinCaptureList, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, UpvarPath, CAPTURE_STRUCT_LOCAL, }; @@ -165,12 +165,8 @@ pub struct ResolverGlobalCtxt { pub effective_visibilities: EffectiveVisibilities, pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>, pub maybe_unused_trait_imports: FxIndexSet<LocalDefId>, - pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, pub reexport_map: FxHashMap<LocalDefId, Vec<ModChild>>, pub glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>, - /// Extern prelude entries. The value is `true` if the entry was introduced - /// via `extern crate` item and not `--extern` option or compiler built-in. - pub extern_prelude: FxHashMap<Symbol, bool>, pub main_def: Option<MainDefinition>, pub trait_impls: FxIndexMap<DefId, Vec<LocalDefId>>, /// A list of proc macro LocalDefIds, written out in the order in which @@ -329,12 +325,15 @@ pub struct ClosureSizeProfileData<'tcx> { pub after_feature_tys: Ty<'tcx>, } -pub trait DefIdTree: Copy { - fn opt_parent(self, id: DefId) -> Option<DefId>; +impl TyCtxt<'_> { + #[inline] + pub fn opt_parent(self, id: DefId) -> Option<DefId> { + self.def_key(id).parent.map(|index| DefId { index, ..id }) + } #[inline] #[track_caller] - fn parent(self, id: DefId) -> DefId { + pub fn parent(self, id: DefId) -> DefId { match self.opt_parent(id) { Some(id) => id, // not `unwrap_or_else` to avoid breaking caller tracking @@ -344,17 +343,17 @@ pub trait DefIdTree: Copy { #[inline] #[track_caller] - fn opt_local_parent(self, id: LocalDefId) -> Option<LocalDefId> { + pub fn opt_local_parent(self, id: LocalDefId) -> Option<LocalDefId> { self.opt_parent(id.to_def_id()).map(DefId::expect_local) } #[inline] #[track_caller] - fn local_parent(self, id: LocalDefId) -> LocalDefId { + pub fn local_parent(self, id: LocalDefId) -> LocalDefId { self.parent(id.to_def_id()).expect_local() } - fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { + pub fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { if descendant.krate != ancestor.krate { return false; } @@ -369,13 +368,6 @@ pub trait DefIdTree: Copy { } } -impl<'tcx> DefIdTree for TyCtxt<'tcx> { - #[inline] - fn opt_parent(self, id: DefId) -> Option<DefId> { - self.def_key(id).parent.map(|index| DefId { index, ..id }) - } -} - impl<Id> Visibility<Id> { pub fn is_public(self) -> bool { matches!(self, Visibility::Public) @@ -395,19 +387,19 @@ impl<Id: Into<DefId>> Visibility<Id> { } /// Returns `true` if an item with this visibility is accessible from the given module. - pub fn is_accessible_from(self, module: impl Into<DefId>, tree: impl DefIdTree) -> bool { + pub fn is_accessible_from(self, module: impl Into<DefId>, tcx: TyCtxt<'_>) -> bool { match self { // Public items are visible everywhere. Visibility::Public => true, - Visibility::Restricted(id) => tree.is_descendant_of(module.into(), id.into()), + Visibility::Restricted(id) => tcx.is_descendant_of(module.into(), id.into()), } } /// Returns `true` if this visibility is at least as accessible as the given visibility - pub fn is_at_least(self, vis: Visibility<impl Into<DefId>>, tree: impl DefIdTree) -> bool { + pub fn is_at_least(self, vis: Visibility<impl Into<DefId>>, tcx: TyCtxt<'_>) -> bool { match vis { Visibility::Public => self.is_public(), - Visibility::Restricted(id) => self.is_accessible_from(id, tree), + Visibility::Restricted(id) => self.is_accessible_from(id, tcx), } } } @@ -714,7 +706,7 @@ impl<'tcx> Predicate<'tcx> { // The substitution from the input trait-ref is therefore going to be // `'a => 'x` (where `'x` has a DB index of 1). // - The supertrait-ref is `for<'b> Bar1<'a,'b>`, where `'a` is an - // early-bound parameter and `'b' is a late-bound parameter with a + // early-bound parameter and `'b` is a late-bound parameter with a // DB index of 1. // - If we replace `'a` with `'x` from the input, it too will have // a DB index of 1, and thus we'll have `for<'x,'b> Bar1<'x,'b>` @@ -2075,6 +2067,12 @@ pub enum ImplOverlapKind { Issue33140, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] +pub enum ImplTraitInTraitData { + Trait { fn_def_id: DefId, opaque_def_id: DefId }, + Impl { fn_def_id: DefId }, +} + impl<'tcx> TyCtxt<'tcx> { pub fn typeck_body(self, body: hir::BodyId) -> &'tcx TypeckResults<'tcx> { self.typeck(self.hir().body_owner_def_id(body)) @@ -2442,7 +2440,7 @@ impl<'tcx> TyCtxt<'tcx> { None } - /// Check if the given `DefId` is `#\[automatically_derived\], *and* + /// Check if the given `DefId` is `#\[automatically_derived\]`, *and* /// whether it was produced by expanding a builtin derive macro. pub fn is_builtin_derived(self, def_id: DefId) -> bool { if self.is_automatically_derived(def_id) @@ -2545,6 +2543,34 @@ impl<'tcx> TyCtxt<'tcx> { } def_id } + + pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { + if self.def_kind(def_id) != DefKind::AssocFn { + return false; + } + + let Some(item) = self.opt_associated_item(def_id) else { return false; }; + if item.container != ty::AssocItemContainer::ImplContainer { + return false; + } + + let Some(trait_item_def_id) = item.trait_item_def_id else { return false; }; + + // FIXME(RPITIT): This does a somewhat manual walk through the signature + // of the trait fn to look for any RPITITs, but that's kinda doing a lot + // of work. We can probably remove this when we refactor RPITITs to be + // associated types. + self.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Alias(ty::Projection, data) = ty.kind() + && self.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder + { + true + } else { + false + } + }) + } } /// Yields the parent function's `LocalDefId` if `def_id` is an `impl Trait` definition. diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 57c8f3075b0..578cd82aa4c 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -7,7 +7,6 @@ //! `normalize_generic_arg_after_erasing_regions` query for each type //! or constant found within. (This underlying query is what is cached.) -use crate::mir; use crate::traits::query::NoSolution; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder}; use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeVisitableExt}; @@ -16,7 +15,6 @@ use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeVisitableExt}; pub enum NormalizationError<'tcx> { Type(Ty<'tcx>), Const(ty::Const<'tcx>), - ConstantKind(mir::ConstantKind<'tcx>), } impl<'tcx> NormalizationError<'tcx> { @@ -24,7 +22,6 @@ impl<'tcx> NormalizationError<'tcx> { match self { NormalizationError::Type(t) => format!("{}", t), NormalizationError::Const(c) => format!("{}", c), - NormalizationError::ConstantKind(ck) => format!("{}", ck), } } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 021c20b5854..d947d96041e 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -1,5 +1,5 @@ use crate::ty::GenericArg; -use crate::ty::{self, DefIdTree, Ty, TyCtxt}; +use crate::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6a053c368d8..b3139d23d36 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,6 +1,6 @@ use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::ty::{ - self, ConstInt, DefIdTree, ParamConst, ScalarInt, Term, TermKind, Ty, TyCtxt, TypeFoldable, + self, ConstInt, ParamConst, ScalarInt, Term, TermKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use crate::ty::{GenericArg, GenericArgKind}; diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 3d9a5075d4a..2bc51baf879 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -32,6 +32,7 @@ use crate::traits::specialization_graph; use crate::traits::{self, ImplSource}; use crate::ty::context::TyCtxtFeed; use crate::ty::fast_reject::SimplifiedType; +use crate::ty::layout::ValidityRequirement; use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::GeneratorDiagnosticData; diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 1d4d76da572..ef643531bb2 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -263,6 +263,7 @@ TrivialTypeTraversalAndLiftImpls! { crate::ty::UniverseIndex, crate::ty::Variance, ::rustc_span::Span, + ::rustc_span::symbol::Ident, ::rustc_errors::ErrorGuaranteed, Field, interpret::Scalar, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ba714541c9e..52d114bae30 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -7,7 +7,7 @@ use crate::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; use crate::ty::visit::ValidateBoundVars; use crate::ty::InferTy::*; use crate::ty::{ - self, AdtDef, DefIdTree, Discr, FallibleTypeFolder, Term, Ty, TyCtxt, TypeFlags, TypeFoldable, + self, AdtDef, Discr, FallibleTypeFolder, Term, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use crate::ty::{List, ParamEnv}; @@ -107,6 +107,15 @@ impl BoundRegionKind { _ => None, } } + + pub fn expect_anon(&self) -> u32 { + match *self { + BoundRegionKind::BrNamed(_, _) | BoundRegionKind::BrEnv => { + bug!("expected anon region: {self:?}") + } + BoundRegionKind::BrAnon(idx, _) => idx, + } + } } pub trait Article { diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 9beaac87183..586958247fc 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -569,7 +569,7 @@ impl<'a, V> LocalTableInContext<'a, V> { self.data.contains_key(&id.local_id) } - pub fn get(&self, id: hir::HirId) -> Option<&V> { + pub fn get(&self, id: hir::HirId) -> Option<&'a V> { validate_hir_id_for_typeck_results(self.hir_owner, id); self.data.get(&id.local_id) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 90270e0ee9d..8b5469743da 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -4,8 +4,8 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::ty::layout::IntegerExt; use crate::ty::{ - self, DefIdTree, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, + self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, }; use crate::ty::{GenericArgKind, SubstsRef}; use rustc_apfloat::Float as _; diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index c4f526dbdc8..55aa4fcff2c 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -4,7 +4,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::Representability; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_query_system::query::QueryInfo; use rustc_query_system::Value; use rustc_span::def_id::LocalDefId; diff --git a/compiler/rustc_mir_build/locales/en-US.ftl b/compiler/rustc_mir_build/locales/en-US.ftl index f9bda721df3..93e7fb330e0 100644 --- a/compiler/rustc_mir_build/locales/en-US.ftl +++ b/compiler/rustc_mir_build/locales/en-US.ftl @@ -374,3 +374,9 @@ mir_build_suggest_let_else = you might want to use `let else` to handle the {$co } matched mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits + + +mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly + .attributes = no other attributes may be applied + .not_box = `#[rustc_box]` may only be applied to a `Box::new()` call + .missing_box = `#[rustc_box]` requires the `owned_box` lang item diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs index 2f63333d46b..cfacb5ea327 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -55,7 +55,7 @@ pub fn as_constant_inner<'tcx>( ConstantKind::Ty(tcx.const_error_with_guaranteed(ty, guar)) } Err(LitToConstError::TypeError) => { - bug!("encountered type error in `lit_to_mir_constant") + bug!("encountered type error in `lit_to_mir_constant`") } }; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index eb20b2308c0..33200b80a57 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::AdtDef; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; +use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, Variance}; use rustc_span::Span; use rustc_target::abi::VariantIdx; @@ -183,7 +183,7 @@ fn to_upvars_resolved_place_builder<'tcx>( &projection, ) else { let closure_span = cx.tcx.def_span(closure_def_id); - if !enable_precise_capture(cx.tcx, closure_span) { + if !enable_precise_capture(closure_span) { bug!( "No associated capture found for {:?}[{:#?}] even though \ capture_disjoint_fields isn't enabled", @@ -745,8 +745,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if -/// user is using Rust Edition 2021 or higher. -fn enable_precise_capture(tcx: TyCtxt<'_>, closure_span: Span) -> bool { - tcx.features().capture_disjoint_fields || closure_span.rust_2021() +/// Precise capture is enabled if user is using Rust Edition 2021 or higher. +fn enable_precise_capture(closure_span: Span) -> bool { + closure_span.rust_2021() } diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 78083685193..ea5aeb67d85 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -40,7 +40,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Generate better code for things that don't need to be // dropped. if lhs.ty.needs_drop(this.tcx, this.param_env) { - let rhs = unpack!(block = this.as_local_operand(block, rhs)); + let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); let lhs = unpack!(block = this.as_place(block, lhs)); unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs)); } else { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index a6de8684c0f..b3f9d82829f 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -20,7 +20,7 @@ use rustc_middle::mir::*; use rustc_middle::thir::{ self, BindingMode, Expr, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir, }; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_span::Symbol; @@ -47,8 +47,6 @@ pub(crate) fn mir_built( /// Construct the MIR for a given `DefId`. fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_> { - let body_owner_kind = tcx.hir().body_owner_kind(def.did); - // Ensure unsafeck and abstract const building is ran before we steal the THIR. // We can't use `ensure()` for `thir_abstract_const` as it doesn't compute the query // if inputs are green. This can cause ICEs when calling `thir_abstract_const` after @@ -65,16 +63,15 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> Body<'_ } let body = match tcx.thir_body(def) { - Err(error_reported) => construct_error(tcx, def.did, body_owner_kind, error_reported), + Err(error_reported) => construct_error(tcx, def.did, error_reported), Ok((thir, expr)) => { // We ran all queries that depended on THIR at the beginning // of `mir_build`, so now we can steal it let thir = thir.steal(); - if body_owner_kind.is_fn_or_closure() { - construct_fn(tcx, def, &thir, expr) - } else { - construct_const(tcx, def, &thir, expr) + match thir.body_type { + thir::BodyTy::Fn(fn_sig) => construct_fn(tcx, def, &thir, expr, fn_sig), + thir::BodyTy::Const(ty) => construct_const(tcx, def, &thir, expr, ty), } } }; @@ -158,13 +155,13 @@ struct BlockContext(Vec<BlockFrame>); struct Builder<'a, 'tcx> { tcx: TyCtxt<'tcx>, infcx: InferCtxt<'tcx>, - typeck_results: &'tcx TypeckResults<'tcx>, region_scope_tree: &'tcx region::ScopeTree, param_env: ty::ParamEnv<'tcx>, thir: &'a Thir<'tcx>, cfg: CFG<'tcx>, + def: ty::WithOptConstParam<LocalDefId>, def_id: DefId, hir_id: hir::HirId, parent_module: DefId, @@ -434,6 +431,7 @@ fn construct_fn<'tcx>( fn_def: ty::WithOptConstParam<LocalDefId>, thir: &Thir<'tcx>, expr: ExprId, + fn_sig: ty::FnSig<'tcx>, ) -> Body<'tcx> { let span = tcx.def_span(fn_def.did); let fn_id = tcx.hir().local_def_id_to_hir_id(fn_def.did); @@ -453,11 +451,6 @@ fn construct_fn<'tcx>( .output .span(); - // fetch the fully liberated fn signature (that is, all bound - // types/lifetimes replaced) - let typeck_results = tcx.typeck_opt_const_arg(fn_def); - let fn_sig = typeck_results.liberated_fn_sigs()[fn_id]; - let safety = match fn_sig.unsafety { hir::Unsafety::Normal => Safety::Safe, hir::Unsafety::Unsafe => Safety::FnUnsafe, @@ -529,13 +522,7 @@ fn construct_fn<'tcx>( let return_block = unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| { Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { - builder.args_and_body( - START_BLOCK, - fn_def.did, - arguments, - arg_scope, - &thir[expr], - ) + builder.args_and_body(START_BLOCK, arguments, arg_scope, &thir[expr]) })) })); let source_info = builder.source_info(fn_end); @@ -563,6 +550,7 @@ fn construct_const<'a, 'tcx>( def: ty::WithOptConstParam<LocalDefId>, thir: &'a Thir<'tcx>, expr: ExprId, + const_ty: Ty<'tcx>, ) -> Body<'tcx> { let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); @@ -586,20 +574,6 @@ fn construct_const<'a, 'tcx>( _ => span_bug!(tcx.def_span(def.did), "can't build MIR for {:?}", def.did), }; - // Get the revealed type of this const. This is *not* the adjusted - // type of its body, which may be a subtype of this type. For - // example: - // - // fn foo(_: &()) {} - // static X: fn(&'static ()) = foo; - // - // The adjusted type of the body of X is `for<'a> fn(&'a ())` which - // is not the same as the type of X. We need the type of the return - // place to be the type of the constant because NLL typeck will - // equate them. - let typeck_results = tcx.typeck_opt_const_arg(def); - let const_ty = typeck_results.node_type(hir_id); - let infcx = tcx.infer_ctxt().build(); let mut builder = Builder::new( thir, @@ -629,15 +603,11 @@ fn construct_const<'a, 'tcx>( /// /// This is required because we may still want to run MIR passes on an item /// with type errors, but normal MIR construction can't handle that in general. -fn construct_error( - tcx: TyCtxt<'_>, - def: LocalDefId, - body_owner_kind: hir::BodyOwnerKind, - err: ErrorGuaranteed, -) -> Body<'_> { +fn construct_error(tcx: TyCtxt<'_>, def: LocalDefId, err: ErrorGuaranteed) -> Body<'_> { let span = tcx.def_span(def); let hir_id = tcx.hir().local_def_id_to_hir_id(def); let generator_kind = tcx.generator_kind(def); + let body_owner_kind = tcx.hir().body_owner_kind(def); let ty = tcx.ty_error(err); let num_params = match body_owner_kind { @@ -728,9 +698,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { thir, tcx, infcx, - typeck_results: tcx.typeck_opt_const_arg(def), region_scope_tree: tcx.region_scope_tree(def.did), param_env, + def, def_id: def.did.to_def_id(), hir_id, parent_module: tcx.parent_module(hir_id).to_def_id(), @@ -780,14 +750,78 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.var_debug_info, self.fn_span, self.generator_kind, - self.typeck_results.tainted_by_errors, + None, ) } + fn insert_upvar_arg(&mut self) { + let Some(closure_arg) = self.local_decls.get(ty::CAPTURE_STRUCT_LOCAL) else { return }; + + let mut closure_ty = closure_arg.ty; + let mut closure_env_projs = vec![]; + if let ty::Ref(_, ty, _) = closure_ty.kind() { + closure_env_projs.push(ProjectionElem::Deref); + closure_ty = *ty; + } + + let upvar_substs = match closure_ty.kind() { + ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), + ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), + _ => return, + }; + + // In analyze_closure() in upvar.rs we gathered a list of upvars used by an + // indexed closure and we stored in a map called closure_min_captures in TypeckResults + // with the closure's DefId. Here, we run through that vec of UpvarIds for + // the given closure and use the necessary information to create upvar + // debuginfo and to fill `self.upvars`. + let capture_tys = upvar_substs.upvar_tys(); + + let tcx = self.tcx; + self.upvars = tcx + .closure_captures(self.def.did) + .iter() + .zip(capture_tys) + .enumerate() + .map(|(i, (captured_place, ty))| { + let name = captured_place.to_symbol(); + + let capture = captured_place.info.capture_kind; + let var_id = match captured_place.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + _ => bug!("Expected an upvar"), + }; + + let mutability = captured_place.mutability; + + let mut projs = closure_env_projs.clone(); + projs.push(ProjectionElem::Field(Field::new(i), ty)); + match capture { + ty::UpvarCapture::ByValue => {} + ty::UpvarCapture::ByRef(..) => { + projs.push(ProjectionElem::Deref); + } + }; + + let use_place = Place { + local: ty::CAPTURE_STRUCT_LOCAL, + projection: tcx.mk_place_elems(&projs), + }; + self.var_debug_info.push(VarDebugInfo { + name, + source_info: SourceInfo::outermost(captured_place.var_ident.span), + value: VarDebugInfoContents::Place(use_place), + }); + + let capture = Capture { captured_place, use_place, mutability }; + (var_id, capture) + }) + .collect(); + } + fn args_and_body( &mut self, mut block: BasicBlock, - fn_def_id: LocalDefId, arguments: &IndexVec<ParamId, Param<'tcx>>, argument_scope: region::Scope, expr: &Expr<'tcx>, @@ -809,69 +843,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let tcx = self.tcx; - let tcx_hir = tcx.hir(); - let hir_typeck_results = self.typeck_results; - - // In analyze_closure() in upvar.rs we gathered a list of upvars used by an - // indexed closure and we stored in a map called closure_min_captures in TypeckResults - // with the closure's DefId. Here, we run through that vec of UpvarIds for - // the given closure and use the necessary information to create upvar - // debuginfo and to fill `self.upvars`. - if hir_typeck_results.closure_min_captures.get(&fn_def_id).is_some() { - let mut closure_env_projs = vec![]; - let mut closure_ty = self.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty; - if let ty::Ref(_, ty, _) = closure_ty.kind() { - closure_env_projs.push(ProjectionElem::Deref); - closure_ty = *ty; - } - let upvar_substs = match closure_ty.kind() { - ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), - ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs), - _ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty), - }; - let def_id = self.def_id.as_local().unwrap(); - let capture_syms = tcx.symbols_for_closure_captures((def_id, fn_def_id)); - let capture_tys = upvar_substs.upvar_tys(); - let captures_with_tys = hir_typeck_results - .closure_min_captures_flattened(fn_def_id) - .zip(capture_tys.zip(capture_syms)); - - self.upvars = captures_with_tys - .enumerate() - .map(|(i, (captured_place, (ty, sym)))| { - let capture = captured_place.info.capture_kind; - let var_id = match captured_place.place.base { - HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - _ => bug!("Expected an upvar"), - }; - - let mutability = captured_place.mutability; - - let mut projs = closure_env_projs.clone(); - projs.push(ProjectionElem::Field(Field::new(i), ty)); - match capture { - ty::UpvarCapture::ByValue => {} - ty::UpvarCapture::ByRef(..) => { - projs.push(ProjectionElem::Deref); - } - }; - - let use_place = Place { - local: ty::CAPTURE_STRUCT_LOCAL, - projection: tcx.mk_place_elems(&projs), - }; - self.var_debug_info.push(VarDebugInfo { - name: *sym, - source_info: SourceInfo::outermost(tcx_hir.span(var_id)), - value: VarDebugInfoContents::Place(use_place), - }); - - let capture = Capture { captured_place, use_place, mutability }; - (var_id, capture) - }) - .collect(); - } + self.insert_upvar_arg(); let mut scope = None; // Bind the argument patterns diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 591b416337b..770701b3750 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -91,7 +91,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{Expr, LintLevel}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{DesugaringKind, Span, DUMMY_SP}; #[derive(Debug)] pub struct Scopes<'tcx> { @@ -1118,24 +1118,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Utility function for *non*-scope code to build their own drops + /// Force a drop at this point in the MIR by creating a new block. pub(crate) fn build_drop_and_replace( &mut self, block: BasicBlock, span: Span, place: Place<'tcx>, - value: Operand<'tcx>, + value: Rvalue<'tcx>, ) -> BlockAnd<()> { + let span = self.tcx.with_stable_hashing_context(|hcx| { + span.mark_with_reason(None, DesugaringKind::Replace, self.tcx.sess.edition(), hcx) + }); let source_info = self.source_info(span); - let next_target = self.cfg.start_new_block(); + + // create the new block for the assignment + let assign = self.cfg.start_new_block(); + self.cfg.push_assign(assign, source_info, place, value.clone()); + + // create the new block for the assignment in the case of unwinding + let assign_unwind = self.cfg.start_new_cleanup_block(); + self.cfg.push_assign(assign_unwind, source_info, place, value.clone()); self.cfg.terminate( block, source_info, - TerminatorKind::DropAndReplace { place, value, target: next_target, unwind: None }, + TerminatorKind::Drop { place, target: assign, unwind: Some(assign_unwind) }, ); self.diverge_from(block); - next_target.unit() + assign.unit() } /// Creates an `Assert` terminator and return the success block. @@ -1413,8 +1424,15 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind { fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { let term = &mut cfg.block_data_mut(from).terminator_mut(); match &mut term.kind { - TerminatorKind::Drop { unwind, .. } - | TerminatorKind::DropAndReplace { unwind, .. } + TerminatorKind::Drop { unwind, .. } => { + if let Some(unwind) = *unwind { + let source_info = term.source_info; + cfg.terminate(unwind, source_info, TerminatorKind::Goto { target: to }); + } else { + *unwind = Some(to); + } + } + TerminatorKind::DropAndReplace { unwind, .. } | TerminatorKind::FalseUnwind { unwind, .. } | TerminatorKind::Call { cleanup: unwind, .. } | TerminatorKind::Assert { cleanup: unwind, .. } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index c1f6b8b59ce..dc4d2276e4a 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -888,3 +888,22 @@ pub enum MiscPatternSuggestion { start_span: Span, }, } + +#[derive(Diagnostic)] +#[diag(mir_build_rustc_box_attribute_error)] +pub struct RustcBoxAttributeError { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub reason: RustcBoxAttrReason, +} + +#[derive(Subdiagnostic)] +pub enum RustcBoxAttrReason { + #[note(mir_build_attributes)] + Attributes, + #[note(mir_build_not_box)] + NotBoxNew, + #[note(mir_build_missing_box)] + MissingBox, +} diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index fbc130501f9..e10a264d385 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -38,6 +38,6 @@ pub fn provide(providers: &mut Providers) { providers.thir_check_unsafety = check_unsafety::thir_check_unsafety; providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg; providers.thir_body = thir::cx::thir_body; - providers.thir_tree = thir::cx::thir_tree; - providers.thir_flat = thir::cx::thir_flat; + providers.thir_tree = thir::print::thir_tree; + providers.thir_flat = thir::print::thir_flat; } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ae203233bd5..9086412c09a 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,3 +1,4 @@ +use crate::errors; use crate::thir::cx::region::Scope; use crate::thir::cx::Cx; use crate::thir::util::UserAnnotatedTyHelpers; @@ -18,7 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{ self, AdtKind, InlineConstSubsts, InlineConstSubstsParts, ScalarInt, Ty, UpvarSubsts, UserType, }; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::abi::VariantIdx; impl<'tcx> Cx<'tcx> { @@ -33,8 +34,6 @@ impl<'tcx> Cx<'tcx> { #[instrument(level = "trace", skip(self, hir_expr))] pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId { - let temp_lifetime = - self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id); let expr_scope = region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node }; @@ -67,7 +66,7 @@ impl<'tcx> Cx<'tcx> { // Next, wrap this up in the expr's scope. expr = Expr { - temp_lifetime, + temp_lifetime: expr.temp_lifetime, ty: expr.ty, span: hir_expr.span, kind: ExprKind::Scope { @@ -82,7 +81,7 @@ impl<'tcx> Cx<'tcx> { self.region_scope_tree.opt_destruction_scope(hir_expr.hir_id.local_id) { expr = Expr { - temp_lifetime, + temp_lifetime: expr.temp_lifetime, ty: expr.ty, span: hir_expr.span, kind: ExprKind::Scope { @@ -262,6 +261,7 @@ impl<'tcx> Cx<'tcx> { } } + #[instrument(level = "debug", skip(self), ret)] fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { let tcx = self.tcx; let expr_ty = self.typeck_results().expr_ty(expr); @@ -322,6 +322,34 @@ impl<'tcx> Cx<'tcx> { fn_span: expr.span, } } else { + let attrs = tcx.hir().attrs(expr.hir_id); + if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_box) { + if attrs.len() != 1 { + tcx.sess.emit_err(errors::RustcBoxAttributeError { + span: attrs[0].span, + reason: errors::RustcBoxAttrReason::Attributes, + }); + } else if let Some(box_item) = tcx.lang_items().owned_box() { + if let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, fn_path)) = fun.kind + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && path.res.opt_def_id().map_or(false, |did| did == box_item) + && fn_path.ident.name == sym::new + && let [value] = args + { + return Expr { temp_lifetime, ty: expr_ty, span: expr.span, kind: ExprKind::Box { value: self.mirror_expr(value) } } + } else { + tcx.sess.emit_err(errors::RustcBoxAttributeError { + span: expr.span, + reason: errors::RustcBoxAttrReason::NotBoxNew + }); + } + } else { + tcx.sess.emit_err(errors::RustcBoxAttributeError { + span: attrs[0].span, + reason: errors::RustcBoxAttrReason::MissingBox, + }); + } + } let adt_data = if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. @@ -541,8 +569,9 @@ impl<'tcx> Cx<'tcx> { let def_id = def_id.expect_local(); let upvars = self - .typeck_results - .closure_min_captures_flattened(def_id) + .tcx + .closure_captures(def_id) + .iter() .zip(substs.upvar_tys()) .map(|(captured_place, ty)| { let upvars = self.capture_upvar(expr, captured_place, ty); diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 20af60a511e..070544446e3 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -52,23 +52,6 @@ pub(crate) fn thir_body( Ok((tcx.alloc_steal_thir(cx.thir), expr)) } -pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String { - match thir_body(tcx, owner_def) { - Ok((thir, _)) => { - let thir = thir.steal(); - tcx.thir_tree_representation(&thir) - } - Err(_) => "error".into(), - } -} - -pub(crate) fn thir_flat(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String { - match thir_body(tcx, owner_def) { - Ok((thir, _)) => format!("{:#?}", thir.steal()), - Err(_) => "error".into(), - } -} - struct Cx<'tcx> { tcx: TyCtxt<'tcx>, thir: Thir<'tcx>, @@ -99,9 +82,30 @@ impl<'tcx> Cx<'tcx> { let typeck_results = tcx.typeck_opt_const_arg(def); let did = def.did; let hir = tcx.hir(); + let hir_id = hir.local_def_id_to_hir_id(did); + + let body_type = if hir.body_owner_kind(did).is_fn_or_closure() { + // fetch the fully liberated fn signature (that is, all bound + // types/lifetimes replaced) + BodyTy::Fn(typeck_results.liberated_fn_sigs()[hir_id]) + } else { + // Get the revealed type of this const. This is *not* the adjusted + // type of its body, which may be a subtype of this type. For + // example: + // + // fn foo(_: &()) {} + // static X: fn(&'static ()) = foo; + // + // The adjusted type of the body of X is `for<'a> fn(&'a ())` which + // is not the same as the type of X. We need the type of the return + // place to be the type of the constant because NLL typeck will + // equate them. + BodyTy::Const(typeck_results.node_type(hir_id)) + }; + Cx { tcx, - thir: Thir::new(), + thir: Thir::new(body_type), param_env: tcx.param_env(def.did), region_scope_tree: tcx.region_scope_tree(def.did), typeck_results, @@ -109,7 +113,7 @@ impl<'tcx> Cx<'tcx> { body_owner: did.to_def_id(), adjustment_span: None, apply_adjustments: hir - .attrs(hir.local_def_id_to_hir_id(did)) + .attrs(hir_id) .iter() .all(|attr| attr.name_or_empty() != rustc_span::sym::custom_mir), } diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index e0e6ac26654..ca26cc13b5e 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -5,9 +5,7 @@ //! structures. pub(crate) mod constant; - pub(crate) mod cx; - pub(crate) mod pattern; - +pub(crate) mod print; mod util; diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 41306dd80fb..f356c8a6838 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -25,7 +25,7 @@ use rustc_middle::mir::{BorrowKind, Field, Mutability}; use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange}; use rustc_middle::ty::subst::{GenericArg, SubstsRef}; use rustc_middle::ty::CanonicalUserTypeAnnotation; -use rustc_middle::ty::{self, AdtDef, ConstKind, DefIdTree, Region, Ty, TyCtxt, UserType}; +use rustc_middle::ty::{self, AdtDef, ConstKind, Region, Ty, TyCtxt, UserType}; use rustc_span::{Span, Symbol}; use std::cmp::Ordering; diff --git a/compiler/rustc_middle/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 60b903e9906..8028227aafd 100644 --- a/compiler/rustc_middle/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -1,13 +1,24 @@ -use crate::thir::*; -use crate::ty::{self, TyCtxt}; - +use rustc_middle::thir::*; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::LocalDefId; use std::fmt::{self, Write}; -impl<'tcx> TyCtxt<'tcx> { - pub fn thir_tree_representation<'a>(self, thir: &'a Thir<'tcx>) -> String { - let mut printer = ThirPrinter::new(thir); - printer.print(); - printer.into_buffer() +pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String { + match super::cx::thir_body(tcx, owner_def) { + Ok((thir, _)) => { + let thir = thir.steal(); + let mut printer = ThirPrinter::new(&thir); + printer.print(); + printer.into_buffer() + } + Err(_) => "error".into(), + } +} + +pub(crate) fn thir_flat(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String { + match super::cx::thir_body(tcx, owner_def) { + Ok((thir, _)) => format!("{:#?}", thir.steal()), + Err(_) => "error".into(), } } diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index fa5f392fa74..536745d2cfe 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -74,7 +74,7 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> { // // `unsafe { *FOO = 0; *BAR.field = 1; }` // `unsafe { &mut *FOO }` - // `unsafe { (*ARRAY)[0] = val; } + // `unsafe { (*ARRAY)[0] = val; }` if !place.projection.iter().any(|p| matches!(p, PlaceElem::Deref)) { let source_info = self.body.source_info(location); let lint_root = self.body.source_scopes[source_info.scope] diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index f973c1ed28f..8ee316773ae 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -407,7 +407,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { if self.prev().is_macro_expansion() && self.curr().is_macro_expansion() { // Macros that expand to include branching (such as // `assert_eq!()`, `assert_ne!()`, `info!()`, `debug!()`, or - // `trace!()) typically generate callee spans with identical + // `trace!()`) typically generate callee spans with identical // ranges (typically the full span of the macro) for all // `BasicBlocks`. This makes it impossible to distinguish // the condition (`if val1 != val2`) from the optional @@ -694,7 +694,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { /// `prev.span.hi()` will be greater than (further right of) `prev_original_span.hi()`. /// If prev.span() was split off to the right of a closure, prev.span().lo() will be /// greater than prev_original_span.lo(). The actual span of `prev_original_span` is - /// not as important as knowing that `prev()` **used to have the same span** as `curr(), + /// not as important as knowing that `prev()` **used to have the same span** as `curr()`, /// which means their sort order is still meaningful for determining the dominator /// relationship. /// diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index bdfd8dc6e99..29424f09695 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -14,7 +14,7 @@ use rustc_mir_dataflow::un_derefer::UnDerefer; use rustc_mir_dataflow::MoveDataParamEnv; use rustc_mir_dataflow::{on_all_children_bits, on_all_drop_children_bits}; use rustc_mir_dataflow::{Analysis, ResultsCursor}; -use rustc_span::Span; +use rustc_span::{DesugaringKind, Span}; use rustc_target::abi::VariantIdx; use std::fmt; @@ -425,10 +425,19 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { bb, ), LookupResult::Parent(..) => { - self.tcx.sess.delay_span_bug( - terminator.source_info.span, - &format!("drop of untracked value {:?}", bb), - ); + if !matches!( + terminator.source_info.span.desugaring_kind(), + Some(DesugaringKind::Replace), + ) { + self.tcx.sess.delay_span_bug( + terminator.source_info.span, + &format!("drop of untracked value {:?}", bb), + ); + } + // A drop and replace behind a pointer/array/whatever. + // The borrow checker requires that these locations are initialized before the assignment, + // so we just leave an unconditional drop. + assert!(!data.is_cleanup); } } } diff --git a/compiler/rustc_mir_transform/src/instcombine.rs b/compiler/rustc_mir_transform/src/instcombine.rs index 14e644bc344..4182da1957e 100644 --- a/compiler/rustc_mir_transform/src/instcombine.rs +++ b/compiler/rustc_mir_transform/src/instcombine.rs @@ -6,9 +6,9 @@ use rustc_middle::mir::{ BinOp, Body, Constant, ConstantKind, LocalDecls, Operand, Place, ProjectionElem, Rvalue, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp, }; -use rustc_middle::ty::layout::LayoutError; -use rustc_middle::ty::{self, ParamEnv, ParamEnvAnd, SubstsRef, Ty, TyCtxt}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_middle::ty::layout::ValidityRequirement; +use rustc_middle::ty::{self, ParamEnv, SubstsRef, Ty, TyCtxt}; +use rustc_span::symbol::Symbol; pub struct InstCombine; @@ -234,16 +234,15 @@ impl<'tcx> InstCombineContext<'tcx, '_> { } let ty = substs.type_at(0); - // Check this is a foldable intrinsic before we query the layout of our generic parameter - let Some(assert_panics) = intrinsic_assert_panics(intrinsic_name) else { return; }; - match assert_panics(self.tcx, self.param_env.and(ty)) { - // We don't know the layout, don't touch the assertion - Err(_) => {} - Ok(true) => { + let known_is_valid = intrinsic_assert_panics(self.tcx, self.param_env, ty, intrinsic_name); + match known_is_valid { + // We don't know the layout or it's not validity assertion at all, don't touch it + None => {} + Some(true) => { // If we know the assert panics, indicate to later opts that the call diverges *target = None; } - Ok(false) => { + Some(false) => { // If we know the assert does not panic, turn the call into a Goto terminator.kind = TerminatorKind::Goto { target: *target_block }; } @@ -252,33 +251,13 @@ impl<'tcx> InstCombineContext<'tcx, '_> { } fn intrinsic_assert_panics<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, intrinsic_name: Symbol, -) -> Option<fn(TyCtxt<'tcx>, ParamEnvAnd<'tcx, Ty<'tcx>>) -> Result<bool, LayoutError<'tcx>>> { - fn inhabited_predicate<'tcx>( - tcx: TyCtxt<'tcx>, - param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result<bool, LayoutError<'tcx>> { - Ok(tcx.layout_of(param_env_and_ty)?.abi.is_uninhabited()) - } - fn zero_valid_predicate<'tcx>( - tcx: TyCtxt<'tcx>, - param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result<bool, LayoutError<'tcx>> { - Ok(!tcx.permits_zero_init(param_env_and_ty)?) - } - fn mem_uninitialized_valid_predicate<'tcx>( - tcx: TyCtxt<'tcx>, - param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>, - ) -> Result<bool, LayoutError<'tcx>> { - Ok(!tcx.permits_uninit_init(param_env_and_ty)?) - } - - match intrinsic_name { - sym::assert_inhabited => Some(inhabited_predicate), - sym::assert_zero_valid => Some(zero_valid_predicate), - sym::assert_mem_uninitialized_valid => Some(mem_uninitialized_valid_predicate), - _ => None, - } +) -> Option<bool> { + let requirement = ValidityRequirement::from_intrinsic(intrinsic_name)?; + Some(!tcx.check_validity_requirement((requirement, param_env.and(ty))).ok()?) } fn resolve_rust_intrinsic<'tcx>( diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 4193eb7d6e8..cdd28ae0c01 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -248,7 +248,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> // N.B., this `borrow()` is guaranteed to be valid (i.e., the value // cannot yet be stolen), because `mir_promoted()`, which steals - // from `mir_const(), forces this query to execute before + // from `mir_const()`, forces this query to execute before // performing the steal. let body = &tcx.mir_const(def).borrow(); diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 2f02d00ec9f..c6e7468aab4 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -68,8 +68,11 @@ fn lower_slice_len_call<'tcx>( ty::FnDef(fn_def_id, _) if fn_def_id == &slice_len_fn_item_def_id => { // perform modifications // from something like `_5 = core::slice::<impl [u8]>::len(move _6) -> bb1` - // into `_5 = Len(*_6) + // into: + // ``` + // _5 = Len(*_6) // goto bb1 + // ``` // make new RValue for Len let deref_arg = tcx.mk_place_deref(arg); diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index 2c56edd89bc..3c7425d83c4 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -9,7 +9,7 @@ use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::ty::print::characteristic_def_id_of_type; -use rustc_middle::ty::{self, visit::TypeVisitableExt, DefIdTree, InstanceDef, TyCtxt}; +use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceDef, TyCtxt}; use rustc_span::symbol::Symbol; use super::PartitioningCx; diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index a31b1f6ac1a..e76e91fc1b1 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -151,6 +151,10 @@ parse_missing_in_in_for_loop = missing `in` in `for` loop parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop .suggestion = try adding an expression to the `for` loop +parse_loop_else = `{$loop_kind}...else` loops are not supported + .note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + .loop_keyword = `else` is attached to this loop + parse_missing_comma_after_match_arm = expected `,` following `match` arm .suggestion = missing a comma here to end this `match` arm @@ -220,7 +224,7 @@ parse_match_arm_body_without_braces = `match` arm body without braces [one] statement *[other] statements } with a body - .suggestion_use_comma_not_semicolon = use a comma to end a `match` arm expression + .suggestion_use_comma_not_semicolon = replace `;` with `,` to end a `match` arm expression parse_inclusive_range_extra_equals = unexpected `=` after inclusive range .suggestion_remove_eq = use `..=` instead diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index c746a870964..1662db36d10 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -452,6 +452,17 @@ pub(crate) struct MissingExpressionInForLoop { } #[derive(Diagnostic)] +#[diag(parse_loop_else)] +#[note] +pub(crate) struct LoopElseNotSupported { + #[primary_span] + pub span: Span, + pub loop_kind: &'static str, + #[label(parse_loop_keyword)] + pub loop_kw: Span, +} + +#[derive(Diagnostic)] #[diag(parse_missing_comma_after_match_arm)] pub(crate) struct MissingCommaAfterMatchArm { #[primary_span] diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index 386bf026bb4..27f4428d306 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -1,4 +1,4 @@ -use super::UnmatchedBrace; +use super::UnmatchedDelim; use rustc_ast::token::Delimiter; use rustc_errors::Diagnostic; use rustc_span::source_map::SourceMap; @@ -8,7 +8,7 @@ use rustc_span::Span; pub struct TokenTreeDiagInfo { /// Stack of open delimiters and their spans. Used for error message. pub open_braces: Vec<(Delimiter, Span)>, - pub unmatched_braces: Vec<UnmatchedBrace>, + pub unmatched_delims: Vec<UnmatchedDelim>, /// Used only for error recovery when arriving to EOF with mismatched braces. pub last_unclosed_found_span: Option<Span>, @@ -32,10 +32,10 @@ pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> b // it's more friendly compared to report `unmatched error` in later phase pub fn report_missing_open_delim( err: &mut Diagnostic, - unmatched_braces: &[UnmatchedBrace], + unmatched_delims: &[UnmatchedDelim], ) -> bool { let mut reported_missing_open = false; - for unmatch_brace in unmatched_braces.iter() { + for unmatch_brace in unmatched_delims.iter() { if let Some(delim) = unmatch_brace.found_delim && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket) { @@ -60,7 +60,7 @@ pub fn report_suspicious_mismatch_block( sm: &SourceMap, delim: Delimiter, ) { - if report_missing_open_delim(err, &diag_info.unmatched_braces) { + if report_missing_open_delim(err, &diag_info.unmatched_delims) { return; } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 37449aaabed..9dbddee5a56 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -1,10 +1,11 @@ use crate::errors; use crate::lexer::unicode_chars::UNICODE_ARRAY; +use crate::make_unclosed_delims_error; use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::contains_text_flow_control_chars; -use rustc_errors::{error_code, Applicability, DiagnosticBuilder, PResult, StashKey}; +use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey}; use rustc_lexer::unescape::{self, Mode}; use rustc_lexer::Cursor; use rustc_lexer::{Base, DocStyle, RawStrError}; @@ -31,7 +32,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char}; rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12); #[derive(Clone, Debug)] -pub struct UnmatchedBrace { +pub struct UnmatchedDelim { pub expected_delim: Delimiter, pub found_delim: Option<Delimiter>, pub found_span: Span, @@ -44,7 +45,7 @@ pub(crate) fn parse_token_trees<'a>( mut src: &'a str, mut start_pos: BytePos, override_span: Option<Span>, -) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { +) -> Result<TokenStream, Vec<Diagnostic>> { // Skip `#!`, if present. if let Some(shebang_len) = rustc_lexer::strip_shebang(src) { src = &src[shebang_len..]; @@ -61,7 +62,29 @@ pub(crate) fn parse_token_trees<'a>( override_span, nbsp_is_whitespace: false, }; - tokentrees::TokenTreesReader::parse_all_token_trees(string_reader) + let (token_trees, unmatched_delims) = + tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); + match token_trees { + Ok(stream) if unmatched_delims.is_empty() => Ok(stream), + _ => { + // Return error if there are unmatched delimiters or unclosng delimiters. + // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch + // because the delimiter mismatch is more likely to be the root cause of error + + let mut buffer = Vec::with_capacity(1); + // Not using `emit_unclosed_delims` to use `db.buffer` + for unmatched in unmatched_delims { + if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { + err.buffer(&mut buffer); + } + } + if let Err(err) = token_trees { + // Add unclosing delimiter error + err.buffer(&mut buffer); + } + Err(buffer) + } + } } struct StringReader<'a> { diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 0de8f79112c..36fd1e37d65 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,7 +1,7 @@ use super::diagnostics::report_suspicious_mismatch_block; use super::diagnostics::same_identation_level; use super::diagnostics::TokenTreeDiagInfo; -use super::{StringReader, UnmatchedBrace}; +use super::{StringReader, UnmatchedDelim}; use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; @@ -18,14 +18,14 @@ pub(super) struct TokenTreesReader<'a> { impl<'a> TokenTreesReader<'a> { pub(super) fn parse_all_token_trees( string_reader: StringReader<'a>, - ) -> (PResult<'a, TokenStream>, Vec<UnmatchedBrace>) { + ) -> (PResult<'a, TokenStream>, Vec<UnmatchedDelim>) { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), diag_info: TokenTreeDiagInfo::default(), }; let res = tt_reader.parse_token_trees(/* is_delimited */ false); - (res, tt_reader.diag_info.unmatched_braces) + (res, tt_reader.diag_info.unmatched_delims) } // Parse a stream of tokens into a list of `TokenTree`s. @@ -34,7 +34,7 @@ impl<'a> TokenTreesReader<'a> { let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)), + token::OpenDelim(delim) => buf.push(self.parse_token_tree_open_delim(delim)?), token::CloseDelim(delim) => { return if is_delimited { Ok(TokenStream::new(buf)) @@ -43,10 +43,11 @@ impl<'a> TokenTreesReader<'a> { }; } token::Eof => { - if is_delimited { - self.eof_err().emit(); - } - return Ok(TokenStream::new(buf)); + return if is_delimited { + Err(self.eof_err()) + } else { + Ok(TokenStream::new(buf)) + }; } _ => { // Get the next normal token. This might require getting multiple adjacent @@ -78,7 +79,7 @@ impl<'a> TokenTreesReader<'a> { let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg); for &(_, sp) in &self.diag_info.open_braces { err.span_label(sp, "unclosed delimiter"); - self.diag_info.unmatched_braces.push(UnmatchedBrace { + self.diag_info.unmatched_delims.push(UnmatchedDelim { expected_delim: Delimiter::Brace, found_delim: None, found_span: self.token.span, @@ -98,7 +99,7 @@ impl<'a> TokenTreesReader<'a> { err } - fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> TokenTree { + fn parse_token_tree_open_delim(&mut self, open_delim: Delimiter) -> PResult<'a, TokenTree> { // The span for beginning of the delimited section let pre_span = self.token.span; @@ -107,7 +108,7 @@ impl<'a> TokenTreesReader<'a> { // Parse the token trees within the delimiters. // We stop at any delimiter so we can try to recover if the user // uses an incorrect delimiter. - let tts = self.parse_token_trees(/* is_delimited */ true).unwrap(); + let tts = self.parse_token_trees(/* is_delimited */ true)?; // Expand to cover the entire delimited token tree let delim_span = DelimSpan::from_pair(pre_span, self.token.span); @@ -160,7 +161,7 @@ impl<'a> TokenTreesReader<'a> { } } let (tok, _) = self.diag_info.open_braces.pop().unwrap(); - self.diag_info.unmatched_braces.push(UnmatchedBrace { + self.diag_info.unmatched_delims.push(UnmatchedDelim { expected_delim: tok, found_delim: Some(close_delim), found_span: self.token.span, @@ -190,7 +191,7 @@ impl<'a> TokenTreesReader<'a> { _ => unreachable!(), } - TokenTree::Delimited(delim_span, open_delim, tts) + Ok(TokenTree::Delimited(delim_span, open_delim, tts)) } fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'a> { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 6f37e9758fc..d1c3fd0cd0f 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -30,7 +30,7 @@ pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] pub mod parser; -use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser}; +use parser::{make_unclosed_delims_error, Parser}; pub mod lexer; pub mod validate_attr; @@ -96,10 +96,7 @@ pub fn parse_stream_from_source_str( sess: &ParseSess, override_span: Option<Span>, ) -> TokenStream { - let (stream, mut errors) = - source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span); - emit_unclosed_delims(&mut errors, &sess); - stream + source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span) } /// Creates a new parser from a source string. @@ -135,9 +132,8 @@ fn maybe_source_file_to_parser( source_file: Lrc<SourceFile>, ) -> Result<Parser<'_>, Vec<Diagnostic>> { let end_pos = source_file.end_pos; - let (stream, unclosed_delims) = maybe_file_to_stream(sess, source_file, None)?; + let stream = maybe_file_to_stream(sess, source_file, None)?; let mut parser = stream_to_parser(sess, stream, None); - parser.unclosed_delims = unclosed_delims; if parser.token == token::Eof { parser.token.span = Span::new(end_pos, end_pos, parser.token.span.ctxt(), None); } @@ -182,7 +178,7 @@ pub fn source_file_to_stream( sess: &ParseSess, source_file: Lrc<SourceFile>, override_span: Option<Span>, -) -> (TokenStream, Vec<lexer::UnmatchedBrace>) { +) -> TokenStream { panictry_buffer!(&sess.span_diagnostic, maybe_file_to_stream(sess, source_file, override_span)) } @@ -192,7 +188,7 @@ pub fn maybe_file_to_stream( sess: &ParseSess, source_file: Lrc<SourceFile>, override_span: Option<Span>, -) -> Result<(TokenStream, Vec<lexer::UnmatchedBrace>), Vec<Diagnostic>> { +) -> Result<TokenStream, Vec<Diagnostic>> { let src = source_file.src.as_ref().unwrap_or_else(|| { sess.span_diagnostic.bug(&format!( "cannot lex `source_file` without source: {}", @@ -200,23 +196,7 @@ pub fn maybe_file_to_stream( )); }); - let (token_trees, unmatched_braces) = - lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span); - - match token_trees { - Ok(stream) => Ok((stream, unmatched_braces)), - Err(err) => { - let mut buffer = Vec::with_capacity(1); - err.buffer(&mut buffer); - // Not using `emit_unclosed_delims` to use `db.buffer` - for unmatched in unmatched_braces { - if let Some(err) = make_unclosed_delims_error(unmatched, &sess) { - err.buffer(&mut buffer); - } - } - Err(buffer) - } - } + lexer::parse_token_trees(sess, src.as_str(), source_file.start_pos, override_span) } /// Given a stream and the `ParseSess`, produces a parser. diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index dbd3b76786f..b0ab0f10624 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -134,11 +134,11 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // Process the replace ranges, starting from the highest start // position and working our way back. If have tokens like: // - // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // Then we will generate replace ranges for both // the `#[cfg(FALSE)] field: bool` and the entire - // `#[cfg(FALSE)]` struct Foo { #[cfg(FALSE)] field: bool }` + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // By starting processing from the replace range with the greatest // start position, we ensure that any replace range which encloses diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b4948dddcc9..0a65c37ea7b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -19,7 +19,6 @@ use crate::errors::{ }; use crate::fluent_generated as fluent; -use crate::lexer::UnmatchedBrace; use crate::parser; use rustc_ast as ast; use rustc_ast::ptr::P; @@ -165,8 +164,6 @@ enum IsStandalone { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum IncOrDec { Inc, - // FIXME: `i--` recovery isn't implemented yet - #[allow(dead_code)] Dec, } @@ -222,7 +219,6 @@ impl MultiSugg { /// is dropped. pub struct SnapshotParser<'a> { parser: Parser<'a>, - unclosed_delims: Vec<UnmatchedBrace>, } impl<'a> Deref for SnapshotParser<'a> { @@ -257,27 +253,15 @@ impl<'a> Parser<'a> { &self.sess.span_diagnostic } - /// Replace `self` with `snapshot.parser` and extend `unclosed_delims` with `snapshot.unclosed_delims`. - /// This is to avoid losing unclosed delims errors `create_snapshot_for_diagnostic` clears. + /// Replace `self` with `snapshot.parser`. pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) { *self = snapshot.parser; - self.unclosed_delims.extend(snapshot.unclosed_delims); - } - - pub fn unclosed_delims(&self) -> &[UnmatchedBrace] { - &self.unclosed_delims } /// Create a snapshot of the `Parser`. pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> { - let mut snapshot = self.clone(); - let unclosed_delims = self.unclosed_delims.clone(); - // Clear `unclosed_delims` in snapshot to avoid - // duplicate errors being emitted when the `Parser` - // is dropped (which may or may not happen, depending - // if the parsing the snapshot is created for is successful) - snapshot.unclosed_delims.clear(); - SnapshotParser { parser: snapshot, unclosed_delims } + let snapshot = self.clone(); + SnapshotParser { parser: snapshot } } pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> { @@ -581,21 +565,6 @@ impl<'a> Parser<'a> { } else { label_sp }; - match self.recover_closing_delimiter( - &expected - .iter() - .filter_map(|tt| match tt { - TokenType::Token(t) => Some(t.clone()), - _ => None, - }) - .collect::<Vec<_>>(), - err, - ) { - Err(e) => err = e, - Ok(recovered) => { - return Ok(recovered); - } - } if self.check_too_many_raw_str_terminators(&mut err) { if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) { @@ -1357,6 +1326,20 @@ impl<'a> Parser<'a> { self.recover_from_inc_dec(operand_expr, kind, op_span) } + pub(super) fn recover_from_postfix_decrement( + &mut self, + operand_expr: P<Expr>, + op_span: Span, + start_stmt: bool, + ) -> PResult<'a, P<Expr>> { + let kind = IncDecRecovery { + standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr }, + op: IncOrDec::Dec, + fixity: UnaryFixity::Post, + }; + self.recover_from_inc_dec(operand_expr, kind, op_span) + } + fn recover_from_inc_dec( &mut self, base: P<Expr>, @@ -1561,12 +1544,6 @@ impl<'a> Parser<'a> { ); let mut err = self.struct_span_err(sp, &msg); let label_exp = format!("expected `{token_str}`"); - match self.recover_closing_delimiter(&[t.clone()], err) { - Err(e) => err = e, - Ok(recovered) => { - return Ok(recovered); - } - } let sm = self.sess.source_map(); if !sm.is_multiline(prev_sp.until(sp)) { // When the spans are in the same line, it means that the only content @@ -1783,81 +1760,6 @@ impl<'a> Parser<'a> { } } - pub(super) fn recover_closing_delimiter( - &mut self, - tokens: &[TokenKind], - mut err: DiagnosticBuilder<'a, ErrorGuaranteed>, - ) -> PResult<'a, bool> { - let mut pos = None; - // We want to use the last closing delim that would apply. - for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() { - if tokens.contains(&token::CloseDelim(unmatched.expected_delim)) - && Some(self.token.span) > unmatched.unclosed_span - { - pos = Some(i); - } - } - match pos { - Some(pos) => { - // Recover and assume that the detected unclosed delimiter was meant for - // this location. Emit the diagnostic and act as if the delimiter was - // present for the parser's sake. - - // Don't attempt to recover from this unclosed delimiter more than once. - let unmatched = self.unclosed_delims.remove(pos); - let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim)); - if unmatched.found_delim.is_none() { - // We encountered `Eof`, set this fact here to avoid complaining about missing - // `fn main()` when we found place to suggest the closing brace. - *self.sess.reached_eof.borrow_mut() = true; - } - - // We want to suggest the inclusion of the closing delimiter where it makes - // the most sense, which is immediately after the last token: - // - // {foo(bar {}} - // ^ ^ - // | | - // | help: `)` may belong here - // | - // unclosed delimiter - if let Some(sp) = unmatched.unclosed_span { - let mut primary_span: Vec<Span> = - err.span.primary_spans().iter().cloned().collect(); - primary_span.push(sp); - let mut primary_span: MultiSpan = primary_span.into(); - for span_label in err.span.span_labels() { - if let Some(label) = span_label.label { - primary_span.push_span_label(span_label.span, label); - } - } - err.set_span(primary_span); - err.span_label(sp, "unclosed delimiter"); - } - // Backticks should be removed to apply suggestions. - let mut delim = delim.to_string(); - delim.retain(|c| c != '`'); - err.span_suggestion_short( - self.prev_token.span.shrink_to_hi(), - &format!("`{delim}` may belong here"), - delim, - Applicability::MaybeIncorrect, - ); - if unmatched.found_delim.is_none() { - // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown - // errors which would be emitted elsewhere in the parser and let other error - // recovery consume the rest of the file. - Err(err) - } else { - err.emit(); - self.expected_tokens.clear(); // Reduce the number of errors. - Ok(true) - } - } - _ => Err(err), - } - } - /// Eats tokens until we can be relatively sure we reached the end of the /// statement. This is something of a best-effort heuristic. /// diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 33254d034c9..e00eda47c66 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -282,6 +282,18 @@ impl<'a> Parser<'a> { continue; } + if self.prev_token == token::BinOp(token::Minus) + && self.token == token::BinOp(token::Minus) + && self.prev_token.span.between(self.token.span).is_empty() + && !self.look_ahead(1, |tok| tok.can_begin_expr()) + { + let op_span = self.prev_token.span.to(self.token.span); + // Eat the second `-` + self.bump(); + lhs = self.recover_from_postfix_decrement(lhs, op_span, starts_stmt)?; + continue; + } + let op = op.node; // Special cases: if op == AssocOp::As { @@ -1198,8 +1210,13 @@ impl<'a> Parser<'a> { // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`. self.restore_snapshot(snapshot); let close_paren = self.prev_token.span; - let span = lo.to(self.prev_token.span); - if !fields.is_empty() { + let span = lo.to(close_paren); + if !fields.is_empty() && + // `token.kind` should not be compared here. + // This is because the `snapshot.token.kind` is treated as the same as + // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different. + self.span_to_snippet(close_paren).map_or(false, |snippet| snippet == ")") + { let mut replacement_err = errors::ParenthesesWithStructFields { span, r#type: path, @@ -1377,19 +1394,6 @@ impl<'a> Parser<'a> { self.parse_expr_let() } else if self.eat_keyword(kw::Underscore) { Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore)) - } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { - // Don't complain about bare semicolons after unclosed braces - // recovery in order to keep the error count down. Fixing the - // delimiters will possibly also fix the bare semicolon found in - // expression context. For example, silence the following error: - // - // error: expected expression, found `;` - // --> file.rs:2:13 - // | - // 2 | foo(bar(; - // | ^ expected expression - self.bump(); - Ok(self.mk_expr_err(self.token.span)) } else if self.token.uninterpolated_span().rust_2018() { // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. if self.check_keyword(kw::Async) { @@ -2491,9 +2495,27 @@ impl<'a> Parser<'a> { let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label); + + self.recover_loop_else("for", lo)?; + Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } + /// Recovers from an `else` clause after a loop (`for...else`, `while...else`) + fn recover_loop_else(&mut self, loop_kind: &'static str, loop_kw: Span) -> PResult<'a, ()> { + if self.token.is_keyword(kw::Else) && self.may_recover() { + let else_span = self.token.span; + self.bump(); + let else_clause = self.parse_expr_else()?; + self.sess.emit_err(errors::LoopElseNotSupported { + span: else_span.to(else_clause.span), + loop_kind, + loop_kw, + }); + } + Ok(()) + } + fn error_missing_in_for_loop(&mut self) { let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) { // Possibly using JS syntax (#75311). @@ -2518,6 +2540,9 @@ impl<'a> Parser<'a> { err.span_label(cond.span, "this `while` condition successfully parsed"); err })?; + + self.recover_loop_else("while", lo)?; + Ok(self.mk_expr_with_attrs( lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), @@ -2529,6 +2554,7 @@ impl<'a> Parser<'a> { fn parse_expr_loop(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { let loop_span = self.prev_token.span; let (attrs, body) = self.parse_inner_attrs_and_block()?; + self.recover_loop_else("loop", lo)?; Ok(self.mk_expr_with_attrs( lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label, loop_span), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9d9ae154ad4..85cc8ca02a9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -125,16 +125,13 @@ impl<'a> Parser<'a> { return Ok(Some(item.into_inner())); }; - let mut unclosed_delims = vec![]; let item = self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| { let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, fn_parse_mode); - unclosed_delims.append(&mut this.unclosed_delims); Ok((item?, TrailingToken::None)) })?; - self.unclosed_delims.append(&mut unclosed_delims); Ok(item) } @@ -1960,21 +1957,12 @@ impl<'a> Parser<'a> { // FIXME: This will make us not emit the help even for declarative // macros within the same crate (that we can fix), which is sad. if !span.from_expansion() { - if self.unclosed_delims.is_empty() { - let DelimSpan { open, close } = args.dspan; - err.multipart_suggestion( - "change the delimiters to curly braces", - vec![(open, "{".to_string()), (close, '}'.to_string())], - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion( - span, - "change the delimiters to curly braces", - " { /* items */ }", - Applicability::HasPlaceholders, - ); - } + let DelimSpan { open, close } = args.dspan; + err.multipart_suggestion( + "change the delimiters to curly braces", + vec![(open, "{".to_string()), (close, '}'.to_string())], + Applicability::MaybeIncorrect, + ); err.span_suggestion( span.shrink_to_hi(), "add a semicolon", diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index fda9151478f..6e9b447fa61 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -10,7 +10,7 @@ mod path; mod stmt; mod ty; -use crate::lexer::UnmatchedBrace; +use crate::lexer::UnmatchedDelim; pub use attr_wrapper::AttrWrapper; pub use diagnostics::AttemptLocalParseRecovery; pub(crate) use item::FnParseMode; @@ -146,10 +146,7 @@ pub struct Parser<'a> { /// See the comments in the `parse_path_segment` function for more details. unmatched_angle_bracket_count: u32, max_angle_bracket_count: u32, - /// A list of all unclosed delimiters found by the lexer. If an entry is used for error recovery - /// it gets removed from here. Every entry left at the end gets emitted as an independent - /// error. - pub(super) unclosed_delims: Vec<UnmatchedBrace>, + last_unexpected_token_span: Option<Span>, /// Span pointing at the `:` for the last type ascription the parser has seen, and whether it /// looked like it could have been a mistyped path or literal `Option:Some(42)`). @@ -168,7 +165,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 312); +rustc_data_structures::static_assert_size!(Parser<'_>, 288); /// Stores span information about a closure. #[derive(Clone)] @@ -215,12 +212,6 @@ struct CaptureState { inner_attr_ranges: FxHashMap<AttrId, ReplaceRange>, } -impl<'a> Drop for Parser<'a> { - fn drop(&mut self) { - emit_unclosed_delims(&mut self.unclosed_delims, &self.sess); - } -} - /// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that /// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b) /// use this type to emit them as a linear sequence. But a linear sequence is @@ -335,7 +326,7 @@ impl TokenCursor { num_of_hashes = cmp::max(num_of_hashes, count); } - // `/// foo` becomes `doc = r"foo". + // `/// foo` becomes `doc = r"foo"`. let delim_span = DelimSpan::from_single(span); let body = TokenTree::Delimited( delim_span, @@ -478,7 +469,6 @@ impl<'a> Parser<'a> { desugar_doc_comments, unmatched_angle_bracket_count: 0, max_angle_bracket_count: 0, - unclosed_delims: Vec::new(), last_unexpected_token_span: None, last_type_ascription: None, subparser_name, @@ -859,7 +849,6 @@ impl<'a> Parser<'a> { let mut recovered = false; let mut trailing = false; let mut v = ThinVec::new(); - let unclosed_delims = !self.unclosed_delims.is_empty(); while !self.expect_any_with_type(kets, expect) { if let token::CloseDelim(..) | token::Eof = self.token.kind { @@ -901,7 +890,7 @@ impl<'a> Parser<'a> { _ => { // Attempt to keep parsing if it was a similar separator. if let Some(tokens) = t.similar_tokens() { - if tokens.contains(&self.token.kind) && !unclosed_delims { + if tokens.contains(&self.token.kind) { self.bump(); } } @@ -1521,11 +1510,11 @@ impl<'a> Parser<'a> { } pub(crate) fn make_unclosed_delims_error( - unmatched: UnmatchedBrace, + unmatched: UnmatchedDelim, sess: &ParseSess, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to - // `unmatched_braces` only for error recovery in the `Parser`. + // `unmatched_delims` only for error recovery in the `Parser`. let found_delim = unmatched.found_delim?; let mut spans = vec![unmatched.found_span]; if let Some(sp) = unmatched.unclosed_span { @@ -1542,7 +1531,7 @@ pub(crate) fn make_unclosed_delims_error( Some(err) } -pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &ParseSess) { +pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedDelim>, sess: &ParseSess) { *sess.reached_eof.borrow_mut() |= unclosed_delims.iter().any(|unmatched_delim| unmatched_delim.found_delim.is_none()); for unmatched in unclosed_delims.drain(..) { diff --git a/compiler/rustc_passes/locales/en-US.ftl b/compiler/rustc_passes/locales/en-US.ftl index 0ed29ce0d47..3fa78efc290 100644 --- a/compiler/rustc_passes/locales/en-US.ftl +++ b/compiler/rustc_passes/locales/en-US.ftl @@ -402,9 +402,6 @@ passes_invalid_attr_at_crate_level = `{$name}` attribute cannot be used at crate level .suggestion = perhaps you meant to use an outer attribute -passes_duplicate_diagnostic_item = - duplicate diagnostic item found: `{$name}`. - passes_duplicate_diagnostic_item_in_crate = duplicate diagnostic item in crate `{$crate_name}`: `{$name}`. .note = the diagnostic item is first defined in crate `{$orig_crate_name}`. diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e2f858a34b6..28e06e5bb3e 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,7 +13,7 @@ use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{sym, Symbol}; use std::mem; diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 0ae7096642c..110eb210df9 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -11,19 +11,19 @@ use rustc_ast as ast; use rustc_hir::diagnostic_items::DiagnosticItems; +use rustc_hir::OwnerId; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_span::symbol::{kw::Empty, sym, Symbol}; +use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_span::symbol::{sym, Symbol}; -use crate::errors::{DuplicateDiagnosticItem, DuplicateDiagnosticItemInCrate}; +use crate::errors::DuplicateDiagnosticItemInCrate; -fn observe_item(tcx: TyCtxt<'_>, diagnostic_items: &mut DiagnosticItems, def_id: LocalDefId) { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let attrs = tcx.hir().attrs(hir_id); +fn observe_item<'tcx>(tcx: TyCtxt<'tcx>, diagnostic_items: &mut DiagnosticItems, owner: OwnerId) { + let attrs = tcx.hir().attrs(owner.into()); if let Some(name) = extract(attrs) { // insert into our table - collect_item(tcx, diagnostic_items, name, def_id.to_def_id()); + collect_item(tcx, diagnostic_items, name, owner.to_def_id()); } } @@ -31,23 +31,29 @@ fn collect_item(tcx: TyCtxt<'_>, items: &mut DiagnosticItems, name: Symbol, item items.id_to_name.insert(item_def_id, name); if let Some(original_def_id) = items.name_to_id.insert(name, item_def_id) { if original_def_id != item_def_id { - let orig_span = tcx.hir().span_if_local(original_def_id); - let orig_crate_name = - orig_span.is_none().then(|| tcx.crate_name(original_def_id.krate)); - match tcx.hir().span_if_local(item_def_id) { - Some(span) => tcx.sess.emit_err(DuplicateDiagnosticItem { span, name }), - None => tcx.sess.emit_err(DuplicateDiagnosticItemInCrate { - span: orig_span, - orig_crate_name: orig_crate_name.unwrap_or(Empty), - have_orig_crate_name: orig_crate_name.map(|_| ()), - crate_name: tcx.crate_name(item_def_id.krate), - name, - }), - }; + report_duplicate_item(tcx, name, original_def_id, item_def_id); } } } +fn report_duplicate_item( + tcx: TyCtxt<'_>, + name: Symbol, + original_def_id: DefId, + item_def_id: DefId, +) { + let orig_span = tcx.hir().span_if_local(original_def_id); + let duplicate_span = tcx.hir().span_if_local(item_def_id); + tcx.sess.emit_err(DuplicateDiagnosticItemInCrate { + duplicate_span, + orig_span, + crate_name: tcx.crate_name(item_def_id.krate), + orig_crate_name: tcx.crate_name(original_def_id.krate), + different_crates: (item_def_id.krate != original_def_id.krate).then_some(()), + name, + }); +} + /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> { attrs.iter().find_map(|attr| { @@ -64,21 +70,8 @@ fn diagnostic_items(tcx: TyCtxt<'_>, cnum: CrateNum) -> DiagnosticItems { // Collect diagnostic items in this crate. let crate_items = tcx.hir_crate_items(()); - - for id in crate_items.items() { - observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); - } - - for id in crate_items.trait_items() { - observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); - } - - for id in crate_items.impl_items() { - observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); - } - - for id in crate_items.foreign_items() { - observe_item(tcx, &mut diagnostic_items, id.owner_id.def_id); + for id in crate_items.owners() { + observe_item(tcx, &mut diagnostic_items, id); } diagnostic_items diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index b327ba63330..b7e6a11998b 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -4,7 +4,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_session::config::{sigpipe, CrateType, EntryFnType}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 2c0d21b4798..9f1c0b5a0b7 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -810,22 +810,16 @@ impl IntoDiagnostic<'_> for InvalidAttrAtCrateLevel { } #[derive(Diagnostic)] -#[diag(passes_duplicate_diagnostic_item)] -pub struct DuplicateDiagnosticItem { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] #[diag(passes_duplicate_diagnostic_item_in_crate)] pub struct DuplicateDiagnosticItemInCrate { + #[primary_span] + pub duplicate_span: Option<Span>, #[note(passes_diagnostic_item_first_defined)] - pub span: Option<Span>, - pub orig_crate_name: Symbol, + pub orig_span: Option<Span>, #[note] - pub have_orig_crate_name: Option<()>, + pub different_crates: Option<()>, pub crate_name: Symbol, + pub orig_crate_name: Symbol, pub name: Symbol, } diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index de0e50a65de..9418f3cd322 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -5,7 +5,7 @@ use rustc_hir::intravisit; use rustc_hir::{HirId, ItemLocalId}; use rustc_index::bit_set::GrowableBitSet; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_middle::ty::TyCtxt; pub fn check_crate(tcx: TyCtxt<'_>) { tcx.dep_graph.assert_ignored(); diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 51a19c8e3c0..5e2d2d3e5a7 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -646,7 +646,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } // `UseTree` has one inline use (in `ast::ItemKind::Use`) and one - // non-inline use (in `ast::UseTreeKind::Nested). The former case is more + // non-inline use (in `ast::UseTreeKind::Nested`). The former case is more // common, so we don't implement `visit_use_tree` and tolerate the missed // coverage in the latter case. diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index df5c8f53ec1..db9d0dcc300 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -95,7 +95,7 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet}; use rustc_index::vec::IndexVec; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, Ty, TyCtxt}; +use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{BytePos, Span}; diff --git a/compiler/rustc_passes/src/liveness/rwu_table.rs b/compiler/rustc_passes/src/liveness/rwu_table.rs index 6d5983f53dc..053bf5c234a 100644 --- a/compiler/rustc_passes/src/liveness/rwu_table.rs +++ b/compiler/rustc_passes/src/liveness/rwu_table.rs @@ -9,7 +9,7 @@ pub(super) struct RWU { } /// Conceptually, this is like a `Vec<Vec<RWU>>`. But the number of -/// RWU`s can get very large, so it uses a more compact representation. +/// RWU's can get very large, so it uses a more compact representation. pub(super) struct RWUTable { /// Total number of live nodes. live_nodes: usize, diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 051100c56f8..d6cb68a9c20 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -14,7 +14,7 @@ use rustc_hir::Node; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::middle::privacy::{self, Level}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::CrateType; use rustc_target::spec::abi::Abi; diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 50176c80232..99a44b0ca4d 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -29,7 +29,7 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, Level}; use rustc_middle::span_bug; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind}; +use rustc_middle::ty::{self, Const, GenericParamDefKind}; use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_session::lint; use rustc_span::hygiene::Transparency; diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 70c481fb0ee..46e34462cf2 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -333,7 +333,7 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> { }, ); - // `Encode the file footer. + // Encode the file footer. let footer_pos = encoder.position() as u64; encoder.encode_tagged( TAG_FILE_FOOTER, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 586bd38fbb8..5f003fa70e1 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -25,7 +25,6 @@ use std::collections::hash_map::Entry; use std::fmt::Debug; use std::hash::Hash; use std::mem; -use std::ptr; use thin_vec::ThinVec; use super::QueryConfig; @@ -250,13 +249,16 @@ where where C: QueryCache<Key = K>, { - // We can move out of `self` here because we `mem::forget` it below - let key = unsafe { ptr::read(&self.key) }; + let key = self.key; let state = self.state; // Forget ourself so our destructor won't poison the query mem::forget(self); + // Mark as complete before we remove the job from the active state + // so no other thread can re-execute this query. + cache.complete(key, result, dep_node_index); + let job = { #[cfg(parallel_compiler)] let mut lock = state.active.get_shard_by_value(&key).lock(); @@ -267,7 +269,6 @@ where QueryResult::Poisoned => panic!(), } }; - cache.complete(key, result, dep_node_index); job.signal_complete(); } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index b1e023f2caa..9526bca3df2 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -25,9 +25,8 @@ use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_metadata::creader::LoadedMacro; -use rustc_middle::bug; use rustc_middle::metadata::ModChild; -use rustc_middle::ty::{self, DefIdTree}; +use rustc_middle::{bug, ty}; use rustc_session::cstore::CrateStore; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; use rustc_span::source_map::respan; @@ -99,7 +98,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { loop { match self.get_module(def_id) { Some(module) => return module, - None => def_id = self.parent(def_id), + None => def_id = self.tcx.parent(def_id), } } } @@ -775,7 +774,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { let field_vis = self .try_resolve_visibility(&field.vis, false) .unwrap_or(ty::Visibility::Public); - if ctor_vis.is_at_least(field_vis, &*self.r) { + if ctor_vis.is_at_least(field_vis, self.r.tcx) { ctor_vis = field_vis; } ret_fields.push(field_vis.to_def_id()); @@ -1414,10 +1413,7 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { if !(ctxt == AssocCtxt::Impl && matches!(item.vis.kind, ast::VisibilityKind::Inherited) - && self - .r - .trait_impl_items - .contains(&ty::DefIdTree::local_parent(&*self.r, local_def_id))) + && self.r.trait_impl_items.contains(&self.r.tcx.local_parent(local_def_id))) { // Trait impl item visibility is inherited from its trait when not specified // explicitly. In that case we cannot determine it here in early resolve, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 0114e116386..b2578e4c4b4 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -29,11 +29,12 @@ use crate::Resolver; use rustc_ast as ast; use rustc_ast::visit::{self, Visitor}; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{pluralize, MultiSpan}; -use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS}; +use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS}; use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; struct UnusedImport<'a> { @@ -53,11 +54,28 @@ struct UnusedImportCheckVisitor<'a, 'b, 'tcx> { r: &'a mut Resolver<'b, 'tcx>, /// All the (so far) unused imports, grouped path list unused_imports: FxIndexMap<ast::NodeId, UnusedImport<'a>>, + extern_crate_items: Vec<ExternCrateToLint>, base_use_tree: Option<&'a ast::UseTree>, base_id: ast::NodeId, item_span: Span, } +struct ExternCrateToLint { + id: ast::NodeId, + /// Span from the item + span: Span, + /// Span to use to suggest complete removal. + span_with_attributes: Span, + /// Span of the visibility, if any. + vis_span: Span, + /// Whether the item has attrs. + has_attrs: bool, + /// Name used to refer to the crate. + ident: Ident, + /// Whether the statement renames the crate `extern crate orig_name as new_name;`. + renames: bool, +} + impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { // We have information about whether `use` (import) items are actually // used now. If an import is not used at all, we signal a lint error. @@ -96,18 +114,27 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> { impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> { fn visit_item(&mut self, item: &'a ast::Item) { - self.item_span = item.span_with_attributes(); - - // Ignore is_public import statements because there's no way to be sure - // whether they're used or not. Also ignore imports with a dummy span - // because this means that they were generated in some fashion by the - // compiler and we don't need to consider them. - if let ast::ItemKind::Use(..) = item.kind { - if item.vis.kind.is_pub() || item.span.is_dummy() { - return; + match item.kind { + // Ignore is_public import statements because there's no way to be sure + // whether they're used or not. Also ignore imports with a dummy span + // because this means that they were generated in some fashion by the + // compiler and we don't need to consider them. + ast::ItemKind::Use(..) if item.vis.kind.is_pub() || item.span.is_dummy() => return, + ast::ItemKind::ExternCrate(orig_name) => { + self.extern_crate_items.push(ExternCrateToLint { + id: item.id, + span: item.span, + vis_span: item.vis.span, + span_with_attributes: item.span_with_attributes(), + has_attrs: !item.attrs.is_empty(), + ident: item.ident, + renames: orig_name.is_some(), + }); } + _ => {} } + self.item_span = item.span_with_attributes(); visit::walk_item(self, item); } @@ -224,6 +251,9 @@ fn calc_unused_spans( impl Resolver<'_, '_> { pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { + let tcx = self.tcx; + let mut maybe_unused_extern_crates = FxHashMap::default(); + for import in self.potentially_unused_imports.iter() { match import.kind { _ if import.used.get() @@ -246,7 +276,14 @@ impl Resolver<'_, '_> { } ImportKind::ExternCrate { id, .. } => { let def_id = self.local_def_id(id); - self.maybe_unused_extern_crates.push((def_id, import.span)); + if self.extern_crate_map.get(&def_id).map_or(true, |&cnum| { + !tcx.is_compiler_builtins(cnum) + && !tcx.is_panic_runtime(cnum) + && !tcx.has_global_allocator(cnum) + && !tcx.has_panic_handler(cnum) + }) { + maybe_unused_extern_crates.insert(id, import.span); + } } ImportKind::MacroUse => { let msg = "unused `#[macro_use]` import"; @@ -259,6 +296,7 @@ impl Resolver<'_, '_> { let mut visitor = UnusedImportCheckVisitor { r: self, unused_imports: Default::default(), + extern_crate_items: Default::default(), base_use_tree: None, base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, @@ -290,7 +328,7 @@ impl Resolver<'_, '_> { let ms = MultiSpan::from_spans(spans.clone()); let mut span_snippets = spans .iter() - .filter_map(|s| match visitor.r.tcx.sess.source_map().span_to_snippet(*s) { + .filter_map(|s| match tcx.sess.source_map().span_to_snippet(*s) { Ok(s) => Some(format!("`{}`", s)), _ => None, }) @@ -317,7 +355,7 @@ impl Resolver<'_, '_> { // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]` // attribute; however, if not, suggest adding the attribute. There is no way to // retrieve attributes here because we do not have a `TyCtxt` yet. - let test_module_span = if visitor.r.tcx.sess.opts.test { + let test_module_span = if tcx.sess.opts.test { None } else { let parent_module = visitor.r.get_nearest_non_block_module( @@ -346,5 +384,74 @@ impl Resolver<'_, '_> { BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes, test_module_span), ); } + + for extern_crate in visitor.extern_crate_items { + let warn_if_unused = !extern_crate.ident.name.as_str().starts_with('_'); + + // If the crate is fully unused, we suggest removing it altogether. + // We do this in any edition. + if warn_if_unused { + if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) { + visitor.r.lint_buffer.buffer_lint_with_diagnostic( + UNUSED_EXTERN_CRATES, + extern_crate.id, + span, + "unused extern crate", + BuiltinLintDiagnostics::UnusedExternCrate { + removal_span: extern_crate.span_with_attributes, + }, + ); + continue; + } + } + + // If we are not in Rust 2018 edition, then we don't make any further + // suggestions. + if !tcx.sess.rust_2018() { + continue; + } + + // If the extern crate has any attributes, they may have funky + // semantics we can't faithfully represent using `use` (most + // notably `#[macro_use]`). Ignore it. + if extern_crate.has_attrs { + continue; + } + + // If the extern crate is renamed, then we cannot suggest replacing it with a use as this + // would not insert the new name into the prelude, where other imports in the crate may be + // expecting it. + if extern_crate.renames { + continue; + } + + // If the extern crate isn't in the extern prelude, + // there is no way it can be written as a `use`. + if !visitor + .r + .extern_prelude + .get(&extern_crate.ident) + .map_or(false, |entry| !entry.introduced_by_item) + { + continue; + } + + let vis_span = extern_crate + .vis_span + .find_ancestor_inside(extern_crate.span) + .unwrap_or(extern_crate.vis_span); + let ident_span = extern_crate + .ident + .span + .find_ancestor_inside(extern_crate.span) + .unwrap_or(extern_crate.ident.span); + visitor.r.lint_buffer.buffer_lint_with_diagnostic( + UNUSED_EXTERN_CRATES, + extern_crate.id, + extern_crate.span, + "`extern crate` is not idiomatic in the new edition", + BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span }, + ); + } } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ee2d2301399..adec7973671 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -15,7 +15,7 @@ use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_middle::bug; -use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE; use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS; use rustc_session::lint::BuiltinLintDiagnostics; @@ -189,7 +189,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let container = match parent.kind { - ModuleKind::Def(kind, _, _) => self.tcx.def_kind_descr(kind, parent.def_id()), + // Avoid using TyCtxt::def_kind_descr in the resolver, because it + // indirectly *calls* the resolver, and would cause a query cycle. + ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()), ModuleKind::Block => "block", }; @@ -1195,7 +1197,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { segms.push(ast::PathSegment::from_ident(ident)); let path = Path { span: name_binding.span, segments: segms, tokens: None }; let did = match res { - Res::Def(DefKind::Ctor(..), did) => this.opt_parent(did), + Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), _ => res.opt_def_id(), }; @@ -1589,7 +1591,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ctor_def_id, )) = binding.kind { - let def_id = self.parent(ctor_def_id); + let def_id = self.tcx.parent(ctor_def_id); let fields = self.field_names.get(&def_id)?; return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()` } @@ -1804,7 +1806,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { found("module") } else { match binding.res() { - Res::Def(kind, id) => found(self.tcx.def_kind_descr(kind, id)), + // Avoid using TyCtxt::def_kind_descr in the resolver, because it + // indirectly *calls* the resolver, and would cause a query cycle. + Res::Def(kind, id) => found(kind.descr(id)), _ => found(ns_to_try.descr()), } } diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 7bd90d7e345..4bb252bfb29 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -9,7 +9,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_middle::middle::privacy::Level; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility}; -use rustc_middle::ty::{DefIdTree, Visibility}; +use rustc_middle::ty::Visibility; use std::mem; type ImportId<'a> = Interned<'a, NameBinding<'a>>; @@ -60,7 +60,7 @@ impl Resolver<'_, '_> { // For mod items `nearest_normal_mod` returns its argument, but we actually need its parent. let normal_mod_id = self.nearest_normal_mod(def_id); if normal_mod_id == def_id { - self.opt_local_parent(def_id).map_or(Visibility::Public, Visibility::Restricted) + self.tcx.opt_local_parent(def_id).map_or(Visibility::Public, Visibility::Restricted) } else { Visibility::Restricted(normal_mod_id) } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index ba7f04239c3..52f0b65fad6 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -368,7 +368,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during /// expansion and import resolution (perhaps they can be merged in the future). /// The function is used for resolving initial segments of macro paths (e.g., `foo` in - /// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition. + /// `foo::bar!();` or `foo!();`) and also for import paths on 2018 edition. #[instrument(level = "debug", skip(self, scope_set))] pub(crate) fn early_resolve_ident_in_lexical_scope( &mut self, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4dab0836d28..5d40c6e4e48 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -245,7 +245,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { import: &'a Import<'a>, ) -> &'a NameBinding<'a> { let import_vis = import.expect_vis().to_def_id(); - let vis = if binding.vis.is_at_least(import_vis, self) + let vis = if binding.vis.is_at_least(import_vis, self.tcx) || pub_use_of_private_extern_crate_hack(import, binding) { import_vis @@ -255,7 +255,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if let ImportKind::Glob { ref max_vis, .. } = import.kind { if vis == import_vis - || max_vis.get().map_or(true, |max_vis| vis.is_at_least(max_vis, self)) + || max_vis.get().map_or(true, |max_vis| vis.is_at_least(max_vis, self.tcx)) { max_vis.set(Some(vis.expect_local())) } @@ -294,7 +294,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { old_binding, binding, )); - } else if !old_binding.vis.is_at_least(binding.vis, &*this) { + } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) { // We are glob-importing the same item but with greater visibility. resolution.binding = Some(binding); } @@ -786,7 +786,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if !is_prelude && let Some(max_vis) = max_vis.get() - && !max_vis.is_at_least(import.expect_vis(), &*self) + && !max_vis.is_at_least(import.expect_vis(), self.tcx) { let msg = "glob import doesn't reexport anything because no candidate is public enough"; self.lint_buffer.buffer_lint(UNUSED_IMPORTS, id, import.span, msg); @@ -977,7 +977,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut crate_private_reexport = false; self.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { - if !binding.vis.is_at_least(import.expect_vis(), &*this) { + if !binding.vis.is_at_least(import.expect_vis(), this.tcx) { reexport_error = Some((ns, binding)); if let ty::Visibility::Restricted(binding_def_id) = binding.vis { if binding_def_id.is_top_level_module() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 7df17376b3e..cc3e142a5fd 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -22,7 +22,6 @@ use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_bound_vars::Set1; -use rustc_middle::ty::DefIdTree; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint; @@ -1671,8 +1670,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Figure out if this is a type/trait segment, // which may need lifetime elision performed. let type_def_id = match partial_res.base_res() { - Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => self.r.parent(def_id), - Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => self.r.parent(def_id), + Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => { + self.r.tcx.parent(def_id) + } + Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => { + self.r.tcx.parent(def_id) + } Res::Def(DefKind::Struct, def_id) | Res::Def(DefKind::Union, def_id) | Res::Def(DefKind::Enum, def_id) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 36415936bdc..206c43f6902 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -21,7 +21,6 @@ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; -use rustc_middle::ty::DefIdTree; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -878,7 +877,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ); let is_assoc_fn = self.self_type_is_available(); if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { - // The current function has a `self' parameter, but we were unable to resolve + // The current function has a `self` parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we // are resolving came from a different hygiene context. if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { @@ -1508,7 +1507,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => { - let def_id = self.r.parent(ctor_def_id); + let def_id = self.r.tcx.parent(ctor_def_id); if let Some(span) = self.def_span(def_id) { err.span_label(span, &format!("`{}` defined here", path_str)); } @@ -1999,7 +1998,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } } else { let needs_placeholder = |ctor_def_id: DefId, kind: CtorKind| { - let def_id = self.r.parent(ctor_def_id); + let def_id = self.r.tcx.parent(ctor_def_id); let has_no_fields = self.r.field_names.get(&def_id).map_or(false, |f| f.is_empty()); match kind { CtorKind::Const => false, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7a1f14f71f2..5eba208e3ed 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -44,7 +44,7 @@ use rustc_metadata::creader::{CStore, CrateLoader}; use rustc_middle::metadata::ModChild; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::span_bug; -use rustc_middle::ty::{self, DefIdTree, MainDefinition, RegisteredTools, TyCtxt}; +use rustc_middle::ty::{self, MainDefinition, RegisteredTools, TyCtxt}; use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs}; use rustc_query_system::ich::StableHashingContext; use rustc_session::cstore::CrateStore; @@ -946,7 +946,6 @@ pub struct Resolver<'a, 'tcx> { has_pub_restricted: bool, used_imports: FxHashSet<NodeId>, maybe_unused_trait_imports: FxIndexSet<LocalDefId>, - maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, /// Privacy errors are delayed until the end in order to deduplicate them. privacy_errors: Vec<PrivacyError<'a>>, @@ -1118,13 +1117,6 @@ impl<'a, 'tcx> AsMut<Resolver<'a, 'tcx>> for Resolver<'a, 'tcx> { } } -impl<'a, 'b, 'tcx> DefIdTree for &'a Resolver<'b, 'tcx> { - #[inline] - fn opt_parent(self, id: DefId) -> Option<DefId> { - self.tcx.opt_parent(id) - } -} - impl<'tcx> Resolver<'_, 'tcx> { fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> { self.node_id_to_def_id.get(&node).copied() @@ -1284,7 +1276,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { has_pub_restricted: false, used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), - maybe_unused_extern_crates: Vec::new(), privacy_errors: Vec::new(), ambiguity_errors: Vec::new(), @@ -1400,7 +1391,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let extern_crate_map = self.extern_crate_map; let reexport_map = self.reexport_map; let maybe_unused_trait_imports = self.maybe_unused_trait_imports; - let maybe_unused_extern_crates = self.maybe_unused_extern_crates; let glob_map = self.glob_map; let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; @@ -1414,12 +1404,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { reexport_map, glob_map, maybe_unused_trait_imports, - maybe_unused_extern_crates, - extern_prelude: self - .extern_prelude - .iter() - .map(|(ident, entry)| (ident.name, entry.introduced_by_item)) - .collect(), main_def, trait_impls: self.trait_impls, proc_macros, @@ -1798,7 +1782,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { vis: ty::Visibility<impl Into<DefId>>, module: Module<'a>, ) -> bool { - vis.is_accessible_from(module.nearest_parent_mod(), self) + vis.is_accessible_from(module.nearest_parent_mod(), self.tcx) } fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) { diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 5e4b66018e4..b8853c1744c 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -265,9 +265,9 @@ fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, Malfor } } -pub fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGenerics> { +pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGenerics> { if !path_str.contains(['<', '>']) { - return Ok(path_str.to_string()); + return Ok(path_str.into()); } let mut stripped_segments = vec![]; let mut path = path_str.chars().peekable(); @@ -322,7 +322,11 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGener let stripped_path = stripped_segments.join("::"); - if !stripped_path.is_empty() { Ok(stripped_path) } else { Err(MalformedGenerics::MissingType) } + if !stripped_path.is_empty() { + Ok(stripped_path.into()) + } else { + Err(MalformedGenerics::MissingType) + } } /// Returns whether the first doc-comment is an inner attribute. @@ -336,7 +340,7 @@ pub fn inner_docs(attrs: &[ast::Attribute]) -> bool { /// Simplified version of the corresponding function in rustdoc. /// If the rustdoc version returns a successful result, this function must return the same result. /// Otherwise this function may return anything. -fn preprocess_link(link: &str) -> String { +fn preprocess_link(link: &str) -> Box<str> { let link = link.replace('`', ""); let link = link.split('#').next().unwrap(); let link = link.trim(); @@ -345,7 +349,7 @@ fn preprocess_link(link: &str) -> String { let link = link.strip_suffix("{}").unwrap_or(link); let link = link.strip_suffix("[]").unwrap_or(link); let link = if link != "!" { link.strip_suffix('!').unwrap_or(link) } else { link }; - strip_generics_from_path(link).unwrap_or_else(|_| link.to_string()) + strip_generics_from_path(link).unwrap_or_else(|_| link.into()) } /// Keep inline and reference links `[]`, @@ -365,7 +369,7 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { /// Simplified version of `preprocessed_markdown_links` from rustdoc. /// Must return at least the same links as it, but may add some more links on top of that. -pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<String> { +pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<str>> { let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap(); diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 377c364961b..567fe06109b 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -43,7 +43,6 @@ pub trait Encoder { fn emit_str(&mut self, v: &str); fn emit_raw_bytes(&mut self, s: &[u8]); - // Convenience for the derive macro: fn emit_enum_variant<F>(&mut self, v_id: usize, f: F) where F: FnOnce(&mut Self), @@ -51,17 +50,6 @@ pub trait Encoder { self.emit_usize(v_id); f(self); } - - // We put the field index in a const generic to allow the emit_usize to be - // compiled into a more efficient form. In practice, the variant index is - // known at compile-time, and that knowledge allows much more efficient - // codegen than we'd otherwise get. LLVM isn't always able to make the - // optimization that would otherwise be necessary here, likely due to the - // multiple levels of inlining and const-prop that are needed. - #[inline] - fn emit_fieldless_enum_variant<const ID: usize>(&mut self) { - self.emit_usize(ID) - } } // Note: all the methods in this trait are infallible, which may be surprising. diff --git a/compiler/rustc_session/locales/en-US.ftl b/compiler/rustc_session/locales/en-US.ftl index fe553edab42..ff53f22d43f 100644 --- a/compiler/rustc_session/locales/en-US.ftl +++ b/compiler/rustc_session/locales/en-US.ftl @@ -5,7 +5,7 @@ session_incorrect_cgu_reuse_type = }`{$expected_reuse}` session_cgu_not_recorded = - CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded` + CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded session_feature_gate_error = {$explain} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 295e93f6103..d4e4ace889b 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2245,7 +2245,7 @@ pub fn parse_externs( early_error( error_format, "the `-Z unstable-options` flag must also be passed to \ - enable `--extern options", + enable `--extern` options", ); } for opt in opts.split(',') { @@ -2792,7 +2792,7 @@ pub enum PpMode { HirTree, /// `-Zunpretty=thir-tree` ThirTree, - /// `-Zunpretty=`thir-flat` + /// `-Zunpretty=thir-flat` ThirFlat, /// `-Zunpretty=mir` Mir, diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 2075ed57a94..f1fbf38217d 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -182,7 +182,17 @@ pub fn get_or_default_sysroot() -> Result<PathBuf, String> { if dir.ends_with(crate::config::host_triple()) { dir.parent() // chop off `$target` .and_then(|p| p.parent()) // chop off `rustlib` - .and_then(|p| p.parent()) // chop off `lib` + .and_then(|p| { + // chop off `lib` (this could be also $arch dir if the host sysroot uses a + // multi-arch layout like Debian or Ubuntu) + match p.parent() { + Some(p) => match p.file_name() { + Some(f) if f == "lib" => p.parent(), // first chop went for $arch, so chop again for `lib` + _ => Some(p), + }, + None => None, + } + }) .map(|s| s.to_owned()) .ok_or(format!( "Could not move 3 levels upper using `parent()` on {}", diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4beac931632..b466a3fcdee 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1503,6 +1503,9 @@ options! { "what location details should be tracked when using caller_location, either \ `none`, or a comma separated list of location details, for which \ valid options are `file`, `line`, and `column` (default: `file,line,column`)"), + lower_impl_trait_in_trait_to_assoc_ty: bool = (false, parse_bool, [TRACKED], + "modify the lowering strategy for `impl Trait` in traits so that they are lowered to \ + generic associated types"), ls: bool = (false, parse_bool, [UNTRACKED], "list the symbols defined by a library crate (default: no)"), macro_backtrace: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 446ba63ed1c..12634f67185 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -882,10 +882,14 @@ impl Session { /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n. /// This expends fuel if applicable, and records fuel if applicable. - pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool { + pub fn consider_optimizing( + &self, + get_crate_name: impl Fn() -> Symbol, + msg: impl Fn() -> String, + ) -> bool { let mut ret = true; if let Some((ref c, _)) = self.opts.unstable_opts.fuel { - if c == crate_name { + if c == get_crate_name().as_str() { assert_eq!(self.threads(), 1); let mut fuel = self.optimization_fuel.lock(); ret = fuel.remaining != 0; @@ -903,7 +907,7 @@ impl Session { } } if let Some(ref c) = self.opts.unstable_opts.print_fuel { - if c == crate_name { + if c == get_crate_name().as_str() { assert_eq!(self.threads(), 1); self.print_fuel.fetch_add(1, SeqCst); } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 2340d501d5a..162c15574b5 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -235,7 +235,7 @@ impl<D: Decoder> Decodable<D> for DefIndex { pub struct DefId { // cfg-ing the order of fields so that the `DefIndex` which is high entropy always ends up in // the lower bits no matter the endianness. This allows the compiler to turn that `Hash` impl - // into a direct call to 'u64::hash(_)`. + // into a direct call to `u64::hash(_)`. #[cfg(not(all(target_pointer_width = "64", target_endian = "big")))] pub index: DefIndex, pub krate: CrateNum, diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index dee823eefde..9f22e9776d4 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1151,6 +1151,7 @@ pub enum DesugaringKind { Await, ForLoop, WhileLoop, + Replace, } impl DesugaringKind { @@ -1166,6 +1167,7 @@ impl DesugaringKind { DesugaringKind::OpaqueTy => "`impl Trait`", DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", + DesugaringKind::Replace => "drop and replace", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fb579e4ff77..6272bf7f25e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1954,7 +1954,7 @@ impl Interner { let name = Symbol::new(inner.strings.len() as u32); // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena, - // and immediately convert the clone back to `&[u8], all because there + // and immediately convert the clone back to `&[u8]`, all because there // is no `inner.arena.alloc_str()` method. This is clearly safe. let string: &str = unsafe { str::from_utf8_unchecked(inner.arena.alloc_slice(string.as_bytes())) }; diff --git a/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs new file mode 100644 index 00000000000..c048d4a2aad --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs @@ -0,0 +1,390 @@ +use std::cmp::Ordering; + +use crate::infer::InferCtxt; +use rustc_middle::infer::canonical::Canonical; +use rustc_middle::infer::canonical::CanonicalTyVarKind; +use rustc_middle::infer::canonical::CanonicalVarInfo; +use rustc_middle::infer::canonical::CanonicalVarInfos; +use rustc_middle::infer::canonical::CanonicalVarKind; +use rustc_middle::ty::BoundRegionKind::BrAnon; +use rustc_middle::ty::BoundTyKind; +use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::TypeVisitableExt; +use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable}; + +/// Whether we're canonicalizing a query input or the query reponse. +/// +/// When canonicalizing an input we're in the context of the caller +/// while canonicalizing the response happens in the context of the +/// query. +#[derive(Debug, Clone, Copy)] +pub enum CanonicalizeMode { + Input, + /// FIXME: We currently return region constraints refering to + /// placeholders and inference variables from a binder instantiated + /// inside of the query. + /// + /// In the long term we should eagerly deal with these constraints + /// inside of the query and only propagate constraints which are + /// actually nameable by the caller. + Response { + /// The highest universe nameable by the caller. + /// + /// All variables in a universe nameable by the caller get mapped + /// to the root universe in the response and then mapped back to + /// their correct universe when applying the query response in the + /// context of the caller. + /// + /// This doesn't work for universes created inside of the query so + /// we do remember their universe in the response. + max_input_universe: ty::UniverseIndex, + }, +} + +pub struct Canonicalizer<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + canonicalize_mode: CanonicalizeMode, + + variables: &'a mut Vec<ty::GenericArg<'tcx>>, + primitive_var_infos: Vec<CanonicalVarInfo<'tcx>>, + binder_index: ty::DebruijnIndex, +} + +impl<'a, 'tcx> Canonicalizer<'a, 'tcx> { + #[instrument(level = "debug", skip(infcx), ret)] + pub fn canonicalize<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'a InferCtxt<'tcx>, + canonicalize_mode: CanonicalizeMode, + variables: &'a mut Vec<ty::GenericArg<'tcx>>, + value: T, + ) -> Canonical<'tcx, T> { + let mut canonicalizer = Canonicalizer { + infcx, + canonicalize_mode, + + variables, + primitive_var_infos: Vec::new(), + binder_index: ty::INNERMOST, + }; + + let value = value.fold_with(&mut canonicalizer); + assert!(!value.needs_infer()); + assert!(!value.has_placeholders()); + + let (max_universe, variables) = canonicalizer.finalize(); + + Canonical { max_universe, variables, value } + } + + fn finalize(self) -> (ty::UniverseIndex, CanonicalVarInfos<'tcx>) { + let mut var_infos = self.primitive_var_infos; + // See the rustc-dev-guide section about how we deal with universes + // during canonicalization in the new solver. + match self.canonicalize_mode { + // We try to deduplicate as many query calls as possible and hide + // all information which should not matter for the solver. + // + // For this we compress universes as much as possible. + CanonicalizeMode::Input => {} + // When canonicalizing a response we map a universes already entered + // by the caller to the root universe and only return useful universe + // information for placeholders and inference variables created inside + // of the query. + CanonicalizeMode::Response { max_input_universe } => { + for var in var_infos.iter_mut() { + let uv = var.universe(); + let new_uv = ty::UniverseIndex::from( + uv.index().saturating_sub(max_input_universe.index()), + ); + *var = var.with_updated_universe(new_uv); + } + let max_universe = var_infos + .iter() + .map(|info| info.universe()) + .max() + .unwrap_or(ty::UniverseIndex::ROOT); + + let var_infos = self.infcx.tcx.mk_canonical_var_infos(&var_infos); + return (max_universe, var_infos); + } + } + + // Given a `var_infos` with existentials `En` and universals `Un` in + // universes `n`, this algorithm compresses them in place so that: + // + // - the new universe indices are as small as possible + // - we only create a new universe if we would otherwise put a placeholder in + // the same compressed universe as an existential which cannot name it + // + // Let's walk through an example: + // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0 + // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1 + // - var_infos: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2 + // - var_infos: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5 + // - var_infos: [E0, U1, E1, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 6 + // - var_infos: [E0, U1, E1, U1, E1, E2, U2], curr_compressed_uv: 2, next_orig_uv: - + // + // This algorithm runs in `O(n²)` where `n` is the number of different universe + // indices in the input. This should be fine as `n` is expected to be small. + let mut curr_compressed_uv = ty::UniverseIndex::ROOT; + let mut existential_in_new_uv = false; + let mut next_orig_uv = Some(ty::UniverseIndex::ROOT); + while let Some(orig_uv) = next_orig_uv.take() { + let mut update_uv = |var: &mut CanonicalVarInfo<'tcx>, orig_uv, is_existential| { + let uv = var.universe(); + match uv.cmp(&orig_uv) { + Ordering::Less => (), // Already updated + Ordering::Equal => { + if is_existential { + existential_in_new_uv = true; + } else if existential_in_new_uv { + // `var` is a placeholder from a universe which is not nameable + // by an existential which we already put into the compressed + // universe `curr_compressed_uv`. We therefore have to create a + // new universe for `var`. + curr_compressed_uv = curr_compressed_uv.next_universe(); + existential_in_new_uv = false; + } + + *var = var.with_updated_universe(curr_compressed_uv); + } + Ordering::Greater => { + // We can ignore this variable in this iteration. We only look at + // universes which actually occur in the input for performance. + // + // For this we set `next_orig_uv` to the next smallest, not yet compressed, + // universe of the input. + if next_orig_uv.map_or(true, |curr_next_uv| uv.cannot_name(curr_next_uv)) { + next_orig_uv = Some(uv); + } + } + } + }; + + // For each universe which occurs in the input, we first iterate over all + // placeholders and then over all inference variables. + // + // Whenever we compress the universe of a placeholder, no existential with + // an already compressed universe can name that placeholder. + for is_existential in [false, true] { + for var in var_infos.iter_mut() { + // We simply put all regions from the input into the highest + // compressed universe, so we only deal with them at the end. + if !var.is_region() { + if is_existential == var.is_existential() { + update_uv(var, orig_uv, is_existential) + } + } + } + } + } + + for var in var_infos.iter_mut() { + if var.is_region() { + assert!(var.is_existential()); + *var = var.with_updated_universe(curr_compressed_uv); + } + } + + let var_infos = self.infcx.tcx.mk_canonical_var_infos(&var_infos); + (curr_compressed_uv, var_infos) + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + let r = self.infcx.shallow_resolve(r); + let kind = match *r { + ty::ReLateBound(..) => return r, + + ty::ReStatic => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => return r, + }, + + ty::ReErased | ty::ReFree(_) | ty::ReEarlyBound(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => bug!("unexpected region in response: {r:?}"), + }, + + ty::RePlaceholder(placeholder) => match self.canonicalize_mode { + // We canonicalize placeholder regions as existentials in query inputs. + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { max_input_universe } => { + // If we have a placeholder region inside of a query, it must be from + // a new universe. + if max_input_universe.can_name(placeholder.universe) { + bug!("new placeholder in universe {max_input_universe:?}: {r:?}"); + } + CanonicalVarKind::PlaceholderRegion(placeholder) + } + }, + + ty::ReVar(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => { + CanonicalVarKind::Region(self.infcx.universe_of_region(r)) + } + }, + + ty::ReError(_) => return r, + }; + + let existing_bound_var = match self.canonicalize_mode { + CanonicalizeMode::Input => None, + CanonicalizeMode::Response { .. } => { + self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from) + } + }; + let var = existing_bound_var.unwrap_or_else(|| { + let var = ty::BoundVar::from(self.variables.len()); + self.variables.push(r.into()); + self.primitive_var_infos.push(CanonicalVarInfo { kind }); + var + }); + let br = ty::BoundRegion { var, kind: BrAnon(var.as_u32(), None) }; + self.interner().mk_re_late_bound(self.binder_index, br) + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + let kind = match *t.kind() { + ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) { + Ok(t) => return self.fold_ty(t), + Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), + }, + ty::Infer(ty::IntVar(_)) => { + let nt = self.infcx.shallow_resolve(t); + if nt != t { + return self.fold_ty(nt); + } else { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + } + } + ty::Infer(ty::FloatVar(_)) => { + let nt = self.infcx.shallow_resolve(t); + if nt != t { + return self.fold_ty(nt); + } else { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + } + } + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("fresh var during canonicalization: {t:?}") + } + ty::Placeholder(placeholder) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(ty::Placeholder { + universe: placeholder.universe, + name: BoundTyKind::Anon(self.variables.len() as u32), + }), + CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder), + }, + ty::Param(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::PlaceholderTy(ty::Placeholder { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundTyKind::Anon(self.variables.len() as u32), + }), + CanonicalizeMode::Response { .. } => bug!("param ty in response: {t:?}"), + }, + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::GeneratorWitness(_) + | ty::GeneratorWitnessMIR(..) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Bound(_, _) + | ty::Error(_) => return t.super_fold_with(self), + }; + + let var = ty::BoundVar::from( + self.variables.iter().position(|&v| v == t.into()).unwrap_or_else(|| { + let var = self.variables.len(); + self.variables.push(t.into()); + self.primitive_var_infos.push(CanonicalVarInfo { kind }); + var + }), + ); + let bt = ty::BoundTy { var, kind: BoundTyKind::Anon(var.index() as u32) }; + self.interner().mk_bound(self.binder_index, bt) + } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + let kind = match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => match self.infcx.probe_const_var(vid) + { + Ok(c) => return self.fold_const(c), + Err(universe) => CanonicalVarKind::Const(universe, c.ty()), + }, + ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => { + bug!("fresh var during canonicalization: {c:?}") + } + ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( + ty::Placeholder { + universe: placeholder.universe, + name: ty::BoundVar::from(self.variables.len()), + }, + c.ty(), + ), + CanonicalizeMode::Response { .. } => { + CanonicalVarKind::PlaceholderConst(placeholder, c.ty()) + } + }, + ty::ConstKind::Param(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst( + ty::Placeholder { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundVar::from(self.variables.len()), + }, + c.ty(), + ), + CanonicalizeMode::Response { .. } => bug!("param ty in response: {c:?}"), + }, + ty::ConstKind::Bound(_, _) + | ty::ConstKind::Unevaluated(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error(_) + | ty::ConstKind::Expr(_) => return c.super_fold_with(self), + }; + + let var = ty::BoundVar::from( + self.variables.iter().position(|&v| v == c.into()).unwrap_or_else(|| { + let var = self.variables.len(); + self.variables.push(c.into()); + self.primitive_var_infos.push(CanonicalVarInfo { kind }); + var + }), + ); + self.interner().mk_const(ty::ConstKind::Bound(self.binder_index, var), c.ty()) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/canonical/mod.rs b/compiler/rustc_trait_selection/src/solve/canonical/mod.rs new file mode 100644 index 00000000000..8c3be8da16b --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/canonical/mod.rs @@ -0,0 +1,240 @@ +/// Canonicalization is used to separate some goal from its context, +/// throwing away unnecessary information in the process. +/// +/// This is necessary to cache goals containing inference variables +/// and placeholders without restricting them to the current `InferCtxt`. +/// +/// Canonicalization is fairly involved, for more details see the relevant +/// section of the [rustc-dev-guide][c]. +/// +/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html +use self::canonicalize::{CanonicalizeMode, Canonicalizer}; +use super::{CanonicalGoal, Certainty, EvalCtxt, Goal}; +use super::{CanonicalResponse, ExternalConstraints, QueryResult, Response}; +use rustc_infer::infer::canonical::query_response::make_query_region_constraints; +use rustc_infer::infer::canonical::CanonicalVarValues; +use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::solve::ExternalConstraintsData; +use rustc_infer::traits::ObligationCause; +use rustc_middle::ty::{self, GenericArgKind}; +use rustc_span::DUMMY_SP; +use std::iter; +use std::ops::Deref; + +mod canonicalize; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + /// Canonicalizes the goal remembering the original values + /// for each bound variable. + pub(super) fn canonicalize_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalGoal<'tcx>) { + let mut orig_values = Default::default(); + let canonical_goal = Canonicalizer::canonicalize( + self.infcx, + CanonicalizeMode::Input, + &mut orig_values, + goal, + ); + (orig_values, canonical_goal) + } + + /// To return the constraints of a canonical query to the caller, we canonicalize: + /// + /// - `var_values`: a map from bound variables in the canonical goal to + /// the values inferred while solving the instantiated goal. + /// - `external_constraints`: additional constraints which aren't expressable + /// using simple unification of inference variables. + #[instrument(level = "debug", skip(self))] + pub(super) fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> { + let external_constraints = self.compute_external_query_constraints()?; + + let response = Response { var_values: self.var_values, external_constraints, certainty }; + let canonical = Canonicalizer::canonicalize( + self.infcx, + CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, + &mut Default::default(), + response, + ); + Ok(canonical) + } + + #[instrument(level = "debug", skip(self), ret)] + fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> { + // Cannot use `take_registered_region_obligations` as we may compute the response + // inside of a `probe` whenever we have multiple choices inside of the solver. + let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); + let region_constraints = self.infcx.with_region_constraints(|region_constraints| { + make_query_region_constraints( + self.tcx(), + region_obligations + .iter() + .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())), + region_constraints, + ) + }); + let opaque_types = self.infcx.clone_opaque_types_for_query_response(); + Ok(self + .tcx() + .mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types })) + } + + /// After calling a canonical query, we apply the constraints returned + /// by the query using this function. + /// + /// This happens in three steps: + /// - we instantiate the bound variables of the query response + /// - we unify the `var_values` of the response with the `original_values` + /// - we apply the `external_constraints` returned by the query + pub(super) fn instantiate_and_apply_query_response( + &mut self, + param_env: ty::ParamEnv<'tcx>, + original_values: Vec<ty::GenericArg<'tcx>>, + response: CanonicalResponse<'tcx>, + ) -> Result<Certainty, NoSolution> { + let substitution = self.compute_query_response_substitution(&original_values, &response); + + let Response { var_values, external_constraints, certainty } = + response.substitute(self.tcx(), &substitution); + + self.unify_query_var_values(param_env, &original_values, var_values)?; + + // FIXME: implement external constraints. + let ExternalConstraintsData { region_constraints, opaque_types: _ } = + external_constraints.deref(); + self.register_region_constraints(region_constraints); + + Ok(certainty) + } + + /// This returns the substitutions to instantiate the bound variables of + /// the canonical reponse. This depends on the `original_values` for the + /// bound variables. + fn compute_query_response_substitution( + &self, + original_values: &[ty::GenericArg<'tcx>], + response: &CanonicalResponse<'tcx>, + ) -> CanonicalVarValues<'tcx> { + // FIXME: Longterm canonical queries should deal with all placeholders + // created inside of the query directly instead of returning them to the + // caller. + let prev_universe = self.infcx.universe(); + let universes_created_in_query = response.max_universe.index() + 1; + for _ in 0..universes_created_in_query { + self.infcx.create_next_universe(); + } + + let var_values = response.value.var_values; + assert_eq!(original_values.len(), var_values.len()); + + // If the query did not make progress with constraining inference variables, + // we would normally create a new inference variables for bound existential variables + // only then unify this new inference variable with the inference variable from + // the input. + // + // We therefore instantiate the existential variable in the canonical response with the + // inference variable of the input right away, which is more performant. + let mut opt_values = vec![None; response.variables.len()]; + for (original_value, result_value) in iter::zip(original_values, var_values.var_values) { + match result_value.unpack() { + GenericArgKind::Type(t) => { + if let &ty::Bound(debruijn, b) = t.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var.index()] = Some(*original_value); + } + } + GenericArgKind::Lifetime(r) => { + if let ty::ReLateBound(debruijn, br) = *r { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var.index()] = Some(*original_value); + } + } + GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debrujin, b) = c.kind() { + assert_eq!(debrujin, ty::INNERMOST); + opt_values[b.index()] = Some(*original_value); + } + } + } + } + + let var_values = self.tcx().mk_substs_from_iter(response.variables.iter().enumerate().map( + |(index, info)| { + if info.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + self.infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| { + ty::UniverseIndex::from(prev_universe.index() + idx.index()) + }) + } else if info.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // + // All new inference variables we create start out in the current universe of the caller. + // This is conceptionally wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[index] { + v + } else { + self.infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe) + } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[info.expect_anon_placeholder() as usize] + } + }, + )); + + CanonicalVarValues { var_values } + } + + #[instrument(level = "debug", skip(self, param_env), ret)] + fn unify_query_var_values( + &self, + param_env: ty::ParamEnv<'tcx>, + original_values: &[ty::GenericArg<'tcx>], + var_values: CanonicalVarValues<'tcx>, + ) -> Result<(), NoSolution> { + assert_eq!(original_values.len(), var_values.len()); + for (&orig, response) in iter::zip(original_values, var_values.var_values) { + // This can fail due to the occurs check, see + // `tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs` for an example + // where that can happen. + // + // FIXME: To deal with #105787 I also expect us to emit nested obligations here at + // some point. We can figure out how to deal with this once we actually have + // an ICE. + let nested_goals = self.eq(param_env, orig, response)?; + assert!(nested_goals.is_empty(), "{nested_goals:?}"); + } + + Ok(()) + } + + fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) { + for &(ty::OutlivesPredicate(lhs, rhs), _) in ®ion_constraints.outlives { + match lhs.unpack() { + GenericArgKind::Lifetime(lhs) => self.infcx.region_outlives_predicate( + &ObligationCause::dummy(), + ty::Binder::dummy(ty::OutlivesPredicate(lhs, rhs)), + ), + GenericArgKind::Type(lhs) => self.infcx.register_region_obligation_with_cause( + lhs, + rhs, + &ObligationCause::dummy(), + ), + GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"), + } + } + + for member_constraint in ®ion_constraints.member_constraints { + // FIXME: Deal with member constraints :< + let _ = member_constraint; + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index c8097e81953..95612674eb9 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -19,8 +19,17 @@ use super::Goal; pub struct EvalCtxt<'a, 'tcx> { // FIXME: should be private. pub(super) infcx: &'a InferCtxt<'tcx>, - pub(super) var_values: CanonicalVarValues<'tcx>, + /// The highest universe index nameable by the caller. + /// + /// When we enter a new binder inside of the query we create new universes + /// which the caller cannot name. We have to be careful with variables from + /// these new universes when creating the query response. + /// + /// Both because these new universes can prevent us from reaching a fixpoint + /// if we have a coinductive cycle and because that's the only way we can return + /// new placeholders to the caller. + pub(super) max_input_universe: ty::UniverseIndex, pub(super) search_graph: &'a mut SearchGraph<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 71f536dd3cb..57b6a452737 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -19,11 +19,9 @@ use std::mem; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; -use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::Obligation; -use rustc_middle::infer::canonical::Certainty as OldCertainty; use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ @@ -35,6 +33,7 @@ use crate::solve::search_graph::OverflowHandler; use crate::traits::ObligationCause; mod assembly; +mod canonical; mod eval_ctxt; mod fulfill; mod project_goals; @@ -89,11 +88,8 @@ trait CanonicalResponseExt { impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { fn has_no_inference_or_external_constraints(&self) -> bool { - // so that we get a compile error when regions are supported - // so this code can be checked for being correct - let _: () = self.value.external_constraints.regions; - - self.value.var_values.is_identity() + self.value.external_constraints.region_constraints.is_empty() + && self.value.var_values.is_identity() && self.value.external_constraints.opaque_types.is_empty() } } @@ -169,6 +165,8 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { let result = EvalCtxt { search_graph: &mut search_graph, infcx: self, + // Only relevant when canonicalizing the response. + max_input_universe: ty::UniverseIndex::ROOT, var_values: CanonicalVarValues::dummy(), in_projection_eq_hack: false, } @@ -201,36 +199,33 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { search_graph.with_new_goal(tcx, canonical_goal, |search_graph| { let (ref infcx, goal, var_values) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal); - let mut ecx = - EvalCtxt { infcx, var_values, search_graph, in_projection_eq_hack: false }; + let mut ecx = EvalCtxt { + infcx, + var_values, + max_input_universe: canonical_goal.max_universe, + search_graph, + in_projection_eq_hack: false, + }; ecx.compute_goal(goal) }) } - fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> { - let external_constraints = compute_external_query_constraints(self.infcx)?; - - Ok(self.infcx.canonicalize_response(Response { - var_values: self.var_values, - external_constraints, - certainty, - })) - } - /// Recursively evaluates `goal`, returning whether any inference vars have /// been constrained and the certainty of the result. fn evaluate_goal( &mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>, ) -> Result<(bool, Certainty), NoSolution> { - let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values); + let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let canonical_response = EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; let has_changed = !canonical_response.value.var_values.is_identity(); - let certainty = - instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response); + let certainty = self.instantiate_and_apply_query_response( + goal.param_env, + orig_values, + canonical_response, + )?; // Check that rerunning this query with its inference constraints applied // doesn't result in new inference constraints and has the same result. @@ -244,8 +239,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { && !self.in_projection_eq_hack && !self.search_graph.in_cycle() { - let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values); + let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); let canonical_response = EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; if !canonical_response.value.var_values.is_identity() { @@ -316,15 +310,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { fn compute_type_outlives_goal( &mut self, - _goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, + goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, ) -> QueryResult<'tcx> { + let ty::OutlivesPredicate(ty, lt) = goal.predicate; + self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); self.make_canonical_response(Certainty::Yes) } fn compute_region_outlives_goal( &mut self, - _goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, + goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, ) -> QueryResult<'tcx> { + self.infcx.region_outlives_predicate( + &ObligationCause::dummy(), + ty::Binder::dummy(goal.predicate), + ); self.make_canonical_response(Certainty::Yes) } @@ -561,49 +561,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -#[instrument(level = "debug", skip(infcx), ret)] -fn compute_external_query_constraints<'tcx>( - infcx: &InferCtxt<'tcx>, -) -> Result<ExternalConstraints<'tcx>, NoSolution> { - let region_obligations = infcx.take_registered_region_obligations(); - let opaque_types = infcx.take_opaque_types_for_query_response(); - Ok(infcx.tcx.mk_external_constraints(ExternalConstraintsData { - // FIXME: Now that's definitely wrong :) - // - // Should also do the leak check here I think - regions: drop(region_obligations), - opaque_types, - })) -} - -fn instantiate_canonical_query_response<'tcx>( - infcx: &InferCtxt<'tcx>, - original_values: &OriginalQueryValues<'tcx>, - response: CanonicalResponse<'tcx>, -) -> Certainty { - let Ok(InferOk { value, obligations }) = infcx - .instantiate_query_response_and_region_obligations( - &ObligationCause::dummy(), - ty::ParamEnv::empty(), - original_values, - &response.unchecked_map(|resp| QueryResponse { - var_values: resp.var_values, - region_constraints: QueryRegionConstraints::default(), - certainty: match resp.certainty { - Certainty::Yes => OldCertainty::Proven, - Certainty::Maybe(_) => OldCertainty::Ambiguous, - }, - // FIXME: This to_owned makes me sad, but we should eventually impl - // `instantiate_query_response_and_region_obligations` separately - // instead of piggybacking off of the old implementation. - opaque_types: resp.external_constraints.opaque_types.to_owned(), - value: resp.certainty, - }), - ) else { bug!(); }; - assert!(obligations.is_empty()); - value -} - pub(super) fn response_no_constraints<'tcx>( tcx: TyCtxt<'tcx>, goal: Canonical<'tcx, impl Sized>, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 88fd8bb8bd0..33c66d072e9 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -77,10 +77,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let nested_goals = self .eq(goal.param_env, goal.predicate.term, normalized_alias.into()) .expect("failed to unify with unconstrained term"); - let rhs_certainty = + + let unify_certainty = self.evaluate_all(nested_goals).expect("failed to unify with unconstrained term"); - self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty)) + self.make_canonical_response(normalization_certainty.unify_and(unify_certainty)) } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 66d74fd05a6..9ab753c5a48 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -30,7 +30,7 @@ use rustc_middle::hir::map; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::{ - self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, + self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index da2416b9646..944436ab82f 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -212,36 +212,44 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { /// Identifies whether a predicate obligation needs processing. /// - /// This is always inlined, despite its size, because it has a single - /// callsite and it is called *very* frequently. + /// This is always inlined because it has a single callsite and it is + /// called *very* frequently. Be careful modifying this code! Several + /// compile-time benchmarks are very sensitive to even small changes. #[inline(always)] fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { // If we were stalled on some unresolved variables, first check whether // any of them have been resolved; if not, don't bother doing more work // yet. - match pending_obligation.stalled_on.len() { - // Match arms are in order of frequency, which matters because this - // code is so hot. 1 and 0 dominate; 2+ is fairly rare. - 1 => { - let infer_var = pending_obligation.stalled_on[0]; - self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) - } - 0 => { - // In this case we haven't changed, but wish to make a change. - true - } - _ => { - // This `for` loop was once a call to `all()`, but this lower-level - // form was a perf win. See #64545 for details. - (|| { - for &infer_var in &pending_obligation.stalled_on { - if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) { - return true; - } + let stalled_on = &pending_obligation.stalled_on; + match stalled_on.len() { + // This case is the hottest most of the time, being hit up to 99% + // of the time. `keccak` and `cranelift-codegen-0.82.1` are + // benchmarks that particularly stress this path. + 1 => self.selcx.infcx.ty_or_const_infer_var_changed(stalled_on[0]), + + // In this case we haven't changed, but wish to make a change. Note + // that this is a special case, and is not equivalent to the `_` + // case below, which would return `false` for an empty `stalled_on` + // vector. + // + // This case is usually hit only 1% of the time or less, though it + // reaches 20% in `wasmparser-0.101.0`. + 0 => true, + + // This case is usually hit only 1% of the time or less, though it + // reaches 95% in `mime-0.3.16`, 64% in `wast-54.0.0`, and 12% in + // `inflate-0.4.5`. + // + // The obvious way of writing this, with a call to `any()` and no + // closure, is currently slower than this version. + _ => (|| { + for &infer_var in stalled_on { + if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) { + return true; } - false - })() - } + } + false + })(), } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index b2317f55d25..0e047977caa 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -28,7 +28,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_span::def_id::{DefId, CRATE_DEF_ID}; use rustc_span::Span; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e5b0f9d3300..00acbf751f0 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -32,7 +32,6 @@ use rustc_infer::traits::ImplSourceBuiltinData; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::DefIdTree; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -1175,7 +1174,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( /// paths you want to take. To make things worse, it was possible for /// cycles to arise, where you basically had a setup like `<MyType<$0> /// as Trait>::Foo == $0`. Here, normalizing `<MyType<$0> as -/// Trait>::Foo> to `[type error]` would lead to an obligation of +/// Trait>::Foo>` to `[type error]` would lead to an obligation of /// `<MyType<[type error]> as Trait>::Foo`. We are supposed to report /// an error for this obligation, but we legitimately should not, /// because it contains `[type error]`. Yuck! (See issue #29857 for @@ -1312,16 +1311,16 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( } Ok(None) => { candidate_set.mark_ambiguous(); - return Err(()); + Err(()) } Ok(Some(_)) => { // Don't know enough about the impl to provide a useful signature - return Err(()); + Err(()) } Err(e) => { debug!(error = ?e, "selection error"); candidate_set.mark_error(e); - return Err(()); + Err(()) } } }); @@ -2199,7 +2198,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( Ok(assoc_ty) => assoc_ty, Err(guar) => return Progress::error(tcx, guar), }; - if !leaf_def.item.defaultness(tcx).has_value() { + // We don't support specialization for RPITITs anyways... yet. + if !leaf_def.is_final() { return Progress { term: tcx.ty_error_misc().into(), obligations }; } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 4ad13dcb645..e91057356a2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -339,7 +339,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Essentially any user-written impl will match with an error type, // so creating `ImplCandidates` isn't useful. However, we might - // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized) + // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`) // This helps us avoid overflow: see issue #72839 // Since compilation is already guaranteed to fail, this is just // to try to show the 'nicest' possible errors to the user. diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 01c1ad3a4ce..4377de15829 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -151,7 +151,7 @@ struct TraitObligationStack<'prev, 'tcx> { /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` /// is `EvaluatedToOk`; this is because they were only considered /// ok on the premise that if `A: AutoTrait` held, but we indeed - /// encountered a problem (later on) with `A: AutoTrait. So we + /// encountered a problem (later on) with `A: AutoTrait`. So we /// currently set a flag on the stack node for `B: AutoTrait` (as /// well as the second instance of `A: AutoTrait`) to suppress /// caching. @@ -727,7 +727,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Otherwise, we can say that `T: NonAutoTrait` is // true. // Let's imagine we have a predicate stack like - // `Foo: Bar -> WF(T) -> T: NonAutoTrait -> T: Auto + // `Foo: Bar -> WF(T) -> T: NonAutoTrait -> T: Auto` // depth ^1 ^2 ^3 // and the current predicate is `WF(T)`. `wf_args` // would contain `(T, 1)`. We want to check all diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index e4f3e7928da..0cae0377ee8 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -58,9 +58,7 @@ mod rustc { use rustc_middle::ty; let parent = if let ty::Adt(adt_def, ..) = scope.kind() { - use rustc_middle::ty::DefIdTree; - let parent = self.parent(adt_def.did()); - parent + self.parent(adt_def.did()) } else { // Is this always how we want to handle a non-ADT scope? return false; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index d4866b5dbdd..2fc55a2527d 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -4,7 +4,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::definitions::DefPathData; use rustc_hir::intravisit::{self, Visitor}; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, ImplTraitInTraitData, InternalSubsts, TyCtxt}; +use rustc_span::symbol::kw; pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { @@ -21,12 +22,68 @@ pub fn provide(providers: &mut ty::query::Providers) { fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] { let item = tcx.hir().expect_item(def_id.expect_local()); match item.kind { - hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter( - trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()), - ), - hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter( - impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id()), - ), + hir::ItemKind::Trait(.., ref trait_item_refs) => { + if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty { + // We collect RPITITs for each trait method's return type and create a + // corresponding associated item using associated_items_for_impl_trait_in_trait + // query. + tcx.arena.alloc_from_iter( + trait_item_refs + .iter() + .map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()) + .chain( + trait_item_refs + .iter() + .filter(|trait_item_ref| { + matches!(trait_item_ref.kind, hir::AssocItemKind::Fn { .. }) + }) + .flat_map(|trait_item_ref| { + let trait_fn_def_id = + trait_item_ref.id.owner_id.def_id.to_def_id(); + tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id) + }) + .map(|def_id| *def_id), + ), + ) + } else { + tcx.arena.alloc_from_iter( + trait_item_refs + .iter() + .map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()), + ) + } + } + hir::ItemKind::Impl(ref impl_) => { + if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty { + // We collect RPITITs for each trait method's return type, on the impl side too and + // create a corresponding associated item using + // associated_items_for_impl_trait_in_trait query. + tcx.arena.alloc_from_iter( + impl_ + .items + .iter() + .map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id()) + .chain(impl_.of_trait.iter().flat_map(|_| { + impl_ + .items + .iter() + .filter(|impl_item_ref| { + matches!(impl_item_ref.kind, hir::AssocItemKind::Fn { .. }) + }) + .flat_map(|impl_item_ref| { + let impl_fn_def_id = + impl_item_ref.id.owner_id.def_id.to_def_id(); + tcx.associated_items_for_impl_trait_in_trait(impl_fn_def_id) + }) + .map(|def_id| *def_id) + })), + ) + } else { + tcx.arena.alloc_from_iter( + impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id()), + ) + } + } _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), } } @@ -193,10 +250,65 @@ fn associated_item_for_impl_trait_in_trait( let span = tcx.def_span(opaque_ty_def_id); let trait_assoc_ty = tcx.at(span).create_def(trait_def_id.expect_local(), DefPathData::ImplTraitAssocTy); - trait_assoc_ty.def_id() + + let local_def_id = trait_assoc_ty.def_id(); + let def_id = local_def_id.to_def_id(); + + trait_assoc_ty.opt_def_kind(Some(DefKind::AssocTy)); + + // There's no HIR associated with this new synthesized `def_id`, so feed + // `opt_local_def_id_to_hir_id` with `None`. + trait_assoc_ty.opt_local_def_id_to_hir_id(None); + + // Copy span of the opaque. + trait_assoc_ty.def_ident_span(Some(span)); + + // Add the def_id of the function and opaque that generated this synthesized associated type. + trait_assoc_ty.opt_rpitit_info(Some(ImplTraitInTraitData::Trait { + fn_def_id, + opaque_def_id: opaque_ty_def_id.to_def_id(), + })); + + trait_assoc_ty.associated_item(ty::AssocItem { + name: kw::Empty, + kind: ty::AssocKind::Type, + def_id, + trait_item_def_id: None, + container: ty::TraitContainer, + fn_has_self_parameter: false, + }); + + // Copy visility of the containing function. + trait_assoc_ty.visibility(tcx.visibility(fn_def_id)); + + // Copy impl_defaultness of the containing function. + trait_assoc_ty.impl_defaultness(tcx.impl_defaultness(fn_def_id)); + + // Copy type_of of the opaque. + trait_assoc_ty.type_of(ty::EarlyBinder(tcx.mk_opaque( + opaque_ty_def_id.to_def_id(), + InternalSubsts::identity_for_item(tcx, opaque_ty_def_id.to_def_id()), + ))); + + // Copy generics_of of the opaque. + trait_assoc_ty.generics_of(tcx.generics_of(opaque_ty_def_id).clone()); + + // There are no predicates for the synthesized associated type. + trait_assoc_ty.explicit_predicates_of(ty::GenericPredicates { + parent: Some(trait_def_id), + predicates: &[], + }); + + // There are no inferred outlives for the synthesized associated type. + trait_assoc_ty.inferred_outlives_of(&[]); + + // FIXME implement this. + trait_assoc_ty.explicit_item_bounds(&[]); + + local_def_id } -/// Given an `trait_assoc_def_id` that corresponds to a previously synthethized impl trait in trait +/// Given an `trait_assoc_def_id` that corresponds to a previously synthesized impl trait in trait /// into an associated type and an `impl_def_id` corresponding to an impl block, create and return /// the corresponding associated item inside the impl block. fn impl_associated_item_for_impl_trait_in_trait( @@ -206,8 +318,40 @@ fn impl_associated_item_for_impl_trait_in_trait( ) -> LocalDefId { let impl_def_id = tcx.local_parent(impl_fn_def_id); - let span = tcx.def_span(trait_assoc_def_id); + // FIXME fix the span, we probably want the def_id of the return type of the function + let span = tcx.def_span(impl_fn_def_id); let impl_assoc_ty = tcx.at(span).create_def(impl_def_id, DefPathData::ImplTraitAssocTy); - impl_assoc_ty.def_id() + let local_def_id = impl_assoc_ty.def_id(); + let def_id = local_def_id.to_def_id(); + + impl_assoc_ty.opt_def_kind(Some(DefKind::AssocTy)); + + // There's no HIR associated with this new synthesized `def_id`, so feed + // `opt_local_def_id_to_hir_id` with `None`. + impl_assoc_ty.opt_local_def_id_to_hir_id(None); + + // Add the def_id of the function that generated this synthesized associated type. + impl_assoc_ty.opt_rpitit_info(Some(ImplTraitInTraitData::Impl { + fn_def_id: impl_fn_def_id.to_def_id(), + })); + + impl_assoc_ty.associated_item(ty::AssocItem { + name: kw::Empty, + kind: ty::AssocKind::Type, + def_id, + trait_item_def_id: Some(trait_assoc_def_id.to_def_id()), + container: ty::ImplContainer, + fn_has_self_parameter: false, + }); + + // Copy impl_defaultness of the containing function. + impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id)); + + // Copy generics_of the trait's associated item. + // FIXME: This is not correct, in particular the parent is going to be wrong. So we would need + // to copy from trait_assoc_def_id and adjust things. + impl_assoc_ty.generics_of(tcx.generics_of(trait_assoc_def_id).clone()); + + local_def_id } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index 7fecee2a38b..56d6cc28bc8 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -1,4 +1,3 @@ -use crate::rustc_middle::ty::DefIdTree; use rustc_hir::{def::DefKind, def_id::DefId}; use rustc_middle::ty::{self, Ty, TyCtxt}; diff --git a/config.toml.example b/config.toml.example index 69eb228a2d5..3aaeb09d4c0 100644 --- a/config.toml.example +++ b/config.toml.example @@ -164,6 +164,7 @@ changelog-seen = 2 # General build configuration options # ============================================================================= [build] + # The default stage to use for the `check` subcommand #check-stage = 0 diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index a563b258723..44a37899007 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -283,9 +283,7 @@ impl<T> Box<T> { #[must_use] #[inline(always)] pub fn pin(x: T) -> Pin<Box<T>> { - (#[rustc_box] - Box::new(x)) - .into() + Box::new(x).into() } /// Allocates memory on the heap then places `x` into it, @@ -1242,8 +1240,8 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box<T, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<T: Default> Default for Box<T> { /// Creates a `Box<T>`, with the `Default` value for T. + #[inline] fn default() -> Self { - #[rustc_box] Box::new(T::default()) } } @@ -1252,6 +1250,7 @@ impl<T: Default> Default for Box<T> { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_default_impls", issue = "87864")] impl<T> const Default for Box<[T]> { + #[inline] fn default() -> Self { let ptr: Unique<[T]> = Unique::<[T; 0]>::dangling(); Box(ptr, Global) @@ -1262,6 +1261,7 @@ impl<T> const Default for Box<[T]> { #[stable(feature = "default_box_extra", since = "1.17.0")] #[rustc_const_unstable(feature = "const_default_impls", issue = "87864")] impl const Default for Box<str> { + #[inline] fn default() -> Self { // SAFETY: This is the same as `Unique::cast<U>` but with an unsized `U = str`. let ptr: Unique<str> = unsafe { @@ -1616,7 +1616,6 @@ impl<T, const N: usize> From<[T; N]> for Box<[T]> { /// println!("{boxed:?}"); /// ``` fn from(array: [T; N]) -> Box<[T]> { - #[rustc_box] Box::new(array) } } diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index d4a12509b1c..8317ac431a5 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1924,7 +1924,7 @@ impl<T, A: Allocator> VecDeque<T, A> { #[stable(feature = "append", since = "1.4.0")] pub fn append(&mut self, other: &mut Self) { if T::IS_ZST { - self.len += other.len; + self.len = self.len.checked_add(other.len).expect("capacity overflow"); other.len = 0; other.head = 0; return; diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 5198bf297d9..4c6ae8f2579 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -48,6 +48,8 @@ macro_rules! vec { ); ($($x:expr),+ $(,)?) => ( $crate::__rust_force_expr!(<[_]>::into_vec( + // This rustc_box is not required, but it produces a dramatic improvement in compile + // time when constructing arrays with many elements. #[rustc_box] $crate::boxed::Box::new([$($x),+]) )) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index fd1e3e0f75b..932a537c598 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2145,7 +2145,7 @@ impl<T, I: iter::TrustedLen<Item = T>> ToRcSlice<T> for I { Rc::from_iter_exact(self, low) } } else { - // TrustedLen contract guarantees that `upper_bound == `None` implies an iterator + // TrustedLen contract guarantees that `upper_bound == None` implies an iterator // length exceeding `usize::MAX`. // The default implementation would collect into a vec which would panic. // Thus we panic here immediately without invoking `Vec` code. diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 2b843647dd5..c7e7ed3e95e 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2213,10 +2213,6 @@ impl PartialEq for String { fn eq(&self, other: &String) -> bool { PartialEq::eq(&self[..], &other[..]) } - #[inline] - fn ne(&self, other: &String) -> bool { - PartialEq::ne(&self[..], &other[..]) - } } macro_rules! impl_eq { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index f20486ca9e4..fdd341a06ef 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2895,7 +2895,7 @@ impl<T, I: iter::TrustedLen<Item = T>> ToArcSlice<T> for I { Arc::from_iter_exact(self, low) } } else { - // TrustedLen contract guarantees that `upper_bound == `None` implies an iterator + // TrustedLen contract guarantees that `upper_bound == None` implies an iterator // length exceeding `usize::MAX`. // The default implementation would collect into a vec which would panic. // Thus we panic here immediately without invoking `Vec` code. diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b279f21b524..f2aa30f18fc 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3131,10 +3131,7 @@ impl<T, const N: usize> From<[T; N]> for Vec<T> { /// ``` #[cfg(not(test))] fn from(s: [T; N]) -> Vec<T> { - <[T]>::into_vec( - #[rustc_box] - Box::new(s), - ) + <[T]>::into_vec(Box::new(s)) } #[cfg(test)] diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 0b8f5281b78..5a0b852e8d5 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1046,6 +1046,20 @@ fn test_append_double_drop() { } #[test] +#[should_panic] +fn test_append_zst_capacity_overflow() { + let mut v = Vec::with_capacity(usize::MAX); + // note: using resize instead of set_len here would + // be *extremely* slow in unoptimized builds. + // SAFETY: `v` has capacity `usize::MAX`, and no initialization + // is needed for empty tuples. + unsafe { v.set_len(usize::MAX) }; + let mut v = VecDeque::from(v); + let mut w = vec![()].into(); + v.append(&mut w); +} + +#[test] fn test_retain() { let mut buf = VecDeque::new(); buf.extend(1..5); diff --git a/library/core/src/any.rs b/library/core/src/any.rs index c0fb0d993c3..c27646b8f33 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -56,7 +56,7 @@ //! let value_any = value as &dyn Any; //! //! // Try to convert our value to a `String`. If successful, we want to -//! // output the String`'s length as well as its value. If not, it's a +//! // output the `String`'s length as well as its value. If not, it's a //! // different type: just print it out unadorned. //! match value_any.downcast_ref::<String>() { //! Some(as_string) => { diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs index b2c895f882c..d749865f76f 100644 --- a/library/core/src/array/equality.rs +++ b/library/core/src/array/equality.rs @@ -1,6 +1,5 @@ +use crate::cmp::BytewiseEq; use crate::convert::TryInto; -use crate::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; -use crate::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; #[stable(feature = "rust1", since = "1.0.0")] impl<A, B, const N: usize> PartialEq<[B; N]> for [A; N] @@ -144,74 +143,14 @@ impl<T: PartialEq<Other>, Other, const N: usize> SpecArrayEq<Other, N> for T { } } -impl<T: IsRawEqComparable<U>, U, const N: usize> SpecArrayEq<U, N> for T { +impl<T: BytewiseEq<U>, U, const N: usize> SpecArrayEq<U, N> for T { fn spec_eq(a: &[T; N], b: &[U; N]) -> bool { - // SAFETY: This is why `IsRawEqComparable` is an `unsafe trait`. - unsafe { - let b = &*b.as_ptr().cast::<[T; N]>(); - crate::intrinsics::raw_eq(a, b) - } + // SAFETY: Arrays are compared element-wise, and don't add any padding + // between elements, so when the elements are `BytewiseEq`, we can + // compare the entire array at once. + unsafe { crate::intrinsics::raw_eq(a, crate::mem::transmute(b)) } } fn spec_ne(a: &[T; N], b: &[U; N]) -> bool { !Self::spec_eq(a, b) } } - -/// `U` exists on here mostly because `min_specialization` didn't let me -/// repeat the `T` type parameter in the above specialization, so instead -/// the `T == U` constraint comes from the impls on this. -/// # Safety -/// - Neither `Self` nor `U` has any padding. -/// - `Self` and `U` have the same layout. -/// - `Self: PartialEq<U>` is byte-wise (this means no floats, among other things) -#[rustc_specialization_trait] -unsafe trait IsRawEqComparable<U>: PartialEq<U> {} - -macro_rules! is_raw_eq_comparable { - ($($t:ty),+ $(,)?) => {$( - unsafe impl IsRawEqComparable<$t> for $t {} - )+}; -} - -// SAFETY: All the ordinary integer types have no padding, and are not pointers. -is_raw_eq_comparable!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); - -// SAFETY: bool and char have *niches*, but no *padding* (and these are not pointer types), so this -// is sound -is_raw_eq_comparable!(bool, char); - -// SAFETY: Similarly, the non-zero types have a niche, but no undef and no pointers, -// and they compare like their underlying numeric type. -is_raw_eq_comparable!( - NonZeroU8, - NonZeroU16, - NonZeroU32, - NonZeroU64, - NonZeroU128, - NonZeroUsize, - NonZeroI8, - NonZeroI16, - NonZeroI32, - NonZeroI64, - NonZeroI128, - NonZeroIsize, -); - -// SAFETY: The NonZero types have the "null" optimization guaranteed, and thus -// are also safe to equality-compare bitwise inside an `Option`. -// The way `PartialOrd` is defined for `Option` means that this wouldn't work -// for `<` or `>` on the signed types, but since we only do `==` it's fine. -is_raw_eq_comparable!( - Option<NonZeroU8>, - Option<NonZeroU16>, - Option<NonZeroU32>, - Option<NonZeroU64>, - Option<NonZeroU128>, - Option<NonZeroUsize>, - Option<NonZeroI8>, - Option<NonZeroI16>, - Option<NonZeroI32>, - Option<NonZeroI64>, - Option<NonZeroI128>, - Option<NonZeroIsize>, -); diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 7f109491350..897d03595d7 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -632,7 +632,7 @@ pub struct RefCell<T: ?Sized> { // Stores the location of the earliest currently active borrow. // This gets updated whenever we go from having zero borrows // to having a single borrow. When a borrow occurs, this gets included - // in the generated `BorrowError/`BorrowMutError` + // in the generated `BorrowError`/`BorrowMutError` #[cfg(feature = "debug_refcell")] borrowed_at: Cell<Option<&'static crate::panic::Location<'static>>>, value: UnsafeCell<T>, diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index b4d58376aea..5b5f55d0e65 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -22,6 +22,9 @@ #![stable(feature = "rust1", since = "1.0.0")] +mod bytewise; +pub(crate) use bytewise::BytewiseEq; + use crate::marker::Destruct; use self::Ordering::*; diff --git a/library/core/src/cmp/bytewise.rs b/library/core/src/cmp/bytewise.rs new file mode 100644 index 00000000000..2548d9e24c9 --- /dev/null +++ b/library/core/src/cmp/bytewise.rs @@ -0,0 +1,83 @@ +use crate::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; +use crate::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + +/// Types where `==` & `!=` are equivalent to comparing their underlying bytes. +/// +/// Importantly, this means no floating-point types, as those have different +/// byte representations (like `-0` and `+0`) which compare as the same. +/// Since byte arrays are `Eq`, that implies that these types are probably also +/// `Eq`, but that's not technically required to use this trait. +/// +/// `Rhs` is *de facto* always `Self`, but the separate parameter is important +/// to avoid the `specializing impl repeats parameter` error when consuming this. +/// +/// # Safety +/// +/// - `Self` and `Rhs` have no padding. +/// - `Self` and `Rhs` have the same layout (size and alignment). +/// - Neither `Self` nor `Rhs` have provenance, so integer comparisons are correct. +/// - `<Self as PartialEq<Rhs>>::{eq,ne}` are equivalent to comparing the bytes. +#[rustc_specialization_trait] +pub(crate) unsafe trait BytewiseEq<Rhs = Self>: PartialEq<Rhs> + Sized {} + +macro_rules! is_bytewise_comparable { + ($($t:ty),+ $(,)?) => {$( + unsafe impl BytewiseEq for $t {} + )+}; +} + +// SAFETY: All the ordinary integer types have no padding, and are not pointers. +is_bytewise_comparable!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); + +// SAFETY: These have *niches*, but no *padding* and no *provenance*, +// so we can compare them directly. +is_bytewise_comparable!(bool, char, super::Ordering); + +// SAFETY: Similarly, the non-zero types have a niche, but no undef and no pointers, +// and they compare like their underlying numeric type. +is_bytewise_comparable!( + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU128, + NonZeroUsize, + NonZeroI8, + NonZeroI16, + NonZeroI32, + NonZeroI64, + NonZeroI128, + NonZeroIsize, +); + +// SAFETY: The NonZero types have the "null" optimization guaranteed, and thus +// are also safe to equality-compare bitwise inside an `Option`. +// The way `PartialOrd` is defined for `Option` means that this wouldn't work +// for `<` or `>` on the signed types, but since we only do `==` it's fine. +is_bytewise_comparable!( + Option<NonZeroU8>, + Option<NonZeroU16>, + Option<NonZeroU32>, + Option<NonZeroU64>, + Option<NonZeroU128>, + Option<NonZeroUsize>, + Option<NonZeroI8>, + Option<NonZeroI16>, + Option<NonZeroI32>, + Option<NonZeroI64>, + Option<NonZeroI128>, + Option<NonZeroIsize>, +); + +macro_rules! is_bytewise_comparable_array_length { + ($($n:literal),+ $(,)?) => {$( + // SAFETY: Arrays have no padding between elements, so if the elements are + // `BytewiseEq`, then the whole array can be too. + unsafe impl<T: BytewiseEq<U>, U> BytewiseEq<[U; $n]> for [T; $n] {} + )+}; +} + +// Frustratingly, this can't be made const-generic as it gets +// error: specializing impl repeats parameter `N` +// so just do it for a couple of plausibly-common ones. +is_bytewise_comparable_array_length!(0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64); diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 76daceecd7b..27f6659049c 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -144,6 +144,7 @@ mod c_char_definition { ) ), all(target_os = "fuchsia", target_arch = "aarch64"), + all(target_os = "nto", target_arch = "aarch64"), target_os = "horizon" ))] { pub type c_char = u8; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index b1ed3b31e43..18a90599c4d 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2093,6 +2093,10 @@ extern "rust-intrinsic" { /// Above some backend-decided threshold this will emit calls to `memcmp`, /// like slice equality does, instead of causing massive code size. /// + /// Since this works by comparing the underlying bytes, the actual `T` is + /// not particularly important. It will be used for its size and alignment, + /// but any validity restrictions will be ignored, not enforced. + /// /// # Safety /// /// It's UB to call this if any of the *bytes* in `*a` or `*b` are uninitialized or carry a diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 995d66dca56..72db1d87ca3 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -42,7 +42,7 @@ //! another function. The `dialect` and `phase` parameters indicate which [version of MIR][dialect //! docs] you are inserting here. Generally you'll want to use `#![custom_mir(dialect = "built")]` //! if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect = -//! "runtime", phase = "optimized")] if you don't. +//! "runtime", phase = "optimized")]` if you don't. //! //! [dialect docs]: //! https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index d3727a824b5..24bad799fc8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -124,6 +124,8 @@ #![feature(const_inherent_unchecked_arith)] #![feature(const_int_unchecked_arith)] #![feature(const_intrinsic_forget)] +#![feature(const_ipv4)] +#![feature(const_ipv6)] #![feature(const_likely)] #![feature(const_maybe_uninit_uninit_array)] #![feature(const_maybe_uninit_as_mut_ptr)] @@ -134,6 +136,7 @@ #![feature(const_option)] #![feature(const_option_ext)] #![feature(const_pin)] +#![feature(const_pointer_byte_offsets)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_sub_ptr)] #![feature(const_replace)] @@ -179,6 +182,7 @@ #![feature(const_slice_index)] #![feature(const_is_char_boundary)] #![feature(const_cstr_methods)] +#![feature(ip)] #![feature(is_ascii_octdigit)] // // Language features: @@ -242,7 +246,6 @@ // Target features: #![feature(arm_target_feature)] #![feature(avx512_target_feature)] -#![feature(cmpxchg16b_target_feature)] #![feature(hexagon_target_feature)] #![feature(mips_target_feature)] #![feature(powerpc_target_feature)] @@ -251,6 +254,7 @@ #![feature(sse4a_target_feature)] #![feature(tbm_target_feature)] #![feature(wasm_target_feature)] +#![cfg_attr(bootstrap, feature(cmpxchg16b_target_feature))] // allow using `core::` in intra-doc links #[allow(unused_extern_crates)] @@ -349,6 +353,7 @@ pub mod cell; pub mod char; pub mod ffi; pub mod iter; +pub mod net; pub mod option; pub mod panic; pub mod panicking; diff --git a/library/std/src/net/display_buffer.rs b/library/core/src/net/display_buffer.rs index 7aadf06e92f..7aadf06e92f 100644 --- a/library/std/src/net/display_buffer.rs +++ b/library/core/src/net/display_buffer.rs diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs new file mode 100644 index 00000000000..954d88d548e --- /dev/null +++ b/library/core/src/net/ip_addr.rs @@ -0,0 +1,2070 @@ +use crate::cmp::Ordering; +use crate::fmt::{self, Write}; +use crate::mem::transmute; + +use super::display_buffer::DisplayBuffer; + +/// An IP address, either IPv4 or IPv6. +/// +/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their +/// respective documentation for more details. +/// +/// # Examples +/// +/// ``` +/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +/// +/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); +/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); +/// +/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4)); +/// assert_eq!("::1".parse(), Ok(localhost_v6)); +/// +/// assert_eq!(localhost_v4.is_ipv6(), false); +/// assert_eq!(localhost_v4.is_ipv4(), true); +/// ``` +#[cfg_attr(not(test), rustc_diagnostic_item = "IpAddr")] +#[stable(feature = "ip_addr", since = "1.7.0")] +#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub enum IpAddr { + /// An IPv4 address. + #[stable(feature = "ip_addr", since = "1.7.0")] + V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr), + /// An IPv6 address. + #[stable(feature = "ip_addr", since = "1.7.0")] + V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr), +} + +/// An IPv4 address. +/// +/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791]. +/// They are usually represented as four octets. +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791 +/// +/// # Textual representation +/// +/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal +/// notation, divided by `.` (this is called "dot-decimal notation"). +/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which +/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943]. +/// +/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1 +/// [`FromStr`]: crate::str::FromStr +/// +/// # Examples +/// +/// ``` +/// use std::net::Ipv4Addr; +/// +/// let localhost = Ipv4Addr::new(127, 0, 0, 1); +/// assert_eq!("127.0.0.1".parse(), Ok(localhost)); +/// assert_eq!(localhost.is_loopback(), true); +/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal +/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal +/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Ipv4Addr { + octets: [u8; 4], +} + +/// An IPv6 address. +/// +/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. +/// They are usually represented as eight 16-bit segments. +/// +/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 +/// +/// # Embedding IPv4 Addresses +/// +/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. +/// +/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined: +/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated. +/// +/// Both types of addresses are not assigned any special meaning by this implementation, +/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`, +/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is. +/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address. +/// +/// ### IPv4-Compatible IPv6 Addresses +/// +/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated. +/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows: +/// +/// ```text +/// | 80 bits | 16 | 32 bits | +/// +--------------------------------------+--------------------------+ +/// |0000..............................0000|0000| IPv4 address | +/// +--------------------------------------+----+---------------------+ +/// ``` +/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`. +/// +/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`]. +/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address. +/// +/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1 +/// +/// ### IPv4-Mapped IPv6 Addresses +/// +/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2]. +/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows: +/// +/// ```text +/// | 80 bits | 16 | 32 bits | +/// +--------------------------------------+--------------------------+ +/// |0000..............................0000|FFFF| IPv4 address | +/// +--------------------------------------+----+---------------------+ +/// ``` +/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`. +/// +/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`]. +/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address. +/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`. Use +/// [`Ipv6Addr::to_ipv4_mapped`] to avoid this. +/// +/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2 +/// +/// # Textual representation +/// +/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent +/// an IPv6 address in text, but in general, each segments is written in hexadecimal +/// notation, and segments are separated by `:`. For more information, see +/// [IETF RFC 5952]. +/// +/// [`FromStr`]: crate::str::FromStr +/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952 +/// +/// # Examples +/// +/// ``` +/// use std::net::Ipv6Addr; +/// +/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); +/// assert_eq!("::1".parse(), Ok(localhost)); +/// assert_eq!(localhost.is_loopback(), true); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Ipv6Addr { + octets: [u8; 16], +} + +/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2]. +/// +/// # Stability Guarantees +/// +/// Not all possible values for a multicast scope have been assigned. +/// Future RFCs may introduce new scopes, which will be added as variants to this enum; +/// because of this the enum is marked as `#[non_exhaustive]`. +/// +/// # Examples +/// ``` +/// #![feature(ip)] +/// +/// use std::net::Ipv6Addr; +/// use std::net::Ipv6MulticastScope::*; +/// +/// // An IPv6 multicast address with global scope (`ff0e::`). +/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0); +/// +/// // Will print "Global scope". +/// match address.multicast_scope() { +/// Some(InterfaceLocal) => println!("Interface-Local scope"), +/// Some(LinkLocal) => println!("Link-Local scope"), +/// Some(RealmLocal) => println!("Realm-Local scope"), +/// Some(AdminLocal) => println!("Admin-Local scope"), +/// Some(SiteLocal) => println!("Site-Local scope"), +/// Some(OrganizationLocal) => println!("Organization-Local scope"), +/// Some(Global) => println!("Global scope"), +/// Some(_) => println!("Unknown scope"), +/// None => println!("Not a multicast address!") +/// } +/// +/// ``` +/// +/// [IPv6 multicast address]: Ipv6Addr +/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2 +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] +#[unstable(feature = "ip", issue = "27709")] +#[non_exhaustive] +pub enum Ipv6MulticastScope { + /// Interface-Local scope. + InterfaceLocal, + /// Link-Local scope. + LinkLocal, + /// Realm-Local scope. + RealmLocal, + /// Admin-Local scope. + AdminLocal, + /// Site-Local scope. + SiteLocal, + /// Organization-Local scope. + OrganizationLocal, + /// Global scope. + Global, +} + +impl IpAddr { + /// Returns [`true`] for the special 'unspecified' address. + /// + /// See the documentation for [`Ipv4Addr::is_unspecified()`] and + /// [`Ipv6Addr::is_unspecified()`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "ip_shared", since = "1.12.0")] + #[must_use] + #[inline] + pub const fn is_unspecified(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_unspecified(), + IpAddr::V6(ip) => ip.is_unspecified(), + } + } + + /// Returns [`true`] if this is a loopback address. + /// + /// See the documentation for [`Ipv4Addr::is_loopback()`] and + /// [`Ipv6Addr::is_loopback()`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "ip_shared", since = "1.12.0")] + #[must_use] + #[inline] + pub const fn is_loopback(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_loopback(), + IpAddr::V6(ip) => ip.is_loopback(), + } + } + + /// Returns [`true`] if the address appears to be globally routable. + /// + /// See the documentation for [`Ipv4Addr::is_global()`] and + /// [`Ipv6Addr::is_global()`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_global(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_global(), + IpAddr::V6(ip) => ip.is_global(), + } + } + + /// Returns [`true`] if this is a multicast address. + /// + /// See the documentation for [`Ipv4Addr::is_multicast()`] and + /// [`Ipv6Addr::is_multicast()`] for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "ip_shared", since = "1.12.0")] + #[must_use] + #[inline] + pub const fn is_multicast(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_multicast(), + IpAddr::V6(ip) => ip.is_multicast(), + } + } + + /// Returns [`true`] if this address is in a range designated for documentation. + /// + /// See the documentation for [`Ipv4Addr::is_documentation()`] and + /// [`Ipv6Addr::is_documentation()`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true); + /// assert_eq!( + /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(), + /// true + /// ); + /// ``` + #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_documentation(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_documentation(), + IpAddr::V6(ip) => ip.is_documentation(), + } + } + + /// Returns [`true`] if this address is in a range designated for benchmarking. + /// + /// See the documentation for [`Ipv4Addr::is_benchmarking()`] and + /// [`Ipv6Addr::is_benchmarking()`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(198, 19, 255, 255)).is_benchmarking(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0)).is_benchmarking(), true); + /// ``` + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_benchmarking(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_benchmarking(), + IpAddr::V6(ip) => ip.is_benchmarking(), + } + } + + /// Returns [`true`] if this address is an [`IPv4` address], and [`false`] + /// otherwise. + /// + /// [`IPv4` address]: IpAddr::V4 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "ipaddr_checker", since = "1.16.0")] + #[must_use] + #[inline] + pub const fn is_ipv4(&self) -> bool { + matches!(self, IpAddr::V4(_)) + } + + /// Returns [`true`] if this address is an [`IPv6` address], and [`false`] + /// otherwise. + /// + /// [`IPv6` address]: IpAddr::V6 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "ipaddr_checker", since = "1.16.0")] + #[must_use] + #[inline] + pub const fn is_ipv6(&self) -> bool { + matches!(self, IpAddr::V6(_)) + } + + /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it + /// return `self` as-is. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true); + /// ``` + #[inline] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[rustc_const_unstable(feature = "const_ip", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + pub const fn to_canonical(&self) -> IpAddr { + match self { + &v4 @ IpAddr::V4(_) => v4, + IpAddr::V6(v6) => v6.to_canonical(), + } + } +} + +impl Ipv4Addr { + /// Creates a new IPv4 address from four eight-bit octets. + /// + /// The result will represent the IP address `a`.`b`.`c`.`d`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::new(127, 0, 0, 1); + /// ``` + #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { + Ipv4Addr { octets: [a, b, c, d] } + } + + /// An IPv4 address with the address pointing to localhost: `127.0.0.1` + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::LOCALHOST; + /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)); + /// ``` + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); + + /// An IPv4 address representing an unspecified address: `0.0.0.0` + /// + /// This corresponds to the constant `INADDR_ANY` in other languages. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::UNSPECIFIED; + /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); + /// ``` + #[doc(alias = "INADDR_ANY")] + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); + + /// An IPv4 address representing the broadcast address: `255.255.255.255` + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::BROADCAST; + /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255)); + /// ``` + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); + + /// Returns the four eight-bit integers that make up this address. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::new(127, 0, 0, 1); + /// assert_eq!(addr.octets(), [127, 0, 0, 1]); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub const fn octets(&self) -> [u8; 4] { + self.octets + } + + /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`). + /// + /// This property is defined in _UNIX Network Programming, Second Edition_, + /// W. Richard Stevens, p. 891; see also [ip7]. + /// + /// [ip7]: https://man7.org/linux/man-pages/man7/ip.7.html + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true); + /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] + #[stable(feature = "ip_shared", since = "1.12.0")] + #[must_use] + #[inline] + pub const fn is_unspecified(&self) -> bool { + u32::from_be_bytes(self.octets) == 0 + } + + /// Returns [`true`] if this is a loopback address (`127.0.0.0/8`). + /// + /// This property is defined by [IETF RFC 1122]. + /// + /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true); + /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_loopback(&self) -> bool { + self.octets()[0] == 127 + } + + /// Returns [`true`] if this is a private address. + /// + /// The private address ranges are defined in [IETF RFC 1918] and include: + /// + /// - `10.0.0.0/8` + /// - `172.16.0.0/12` + /// - `192.168.0.0/16` + /// + /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); + /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); + /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); + /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); + /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_private(&self) -> bool { + match self.octets() { + [10, ..] => true, + [172, b, ..] if b >= 16 && b <= 31 => true, + [192, 168, ..] => true, + _ => false, + } + } + + /// Returns [`true`] if the address is link-local (`169.254.0.0/16`). + /// + /// This property is defined by [IETF RFC 3927]. + /// + /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true); + /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true); + /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_link_local(&self) -> bool { + matches!(self.octets(), [169, 254, ..]) + } + + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv4 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// + /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) + /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) + /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) + /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) + /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) + /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) + /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) + /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) + /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. + /// + /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml + /// [unspecified address]: Ipv4Addr::UNSPECIFIED + /// [broadcast address]: Ipv4Addr::BROADCAST + + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv4Addr; + /// + /// // Most IPv4 addresses are globally reachable: + /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); + /// + /// // However some addresses have been assigned a special meaning + /// // that makes them not globally reachable. Some examples are: + /// + /// // The unspecified address (`0.0.0.0`) + /// assert_eq!(Ipv4Addr::UNSPECIFIED.is_global(), false); + /// + /// // Addresses reserved for private use (`10.0.0.0/8`, `172.16.0.0/12`, 192.168.0.0/16) + /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); + /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); + /// + /// // Addresses in the shared address space (`100.64.0.0/10`) + /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); + /// + /// // The loopback addresses (`127.0.0.0/8`) + /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false); + /// + /// // Link-local addresses (`169.254.0.0/16`) + /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); + /// + /// // Addresses reserved for documentation (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`) + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); + /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); + /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); + /// + /// // Addresses reserved for benchmarking (`198.18.0.0/15`) + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); + /// + /// // Reserved addresses (`240.0.0.0/4`) + /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); + /// + /// // The broadcast address (`255.255.255.255`) + /// assert_eq!(Ipv4Addr::BROADCAST.is_global(), false); + /// + /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry. + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_global(&self) -> bool { + !(self.octets()[0] == 0 // "This network" + || self.is_private() + || self.is_shared() + || self.is_loopback() + || self.is_link_local() + // addresses reserved for future protocols (`192.0.0.0/24`) + ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) + || self.is_documentation() + || self.is_benchmarking() + || self.is_reserved() + || self.is_broadcast()) + } + + /// Returns [`true`] if this address is part of the Shared Address Space defined in + /// [IETF RFC 6598] (`100.64.0.0/10`). + /// + /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); + /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_shared(&self) -> bool { + self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) + } + + /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for + /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` + /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. + /// + /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 + /// [errata 423]: https://www.rfc-editor.org/errata/eid423 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); + /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); + /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_benchmarking(&self) -> bool { + self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 + } + + /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] + /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the + /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since + /// it is obviously not reserved for future use. + /// + /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 + /// + /// # Warning + /// + /// As IANA assigns new addresses, this method will be + /// updated. This may result in non-reserved addresses being + /// treated as reserved in code that relies on an outdated version + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); + /// + /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); + /// // The broadcast address is not considered as reserved for future use by this implementation + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_reserved(&self) -> bool { + self.octets()[0] & 240 == 240 && !self.is_broadcast() + } + + /// Returns [`true`] if this is a multicast address (`224.0.0.0/4`). + /// + /// Multicast addresses have a most significant octet between `224` and `239`, + /// and is defined by [IETF RFC 5771]. + /// + /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true); + /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true); + /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_multicast(&self) -> bool { + self.octets()[0] >= 224 && self.octets()[0] <= 239 + } + + /// Returns [`true`] if this is a broadcast address (`255.255.255.255`). + /// + /// A broadcast address has all octets set to `255` as defined in [IETF RFC 919]. + /// + /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true); + /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_broadcast(&self) -> bool { + u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) + } + + /// Returns [`true`] if this address is in a range designated for documentation. + /// + /// This is defined in [IETF RFC 5737]: + /// + /// - `192.0.2.0/24` (TEST-NET-1) + /// - `198.51.100.0/24` (TEST-NET-2) + /// - `203.0.113.0/24` (TEST-NET-3) + /// + /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true); + /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true); + /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true); + /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_documentation(&self) -> bool { + matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _]) + } + + /// Converts this address to an [IPv4-compatible] [`IPv6` address]. + /// + /// `a.b.c.d` becomes `::a.b.c.d` + /// + /// Note that IPv4-compatible addresses have been officially deprecated. + /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead. + /// + /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses + /// [`IPv6` address]: Ipv6Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!( + /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), + /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff) + /// ); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { + let [a, b, c, d] = self.octets(); + Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] } + } + + /// Converts this address to an [IPv4-mapped] [`IPv6` address]. + /// + /// `a.b.c.d` becomes `::ffff:a.b.c.d` + /// + /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses + /// [`IPv6` address]: Ipv6Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), + /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff)); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { + let [a, b, c, d] = self.octets(); + Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] } + } +} + +#[stable(feature = "ip_addr", since = "1.7.0")] +impl fmt::Display for IpAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IpAddr::V4(ip) => ip.fmt(fmt), + IpAddr::V6(ip) => ip.fmt(fmt), + } + } +} + +#[stable(feature = "ip_addr", since = "1.7.0")] +impl fmt::Debug for IpAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From<Ipv4Addr> for IpAddr { + /// Copies this address to a new `IpAddr::V4`. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr}; + /// + /// let addr = Ipv4Addr::new(127, 0, 0, 1); + /// + /// assert_eq!( + /// IpAddr::V4(addr), + /// IpAddr::from(addr) + /// ) + /// ``` + #[inline] + fn from(ipv4: Ipv4Addr) -> IpAddr { + IpAddr::V4(ipv4) + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From<Ipv6Addr> for IpAddr { + /// Copies this address to a new `IpAddr::V6`. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr}; + /// + /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); + /// + /// assert_eq!( + /// IpAddr::V6(addr), + /// IpAddr::from(addr) + /// ); + /// ``` + #[inline] + fn from(ipv6: Ipv6Addr) -> IpAddr { + IpAddr::V6(ipv6) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let octets = self.octets(); + + // If there are no alignment requirements, write the IP address directly to `f`. + // Otherwise, write it to a local buffer and then use `f.pad`. + if fmt.precision().is_none() && fmt.width().is_none() { + write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) + } else { + const LONGEST_IPV4_ADDR: &str = "255.255.255.255"; + + let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new(); + // Buffer is long enough for the longest possible IPv4 address, so this should never fail. + write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); + + fmt.pad(buf.as_str()) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Ipv4Addr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq<Ipv4Addr> for IpAddr { + #[inline] + fn eq(&self, other: &Ipv4Addr) -> bool { + match self { + IpAddr::V4(v4) => v4 == other, + IpAddr::V6(_) => false, + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq<IpAddr> for Ipv4Addr { + #[inline] + fn eq(&self, other: &IpAddr) -> bool { + match other { + IpAddr::V4(v4) => self == v4, + IpAddr::V6(_) => false, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Ipv4Addr { + #[inline] + fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd<Ipv4Addr> for IpAddr { + #[inline] + fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> { + match self { + IpAddr::V4(v4) => v4.partial_cmp(other), + IpAddr::V6(_) => Some(Ordering::Greater), + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd<IpAddr> for Ipv4Addr { + #[inline] + fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> { + match other { + IpAddr::V4(v4) => self.partial_cmp(v4), + IpAddr::V6(_) => Some(Ordering::Less), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Ipv4Addr { + #[inline] + fn cmp(&self, other: &Ipv4Addr) -> Ordering { + self.octets.cmp(&other.octets) + } +} + +#[stable(feature = "ip_u32", since = "1.1.0")] +impl From<Ipv4Addr> for u32 { + /// Converts an `Ipv4Addr` into a host byte order `u32`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); + /// assert_eq!(0x12345678, u32::from(addr)); + /// ``` + #[inline] + fn from(ip: Ipv4Addr) -> u32 { + u32::from_be_bytes(ip.octets) + } +} + +#[stable(feature = "ip_u32", since = "1.1.0")] +impl From<u32> for Ipv4Addr { + /// Converts a host byte order `u32` into an `Ipv4Addr`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::from(0x12345678); + /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); + /// ``` + #[inline] + fn from(ip: u32) -> Ipv4Addr { + Ipv4Addr { octets: ip.to_be_bytes() } + } +} + +#[stable(feature = "from_slice_v4", since = "1.9.0")] +impl From<[u8; 4]> for Ipv4Addr { + /// Creates an `Ipv4Addr` from a four element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv4Addr; + /// + /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]); + /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); + /// ``` + #[inline] + fn from(octets: [u8; 4]) -> Ipv4Addr { + Ipv4Addr { octets } + } +} + +#[stable(feature = "ip_from_slice", since = "1.17.0")] +impl From<[u8; 4]> for IpAddr { + /// Creates an `IpAddr::V4` from a four element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr}; + /// + /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]); + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr); + /// ``` + #[inline] + fn from(octets: [u8; 4]) -> IpAddr { + IpAddr::V4(Ipv4Addr::from(octets)) + } +} + +impl Ipv6Addr { + /// Creates a new IPv6 address from eight 16-bit segments. + /// + /// The result will represent the IP address `a:b:c:d:e:f:g:h`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); + /// ``` + #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { + let addr16 = [ + a.to_be(), + b.to_be(), + c.to_be(), + d.to_be(), + e.to_be(), + f.to_be(), + g.to_be(), + h.to_be(), + ]; + Ipv6Addr { + // All elements in `addr16` are big endian. + // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. + octets: unsafe { transmute::<_, [u8; 16]>(addr16) }, + } + } + + /// An IPv6 address representing localhost: `::1`. + /// + /// This corresponds to constant `IN6ADDR_LOOPBACK_INIT` or `in6addr_loopback` in other + /// languages. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::LOCALHOST; + /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + /// ``` + #[doc(alias = "IN6ADDR_LOOPBACK_INIT")] + #[doc(alias = "in6addr_loopback")] + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + + /// An IPv6 address representing the unspecified address: `::` + /// + /// This corresponds to constant `IN6ADDR_ANY_INIT` or `in6addr_any` in other languages. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::UNSPECIFIED; + /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + /// ``` + #[doc(alias = "IN6ADDR_ANY_INIT")] + #[doc(alias = "in6addr_any")] + #[stable(feature = "ip_constructors", since = "1.30.0")] + pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); + + /// Returns the eight 16-bit segments that make up this address. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), + /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub const fn segments(&self) -> [u16; 8] { + // All elements in `self.octets` must be big endian. + // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. + let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.octets) }; + // We want native endian u16 + [ + u16::from_be(a), + u16::from_be(b), + u16::from_be(c), + u16::from_be(d), + u16::from_be(e), + u16::from_be(f), + u16::from_be(g), + u16::from_be(h), + ] + } + + /// Returns [`true`] for the special 'unspecified' address (`::`). + /// + /// This property is defined in [IETF RFC 4291]. + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_unspecified(&self) -> bool { + u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) + } + + /// Returns [`true`] if this is the [loopback address] (`::1`), + /// as defined in [IETF RFC 4291 section 2.5.3]. + /// + /// Contrary to IPv4, in IPv6 there is only one loopback address. + /// + /// [loopback address]: Ipv6Addr::LOCALHOST + /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_loopback(&self) -> bool { + u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) + } + + /// Returns [`true`] if the address appears to be globally reachable + /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. + /// Whether or not an address is practically reachable will depend on your network configuration. + /// + /// Most IPv6 addresses are globally reachable; + /// unless they are specifically defined as *not* globally reachable. + /// + /// Non-exhaustive list of notable addresses that are not globally reachable: + /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) + /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) + /// - IPv4-mapped addresses + /// - Addresses reserved for benchmarking + /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) + /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) + /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) + /// + /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. + /// + /// Note that an address having global scope is not the same as being globally reachable, + /// and there is no direct relation between the two concepts: There exist addresses with global scope + /// that are not globally reachable (for example unique local addresses), + /// and addresses that are globally reachable without having global scope + /// (multicast addresses with non-global scope). + /// + /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml + /// [unspecified address]: Ipv6Addr::UNSPECIFIED + /// [loopback address]: Ipv6Addr::LOCALHOST + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// // Most IPv6 addresses are globally reachable: + /// assert_eq!(Ipv6Addr::new(0x26, 0, 0x1c9, 0, 0, 0xafc8, 0x10, 0x1).is_global(), true); + /// + /// // However some addresses have been assigned a special meaning + /// // that makes them not globally reachable. Some examples are: + /// + /// // The unspecified address (`::`) + /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_global(), false); + /// + /// // The loopback address (`::1`) + /// assert_eq!(Ipv6Addr::LOCALHOST.is_global(), false); + /// + /// // IPv4-mapped addresses (`::ffff:0:0/96`) + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), false); + /// + /// // Addresses reserved for benchmarking (`2001:2::/48`) + /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); + /// + /// // Addresses reserved for documentation (`2001:db8::/32`) + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); + /// + /// // Unique local addresses (`fc00::/7`) + /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); + /// + /// // Unicast addresses with link-local scope (`fe80::/10`) + /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 1).is_global(), false); + /// + /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry. + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_global(&self) -> bool { + !(self.is_unspecified() + || self.is_loopback() + // IPv4-mapped Address (`::ffff:0:0/96`) + || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) + // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) + || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) + // Discard-Only Address Block (`100::/64`) + || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) + // IETF Protocol Assignments (`2001::/23`) + || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) + && !( + // Port Control Protocol Anycast (`2001:1::1`) + u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 + // Traversal Using Relays around NAT Anycast (`2001:1::2`) + || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 + // AMT (`2001:3::/32`) + || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) + // AS112-v6 (`2001:4:112::/48`) + || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) + // ORCHIDv2 (`2001:20::/28`) + || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) + )) + || self.is_documentation() + || self.is_unique_local() + || self.is_unicast_link_local()) + } + + /// Returns [`true`] if this is a unique local address (`fc00::/7`). + /// + /// This property is defined in [IETF RFC 4193]. + /// + /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); + /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_unique_local(&self) -> bool { + (self.segments()[0] & 0xfe00) == 0xfc00 + } + + /// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291]. + /// Any address that is not a [multicast address] (`ff00::/8`) is unicast. + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [multicast address]: Ipv6Addr::is_multicast + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// // The unspecified and loopback addresses are unicast. + /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_unicast(), true); + /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast(), true); + /// + /// // Any address that is not a multicast address (`ff00::/8`) is unicast. + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true); + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_unicast(&self) -> bool { + !self.is_multicast() + } + + /// Returns `true` if the address is a unicast address with link-local scope, + /// as defined in [RFC 4291]. + /// + /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. + /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], + /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: + /// + /// ```text + /// | 10 bits | 54 bits | 64 bits | + /// +----------+-------------------------+----------------------------+ + /// |1111111010| 0 | interface ID | + /// +----------+-------------------------+----------------------------+ + /// ``` + /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, + /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, + /// and those addresses will have link-local scope. + /// + /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", + /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. + /// + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 + /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 + /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 + /// [loopback address]: Ipv6Addr::LOCALHOST + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// // The loopback address (`::1`) does not actually have link-local scope. + /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false); + /// + /// // Only addresses in `fe80::/10` have link-local scope. + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false); + /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); + /// + /// // Addresses outside the stricter `fe80::/64` also have link-local scope. + /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true); + /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_unicast_link_local(&self) -> bool { + (self.segments()[0] & 0xffc0) == 0xfe80 + } + + /// Returns [`true`] if this is an address reserved for documentation + /// (`2001:db8::/32`). + /// + /// This property is defined in [IETF RFC 3849]. + /// + /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_documentation(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + } + + /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). + /// + /// This property is defined in [IETF RFC 5180], where it is mistakenly specified as covering the range `2001:0200::/48`. + /// This is corrected in [IETF RFC Errata 1752] to `2001:0002::/48`. + /// + /// [IETF RFC 5180]: https://tools.ietf.org/html/rfc5180 + /// [IETF RFC Errata 1752]: https://www.rfc-editor.org/errata_search.php?eid=1752 + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc613, 0x0).is_benchmarking(), false); + /// assert_eq!(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0).is_benchmarking(), true); + /// ``` + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_benchmarking(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) + } + + /// Returns [`true`] if the address is a globally routable unicast address. + /// + /// The following return false: + /// + /// - the loopback address + /// - the link-local addresses + /// - unique local addresses + /// - the unspecified address + /// - the address range reserved for documentation + /// + /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] + /// + /// ```no_rust + /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer + /// be supported in new implementations (i.e., new implementations must treat this prefix as + /// Global Unicast). + /// ``` + /// + /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn is_unicast_global(&self) -> bool { + self.is_unicast() + && !self.is_loopback() + && !self.is_unicast_link_local() + && !self.is_unique_local() + && !self.is_unspecified() + && !self.is_documentation() + && !self.is_benchmarking() + } + + /// Returns the address's multicast scope if the address is multicast. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{Ipv6Addr, Ipv6MulticastScope}; + /// + /// assert_eq!( + /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(), + /// Some(Ipv6MulticastScope::Global) + /// ); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use] + #[inline] + pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> { + if self.is_multicast() { + match self.segments()[0] & 0x000f { + 1 => Some(Ipv6MulticastScope::InterfaceLocal), + 2 => Some(Ipv6MulticastScope::LinkLocal), + 3 => Some(Ipv6MulticastScope::RealmLocal), + 4 => Some(Ipv6MulticastScope::AdminLocal), + 5 => Some(Ipv6MulticastScope::SiteLocal), + 8 => Some(Ipv6MulticastScope::OrganizationLocal), + 14 => Some(Ipv6MulticastScope::Global), + _ => None, + } + } else { + None + } + } + + /// Returns [`true`] if this is a multicast address (`ff00::/8`). + /// + /// This property is defined by [IETF RFC 4291]. + /// + /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(since = "1.7.0", feature = "ip_17")] + #[must_use] + #[inline] + pub const fn is_multicast(&self) -> bool { + (self.segments()[0] & 0xff00) == 0xff00 + } + + /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address, + /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`]. + /// + /// `::ffff:a.b.c.d` becomes `a.b.c.d`. + /// All addresses *not* starting with `::ffff` will return `None`. + /// + /// [`IPv4` address]: Ipv4Addr + /// [IPv4-mapped]: Ipv6Addr + /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(), + /// Some(Ipv4Addr::new(192, 10, 2, 255))); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> { + match self.octets() { + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { + Some(Ipv4Addr::new(a, b, c, d)) + } + _ => None, + } + } + + /// Converts this address to an [`IPv4` address] if it is either + /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1], + /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2], + /// otherwise returns [`None`]. + /// + /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`. Use + /// [`Ipv6Addr::to_ipv4_mapped`] to avoid this. + /// + /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`. + /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`. + /// + /// [`IPv4` address]: Ipv4Addr + /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses + /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses + /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1 + /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 + /// + /// # Examples + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), + /// Some(Ipv4Addr::new(192, 10, 2, 255))); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), + /// Some(Ipv4Addr::new(0, 0, 0, 1))); + /// ``` + #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_ipv4(&self) -> Option<Ipv4Addr> { + if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { + let [a, b] = ab.to_be_bytes(); + let [c, d] = cd.to_be_bytes(); + Some(Ipv4Addr::new(a, b, c, d)) + } else { + None + } + } + + /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it + /// returns self wrapped in an `IpAddr::V6`. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true); + /// ``` + #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] + #[unstable(feature = "ip", issue = "27709")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn to_canonical(&self) -> IpAddr { + if let Some(mapped) = self.to_ipv4_mapped() { + return IpAddr::V4(mapped); + } + IpAddr::V6(*self) + } + + /// Returns the sixteen eight-bit integers the IPv6 address consists of. + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), + /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + /// ``` + #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] + #[stable(feature = "ipv6_to_octets", since = "1.12.0")] + #[must_use] + #[inline] + pub const fn octets(&self) -> [u8; 16] { + self.octets + } +} + +/// Write an Ipv6Addr, conforming to the canonical style described by +/// [RFC 5952](https://tools.ietf.org/html/rfc5952). +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Ipv6Addr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If there are no alignment requirements, write the IP address directly to `f`. + // Otherwise, write it to a local buffer and then use `f.pad`. + if f.precision().is_none() && f.width().is_none() { + let segments = self.segments(); + + // Special case for :: and ::1; otherwise they get written with the + // IPv4 formatter + if self.is_unspecified() { + f.write_str("::") + } else if self.is_loopback() { + f.write_str("::1") + } else if let Some(ipv4) = self.to_ipv4() { + match segments[5] { + // IPv4 Compatible address + 0 => write!(f, "::{}", ipv4), + // IPv4 Mapped address + 0xffff => write!(f, "::ffff:{}", ipv4), + _ => unreachable!(), + } + } else { + #[derive(Copy, Clone, Default)] + struct Span { + start: usize, + len: usize, + } + + // Find the inner 0 span + let zeroes = { + let mut longest = Span::default(); + let mut current = Span::default(); + + for (i, &segment) in segments.iter().enumerate() { + if segment == 0 { + if current.len == 0 { + current.start = i; + } + + current.len += 1; + + if current.len > longest.len { + longest = current; + } + } else { + current = Span::default(); + } + } + + longest + }; + + /// Write a colon-separated part of the address + #[inline] + fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { + if let Some((first, tail)) = chunk.split_first() { + write!(f, "{:x}", first)?; + for segment in tail { + f.write_char(':')?; + write!(f, "{:x}", segment)?; + } + } + Ok(()) + } + + if zeroes.len > 1 { + fmt_subslice(f, &segments[..zeroes.start])?; + f.write_str("::")?; + fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) + } else { + fmt_subslice(f, &segments) + } + } + } else { + const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; + + let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new(); + // Buffer is long enough for the longest possible IPv6 address, so this should never fail. + write!(buf, "{}", self).unwrap(); + + f.pad(buf.as_str()) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Ipv6Addr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq<IpAddr> for Ipv6Addr { + #[inline] + fn eq(&self, other: &IpAddr) -> bool { + match other { + IpAddr::V4(_) => false, + IpAddr::V6(v6) => self == v6, + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialEq<Ipv6Addr> for IpAddr { + #[inline] + fn eq(&self, other: &Ipv6Addr) -> bool { + match self { + IpAddr::V4(_) => false, + IpAddr::V6(v6) => v6 == other, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Ipv6Addr { + #[inline] + fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd<Ipv6Addr> for IpAddr { + #[inline] + fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> { + match self { + IpAddr::V4(_) => Some(Ordering::Less), + IpAddr::V6(v6) => v6.partial_cmp(other), + } + } +} + +#[stable(feature = "ip_cmp", since = "1.16.0")] +impl PartialOrd<IpAddr> for Ipv6Addr { + #[inline] + fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> { + match other { + IpAddr::V4(_) => Some(Ordering::Greater), + IpAddr::V6(v6) => self.partial_cmp(v6), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Ipv6Addr { + #[inline] + fn cmp(&self, other: &Ipv6Addr) -> Ordering { + self.segments().cmp(&other.segments()) + } +} + +#[stable(feature = "i128", since = "1.26.0")] +impl From<Ipv6Addr> for u128 { + /// Convert an `Ipv6Addr` into a host byte order `u128`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::new( + /// 0x1020, 0x3040, 0x5060, 0x7080, + /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, + /// ); + /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); + /// ``` + #[inline] + fn from(ip: Ipv6Addr) -> u128 { + u128::from_be_bytes(ip.octets) + } +} +#[stable(feature = "i128", since = "1.26.0")] +impl From<u128> for Ipv6Addr { + /// Convert a host byte order `u128` into an `Ipv6Addr`. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x1020, 0x3040, 0x5060, 0x7080, + /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, + /// ), + /// addr); + /// ``` + #[inline] + fn from(ip: u128) -> Ipv6Addr { + Ipv6Addr::from(ip.to_be_bytes()) + } +} + +#[stable(feature = "ipv6_from_octets", since = "1.9.0")] +impl From<[u8; 16]> for Ipv6Addr { + /// Creates an `Ipv6Addr` from a sixteen element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from([ + /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, + /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, + /// ]); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x1918, 0x1716, + /// 0x1514, 0x1312, + /// 0x1110, 0x0f0e, + /// 0x0d0c, 0x0b0a + /// ), + /// addr + /// ); + /// ``` + #[inline] + fn from(octets: [u8; 16]) -> Ipv6Addr { + Ipv6Addr { octets } + } +} + +#[stable(feature = "ipv6_from_segments", since = "1.16.0")] +impl From<[u16; 8]> for Ipv6Addr { + /// Creates an `Ipv6Addr` from an eight element 16-bit array. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::from([ + /// 525u16, 524u16, 523u16, 522u16, + /// 521u16, 520u16, 519u16, 518u16, + /// ]); + /// assert_eq!( + /// Ipv6Addr::new( + /// 0x20d, 0x20c, + /// 0x20b, 0x20a, + /// 0x209, 0x208, + /// 0x207, 0x206 + /// ), + /// addr + /// ); + /// ``` + #[inline] + fn from(segments: [u16; 8]) -> Ipv6Addr { + let [a, b, c, d, e, f, g, h] = segments; + Ipv6Addr::new(a, b, c, d, e, f, g, h) + } +} + +#[stable(feature = "ip_from_slice", since = "1.17.0")] +impl From<[u8; 16]> for IpAddr { + /// Creates an `IpAddr::V6` from a sixteen element byte array. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr}; + /// + /// let addr = IpAddr::from([ + /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, + /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, + /// ]); + /// assert_eq!( + /// IpAddr::V6(Ipv6Addr::new( + /// 0x1918, 0x1716, + /// 0x1514, 0x1312, + /// 0x1110, 0x0f0e, + /// 0x0d0c, 0x0b0a + /// )), + /// addr + /// ); + /// ``` + #[inline] + fn from(octets: [u8; 16]) -> IpAddr { + IpAddr::V6(Ipv6Addr::from(octets)) + } +} + +#[stable(feature = "ip_from_slice", since = "1.17.0")] +impl From<[u16; 8]> for IpAddr { + /// Creates an `IpAddr::V6` from an eight element 16-bit array. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr}; + /// + /// let addr = IpAddr::from([ + /// 525u16, 524u16, 523u16, 522u16, + /// 521u16, 520u16, 519u16, 518u16, + /// ]); + /// assert_eq!( + /// IpAddr::V6(Ipv6Addr::new( + /// 0x20d, 0x20c, + /// 0x20b, 0x20a, + /// 0x209, 0x208, + /// 0x207, 0x206 + /// )), + /// addr + /// ); + /// ``` + #[inline] + fn from(segments: [u16; 8]) -> IpAddr { + IpAddr::V6(Ipv6Addr::from(segments)) + } +} diff --git a/library/core/src/net/mod.rs b/library/core/src/net/mod.rs new file mode 100644 index 00000000000..31f5f5d3c22 --- /dev/null +++ b/library/core/src/net/mod.rs @@ -0,0 +1,24 @@ +//! Networking primitives for IP communication. +//! +//! This module provides types for IP and socket addresses. +//! +//! # Organization +//! +//! * [`IpAddr`] represents IP addresses of either IPv4 or IPv6; [`Ipv4Addr`] and +//! [`Ipv6Addr`] are respectively IPv4 and IPv6 addresses +//! * [`SocketAddr`] represents socket addresses of either IPv4 or IPv6; [`SocketAddrV4`] +//! and [`SocketAddrV6`] are respectively IPv4 and IPv6 socket addresses + +#![unstable(feature = "ip_in_core", issue = "108443")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::parser::AddrParseError; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6}; + +mod display_buffer; +mod ip_addr; +mod parser; +mod socket_addr; diff --git a/library/std/src/net/parser.rs b/library/core/src/net/parser.rs index a38031c48c8..a08d2792d04 100644 --- a/library/std/src/net/parser.rs +++ b/library/core/src/net/parser.rs @@ -3,9 +3,7 @@ //! This module is "publicly exported" through the `FromStr` implementations //! below. -#[cfg(test)] -mod tests; - +use crate::convert::TryInto; use crate::error::Error; use crate::fmt; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; diff --git a/library/core/src/net/socket_addr.rs b/library/core/src/net/socket_addr.rs new file mode 100644 index 00000000000..0d25ab1d5e1 --- /dev/null +++ b/library/core/src/net/socket_addr.rs @@ -0,0 +1,664 @@ +use crate::cmp::Ordering; +use crate::fmt::{self, Write}; +use crate::hash; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use super::display_buffer::DisplayBuffer; + +/// An internet socket address, either IPv4 or IPv6. +/// +/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well +/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and +/// [`SocketAddrV6`]'s respective documentation for more details. +/// +/// The size of a `SocketAddr` instance may vary depending on the target operating +/// system. +/// +/// [IP address]: IpAddr +/// +/// # Examples +/// +/// ``` +/// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +/// +/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); +/// +/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); +/// assert_eq!(socket.port(), 8080); +/// assert_eq!(socket.is_ipv4(), true); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum SocketAddr { + /// An IPv4 socket address. + #[stable(feature = "rust1", since = "1.0.0")] + V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4), + /// An IPv6 socket address. + #[stable(feature = "rust1", since = "1.0.0")] + V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6), +} + +/// An IPv4 socket address. +/// +/// IPv4 socket addresses consist of an [`IPv4` address] and a 16-bit port number, as +/// stated in [IETF RFC 793]. +/// +/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. +/// +/// The size of a `SocketAddrV4` struct may vary depending on the target operating +/// system. Do not assume that this type has the same memory layout as the underlying +/// system representation. +/// +/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 +/// [`IPv4` address]: Ipv4Addr +/// +/// # Examples +/// +/// ``` +/// use std::net::{Ipv4Addr, SocketAddrV4}; +/// +/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); +/// +/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); +/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); +/// assert_eq!(socket.port(), 8080); +/// ``` +#[derive(Copy, Clone, Eq, PartialEq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SocketAddrV4 { + ip: Ipv4Addr, + port: u16, +} + +/// An IPv6 socket address. +/// +/// IPv6 socket addresses consist of an [`IPv6` address], a 16-bit port number, as well +/// as fields containing the traffic class, the flow label, and a scope identifier +/// (see [IETF RFC 2553, Section 3.3] for more details). +/// +/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. +/// +/// The size of a `SocketAddrV6` struct may vary depending on the target operating +/// system. Do not assume that this type has the same memory layout as the underlying +/// system representation. +/// +/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 +/// [`IPv6` address]: Ipv6Addr +/// +/// # Examples +/// +/// ``` +/// use std::net::{Ipv6Addr, SocketAddrV6}; +/// +/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); +/// +/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket)); +/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); +/// assert_eq!(socket.port(), 8080); +/// ``` +#[derive(Copy, Clone, Eq, PartialEq)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SocketAddrV6 { + ip: Ipv6Addr, + port: u16, + flowinfo: u32, + scope_id: u32, +} + +impl SocketAddr { + /// Creates a new socket address from an [IP address] and a port number. + /// + /// [IP address]: IpAddr + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[stable(feature = "ip_addr", since = "1.7.0")] + #[must_use] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn new(ip: IpAddr, port: u16) -> SocketAddr { + match ip { + IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)), + IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)), + } + } + + /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + /// ``` + #[must_use] + #[stable(feature = "ip_addr", since = "1.7.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn ip(&self) -> IpAddr { + match *self { + SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()), + SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()), + } + } + + /// Changes the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); + /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_ip(&mut self, new_ip: IpAddr) { + // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away. + match (self, new_ip) { + (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip), + (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip), + (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()), + } + } + + /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn port(&self) -> u16 { + match *self { + SocketAddr::V4(ref a) => a.port(), + SocketAddr::V6(ref a) => a.port(), + } + } + + /// Changes the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// socket.set_port(1025); + /// assert_eq!(socket.port(), 1025); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_port(&mut self, new_port: u16) { + match *self { + SocketAddr::V4(ref mut a) => a.set_port(new_port), + SocketAddr::V6(ref mut a) => a.set_port(new_port), + } + } + + /// Returns [`true`] if the [IP address] in this `SocketAddr` is an + /// [`IPv4` address], and [`false`] otherwise. + /// + /// [IP address]: IpAddr + /// [`IPv4` address]: IpAddr::V4 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); + /// assert_eq!(socket.is_ipv4(), true); + /// assert_eq!(socket.is_ipv6(), false); + /// ``` + #[must_use] + #[stable(feature = "sockaddr_checker", since = "1.16.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn is_ipv4(&self) -> bool { + matches!(*self, SocketAddr::V4(_)) + } + + /// Returns [`true`] if the [IP address] in this `SocketAddr` is an + /// [`IPv6` address], and [`false`] otherwise. + /// + /// [IP address]: IpAddr + /// [`IPv6` address]: IpAddr::V6 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv6Addr, SocketAddr}; + /// + /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080); + /// assert_eq!(socket.is_ipv4(), false); + /// assert_eq!(socket.is_ipv6(), true); + /// ``` + #[must_use] + #[stable(feature = "sockaddr_checker", since = "1.16.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn is_ipv6(&self) -> bool { + matches!(*self, SocketAddr::V6(_)) + } +} + +impl SocketAddrV4 { + /// Creates a new socket address from an [`IPv4` address] and a port number. + /// + /// [`IPv4` address]: Ipv4Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 { + SocketAddrV4 { ip, port } + } + + /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn ip(&self) -> &Ipv4Addr { + &self.ip + } + + /// Changes the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1)); + /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1)); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_ip(&mut self, new_ip: Ipv4Addr) { + self.ip = new_ip; + } + + /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn port(&self) -> u16 { + self.port + } + + /// Changes the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// socket.set_port(4242); + /// assert_eq!(socket.port(), 4242); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_port(&mut self, new_port: u16) { + self.port = new_port; + } +} + +impl SocketAddrV6 { + /// Creates a new socket address from an [`IPv6` address], a 16-bit port number, + /// and the `flowinfo` and `scope_id` fields. + /// + /// For more information on the meaning and layout of the `flowinfo` and `scope_id` + /// parameters, see [IETF RFC 2553, Section 3.3]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// [`IPv6` address]: Ipv6Addr + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 { + SocketAddrV6 { ip, port, flowinfo, scope_id } + } + + /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn ip(&self) -> &Ipv6Addr { + &self.ip + } + + /// Changes the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); + /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_ip(&mut self, new_ip: Ipv6Addr) { + self.ip = new_ip; + } + + /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// assert_eq!(socket.port(), 8080); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn port(&self) -> u16 { + self.port + } + + /// Changes the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// socket.set_port(4242); + /// assert_eq!(socket.port(), 4242); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_port(&mut self, new_port: u16) { + self.port = new_port; + } + + /// Returns the flow information associated with this address. + /// + /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`, + /// as specified in [IETF RFC 2553, Section 3.3]. + /// It combines information about the flow label and the traffic class as specified + /// in [IETF RFC 2460], respectively [Section 6] and [Section 7]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 + /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6 + /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7 + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); + /// assert_eq!(socket.flowinfo(), 10); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn flowinfo(&self) -> u32 { + self.flowinfo + } + + /// Changes the flow information associated with this socket address. + /// + /// See [`SocketAddrV6::flowinfo`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); + /// socket.set_flowinfo(56); + /// assert_eq!(socket.flowinfo(), 56); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_flowinfo(&mut self, new_flowinfo: u32) { + self.flowinfo = new_flowinfo; + } + + /// Returns the scope ID associated with this address. + /// + /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`, + /// as specified in [IETF RFC 2553, Section 3.3]. + /// + /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); + /// assert_eq!(socket.scope_id(), 78); + /// ``` + #[must_use] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] + pub const fn scope_id(&self) -> u32 { + self.scope_id + } + + /// Changes the scope ID associated with this socket address. + /// + /// See [`SocketAddrV6::scope_id`]'s documentation for more details. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); + /// socket.set_scope_id(42); + /// assert_eq!(socket.scope_id(), 42); + /// ``` + #[stable(feature = "sockaddr_setters", since = "1.9.0")] + pub fn set_scope_id(&mut self, new_scope_id: u32) { + self.scope_id = new_scope_id; + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From<SocketAddrV4> for SocketAddr { + /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. + fn from(sock4: SocketAddrV4) -> SocketAddr { + SocketAddr::V4(sock4) + } +} + +#[stable(feature = "ip_from_ip", since = "1.16.0")] +impl From<SocketAddrV6> for SocketAddr { + /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. + fn from(sock6: SocketAddrV6) -> SocketAddr { + SocketAddr::V6(sock6) + } +} + +#[stable(feature = "addr_from_into_ip", since = "1.17.0")] +impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr { + /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`]. + /// + /// This conversion creates a [`SocketAddr::V4`] for an [`IpAddr::V4`] + /// and creates a [`SocketAddr::V6`] for an [`IpAddr::V6`]. + /// + /// `u16` is treated as port of the newly created [`SocketAddr`]. + fn from(pieces: (I, u16)) -> SocketAddr { + SocketAddr::new(pieces.0.into(), pieces.1) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + SocketAddr::V4(ref a) => a.fmt(f), + SocketAddr::V6(ref a) => a.fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddrV4 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If there are no alignment requirements, write the socket address directly to `f`. + // Otherwise, write it to a local buffer and then use `f.pad`. + if f.precision().is_none() && f.width().is_none() { + write!(f, "{}:{}", self.ip(), self.port()) + } else { + const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536"; + + let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new(); + // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail. + write!(buf, "{}:{}", self.ip(), self.port()).unwrap(); + + f.pad(buf.as_str()) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SocketAddrV4 { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SocketAddrV6 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // If there are no alignment requirements, write the socket address directly to `f`. + // Otherwise, write it to a local buffer and then use `f.pad`. + if f.precision().is_none() && f.width().is_none() { + match self.scope_id() { + 0 => write!(f, "[{}]:{}", self.ip(), self.port()), + scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()), + } + } else { + const LONGEST_IPV6_SOCKET_ADDR: &str = + "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536"; + + let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new(); + match self.scope_id() { + 0 => write!(buf, "[{}]:{}", self.ip(), self.port()), + scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()), + } + // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail. + .unwrap(); + + f.pad(buf.as_str()) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SocketAddrV6 { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, fmt) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl PartialOrd for SocketAddrV4 { + fn partial_cmp(&self, other: &SocketAddrV4) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl PartialOrd for SocketAddrV6 { + fn partial_cmp(&self, other: &SocketAddrV6) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl Ord for SocketAddrV4 { + fn cmp(&self, other: &SocketAddrV4) -> Ordering { + self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) + } +} + +#[stable(feature = "socketaddr_ordering", since = "1.45.0")] +impl Ord for SocketAddrV6 { + fn cmp(&self, other: &SocketAddrV6) -> Ordering { + self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for SocketAddrV4 { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + (self.port, self.ip).hash(s) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for SocketAddrV6 { + fn hash<H: hash::Hasher>(&self, s: &mut H) { + (self.port, &self.ip, self.flowinfo, self.scope_id).hash(s) + } +} diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 5d5e9559034..994c08d1fb5 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -553,6 +553,7 @@ use crate::pin::Pin; use crate::{ cmp, convert, hint, mem, ops::{self, ControlFlow, Deref, DerefMut}, + slice, }; /// The `Option` type. See [the module level documentation](self) for more. @@ -734,6 +735,124 @@ impl<T> Option<T> { } } + const fn get_some_offset() -> isize { + if mem::size_of::<Option<T>>() == mem::size_of::<T>() { + // niche optimization means the `T` is always stored at the same position as the Option. + 0 + } else { + assert!(mem::size_of::<Option<T>>() == mem::size_of::<Option<mem::MaybeUninit<T>>>()); + let some_uninit = Some(mem::MaybeUninit::<T>::uninit()); + // SAFETY: This gets the byte offset of the `Some(_)` value following the fact that + // niche optimization is not active, and thus Option<T> and Option<MaybeUninit<t>> share + // the same layout. + unsafe { + (some_uninit.as_ref().unwrap() as *const mem::MaybeUninit<T>) + .byte_offset_from(&some_uninit as *const Option<mem::MaybeUninit<T>>) + } + } + } + + /// Returns a slice of the contained value, if any. If this is `None`, an + /// empty slice is returned. This can be useful to have a single type of + /// iterator over an `Option` or slice. + /// + /// Note: Should you have an `Option<&T>` and wish to get a slice of `T`, + /// you can unpack it via `opt.map_or(&[], std::slice::from_ref)`. + /// + /// # Examples + /// + /// ```rust + /// #![feature(option_as_slice)] + /// + /// assert_eq!( + /// [Some(1234).as_slice(), None.as_slice()], + /// [&[1234][..], &[][..]], + /// ); + /// ``` + /// + /// The inverse of this function is (discounting + /// borrowing) [`[_]::first`](slice::first): + /// + /// ```rust + /// #![feature(option_as_slice)] + /// + /// for i in [Some(1234_u16), None] { + /// assert_eq!(i.as_ref(), i.as_slice().first()); + /// } + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "option_as_slice", issue = "108545")] + pub fn as_slice(&self) -> &[T] { + // SAFETY: This is sound as long as `get_some_offset` returns the + // correct offset. Though in the `None` case, the slice may be located + // at a pointer pointing into padding, the fact that the slice is + // empty, and the padding is at a properly aligned position for a + // value of that type makes it sound. + unsafe { + slice::from_raw_parts( + (self as *const Option<T>).wrapping_byte_offset(Self::get_some_offset()) + as *const T, + self.is_some() as usize, + ) + } + } + + /// Returns a mutable slice of the contained value, if any. If this is + /// `None`, an empty slice is returned. This can be useful to have a + /// single type of iterator over an `Option` or slice. + /// + /// Note: Should you have an `Option<&mut T>` instead of a + /// `&mut Option<T>`, which this method takes, you can obtain a mutable + /// slice via `opt.map_or(&mut [], std::slice::from_mut)`. + /// + /// # Examples + /// + /// ```rust + /// #![feature(option_as_slice)] + /// + /// assert_eq!( + /// [Some(1234).as_mut_slice(), None.as_mut_slice()], + /// [&mut [1234][..], &mut [][..]], + /// ); + /// ``` + /// + /// The result is a mutable slice of zero or one items that points into + /// our original `Option`: + /// + /// ```rust + /// #![feature(option_as_slice)] + /// + /// let mut x = Some(1234); + /// x.as_mut_slice()[0] += 1; + /// assert_eq!(x, Some(1235)); + /// ``` + /// + /// The inverse of this method (discounting borrowing) + /// is [`[_]::first_mut`](slice::first_mut): + /// + /// ```rust + /// #![feature(option_as_slice)] + /// + /// assert_eq!(Some(123).as_mut_slice().first_mut(), Some(&mut 123)) + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "option_as_slice", issue = "108545")] + pub fn as_mut_slice(&mut self) -> &mut [T] { + // SAFETY: This is sound as long as `get_some_offset` returns the + // correct offset. Though in the `None` case, the slice may be located + // at a pointer pointing into padding, the fact that the slice is + // empty, and the padding is at a properly aligned position for a + // value of that type makes it sound. + unsafe { + slice::from_raw_parts_mut( + (self as *mut Option<T>).wrapping_byte_offset(Self::get_some_offset()) as *mut T, + self.is_some() as usize, + ) + } + } + ///////////////////////////////////////////////////////////////////////// // Getting to contained values ///////////////////////////////////////////////////////////////////////// diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index d6e9da187e8..6f78811a186 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -587,8 +587,10 @@ mod prim_pointer {} /// There are two syntactic forms for creating an array: /// /// * A list with each element, i.e., `[x, y, z]`. -/// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`. -/// The type of `x` must be [`Copy`]. +/// * A repeat expression `[expr; N]` where `N` is how many times to repeat `expr` in the array. `expr` must either be: +/// +/// * A value of a type implementing the [`Copy`] trait +/// * A `const` value /// /// Note that `[expr; 0]` is allowed, and produces an empty array. /// This will still evaluate `expr`, however, and immediately drop the resulting value, so diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 2123147c7e4..efe6d4183e3 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -41,7 +41,7 @@ impl Alignment { /// Returns the alignment for a type. /// /// This provides the same numerical value as [`mem::align_of`], - /// but in an `Alignment` instead of a `usize. + /// but in an `Alignment` instead of a `usize`. #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] pub const fn of<T>() -> Self { diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 5e1b218e507..7601dd3c756 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -1,6 +1,6 @@ //! Comparison traits for `[T]`. -use crate::cmp::{self, Ordering}; +use crate::cmp::{self, BytewiseEq, Ordering}; use crate::ffi; use crate::mem; @@ -77,7 +77,7 @@ where // Use memcmp for bytewise equality when the types allow impl<A, B> SlicePartialEq<B> for [A] where - A: BytewiseEquality<B>, + A: BytewiseEq<B>, { fn equal(&self, other: &[B]) -> bool { if self.len() != other.len() { @@ -203,29 +203,6 @@ impl SliceOrd for u8 { } } -// Hack to allow specializing on `Eq` even though `Eq` has a method. -#[rustc_unsafe_specialization_marker] -trait MarkerEq<T>: PartialEq<T> {} - -impl<T: Eq> MarkerEq<T> for T {} - -#[doc(hidden)] -/// Trait implemented for types that can be compared for equality using -/// their bytewise representation -#[rustc_specialization_trait] -trait BytewiseEquality<T>: MarkerEq<T> + Copy {} - -macro_rules! impl_marker_for { - ($traitname:ident, $($ty:ty)*) => { - $( - impl $traitname<$ty> for $ty { } - )* - } -} - -impl_marker_for!(BytewiseEquality, - u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize char bool); - pub(super) trait SliceContains: Sized { fn slice_contains(&self, x: &[Self]) -> bool; } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index e8374784501..1cd86b445b0 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2955,7 +2955,7 @@ impl<T> [T] { // This operation is still `O(n)`. // // Example: We start in this state, where `r` represents "next - // read" and `w` represents "next_write`. + // read" and `w` represents "next_write". // // r // +---+---+---+---+---+---+ diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 7b8062c431e..2333f60a888 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -317,7 +317,7 @@ where // 1. `block` - Number of elements in the block. // 2. `start` - Start pointer into the `offsets` array. // 3. `end` - End pointer into the `offsets` array. - // 4. `offsets - Indices of out-of-order elements within the block. + // 4. `offsets` - Indices of out-of-order elements within the block. // The current block on the left side (from `l` to `l.add(block_l)`). let mut l = v.as_mut_ptr(); @@ -327,7 +327,7 @@ where let mut offsets_l = [MaybeUninit::<u8>::uninit(); BLOCK]; // The current block on the right side (from `r.sub(block_r)` to `r`). - // SAFETY: The documentation for .add() specifically mention that `vec.as_ptr().add(vec.len())` is always safe` + // SAFETY: The documentation for .add() specifically mention that `vec.as_ptr().add(vec.len())` is always safe let mut r = unsafe { l.add(v.len()) }; let mut block_r = BLOCK; let mut start_r = ptr::null_mut(); diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index d3ed811b157..68f62ce8be5 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -28,10 +28,6 @@ impl PartialEq for str { fn eq(&self, other: &str) -> bool { self.as_bytes() == other.as_bytes() } - #[inline] - fn ne(&self, other: &str) -> bool { - !(*self).eq(other) - } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 00bcaf3e18c..040a59184a6 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -305,6 +305,50 @@ impl AtomicBool { AtomicBool { v: UnsafeCell::new(v as u8) } } + /// Creates a new `AtomicBool` from a pointer. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_ptr, pointer_is_aligned)] + /// use std::sync::atomic::{self, AtomicBool}; + /// use std::mem::align_of; + /// + /// // Get a pointer to an allocated value + /// let ptr: *mut bool = Box::into_raw(Box::new(false)); + /// + /// assert!(ptr.is_aligned_to(align_of::<AtomicBool>())); + /// + /// { + /// // Create an atomic view of the allocated value + /// let atomic = unsafe { AtomicBool::from_ptr(ptr) }; + /// + /// // Use `atomic` for atomic operations, possibly share it with other threads + /// atomic.store(true, atomic::Ordering::Relaxed); + /// } + /// + /// // It's ok to non-atomically access the value behind `ptr`, + /// // since the reference to the atomic ended its lifetime in the block above + /// assert_eq!(unsafe { *ptr }, true); + /// + /// // Deallocate the value + /// unsafe { drop(Box::from_raw(ptr)) } + /// ``` + /// + /// # Safety + /// + /// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can be bigger than `align_of::<bool>()`). + /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. + /// * The value behind `ptr` must not be accessed through non-atomic operations for the whole lifetime `'a`. + /// + /// [valid]: crate::ptr#safety + #[unstable(feature = "atomic_from_ptr", issue = "108652")] + #[rustc_const_unstable(feature = "atomic_from_ptr", issue = "108652")] + pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool { + // SAFETY: guaranteed by the caller + unsafe { &*ptr.cast() } + } + /// Returns a mutable reference to the underlying [`bool`]. /// /// This is safe because the mutable reference guarantees that no other threads are @@ -1017,6 +1061,50 @@ impl<T> AtomicPtr<T> { AtomicPtr { p: UnsafeCell::new(p) } } + /// Creates a new `AtomicPtr` from a pointer. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_ptr, pointer_is_aligned)] + /// use std::sync::atomic::{self, AtomicPtr}; + /// use std::mem::align_of; + /// + /// // Get a pointer to an allocated value + /// let ptr: *mut *mut u8 = Box::into_raw(Box::new(std::ptr::null_mut())); + /// + /// assert!(ptr.is_aligned_to(align_of::<AtomicPtr<u8>>())); + /// + /// { + /// // Create an atomic view of the allocated value + /// let atomic = unsafe { AtomicPtr::from_ptr(ptr) }; + /// + /// // Use `atomic` for atomic operations, possibly share it with other threads + /// atomic.store(std::ptr::NonNull::dangling().as_ptr(), atomic::Ordering::Relaxed); + /// } + /// + /// // It's ok to non-atomically access the value behind `ptr`, + /// // since the reference to the atomic ended its lifetime in the block above + /// assert!(!unsafe { *ptr }.is_null()); + /// + /// // Deallocate the value + /// unsafe { drop(Box::from_raw(ptr)) } + /// ``` + /// + /// # Safety + /// + /// * `ptr` must be aligned to `align_of::<AtomicPtr<T>>()` (note that on some platforms this can be bigger than `align_of::<*mut T>()`). + /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. + /// * The value behind `ptr` must not be accessed through non-atomic operations for the whole lifetime `'a`. + /// + /// [valid]: crate::ptr#safety + #[unstable(feature = "atomic_from_ptr", issue = "108652")] + #[rustc_const_unstable(feature = "atomic_from_ptr", issue = "108652")] + pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr<T> { + // SAFETY: guaranteed by the caller + unsafe { &*ptr.cast() } + } + /// Returns a mutable reference to the underlying pointer. /// /// This is safe because the mutable reference guarantees that no other threads are @@ -1958,6 +2046,53 @@ macro_rules! atomic_int { Self {v: UnsafeCell::new(v)} } + /// Creates a new reference to an atomic integer from a pointer. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_ptr, pointer_is_aligned)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{self, ", stringify!($atomic_type), "};")] + /// use std::mem::align_of; + /// + /// // Get a pointer to an allocated value + #[doc = concat!("let ptr: *mut ", stringify!($int_type), " = Box::into_raw(Box::new(0));")] + /// + #[doc = concat!("assert!(ptr.is_aligned_to(align_of::<", stringify!($atomic_type), ">()));")] + /// + /// { + /// // Create an atomic view of the allocated value + // SAFETY: this is a doc comment, tidy, it can't hurt you (also guaranteed by the construction of `ptr` and the assert above) + #[doc = concat!(" let atomic = unsafe {", stringify!($atomic_type), "::from_ptr(ptr) };")] + /// + /// // Use `atomic` for atomic operations, possibly share it with other threads + /// atomic.store(1, atomic::Ordering::Relaxed); + /// } + /// + /// // It's ok to non-atomically access the value behind `ptr`, + /// // since the reference to the atomic ended its lifetime in the block above + /// assert_eq!(unsafe { *ptr }, 1); + /// + /// // Deallocate the value + /// unsafe { drop(Box::from_raw(ptr)) } + /// ``` + /// + /// # Safety + /// + /// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can be bigger than `align_of::<bool>()`). + #[doc = concat!(" * `ptr` must be aligned to `align_of::<", stringify!($atomic_type), ">()` (note that on some platforms this can be bigger than `align_of::<", stringify!($int_type), ">()`).")] + /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. + /// * The value behind `ptr` must not be accessed through non-atomic operations for the whole lifetime `'a`. + /// + /// [valid]: crate::ptr#safety + #[unstable(feature = "atomic_from_ptr", issue = "108652")] + #[rustc_const_unstable(feature = "atomic_from_ptr", issue = "108652")] + pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type { + // SAFETY: guaranteed by the caller + unsafe { &*ptr.cast() } + } + + /// Returns a mutable reference to the underlying integer. /// /// This is safe because the mutable reference guarantees that no other threads are diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 25b61c0e666..af5bf441bb2 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -45,6 +45,7 @@ impl<T> Poll<T> { /// assert_eq!(poll_some_len, Poll::Ready(13)); /// ``` #[stable(feature = "futures_api", since = "1.36.0")] + #[inline] pub fn map<U, F>(self, f: F) -> Poll<U> where F: FnOnce(T) -> U, @@ -144,6 +145,7 @@ impl<T, E> Poll<Result<T, E>> { /// assert_eq!(squared, Poll::Ready(Ok(144))); /// ``` #[stable(feature = "futures_api", since = "1.36.0")] + #[inline] pub fn map_ok<U, F>(self, f: F) -> Poll<Result<U, E>> where F: FnOnce(T) -> U, @@ -171,6 +173,7 @@ impl<T, E> Poll<Result<T, E>> { /// assert_eq!(res, Poll::Ready(Err(0))); /// ``` #[stable(feature = "futures_api", since = "1.36.0")] + #[inline] pub fn map_err<U, F>(self, f: F) -> Poll<Result<T, U>> where F: FnOnce(E) -> U, @@ -199,6 +202,7 @@ impl<T, E> Poll<Option<Result<T, E>>> { /// assert_eq!(squared, Poll::Ready(Some(Ok(144)))); /// ``` #[stable(feature = "poll_map", since = "1.51.0")] + #[inline] pub fn map_ok<U, F>(self, f: F) -> Poll<Option<Result<U, E>>> where F: FnOnce(T) -> U, @@ -228,6 +232,7 @@ impl<T, E> Poll<Option<Result<T, E>>> { /// assert_eq!(res, Poll::Ready(Some(Err(0)))); /// ``` #[stable(feature = "poll_map", since = "1.51.0")] + #[inline] pub fn map_err<U, F>(self, f: F) -> Poll<Option<Result<T, U>>> where F: FnOnce(E) -> U, diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index ffd5f3857ae..ca3463aa7f7 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -24,7 +24,7 @@ mod zip; use core::cell::Cell; -/// An iterator that panics whenever `next` or next_back` is called +/// An iterator that panics whenever `next` or `next_back` is called /// after `None` has already been returned. This does not violate /// `Iterator`'s contract. Used to test that iterator adapters don't /// poll their inner iterators after exhausting them. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 42a26ae1675..ccb7be68eb1 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -66,6 +66,8 @@ #![feature(try_trait_v2)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] +#![feature(ip)] +#![feature(ip_in_core)] #![feature(iter_advance_by)] #![feature(iter_array_chunks)] #![feature(iter_collect_into)] @@ -77,6 +79,9 @@ #![feature(iter_repeat_n)] #![feature(iterator_try_collect)] #![feature(iterator_try_reduce)] +#![feature(const_ip)] +#![feature(const_ipv4)] +#![feature(const_ipv6)] #![feature(const_mut_refs)] #![feature(const_pin)] #![feature(const_waker)] @@ -135,6 +140,7 @@ mod lazy; mod macros; mod manually_drop; mod mem; +mod net; mod nonzero; mod num; mod ops; diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs new file mode 100644 index 00000000000..5a6ac08c088 --- /dev/null +++ b/library/core/tests/net/ip_addr.rs @@ -0,0 +1,1035 @@ +use super::{sa4, sa6}; +use core::net::{ + IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope, SocketAddr, SocketAddrV4, SocketAddrV6, +}; +use core::str::FromStr; + +#[test] +fn test_from_str_ipv4() { + assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse()); + assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse()); + assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse()); + + // out of range + let none: Option<Ipv4Addr> = "256.0.0.1".parse().ok(); + assert_eq!(None, none); + // too short + let none: Option<Ipv4Addr> = "255.0.0".parse().ok(); + assert_eq!(None, none); + // too long + let none: Option<Ipv4Addr> = "255.0.0.1.2".parse().ok(); + assert_eq!(None, none); + // no number between dots + let none: Option<Ipv4Addr> = "255.0..1".parse().ok(); + assert_eq!(None, none); + // octal + let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok(); + assert_eq!(None, none); + // octal zero + let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok(); + assert_eq!(None, none); + let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_ipv6() { + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse()); + + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse()); + + assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse()); + + // too long group + let none: Option<Ipv6Addr> = "::00000".parse().ok(); + assert_eq!(None, none); + // too short + let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7".parse().ok(); + assert_eq!(None, none); + // too long + let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:8:9".parse().ok(); + assert_eq!(None, none); + // triple colon + let none: Option<Ipv6Addr> = "1:2:::6:7:8".parse().ok(); + assert_eq!(None, none); + // two double colons + let none: Option<Ipv6Addr> = "1:2::6::8".parse().ok(); + assert_eq!(None, none); + // `::` indicating zero groups of zeros + let none: Option<Ipv6Addr> = "1:2:3:4::5:6:7:8".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_ipv4_in_ipv6() { + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse()); + assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse()); + assert_eq!( + Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), + "64:ff9b::192.0.2.33".parse() + ); + assert_eq!( + Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), + "2001:db8:122:c000:2:2100:192.0.2.33".parse() + ); + + // colon after v4 + let none: Option<Ipv4Addr> = "::127.0.0.1:".parse().ok(); + assert_eq!(None, none); + // not enough groups + let none: Option<Ipv6Addr> = "1:2:3:4:5:127.0.0.1".parse().ok(); + assert_eq!(None, none); + // too many groups + let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:127.0.0.1".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn test_from_str_socket_addr() { + assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); + assert_eq!(Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); + assert_eq!( + Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)), + "[2a02:6b8:0:1::1]:53".parse() + ); + assert_eq!( + Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)), + "[2a02:6b8:0:1::1]:53".parse() + ); + assert_eq!(Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), "[::127.0.0.1]:22".parse()); + assert_eq!( + Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)), + "[::127.0.0.1]:22".parse() + ); + + // without port + let none: Option<SocketAddr> = "127.0.0.1".parse().ok(); + assert_eq!(None, none); + // without port + let none: Option<SocketAddr> = "127.0.0.1:".parse().ok(); + assert_eq!(None, none); + // wrong brackets around v4 + let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok(); + assert_eq!(None, none); + // port out of range + let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok(); + assert_eq!(None, none); +} + +#[test] +fn ipv4_addr_to_string() { + assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1"); + // Short address + assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1"); + // Long address + assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127"); + + // Test padding + assert_eq!(format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 "); + assert_eq!(format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1"); +} + +#[test] +fn ipv6_addr_to_string() { + // ipv4-mapped address + let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); + assert_eq!(a1.to_string(), "::ffff:192.0.2.128"); + + // ipv4-compatible address + let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280); + assert_eq!(a1.to_string(), "::192.0.2.128"); + + // v6 address with no zero segments + assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f"); + + // longest possible IPv6 length + assert_eq!( + Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888).to_string(), + "1111:2222:3333:4444:5555:6666:7777:8888" + ); + // padding + assert_eq!(format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 "); + assert_eq!(format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8"); + + // reduce a single run of zeros + assert_eq!( + "ae::ffff:102:304", + Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string() + ); + + // don't reduce just a single zero segment + assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string()); + + // 'any' address + assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string()); + + // loopback address + assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string()); + + // ends in zeros + assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string()); + + // two runs of zeros, second one is longer + assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string()); + + // two runs of zeros, equal length + assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string()); + + // don't prefix `0x` to each segment in `dbg!`. + assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8))); +} + +#[test] +fn ipv4_to_ipv6() { + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678), + Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped() + ); + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678), + Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible() + ); +} + +#[test] +fn ipv6_to_ipv4_mapped() { + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) + ); + assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None); +} + +#[test] +fn ipv6_to_ipv4() { + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) + ); + assert_eq!( + Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), + Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) + ); + assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None); +} + +#[test] +fn ip_properties() { + macro_rules! ip { + ($s:expr) => { + IpAddr::from_str($s).unwrap() + }; + } + + macro_rules! check { + ($s:expr) => { + check!($s, 0); + }; + + ($s:expr, $mask:expr) => {{ + let unspec: u8 = 1 << 0; + let loopback: u8 = 1 << 1; + let global: u8 = 1 << 2; + let multicast: u8 = 1 << 3; + let doc: u8 = 1 << 4; + let benchmarking: u8 = 1 << 5; + + if ($mask & unspec) == unspec { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + + if ($mask & multicast) == multicast { + assert!(ip!($s).is_multicast()); + } else { + assert!(!ip!($s).is_multicast()); + } + + if ($mask & doc) == doc { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } + }}; + } + + let unspec: u8 = 1 << 0; + let loopback: u8 = 1 << 1; + let global: u8 = 1 << 2; + let multicast: u8 = 1 << 3; + let doc: u8 = 1 << 4; + let benchmarking: u8 = 1 << 5; + + check!("0.0.0.0", unspec); + check!("0.0.0.1"); + check!("0.1.0.0"); + check!("10.9.8.7"); + check!("127.1.2.3", loopback); + check!("172.31.254.253"); + check!("169.254.253.242"); + check!("192.0.2.183", doc); + check!("192.1.2.183", global); + check!("192.168.254.253"); + check!("198.51.100.0", doc); + check!("203.0.113.0", doc); + check!("203.2.113.0", global); + check!("224.0.0.0", global | multicast); + check!("239.255.255.255", global | multicast); + check!("255.255.255.255"); + // make sure benchmarking addresses are not global + check!("198.18.0.0", benchmarking); + check!("198.18.54.2", benchmarking); + check!("198.19.255.255", benchmarking); + // make sure addresses reserved for protocol assignment are not global + check!("192.0.0.0"); + check!("192.0.0.255"); + check!("192.0.0.100"); + // make sure reserved addresses are not global + check!("240.0.0.0"); + check!("251.54.1.76"); + check!("254.255.255.255"); + // make sure shared addresses are not global + check!("100.64.0.0"); + check!("100.127.255.255"); + check!("100.100.100.0"); + + check!("::", unspec); + check!("::1", loopback); + check!("::0.0.0.2", global); + check!("1::", global); + check!("fc00::"); + check!("fdff:ffff::"); + check!("fe80:ffff::"); + check!("febf:ffff::"); + check!("fec0::", global); + check!("ff01::", global | multicast); + check!("ff02::", global | multicast); + check!("ff03::", global | multicast); + check!("ff04::", global | multicast); + check!("ff05::", global | multicast); + check!("ff08::", global | multicast); + check!("ff0e::", global | multicast); + check!("2001:db8:85a3::8a2e:370:7334", doc); + check!("2001:2::ac32:23ff:21", benchmarking); + check!("102:304:506:708:90a:b0c:d0e:f10", global); +} + +#[test] +fn ipv4_properties() { + macro_rules! ip { + ($s:expr) => { + Ipv4Addr::from_str($s).unwrap() + }; + } + + macro_rules! check { + ($s:expr) => { + check!($s, 0); + }; + + ($s:expr, $mask:expr) => {{ + let unspec: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let private: u16 = 1 << 2; + let link_local: u16 = 1 << 3; + let global: u16 = 1 << 4; + let multicast: u16 = 1 << 5; + let broadcast: u16 = 1 << 6; + let documentation: u16 = 1 << 7; + let benchmarking: u16 = 1 << 8; + let reserved: u16 = 1 << 10; + let shared: u16 = 1 << 11; + + if ($mask & unspec) == unspec { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + + if ($mask & private) == private { + assert!(ip!($s).is_private()); + } else { + assert!(!ip!($s).is_private()); + } + + if ($mask & link_local) == link_local { + assert!(ip!($s).is_link_local()); + } else { + assert!(!ip!($s).is_link_local()); + } + + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + + if ($mask & multicast) == multicast { + assert!(ip!($s).is_multicast()); + } else { + assert!(!ip!($s).is_multicast()); + } + + if ($mask & broadcast) == broadcast { + assert!(ip!($s).is_broadcast()); + } else { + assert!(!ip!($s).is_broadcast()); + } + + if ($mask & documentation) == documentation { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } + + if ($mask & reserved) == reserved { + assert!(ip!($s).is_reserved()); + } else { + assert!(!ip!($s).is_reserved()); + } + + if ($mask & shared) == shared { + assert!(ip!($s).is_shared()); + } else { + assert!(!ip!($s).is_shared()); + } + }}; + } + + let unspec: u16 = 1 << 0; + let loopback: u16 = 1 << 1; + let private: u16 = 1 << 2; + let link_local: u16 = 1 << 3; + let global: u16 = 1 << 4; + let multicast: u16 = 1 << 5; + let broadcast: u16 = 1 << 6; + let documentation: u16 = 1 << 7; + let benchmarking: u16 = 1 << 8; + let reserved: u16 = 1 << 10; + let shared: u16 = 1 << 11; + + check!("0.0.0.0", unspec); + check!("0.0.0.1"); + check!("0.1.0.0"); + check!("10.9.8.7", private); + check!("127.1.2.3", loopback); + check!("172.31.254.253", private); + check!("169.254.253.242", link_local); + check!("192.0.2.183", documentation); + check!("192.1.2.183", global); + check!("192.168.254.253", private); + check!("198.51.100.0", documentation); + check!("203.0.113.0", documentation); + check!("203.2.113.0", global); + check!("224.0.0.0", global | multicast); + check!("239.255.255.255", global | multicast); + check!("255.255.255.255", broadcast); + check!("198.18.0.0", benchmarking); + check!("198.18.54.2", benchmarking); + check!("198.19.255.255", benchmarking); + check!("192.0.0.0"); + check!("192.0.0.255"); + check!("192.0.0.100"); + check!("240.0.0.0", reserved); + check!("251.54.1.76", reserved); + check!("254.255.255.255", reserved); + check!("100.64.0.0", shared); + check!("100.127.255.255", shared); + check!("100.100.100.0", shared); +} + +#[test] +fn ipv6_properties() { + macro_rules! ip { + ($s:expr) => { + Ipv6Addr::from_str($s).unwrap() + }; + } + + macro_rules! check { + ($s:expr, &[$($octet:expr),*], $mask:expr) => { + assert_eq!($s, ip!($s).to_string()); + let octets = &[$($octet),*]; + assert_eq!(&ip!($s).octets(), octets); + assert_eq!(Ipv6Addr::from(*octets), ip!($s)); + + let unspecified: u32 = 1 << 0; + let loopback: u32 = 1 << 1; + let unique_local: u32 = 1 << 2; + let global: u32 = 1 << 3; + let unicast_link_local: u32 = 1 << 4; + let unicast_global: u32 = 1 << 7; + let documentation: u32 = 1 << 8; + let benchmarking: u32 = 1 << 16; + let multicast_interface_local: u32 = 1 << 9; + let multicast_link_local: u32 = 1 << 10; + let multicast_realm_local: u32 = 1 << 11; + let multicast_admin_local: u32 = 1 << 12; + let multicast_site_local: u32 = 1 << 13; + let multicast_organization_local: u32 = 1 << 14; + let multicast_global: u32 = 1 << 15; + let multicast: u32 = multicast_interface_local + | multicast_admin_local + | multicast_global + | multicast_link_local + | multicast_realm_local + | multicast_site_local + | multicast_organization_local; + + if ($mask & unspecified) == unspecified { + assert!(ip!($s).is_unspecified()); + } else { + assert!(!ip!($s).is_unspecified()); + } + if ($mask & loopback) == loopback { + assert!(ip!($s).is_loopback()); + } else { + assert!(!ip!($s).is_loopback()); + } + if ($mask & unique_local) == unique_local { + assert!(ip!($s).is_unique_local()); + } else { + assert!(!ip!($s).is_unique_local()); + } + if ($mask & global) == global { + assert!(ip!($s).is_global()); + } else { + assert!(!ip!($s).is_global()); + } + if ($mask & unicast_link_local) == unicast_link_local { + assert!(ip!($s).is_unicast_link_local()); + } else { + assert!(!ip!($s).is_unicast_link_local()); + } + if ($mask & unicast_global) == unicast_global { + assert!(ip!($s).is_unicast_global()); + } else { + assert!(!ip!($s).is_unicast_global()); + } + if ($mask & documentation) == documentation { + assert!(ip!($s).is_documentation()); + } else { + assert!(!ip!($s).is_documentation()); + } + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } + if ($mask & multicast) != 0 { + assert!(ip!($s).multicast_scope().is_some()); + assert!(ip!($s).is_multicast()); + } else { + assert!(ip!($s).multicast_scope().is_none()); + assert!(!ip!($s).is_multicast()); + } + if ($mask & multicast_interface_local) == multicast_interface_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::InterfaceLocal); + } + if ($mask & multicast_link_local) == multicast_link_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::LinkLocal); + } + if ($mask & multicast_realm_local) == multicast_realm_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::RealmLocal); + } + if ($mask & multicast_admin_local) == multicast_admin_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::AdminLocal); + } + if ($mask & multicast_site_local) == multicast_site_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::SiteLocal); + } + if ($mask & multicast_organization_local) == multicast_organization_local { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::OrganizationLocal); + } + if ($mask & multicast_global) == multicast_global { + assert_eq!(ip!($s).multicast_scope().unwrap(), + Ipv6MulticastScope::Global); + } + } + } + + let unspecified: u32 = 1 << 0; + let loopback: u32 = 1 << 1; + let unique_local: u32 = 1 << 2; + let global: u32 = 1 << 3; + let unicast_link_local: u32 = 1 << 4; + let unicast_global: u32 = 1 << 7; + let documentation: u32 = 1 << 8; + let benchmarking: u32 = 1 << 16; + let multicast_interface_local: u32 = 1 << 9; + let multicast_link_local: u32 = 1 << 10; + let multicast_realm_local: u32 = 1 << 11; + let multicast_admin_local: u32 = 1 << 12; + let multicast_site_local: u32 = 1 << 13; + let multicast_organization_local: u32 = 1 << 14; + let multicast_global: u32 = 1 << 15; + + check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified); + + check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback); + + check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global); + + check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); + + check!( + "::ffff:127.0.0.1", + &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1], + unicast_global + ); + + check!( + "64:ff9b:1::", + &[0, 0x64, 0xff, 0x9b, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_global + ); + + check!("100::", &[0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + + check!("2001::", &[0x20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + + check!( + "2001:1::1", + &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + global | unicast_global + ); + + check!( + "2001:1::2", + &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], + global | unicast_global + ); + + check!( + "2001:3::", + &[0x20, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + global | unicast_global + ); + + check!( + "2001:4:112::", + &[0x20, 1, 0, 4, 1, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + global | unicast_global + ); + + check!( + "2001:20::", + &[0x20, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + global | unicast_global + ); + + check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); + + check!( + "2001:200::", + &[0x20, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + global | unicast_global + ); + + check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local); + + check!( + "fdff:ffff::", + &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unique_local + ); + + check!( + "fe80:ffff::", + &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local + ); + + check!("fe80::", &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local); + + check!( + "febf:ffff::", + &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local + ); + + check!("febf::", &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local); + + check!( + "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + &[ + 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + ], + unicast_link_local + ); + + check!( + "fe80::ffff:ffff:ffff:ffff", + &[ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + ], + unicast_link_local + ); + + check!( + "fe80:0:0:1::", + &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_link_local + ); + + check!( + "fec0::", + &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + unicast_global | global + ); + + check!( + "ff01::", + &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_interface_local | global + ); + + check!( + "ff02::", + &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_link_local | global + ); + + check!( + "ff03::", + &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_realm_local | global + ); + + check!( + "ff04::", + &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_admin_local | global + ); + + check!( + "ff05::", + &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_site_local | global + ); + + check!( + "ff08::", + &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_organization_local | global + ); + + check!( + "ff0e::", + &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + multicast_global | global + ); + + check!( + "2001:db8:85a3::8a2e:370:7334", + &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34], + documentation + ); + + check!( + "2001:2::ac32:23ff:21", + &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], + benchmarking + ); + + check!( + "102:304:506:708:90a:b0c:d0e:f10", + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + global | unicast_global + ); +} + +#[test] +fn test_ipv4_to_int() { + let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); + assert_eq!(u32::from(a), 0x11223344); +} + +#[test] +fn test_int_to_ipv4() { + let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); + assert_eq!(Ipv4Addr::from(0x11223344), a); +} + +#[test] +fn test_ipv6_to_int() { + let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); + assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128); +} + +#[test] +fn test_int_to_ipv6() { + let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); + assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a); +} + +#[test] +fn ipv4_from_constructors() { + assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1)); + assert!(Ipv4Addr::LOCALHOST.is_loopback()); + assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0)); + assert!(Ipv4Addr::UNSPECIFIED.is_unspecified()); + assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255)); + assert!(Ipv4Addr::BROADCAST.is_broadcast()); +} + +#[test] +fn ipv6_from_constructors() { + assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + assert!(Ipv6Addr::LOCALHOST.is_loopback()); + assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); + assert!(Ipv6Addr::UNSPECIFIED.is_unspecified()); +} + +#[test] +fn ipv4_from_octets() { + assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)) +} + +#[test] +fn ipv6_from_segments() { + let from_u16s = + Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff); + assert_eq!(new, from_u16s); +} + +#[test] +fn ipv6_from_octets() { + let from_u16s = + Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); + let from_u8s = Ipv6Addr::from([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff, + ]); + assert_eq!(from_u16s, from_u8s); +} + +#[test] +fn cmp() { + let v41 = Ipv4Addr::new(100, 64, 3, 3); + let v42 = Ipv4Addr::new(192, 0, 2, 2); + let v61 = "2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap(); + let v62 = "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap(); + assert!(v41 < v42); + assert!(v61 < v62); + + assert_eq!(v41, IpAddr::V4(v41)); + assert_eq!(v61, IpAddr::V6(v61)); + assert!(v41 != IpAddr::V4(v42)); + assert!(v61 != IpAddr::V6(v62)); + + assert!(v41 < IpAddr::V4(v42)); + assert!(v61 < IpAddr::V6(v62)); + assert!(IpAddr::V4(v41) < v42); + assert!(IpAddr::V6(v61) < v62); + + assert!(v41 < IpAddr::V6(v61)); + assert!(IpAddr::V4(v41) < v61); +} + +#[test] +fn is_v4() { + let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3)); + assert!(ip.is_ipv4()); + assert!(!ip.is_ipv6()); +} + +#[test] +fn is_v6() { + let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678)); + assert!(!ip.is_ipv4()); + assert!(ip.is_ipv6()); +} + +#[test] +fn ipv4_const() { + // test that the methods of `Ipv4Addr` are usable in a const context + + const IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); + assert_eq!(IP_ADDRESS, Ipv4Addr::LOCALHOST); + + const OCTETS: [u8; 4] = IP_ADDRESS.octets(); + assert_eq!(OCTETS, [127, 0, 0, 1]); + + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); + assert!(!IS_UNSPECIFIED); + + const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); + assert!(IS_LOOPBACK); + + const IS_PRIVATE: bool = IP_ADDRESS.is_private(); + assert!(!IS_PRIVATE); + + const IS_LINK_LOCAL: bool = IP_ADDRESS.is_link_local(); + assert!(!IS_LINK_LOCAL); + + const IS_GLOBAL: bool = IP_ADDRESS.is_global(); + assert!(!IS_GLOBAL); + + const IS_SHARED: bool = IP_ADDRESS.is_shared(); + assert!(!IS_SHARED); + + const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); + assert!(!IS_BENCHMARKING); + + const IS_RESERVED: bool = IP_ADDRESS.is_reserved(); + assert!(!IS_RESERVED); + + const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); + assert!(!IS_MULTICAST); + + const IS_BROADCAST: bool = IP_ADDRESS.is_broadcast(); + assert!(!IS_BROADCAST); + + const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); + assert!(!IS_DOCUMENTATION); + + const IP_V6_COMPATIBLE: Ipv6Addr = IP_ADDRESS.to_ipv6_compatible(); + assert_eq!( + IP_V6_COMPATIBLE, + Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1]) + ); + + const IP_V6_MAPPED: Ipv6Addr = IP_ADDRESS.to_ipv6_mapped(); + assert_eq!( + IP_V6_MAPPED, + Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1]) + ); +} + +#[test] +fn ipv6_const() { + // test that the methods of `Ipv6Addr` are usable in a const context + + const IP_ADDRESS: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + assert_eq!(IP_ADDRESS, Ipv6Addr::LOCALHOST); + + const SEGMENTS: [u16; 8] = IP_ADDRESS.segments(); + assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]); + + const OCTETS: [u8; 16] = IP_ADDRESS.octets(); + assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); + assert!(!IS_UNSPECIFIED); + + const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); + assert!(IS_LOOPBACK); + + const IS_GLOBAL: bool = IP_ADDRESS.is_global(); + assert!(!IS_GLOBAL); + + const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local(); + assert!(!IS_UNIQUE_LOCAL); + + const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local(); + assert!(!IS_UNICAST_LINK_LOCAL); + + const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); + assert!(!IS_DOCUMENTATION); + + const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); + assert!(!IS_BENCHMARKING); + + const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global(); + assert!(!IS_UNICAST_GLOBAL); + + const MULTICAST_SCOPE: Option<Ipv6MulticastScope> = IP_ADDRESS.multicast_scope(); + assert_eq!(MULTICAST_SCOPE, None); + + const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); + assert!(!IS_MULTICAST); + + const IP_V4: Option<Ipv4Addr> = IP_ADDRESS.to_ipv4(); + assert_eq!(IP_V4.unwrap(), Ipv4Addr::new(0, 0, 0, 1)); +} + +#[test] +fn ip_const() { + // test that the methods of `IpAddr` are usable in a const context + + const IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); + + const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); + assert!(!IS_UNSPECIFIED); + + const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); + assert!(IS_LOOPBACK); + + const IS_GLOBAL: bool = IP_ADDRESS.is_global(); + assert!(!IS_GLOBAL); + + const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); + assert!(!IS_MULTICAST); + + const IS_IP_V4: bool = IP_ADDRESS.is_ipv4(); + assert!(IS_IP_V4); + + const IS_IP_V6: bool = IP_ADDRESS.is_ipv6(); + assert!(!IS_IP_V6); +} + +#[test] +fn structural_match() { + // test that all IP types can be structurally matched upon + + const IPV4: Ipv4Addr = Ipv4Addr::LOCALHOST; + match IPV4 { + Ipv4Addr::LOCALHOST => {} + _ => unreachable!(), + } + + const IPV6: Ipv6Addr = Ipv6Addr::LOCALHOST; + match IPV6 { + Ipv6Addr::LOCALHOST => {} + _ => unreachable!(), + } + + const IP: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); + match IP { + IpAddr::V4(Ipv4Addr::LOCALHOST) => {} + _ => unreachable!(), + } +} diff --git a/library/core/tests/net/mod.rs b/library/core/tests/net/mod.rs new file mode 100644 index 00000000000..8f17bbe5548 --- /dev/null +++ b/library/core/tests/net/mod.rs @@ -0,0 +1,13 @@ +use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; + +mod ip_addr; +mod parser; +mod socket_addr; + +pub fn sa4(a: Ipv4Addr, p: u16) -> SocketAddr { + SocketAddr::V4(SocketAddrV4::new(a, p)) +} + +pub fn sa6(a: Ipv6Addr, p: u16) -> SocketAddr { + SocketAddr::V6(SocketAddrV6::new(a, p, 0, 0)) +} diff --git a/library/std/src/net/parser/tests.rs b/library/core/tests/net/parser.rs index 6d2d48ecad0..36b87d7c1f5 100644 --- a/library/std/src/net/parser/tests.rs +++ b/library/core/tests/net/parser.rs @@ -1,6 +1,6 @@ // FIXME: These tests are all excellent candidates for AFL fuzz testing -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use crate::str::FromStr; +use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use core::str::FromStr; const PORT: u16 = 8080; const SCOPE_ID: u32 = 1337; diff --git a/library/core/tests/net/socket_addr.rs b/library/core/tests/net/socket_addr.rs new file mode 100644 index 00000000000..68c7cd94d32 --- /dev/null +++ b/library/core/tests/net/socket_addr.rs @@ -0,0 +1,233 @@ +use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; + +#[test] +fn ipv4_socket_addr_to_string() { + // Shortest possible IPv4 length. + assert_eq!(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0).to_string(), "0.0.0.0:0"); + + // Longest possible IPv4 length. + assert_eq!( + SocketAddrV4::new(Ipv4Addr::new(255, 255, 255, 255), u16::MAX).to_string(), + "255.255.255.255:65535" + ); + + // Test padding. + assert_eq!( + &format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), + "1.1.1.1:53 " + ); + assert_eq!( + &format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)), + " 1.1.1.1:53" + ); +} + +#[test] +fn ipv6_socket_addr_to_string() { + // IPv4-mapped address. + assert_eq!( + SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280), 8080, 0, 0) + .to_string(), + "[::ffff:192.0.2.128]:8080" + ); + + // IPv4-compatible address. + assert_eq!( + SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280), 8080, 0, 0).to_string(), + "[::192.0.2.128]:8080" + ); + + // IPv6 address with no zero segments. + assert_eq!( + SocketAddrV6::new(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15), 80, 0, 0).to_string(), + "[8:9:a:b:c:d:e:f]:80" + ); + + // Shortest possible IPv6 length. + assert_eq!(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).to_string(), "[::]:0"); + + // Longest possible IPv6 length. + assert_eq!( + SocketAddrV6::new( + Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888), + u16::MAX, + u32::MAX, + u32::MAX, + ) + .to_string(), + "[1111:2222:3333:4444:5555:6666:7777:8888%4294967295]:65535" + ); + + // Test padding. + assert_eq!( + &format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), + "[1:2:3:4:5:6:7:8]:9 " + ); + assert_eq!( + &format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)), + " [1:2:3:4:5:6:7:8]:9" + ); +} + +#[test] +fn set_ip() { + fn ip4(low: u8) -> Ipv4Addr { + Ipv4Addr::new(77, 88, 21, low) + } + fn ip6(low: u16) -> Ipv6Addr { + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, low) + } + + let mut v4 = SocketAddrV4::new(ip4(11), 80); + assert_eq!(v4.ip(), &ip4(11)); + v4.set_ip(ip4(12)); + assert_eq!(v4.ip(), &ip4(12)); + + let mut addr = SocketAddr::V4(v4); + assert_eq!(addr.ip(), IpAddr::V4(ip4(12))); + addr.set_ip(IpAddr::V4(ip4(13))); + assert_eq!(addr.ip(), IpAddr::V4(ip4(13))); + addr.set_ip(IpAddr::V6(ip6(14))); + assert_eq!(addr.ip(), IpAddr::V6(ip6(14))); + + let mut v6 = SocketAddrV6::new(ip6(1), 80, 0, 0); + assert_eq!(v6.ip(), &ip6(1)); + v6.set_ip(ip6(2)); + assert_eq!(v6.ip(), &ip6(2)); + + let mut addr = SocketAddr::V6(v6); + assert_eq!(addr.ip(), IpAddr::V6(ip6(2))); + addr.set_ip(IpAddr::V6(ip6(3))); + assert_eq!(addr.ip(), IpAddr::V6(ip6(3))); + addr.set_ip(IpAddr::V4(ip4(4))); + assert_eq!(addr.ip(), IpAddr::V4(ip4(4))); +} + +#[test] +fn set_port() { + let mut v4 = SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80); + assert_eq!(v4.port(), 80); + v4.set_port(443); + assert_eq!(v4.port(), 443); + + let mut addr = SocketAddr::V4(v4); + assert_eq!(addr.port(), 443); + addr.set_port(8080); + assert_eq!(addr.port(), 8080); + + let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 0); + assert_eq!(v6.port(), 80); + v6.set_port(443); + assert_eq!(v6.port(), 443); + + let mut addr = SocketAddr::V6(v6); + assert_eq!(addr.port(), 443); + addr.set_port(8080); + assert_eq!(addr.port(), 8080); +} + +#[test] +fn set_flowinfo() { + let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 10, 0); + assert_eq!(v6.flowinfo(), 10); + v6.set_flowinfo(20); + assert_eq!(v6.flowinfo(), 20); +} + +#[test] +fn set_scope_id() { + let mut v6 = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 80, 0, 10); + assert_eq!(v6.scope_id(), 10); + v6.set_scope_id(20); + assert_eq!(v6.scope_id(), 20); +} + +#[test] +fn is_v4() { + let v4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)); + assert!(v4.is_ipv4()); + assert!(!v4.is_ipv6()); +} + +#[test] +fn is_v6() { + let v6 = SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), + 80, + 10, + 0, + )); + assert!(!v6.is_ipv4()); + assert!(v6.is_ipv6()); +} + +#[test] +fn socket_v4_to_str() { + let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080); + + assert_eq!(format!("{socket}"), "192.168.0.1:8080"); + assert_eq!(format!("{socket:<20}"), "192.168.0.1:8080 "); + assert_eq!(format!("{socket:>20}"), " 192.168.0.1:8080"); + assert_eq!(format!("{socket:^20}"), " 192.168.0.1:8080 "); + assert_eq!(format!("{socket:.10}"), "192.168.0."); +} + +#[test] +fn socket_v6_to_str() { + let mut socket = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0); + + assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1]:53"); + assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1]:53 "); + assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1]:53"); + assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1]:53 "); + assert_eq!(format!("{socket:.15}"), "[2a02:6b8:0:1::"); + + socket.set_scope_id(5); + + assert_eq!(format!("{socket}"), "[2a02:6b8:0:1::1%5]:53"); + assert_eq!(format!("{socket:<24}"), "[2a02:6b8:0:1::1%5]:53 "); + assert_eq!(format!("{socket:>24}"), " [2a02:6b8:0:1::1%5]:53"); + assert_eq!(format!("{socket:^24}"), " [2a02:6b8:0:1::1%5]:53 "); + assert_eq!(format!("{socket:.18}"), "[2a02:6b8:0:1::1%5"); +} + +#[test] +fn compare() { + let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap(); + let v4_2 = "224.210.103.5:12345".parse::<SocketAddrV4>().unwrap(); + let v4_3 = "224.210.103.5:23456".parse::<SocketAddrV4>().unwrap(); + let v6_1 = "[2001:db8:f00::1002]:23456".parse::<SocketAddrV6>().unwrap(); + let v6_2 = "[2001:db8:f00::2001]:12345".parse::<SocketAddrV6>().unwrap(); + let v6_3 = "[2001:db8:f00::2001]:23456".parse::<SocketAddrV6>().unwrap(); + + // equality + assert_eq!(v4_1, v4_1); + assert_eq!(v6_1, v6_1); + assert_eq!(SocketAddr::V4(v4_1), SocketAddr::V4(v4_1)); + assert_eq!(SocketAddr::V6(v6_1), SocketAddr::V6(v6_1)); + assert!(v4_1 != v4_2); + assert!(v6_1 != v6_2); + + // compare different addresses + assert!(v4_1 < v4_2); + assert!(v6_1 < v6_2); + assert!(v4_2 > v4_1); + assert!(v6_2 > v6_1); + + // compare the same address with different ports + assert!(v4_2 < v4_3); + assert!(v6_2 < v6_3); + assert!(v4_3 > v4_2); + assert!(v6_3 > v6_2); + + // compare different addresses with the same port + assert!(v4_1 < v4_3); + assert!(v6_1 < v6_3); + assert!(v4_3 > v4_1); + assert!(v6_3 > v6_1); + + // compare with an inferred right-hand side + assert_eq!(v4_1, "224.120.45.1:23456".parse().unwrap()); + assert_eq!(v6_1, "[2001:db8:f00::1002]:23456".parse().unwrap()); + assert_eq!(SocketAddr::V4(v4_1), "224.120.45.1:23456".parse().unwrap()); +} diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 78f56402eba..d52d1ac4d3a 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -28,7 +28,7 @@ use crate::simd::{ /// let zm_add = a0.zip(a1).map(|(lhs, rhs)| lhs + rhs); /// let zm_mul = a0.zip(a1).map(|(lhs, rhs)| lhs * rhs); /// -/// // `Simd<T, N>` implements `From<[T; N]> +/// // `Simd<T, N>` implements `From<[T; N]>` /// let (v0, v1) = (Simd::from(a0), Simd::from(a1)); /// // Which means arrays implement `Into<Simd<T, N>>`. /// assert_eq!(v0 + v1, zm_add.into()); diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 311b2e21c17..598a4bf9282 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -15,7 +15,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.138", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.139", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.87" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/build.rs b/library/std/build.rs index 8b1a06ee750..ea879667558 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -31,6 +31,7 @@ fn main() { || target.contains("espidf") || target.contains("solid") || target.contains("nintendo-3ds") + || target.contains("nto") { // These platforms don't have any special requirements. } else { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 363a2667174..b62f3ad29d3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -232,6 +232,7 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] +#![cfg_attr(windows, feature(round_char_boundary))] // // Language features: #![feature(alloc_error_handler)] @@ -287,6 +288,8 @@ #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] +#![feature(ip)] +#![feature(ip_in_core)] #![feature(is_some_and)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs index 07f08c1b586..e167fbd1b9c 100644 --- a/library/std/src/net/ip_addr.rs +++ b/library/std/src/net/ip_addr.rs @@ -2,2101 +2,40 @@ #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::cmp::Ordering; -use crate::fmt::{self, Write}; -use crate::mem::transmute; use crate::sys::net::netc as c; use crate::sys_common::{FromInner, IntoInner}; -use super::display_buffer::DisplayBuffer; - -/// An IP address, either IPv4 or IPv6. -/// -/// This enum can contain either an [`Ipv4Addr`] or an [`Ipv6Addr`], see their -/// respective documentation for more details. -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -/// -/// let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); -/// let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); -/// -/// assert_eq!("127.0.0.1".parse(), Ok(localhost_v4)); -/// assert_eq!("::1".parse(), Ok(localhost_v6)); -/// -/// assert_eq!(localhost_v4.is_ipv6(), false); -/// assert_eq!(localhost_v4.is_ipv4(), true); -/// ``` -#[cfg_attr(not(test), rustc_diagnostic_item = "IpAddr")] #[stable(feature = "ip_addr", since = "1.7.0")] -#[derive(Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] -pub enum IpAddr { - /// An IPv4 address. - #[stable(feature = "ip_addr", since = "1.7.0")] - V4(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv4Addr), - /// An IPv6 address. - #[stable(feature = "ip_addr", since = "1.7.0")] - V6(#[stable(feature = "ip_addr", since = "1.7.0")] Ipv6Addr), -} +pub use core::net::IpAddr; -/// An IPv4 address. -/// -/// IPv4 addresses are defined as 32-bit integers in [IETF RFC 791]. -/// They are usually represented as four octets. -/// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// -/// [IETF RFC 791]: https://tools.ietf.org/html/rfc791 -/// -/// # Textual representation -/// -/// `Ipv4Addr` provides a [`FromStr`] implementation. The four octets are in decimal -/// notation, divided by `.` (this is called "dot-decimal notation"). -/// Notably, octal numbers (which are indicated with a leading `0`) and hexadecimal numbers (which -/// are indicated with a leading `0x`) are not allowed per [IETF RFC 6943]. -/// -/// [IETF RFC 6943]: https://tools.ietf.org/html/rfc6943#section-3.1.1 -/// [`FromStr`]: crate::str::FromStr -/// -/// # Examples -/// -/// ``` -/// use std::net::Ipv4Addr; -/// -/// let localhost = Ipv4Addr::new(127, 0, 0, 1); -/// assert_eq!("127.0.0.1".parse(), Ok(localhost)); -/// assert_eq!(localhost.is_loopback(), true); -/// assert!("012.004.002.000".parse::<Ipv4Addr>().is_err()); // all octets are in octal -/// assert!("0000000.0.0.0".parse::<Ipv4Addr>().is_err()); // first octet is a zero in octal -/// assert!("0xcb.0x0.0x71.0x00".parse::<Ipv4Addr>().is_err()); // all octets are in hex -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Ipv4Addr { - octets: [u8; 4], -} +pub use core::net::{Ipv4Addr, Ipv6Addr}; -/// An IPv6 address. -/// -/// IPv6 addresses are defined as 128-bit integers in [IETF RFC 4291]. -/// They are usually represented as eight 16-bit segments. -/// -/// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 -/// -/// # Embedding IPv4 Addresses -/// -/// See [`IpAddr`] for a type encompassing both IPv4 and IPv6 addresses. -/// -/// To assist in the transition from IPv4 to IPv6 two types of IPv6 addresses that embed an IPv4 address were defined: -/// IPv4-compatible and IPv4-mapped addresses. Of these IPv4-compatible addresses have been officially deprecated. -/// -/// Both types of addresses are not assigned any special meaning by this implementation, -/// other than what the relevant standards prescribe. This means that an address like `::ffff:127.0.0.1`, -/// while representing an IPv4 loopback address, is not itself an IPv6 loopback address; only `::1` is. -/// To handle these so called "IPv4-in-IPv6" addresses, they have to first be converted to their canonical IPv4 address. -/// -/// ### IPv4-Compatible IPv6 Addresses -/// -/// IPv4-compatible IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.1], and have been officially deprecated. -/// The RFC describes the format of an "IPv4-Compatible IPv6 address" as follows: -/// -/// ```text -/// | 80 bits | 16 | 32 bits | -/// +--------------------------------------+--------------------------+ -/// |0000..............................0000|0000| IPv4 address | -/// +--------------------------------------+----+---------------------+ -/// ``` -/// So `::a.b.c.d` would be an IPv4-compatible IPv6 address representing the IPv4 address `a.b.c.d`. -/// -/// To convert from an IPv4 address to an IPv4-compatible IPv6 address, use [`Ipv4Addr::to_ipv6_compatible`]. -/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-compatible IPv6 address to the canonical IPv4 address. -/// -/// [IETF RFC 4291 Section 2.5.5.1]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.1 -/// -/// ### IPv4-Mapped IPv6 Addresses -/// -/// IPv4-mapped IPv6 addresses are defined in [IETF RFC 4291 Section 2.5.5.2]. -/// The RFC describes the format of an "IPv4-Mapped IPv6 address" as follows: -/// -/// ```text -/// | 80 bits | 16 | 32 bits | -/// +--------------------------------------+--------------------------+ -/// |0000..............................0000|FFFF| IPv4 address | -/// +--------------------------------------+----+---------------------+ -/// ``` -/// So `::ffff:a.b.c.d` would be an IPv4-mapped IPv6 address representing the IPv4 address `a.b.c.d`. -/// -/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`]. -/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address. -/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`. Use -/// [`Ipv6Addr::to_ipv4_mapped`] to avoid this. -/// -/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2 -/// -/// # Textual representation -/// -/// `Ipv6Addr` provides a [`FromStr`] implementation. There are many ways to represent -/// an IPv6 address in text, but in general, each segments is written in hexadecimal -/// notation, and segments are separated by `:`. For more information, see -/// [IETF RFC 5952]. -/// -/// [`FromStr`]: crate::str::FromStr -/// [IETF RFC 5952]: https://tools.ietf.org/html/rfc5952 -/// -/// # Examples -/// -/// ``` -/// use std::net::Ipv6Addr; -/// -/// let localhost = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); -/// assert_eq!("::1".parse(), Ok(localhost)); -/// assert_eq!(localhost.is_loopback(), true); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Ipv6Addr { - octets: [u8; 16], -} - -/// Scope of an [IPv6 multicast address] as defined in [IETF RFC 7346 section 2]. -/// -/// # Stability Guarantees -/// -/// Not all possible values for a multicast scope have been assigned. -/// Future RFCs may introduce new scopes, which will be added as variants to this enum; -/// because of this the enum is marked as `#[non_exhaustive]`. -/// -/// # Examples -/// ``` -/// #![feature(ip)] -/// -/// use std::net::Ipv6Addr; -/// use std::net::Ipv6MulticastScope::*; -/// -/// // An IPv6 multicast address with global scope (`ff0e::`). -/// let address = Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0); -/// -/// // Will print "Global scope". -/// match address.multicast_scope() { -/// Some(InterfaceLocal) => println!("Interface-Local scope"), -/// Some(LinkLocal) => println!("Link-Local scope"), -/// Some(RealmLocal) => println!("Realm-Local scope"), -/// Some(AdminLocal) => println!("Admin-Local scope"), -/// Some(SiteLocal) => println!("Site-Local scope"), -/// Some(OrganizationLocal) => println!("Organization-Local scope"), -/// Some(Global) => println!("Global scope"), -/// Some(_) => println!("Unknown scope"), -/// None => println!("Not a multicast address!") -/// } -/// -/// ``` -/// -/// [IPv6 multicast address]: Ipv6Addr -/// [IETF RFC 7346 section 2]: https://tools.ietf.org/html/rfc7346#section-2 -#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)] #[unstable(feature = "ip", issue = "27709")] -#[non_exhaustive] -pub enum Ipv6MulticastScope { - /// Interface-Local scope. - InterfaceLocal, - /// Link-Local scope. - LinkLocal, - /// Realm-Local scope. - RealmLocal, - /// Admin-Local scope. - AdminLocal, - /// Site-Local scope. - SiteLocal, - /// Organization-Local scope. - OrganizationLocal, - /// Global scope. - Global, -} - -impl IpAddr { - /// Returns [`true`] for the special 'unspecified' address. - /// - /// See the documentation for [`Ipv4Addr::is_unspecified()`] and - /// [`Ipv6Addr::is_unspecified()`] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_unspecified(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_unspecified(), - IpAddr::V6(ip) => ip.is_unspecified(), - } - } - - /// Returns [`true`] if this is a loopback address. - /// - /// See the documentation for [`Ipv4Addr::is_loopback()`] and - /// [`Ipv6Addr::is_loopback()`] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_loopback(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_loopback(), - IpAddr::V6(ip) => ip.is_loopback(), - } - } - - /// Returns [`true`] if the address appears to be globally routable. - /// - /// See the documentation for [`Ipv4Addr::is_global()`] and - /// [`Ipv6Addr::is_global()`] for more details. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_global(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_global(), - IpAddr::V6(ip) => ip.is_global(), - } - } - - /// Returns [`true`] if this is a multicast address. - /// - /// See the documentation for [`Ipv4Addr::is_multicast()`] and - /// [`Ipv6Addr::is_multicast()`] for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_multicast(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_multicast(), - IpAddr::V6(ip) => ip.is_multicast(), - } - } - - /// Returns [`true`] if this address is in a range designated for documentation. - /// - /// See the documentation for [`Ipv4Addr::is_documentation()`] and - /// [`Ipv6Addr::is_documentation()`] for more details. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_documentation(), - /// true - /// ); - /// ``` - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_documentation(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_documentation(), - IpAddr::V6(ip) => ip.is_documentation(), - } - } - - /// Returns [`true`] if this address is in a range designated for benchmarking. - /// - /// See the documentation for [`Ipv4Addr::is_benchmarking()`] and - /// [`Ipv6Addr::is_benchmarking()`] for more details. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(198, 19, 255, 255)).is_benchmarking(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0)).is_benchmarking(), true); - /// ``` - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_benchmarking(&self) -> bool { - match self { - IpAddr::V4(ip) => ip.is_benchmarking(), - IpAddr::V6(ip) => ip.is_benchmarking(), - } - } - - /// Returns [`true`] if this address is an [`IPv4` address], and [`false`] - /// otherwise. - /// - /// [`IPv4` address]: IpAddr::V4 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ipaddr_checker", since = "1.16.0")] - #[must_use] - #[inline] - pub const fn is_ipv4(&self) -> bool { - matches!(self, IpAddr::V4(_)) - } - - /// Returns [`true`] if this address is an [`IPv6` address], and [`false`] - /// otherwise. - /// - /// [`IPv6` address]: IpAddr::V6 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "ipaddr_checker", since = "1.16.0")] - #[must_use] - #[inline] - pub const fn is_ipv6(&self) -> bool { - matches!(self, IpAddr::V6(_)) - } - - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped IPv6 addresses, otherwise it - /// return `self` as-is. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).to_canonical().is_loopback(), true); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).is_loopback(), false); - /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1)).to_canonical().is_loopback(), true); - /// ``` - #[inline] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[rustc_const_unstable(feature = "const_ip", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - pub const fn to_canonical(&self) -> IpAddr { - match self { - &v4 @ IpAddr::V4(_) => v4, - IpAddr::V6(v6) => v6.to_canonical(), - } - } -} - -impl Ipv4Addr { - /// Creates a new IPv4 address from four eight-bit octets. - /// - /// The result will represent the IP address `a`.`b`.`c`.`d`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr { - Ipv4Addr { octets: [a, b, c, d] } - } - - /// An IPv4 address with the address pointing to localhost: `127.0.0.1` - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::LOCALHOST; - /// assert_eq!(addr, Ipv4Addr::new(127, 0, 0, 1)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const LOCALHOST: Self = Ipv4Addr::new(127, 0, 0, 1); - - /// An IPv4 address representing an unspecified address: `0.0.0.0` - /// - /// This corresponds to the constant `INADDR_ANY` in other languages. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::UNSPECIFIED; - /// assert_eq!(addr, Ipv4Addr::new(0, 0, 0, 0)); - /// ``` - #[doc(alias = "INADDR_ANY")] - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const UNSPECIFIED: Self = Ipv4Addr::new(0, 0, 0, 0); - - /// An IPv4 address representing the broadcast address: `255.255.255.255` - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::BROADCAST; - /// assert_eq!(addr, Ipv4Addr::new(255, 255, 255, 255)); - /// ``` - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const BROADCAST: Self = Ipv4Addr::new(255, 255, 255, 255); - - /// Returns the four eight-bit integers that make up this address. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// assert_eq!(addr.octets(), [127, 0, 0, 1]); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn octets(&self) -> [u8; 4] { - self.octets - } - - /// Returns [`true`] for the special 'unspecified' address (`0.0.0.0`). - /// - /// This property is defined in _UNIX Network Programming, Second Edition_, - /// W. Richard Stevens, p. 891; see also [ip7]. - /// - /// [ip7]: https://man7.org/linux/man-pages/man7/ip.7.html - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(0, 0, 0, 0).is_unspecified(), true); - /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_unspecified(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "ip_shared", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn is_unspecified(&self) -> bool { - u32::from_be_bytes(self.octets) == 0 - } - - /// Returns [`true`] if this is a loopback address (`127.0.0.0/8`). - /// - /// This property is defined by [IETF RFC 1122]. - /// - /// [IETF RFC 1122]: https://tools.ietf.org/html/rfc1122 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(127, 0, 0, 1).is_loopback(), true); - /// assert_eq!(Ipv4Addr::new(45, 22, 13, 197).is_loopback(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_loopback(&self) -> bool { - self.octets()[0] == 127 - } - - /// Returns [`true`] if this is a private address. - /// - /// The private address ranges are defined in [IETF RFC 1918] and include: - /// - /// - `10.0.0.0/8` - /// - `172.16.0.0/12` - /// - `192.168.0.0/16` - /// - /// [IETF RFC 1918]: https://tools.ietf.org/html/rfc1918 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(10, 0, 0, 1).is_private(), true); - /// assert_eq!(Ipv4Addr::new(10, 10, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 10).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 29, 45, 14).is_private(), true); - /// assert_eq!(Ipv4Addr::new(172, 32, 0, 2).is_private(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 0, 2).is_private(), true); - /// assert_eq!(Ipv4Addr::new(192, 169, 0, 2).is_private(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_private(&self) -> bool { - match self.octets() { - [10, ..] => true, - [172, b, ..] if b >= 16 && b <= 31 => true, - [192, 168, ..] => true, - _ => false, - } - } - - /// Returns [`true`] if the address is link-local (`169.254.0.0/16`). - /// - /// This property is defined by [IETF RFC 3927]. - /// - /// [IETF RFC 3927]: https://tools.ietf.org/html/rfc3927 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(169, 254, 0, 0).is_link_local(), true); - /// assert_eq!(Ipv4Addr::new(169, 254, 10, 65).is_link_local(), true); - /// assert_eq!(Ipv4Addr::new(16, 89, 10, 65).is_link_local(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_link_local(&self) -> bool { - matches!(self.octets(), [169, 254, ..]) - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv4 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv4 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - /// - The [unspecified address] ([`is_unspecified`](Ipv4Addr::is_unspecified)) - /// - Addresses reserved for private use ([`is_private`](Ipv4Addr::is_private)) - /// - Addresses in the shared address space ([`is_shared`](Ipv4Addr::is_shared)) - /// - Loopback addresses ([`is_loopback`](Ipv4Addr::is_loopback)) - /// - Link-local addresses ([`is_link_local`](Ipv4Addr::is_link_local)) - /// - Addresses reserved for documentation ([`is_documentation`](Ipv4Addr::is_documentation)) - /// - Addresses reserved for benchmarking ([`is_benchmarking`](Ipv4Addr::is_benchmarking)) - /// - Reserved addresses ([`is_reserved`](Ipv4Addr::is_reserved)) - /// - The [broadcast address] ([`is_broadcast`](Ipv4Addr::is_broadcast)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv4 Special-Purpose Address Registry]. - /// - /// [IANA IPv4 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml - /// [unspecified address]: Ipv4Addr::UNSPECIFIED - /// [broadcast address]: Ipv4Addr::BROADCAST - - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv4Addr; - /// - /// // Most IPv4 addresses are globally reachable: - /// assert_eq!(Ipv4Addr::new(80, 9, 12, 3).is_global(), true); - /// - /// // However some addresses have been assigned a special meaning - /// // that makes them not globally reachable. Some examples are: - /// - /// // The unspecified address (`0.0.0.0`) - /// assert_eq!(Ipv4Addr::UNSPECIFIED.is_global(), false); - /// - /// // Addresses reserved for private use (`10.0.0.0/8`, `172.16.0.0/12`, 192.168.0.0/16) - /// assert_eq!(Ipv4Addr::new(10, 254, 0, 0).is_global(), false); - /// assert_eq!(Ipv4Addr::new(192, 168, 10, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_global(), false); - /// - /// // Addresses in the shared address space (`100.64.0.0/10`) - /// assert_eq!(Ipv4Addr::new(100, 100, 0, 0).is_global(), false); - /// - /// // The loopback addresses (`127.0.0.0/8`) - /// assert_eq!(Ipv4Addr::LOCALHOST.is_global(), false); - /// - /// // Link-local addresses (`169.254.0.0/16`) - /// assert_eq!(Ipv4Addr::new(169, 254, 45, 1).is_global(), false); - /// - /// // Addresses reserved for documentation (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`) - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_global(), false); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_global(), false); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_global(), false); - /// - /// // Addresses reserved for benchmarking (`198.18.0.0/15`) - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_global(), false); - /// - /// // Reserved addresses (`240.0.0.0/4`) - /// assert_eq!(Ipv4Addr::new(250, 10, 20, 30).is_global(), false); - /// - /// // The broadcast address (`255.255.255.255`) - /// assert_eq!(Ipv4Addr::BROADCAST.is_global(), false); - /// - /// // For a complete overview see the IANA IPv4 Special-Purpose Address Registry. - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_global(&self) -> bool { - !(self.octets()[0] == 0 // "This network" - || self.is_private() - || self.is_shared() - || self.is_loopback() - || self.is_link_local() - // addresses reserved for future protocols (`192.0.0.0/24`) - ||(self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0) - || self.is_documentation() - || self.is_benchmarking() - || self.is_reserved() - || self.is_broadcast()) - } - - /// Returns [`true`] if this address is part of the Shared Address Space defined in - /// [IETF RFC 6598] (`100.64.0.0/10`). - /// - /// [IETF RFC 6598]: https://tools.ietf.org/html/rfc6598 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(100, 64, 0, 0).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 127, 255, 255).is_shared(), true); - /// assert_eq!(Ipv4Addr::new(100, 128, 0, 0).is_shared(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_shared(&self) -> bool { - self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000) - } - - /// Returns [`true`] if this address part of the `198.18.0.0/15` range, which is reserved for - /// network devices benchmarking. This range is defined in [IETF RFC 2544] as `192.18.0.0` - /// through `198.19.255.255` but [errata 423] corrects it to `198.18.0.0/15`. - /// - /// [IETF RFC 2544]: https://tools.ietf.org/html/rfc2544 - /// [errata 423]: https://www.rfc-editor.org/errata/eid423 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(198, 17, 255, 255).is_benchmarking(), false); - /// assert_eq!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking(), true); - /// assert_eq!(Ipv4Addr::new(198, 20, 0, 0).is_benchmarking(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_benchmarking(&self) -> bool { - self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18 - } - - /// Returns [`true`] if this address is reserved by IANA for future use. [IETF RFC 1112] - /// defines the block of reserved addresses as `240.0.0.0/4`. This range normally includes the - /// broadcast address `255.255.255.255`, but this implementation explicitly excludes it, since - /// it is obviously not reserved for future use. - /// - /// [IETF RFC 1112]: https://tools.ietf.org/html/rfc1112 - /// - /// # Warning - /// - /// As IANA assigns new addresses, this method will be - /// updated. This may result in non-reserved addresses being - /// treated as reserved in code that relies on an outdated version - /// of this method. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(240, 0, 0, 0).is_reserved(), true); - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 254).is_reserved(), true); - /// - /// assert_eq!(Ipv4Addr::new(239, 255, 255, 255).is_reserved(), false); - /// // The broadcast address is not considered as reserved for future use by this implementation - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_reserved(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_reserved(&self) -> bool { - self.octets()[0] & 240 == 240 && !self.is_broadcast() - } - - /// Returns [`true`] if this is a multicast address (`224.0.0.0/4`). - /// - /// Multicast addresses have a most significant octet between `224` and `239`, - /// and is defined by [IETF RFC 5771]. - /// - /// [IETF RFC 5771]: https://tools.ietf.org/html/rfc5771 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(224, 254, 0, 0).is_multicast(), true); - /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_multicast(), true); - /// assert_eq!(Ipv4Addr::new(172, 16, 10, 65).is_multicast(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_multicast(&self) -> bool { - self.octets()[0] >= 224 && self.octets()[0] <= 239 - } - - /// Returns [`true`] if this is a broadcast address (`255.255.255.255`). - /// - /// A broadcast address has all octets set to `255` as defined in [IETF RFC 919]. - /// - /// [IETF RFC 919]: https://tools.ietf.org/html/rfc919 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(255, 255, 255, 255).is_broadcast(), true); - /// assert_eq!(Ipv4Addr::new(236, 168, 10, 65).is_broadcast(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_broadcast(&self) -> bool { - u32::from_be_bytes(self.octets()) == u32::from_be_bytes(Self::BROADCAST.octets()) - } - - /// Returns [`true`] if this address is in a range designated for documentation. - /// - /// This is defined in [IETF RFC 5737]: - /// - /// - `192.0.2.0/24` (TEST-NET-1) - /// - `198.51.100.0/24` (TEST-NET-2) - /// - `203.0.113.0/24` (TEST-NET-3) - /// - /// [IETF RFC 5737]: https://tools.ietf.org/html/rfc5737 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(198, 51, 100, 65).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(203, 0, 113, 6).is_documentation(), true); - /// assert_eq!(Ipv4Addr::new(193, 34, 17, 19).is_documentation(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_documentation(&self) -> bool { - matches!(self.octets(), [192, 0, 2, _] | [198, 51, 100, _] | [203, 0, 113, _]) - } - - /// Converts this address to an [IPv4-compatible] [`IPv6` address]. - /// - /// `a.b.c.d` becomes `::a.b.c.d` - /// - /// Note that IPv4-compatible addresses have been officially deprecated. - /// If you don't explicitly need an IPv4-compatible address for legacy reasons, consider using `to_ipv6_mapped` instead. - /// - /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses - /// [`IPv6` address]: Ipv6Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!( - /// Ipv4Addr::new(192, 0, 2, 255).to_ipv6_compatible(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x2ff) - /// ); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_ipv6_compatible(&self) -> Ipv6Addr { - let [a, b, c, d] = self.octets(); - Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a, b, c, d] } - } - - /// Converts this address to an [IPv4-mapped] [`IPv6` address]. - /// - /// `a.b.c.d` becomes `::ffff:a.b.c.d` - /// - /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses - /// [`IPv6` address]: Ipv6Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv4Addr::new(192, 0, 2, 255).to_ipv6_mapped(), - /// Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x2ff)); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_ipv6_mapped(&self) -> Ipv6Addr { - let [a, b, c, d] = self.octets(); - Ipv6Addr { octets: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, a, b, c, d] } - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl fmt::Display for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - IpAddr::V4(ip) => ip.fmt(fmt), - IpAddr::V6(ip) => ip.fmt(fmt), - } - } -} - -#[stable(feature = "ip_addr", since = "1.7.0")] -impl fmt::Debug for IpAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<Ipv4Addr> for IpAddr { - /// Copies this address to a new `IpAddr::V4`. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr}; - /// - /// let addr = Ipv4Addr::new(127, 0, 0, 1); - /// - /// assert_eq!( - /// IpAddr::V4(addr), - /// IpAddr::from(addr) - /// ) - /// ``` - #[inline] - fn from(ipv4: Ipv4Addr) -> IpAddr { - IpAddr::V4(ipv4) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<Ipv6Addr> for IpAddr { - /// Copies this address to a new `IpAddr::V6`. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); - /// - /// assert_eq!( - /// IpAddr::V6(addr), - /// IpAddr::from(addr) - /// ); - /// ``` - #[inline] - fn from(ipv6: Ipv6Addr) -> IpAddr { - IpAddr::V6(ipv6) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let octets = self.octets(); - - // If there are no alignment requirements, write the IP address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if fmt.precision().is_none() && fmt.width().is_none() { - write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]) - } else { - const LONGEST_IPV4_ADDR: &str = "255.255.255.255"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV4_ADDR.len() }>::new(); - // Buffer is long enough for the longest possible IPv4 address, so this should never fail. - write!(buf, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]).unwrap(); - - fmt.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Ipv4Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<Ipv4Addr> for IpAddr { - #[inline] - fn eq(&self, other: &Ipv4Addr) -> bool { - match self { - IpAddr::V4(v4) => v4 == other, - IpAddr::V6(_) => false, - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<IpAddr> for Ipv4Addr { - #[inline] - fn eq(&self, other: &IpAddr) -> bool { - match other { - IpAddr::V4(v4) => self == v4, - IpAddr::V6(_) => false, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Ipv4Addr { - #[inline] - fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<Ipv4Addr> for IpAddr { - #[inline] - fn partial_cmp(&self, other: &Ipv4Addr) -> Option<Ordering> { - match self { - IpAddr::V4(v4) => v4.partial_cmp(other), - IpAddr::V6(_) => Some(Ordering::Greater), - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<IpAddr> for Ipv4Addr { - #[inline] - fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> { - match other { - IpAddr::V4(v4) => self.partial_cmp(v4), - IpAddr::V6(_) => Some(Ordering::Less), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Ipv4Addr { - #[inline] - fn cmp(&self, other: &Ipv4Addr) -> Ordering { - self.octets.cmp(&other.octets) - } -} +pub use core::net::Ipv6MulticastScope; impl IntoInner<c::in_addr> for Ipv4Addr { #[inline] fn into_inner(self) -> c::in_addr { // `s_addr` is stored as BE on all machines and the array is in BE order. // So the native endian conversion method is used so that it's never swapped. - c::in_addr { s_addr: u32::from_ne_bytes(self.octets) } + c::in_addr { s_addr: u32::from_ne_bytes(self.octets()) } } } impl FromInner<c::in_addr> for Ipv4Addr { fn from_inner(addr: c::in_addr) -> Ipv4Addr { - Ipv4Addr { octets: addr.s_addr.to_ne_bytes() } - } -} - -#[stable(feature = "ip_u32", since = "1.1.0")] -impl From<Ipv4Addr> for u32 { - /// Converts an `Ipv4Addr` into a host byte order `u32`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78); - /// assert_eq!(0x12345678, u32::from(addr)); - /// ``` - #[inline] - fn from(ip: Ipv4Addr) -> u32 { - u32::from_be_bytes(ip.octets) - } -} - -#[stable(feature = "ip_u32", since = "1.1.0")] -impl From<u32> for Ipv4Addr { - /// Converts a host byte order `u32` into an `Ipv4Addr`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::from(0x12345678); - /// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78), addr); - /// ``` - #[inline] - fn from(ip: u32) -> Ipv4Addr { - Ipv4Addr { octets: ip.to_be_bytes() } - } -} - -#[stable(feature = "from_slice_v4", since = "1.9.0")] -impl From<[u8; 4]> for Ipv4Addr { - /// Creates an `Ipv4Addr` from a four element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv4Addr; - /// - /// let addr = Ipv4Addr::from([13u8, 12u8, 11u8, 10u8]); - /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr); - /// ``` - #[inline] - fn from(octets: [u8; 4]) -> Ipv4Addr { - Ipv4Addr { octets } - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u8; 4]> for IpAddr { - /// Creates an `IpAddr::V4` from a four element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr}; - /// - /// let addr = IpAddr::from([13u8, 12u8, 11u8, 10u8]); - /// assert_eq!(IpAddr::V4(Ipv4Addr::new(13, 12, 11, 10)), addr); - /// ``` - #[inline] - fn from(octets: [u8; 4]) -> IpAddr { - IpAddr::V4(Ipv4Addr::from(octets)) - } -} - -impl Ipv6Addr { - /// Creates a new IPv6 address from eight 16-bit segments. - /// - /// The result will represent the IP address `a:b:c:d:e:f:g:h`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { - let addr16 = [ - a.to_be(), - b.to_be(), - c.to_be(), - d.to_be(), - e.to_be(), - f.to_be(), - g.to_be(), - h.to_be(), - ]; - Ipv6Addr { - // All elements in `addr16` are big endian. - // SAFETY: `[u16; 8]` is always safe to transmute to `[u8; 16]`. - octets: unsafe { transmute::<_, [u8; 16]>(addr16) }, - } - } - - /// An IPv6 address representing localhost: `::1`. - /// - /// This corresponds to constant `IN6ADDR_LOOPBACK_INIT` or `in6addr_loopback` in other - /// languages. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::LOCALHOST; - /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// ``` - #[doc(alias = "IN6ADDR_LOOPBACK_INIT")] - #[doc(alias = "in6addr_loopback")] - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const LOCALHOST: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); - - /// An IPv6 address representing the unspecified address: `::` - /// - /// This corresponds to constant `IN6ADDR_ANY_INIT` or `in6addr_any` in other languages. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::UNSPECIFIED; - /// assert_eq!(addr, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); - /// ``` - #[doc(alias = "IN6ADDR_ANY_INIT")] - #[doc(alias = "in6addr_any")] - #[stable(feature = "ip_constructors", since = "1.30.0")] - pub const UNSPECIFIED: Self = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0); - - /// Returns the eight 16-bit segments that make up this address. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), - /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub const fn segments(&self) -> [u16; 8] { - // All elements in `self.octets` must be big endian. - // SAFETY: `[u8; 16]` is always safe to transmute to `[u16; 8]`. - let [a, b, c, d, e, f, g, h] = unsafe { transmute::<_, [u16; 8]>(self.octets) }; - // We want native endian u16 - [ - u16::from_be(a), - u16::from_be(b), - u16::from_be(c), - u16::from_be(d), - u16::from_be(e), - u16::from_be(f), - u16::from_be(g), - u16::from_be(h), - ] - } - - /// Returns [`true`] for the special 'unspecified' address (`::`). - /// - /// This property is defined in [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_unspecified(&self) -> bool { - u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::UNSPECIFIED.octets()) - } - - /// Returns [`true`] if this is the [loopback address] (`::1`), - /// as defined in [IETF RFC 4291 section 2.5.3]. - /// - /// Contrary to IPv4, in IPv6 there is only one loopback address. - /// - /// [loopback address]: Ipv6Addr::LOCALHOST - /// [IETF RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_loopback(&self) -> bool { - u128::from_be_bytes(self.octets()) == u128::from_be_bytes(Ipv6Addr::LOCALHOST.octets()) - } - - /// Returns [`true`] if the address appears to be globally reachable - /// as specified by the [IANA IPv6 Special-Purpose Address Registry]. - /// Whether or not an address is practically reachable will depend on your network configuration. - /// - /// Most IPv6 addresses are globally reachable; - /// unless they are specifically defined as *not* globally reachable. - /// - /// Non-exhaustive list of notable addresses that are not globally reachable: - /// - The [unspecified address] ([`is_unspecified`](Ipv6Addr::is_unspecified)) - /// - The [loopback address] ([`is_loopback`](Ipv6Addr::is_loopback)) - /// - IPv4-mapped addresses - /// - Addresses reserved for benchmarking - /// - Addresses reserved for documentation ([`is_documentation`](Ipv6Addr::is_documentation)) - /// - Unique local addresses ([`is_unique_local`](Ipv6Addr::is_unique_local)) - /// - Unicast addresses with link-local scope ([`is_unicast_link_local`](Ipv6Addr::is_unicast_link_local)) - /// - /// For the complete overview of which addresses are globally reachable, see the table at the [IANA IPv6 Special-Purpose Address Registry]. - /// - /// Note that an address having global scope is not the same as being globally reachable, - /// and there is no direct relation between the two concepts: There exist addresses with global scope - /// that are not globally reachable (for example unique local addresses), - /// and addresses that are globally reachable without having global scope - /// (multicast addresses with non-global scope). - /// - /// [IANA IPv6 Special-Purpose Address Registry]: https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml - /// [unspecified address]: Ipv6Addr::UNSPECIFIED - /// [loopback address]: Ipv6Addr::LOCALHOST - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // Most IPv6 addresses are globally reachable: - /// assert_eq!(Ipv6Addr::new(0x26, 0, 0x1c9, 0, 0, 0xafc8, 0x10, 0x1).is_global(), true); - /// - /// // However some addresses have been assigned a special meaning - /// // that makes them not globally reachable. Some examples are: - /// - /// // The unspecified address (`::`) - /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_global(), false); - /// - /// // The loopback address (`::1`) - /// assert_eq!(Ipv6Addr::LOCALHOST.is_global(), false); - /// - /// // IPv4-mapped addresses (`::ffff:0:0/96`) - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), false); - /// - /// // Addresses reserved for benchmarking (`2001:2::/48`) - /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); - /// - /// // Addresses reserved for documentation (`2001:db8::/32`) - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // Unique local addresses (`fc00::/7`) - /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // Unicast addresses with link-local scope (`fe80::/10`) - /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 1).is_global(), false); - /// - /// // For a complete overview see the IANA IPv6 Special-Purpose Address Registry. - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_global(&self) -> bool { - !(self.is_unspecified() - || self.is_loopback() - // IPv4-mapped Address (`::ffff:0:0/96`) - || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _]) - // IPv4-IPv6 Translat. (`64:ff9b:1::/48`) - || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _]) - // Discard-Only Address Block (`100::/64`) - || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _]) - // IETF Protocol Assignments (`2001::/23`) - || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200) - && !( - // Port Control Protocol Anycast (`2001:1::1`) - u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001 - // Traversal Using Relays around NAT Anycast (`2001:1::2`) - || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002 - // AMT (`2001:3::/32`) - || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _]) - // AS112-v6 (`2001:4:112::/48`) - || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _]) - // ORCHIDv2 (`2001:20::/28`) - || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F) - )) - || self.is_documentation() - || self.is_unique_local() - || self.is_unicast_link_local()) - } - - /// Returns [`true`] if this is a unique local address (`fc00::/7`). - /// - /// This property is defined in [IETF RFC 4193]. - /// - /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), false); - /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unique_local(&self) -> bool { - (self.segments()[0] & 0xfe00) == 0xfc00 - } - - /// Returns [`true`] if this is a unicast address, as defined by [IETF RFC 4291]. - /// Any address that is not a [multicast address] (`ff00::/8`) is unicast. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [multicast address]: Ipv6Addr::is_multicast - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // The unspecified and loopback addresses are unicast. - /// assert_eq!(Ipv6Addr::UNSPECIFIED.is_unicast(), true); - /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast(), true); - /// - /// // Any address that is not a multicast address (`ff00::/8`) is unicast. - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast(), true); - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_unicast(), false); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unicast(&self) -> bool { - !self.is_multicast() - } - - /// Returns `true` if the address is a unicast address with link-local scope, - /// as defined in [RFC 4291]. - /// - /// A unicast address has link-local scope if it has the prefix `fe80::/10`, as per [RFC 4291 section 2.4]. - /// Note that this encompasses more addresses than those defined in [RFC 4291 section 2.5.6], - /// which describes "Link-Local IPv6 Unicast Addresses" as having the following stricter format: - /// - /// ```text - /// | 10 bits | 54 bits | 64 bits | - /// +----------+-------------------------+----------------------------+ - /// |1111111010| 0 | interface ID | - /// +----------+-------------------------+----------------------------+ - /// ``` - /// So while currently the only addresses with link-local scope an application will encounter are all in `fe80::/64`, - /// this might change in the future with the publication of new standards. More addresses in `fe80::/10` could be allocated, - /// and those addresses will have link-local scope. - /// - /// Also note that while [RFC 4291 section 2.5.3] mentions about the [loopback address] (`::1`) that "it is treated as having Link-Local scope", - /// this does not mean that the loopback address actually has link-local scope and this method will return `false` on it. - /// - /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// [RFC 4291 section 2.4]: https://tools.ietf.org/html/rfc4291#section-2.4 - /// [RFC 4291 section 2.5.3]: https://tools.ietf.org/html/rfc4291#section-2.5.3 - /// [RFC 4291 section 2.5.6]: https://tools.ietf.org/html/rfc4291#section-2.5.6 - /// [loopback address]: Ipv6Addr::LOCALHOST - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// // The loopback address (`::1`) does not actually have link-local scope. - /// assert_eq!(Ipv6Addr::LOCALHOST.is_unicast_link_local(), false); - /// - /// // Only addresses in `fe80::/10` have link-local scope. - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), false); - /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); - /// - /// // Addresses outside the stricter `fe80::/64` also have link-local scope. - /// assert_eq!(Ipv6Addr::new(0xfe80, 0, 0, 1, 0, 0, 0, 0).is_unicast_link_local(), true); - /// assert_eq!(Ipv6Addr::new(0xfe81, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unicast_link_local(&self) -> bool { - (self.segments()[0] & 0xffc0) == 0xfe80 - } - - /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). - /// - /// This property is defined in [IETF RFC 3849]. - /// - /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) - } - - /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). - /// - /// This property is defined in [IETF RFC 5180], where it is mistakenly specified as covering the range `2001:0200::/48`. - /// This is corrected in [IETF RFC Errata 1752] to `2001:0002::/48`. - /// - /// [IETF RFC 5180]: https://tools.ietf.org/html/rfc5180 - /// [IETF RFC Errata 1752]: https://www.rfc-editor.org/errata_search.php?eid=1752 - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc613, 0x0).is_benchmarking(), false); - /// assert_eq!(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0).is_benchmarking(), true); - /// ``` - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_benchmarking(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) - } - - /// Returns [`true`] if the address is a globally routable unicast address. - /// - /// The following return false: - /// - /// - the loopback address - /// - the link-local addresses - /// - unique local addresses - /// - the unspecified address - /// - the address range reserved for documentation - /// - /// This method returns [`true`] for site-local addresses as per [RFC 4291 section 2.5.7] - /// - /// ```no_rust - /// The special behavior of [the site-local unicast] prefix defined in [RFC3513] must no longer - /// be supported in new implementations (i.e., new implementations must treat this prefix as - /// Global Unicast). - /// ``` - /// - /// [RFC 4291 section 2.5.7]: https://tools.ietf.org/html/rfc4291#section-2.5.7 - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn is_unicast_global(&self) -> bool { - self.is_unicast() - && !self.is_loopback() - && !self.is_unicast_link_local() - && !self.is_unique_local() - && !self.is_unspecified() - && !self.is_documentation() - && !self.is_benchmarking() - } - - /// Returns the address's multicast scope if the address is multicast. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// - /// use std::net::{Ipv6Addr, Ipv6MulticastScope}; - /// - /// assert_eq!( - /// Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(), - /// Some(Ipv6MulticastScope::Global) - /// ); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use] - #[inline] - pub const fn multicast_scope(&self) -> Option<Ipv6MulticastScope> { - if self.is_multicast() { - match self.segments()[0] & 0x000f { - 1 => Some(Ipv6MulticastScope::InterfaceLocal), - 2 => Some(Ipv6MulticastScope::LinkLocal), - 3 => Some(Ipv6MulticastScope::RealmLocal), - 4 => Some(Ipv6MulticastScope::AdminLocal), - 5 => Some(Ipv6MulticastScope::SiteLocal), - 8 => Some(Ipv6MulticastScope::OrganizationLocal), - 14 => Some(Ipv6MulticastScope::Global), - _ => None, - } - } else { - None - } - } - - /// Returns [`true`] if this is a multicast address (`ff00::/8`). - /// - /// This property is defined by [IETF RFC 4291]. - /// - /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291 - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(since = "1.7.0", feature = "ip_17")] - #[must_use] - #[inline] - pub const fn is_multicast(&self) -> bool { - (self.segments()[0] & 0xff00) == 0xff00 - } - - /// Converts this address to an [`IPv4` address] if it's an [IPv4-mapped] address, - /// as defined in [IETF RFC 4291 section 2.5.5.2], otherwise returns [`None`]. - /// - /// `::ffff:a.b.c.d` becomes `a.b.c.d`. - /// All addresses *not* starting with `::ffff` will return `None`. - /// - /// [`IPv4` address]: Ipv4Addr - /// [IPv4-mapped]: Ipv6Addr - /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4_mapped(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4_mapped(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4_mapped(), None); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[stable(feature = "ipv6_to_ipv4_mapped", since = "1.63.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_ipv4_mapped(&self) -> Option<Ipv4Addr> { - match self.octets() { - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, a, b, c, d] => { - Some(Ipv4Addr::new(a, b, c, d)) - } - _ => None, - } - } - - /// Converts this address to an [`IPv4` address] if it is either - /// an [IPv4-compatible] address as defined in [IETF RFC 4291 section 2.5.5.1], - /// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2], - /// otherwise returns [`None`]. - /// - /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`. Use - /// [`Ipv6Addr::to_ipv4_mapped`] to avoid this. - /// - /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`. - /// All addresses *not* starting with either all zeroes or `::ffff` will return `None`. - /// - /// [`IPv4` address]: Ipv4Addr - /// [IPv4-compatible]: Ipv6Addr#ipv4-compatible-ipv6-addresses - /// [IPv4-mapped]: Ipv6Addr#ipv4-mapped-ipv6-addresses - /// [IETF RFC 4291 section 2.5.5.1]: https://tools.ietf.org/html/rfc4291#section-2.5.5.1 - /// [IETF RFC 4291 section 2.5.5.2]: https://tools.ietf.org/html/rfc4291#section-2.5.5.2 - /// - /// # Examples - /// - /// ``` - /// use std::net::{Ipv4Addr, Ipv6Addr}; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), - /// Some(Ipv4Addr::new(192, 10, 2, 255))); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), - /// Some(Ipv4Addr::new(0, 0, 0, 1))); - /// ``` - #[rustc_const_stable(feature = "const_ip_50", since = "1.50.0")] - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_ipv4(&self) -> Option<Ipv4Addr> { - if let [0, 0, 0, 0, 0, 0 | 0xffff, ab, cd] = self.segments() { - let [a, b] = ab.to_be_bytes(); - let [c, d] = cd.to_be_bytes(); - Some(Ipv4Addr::new(a, b, c, d)) - } else { - None - } - } - - /// Converts this address to an `IpAddr::V4` if it is an IPv4-mapped addresses, otherwise it - /// returns self wrapped in an `IpAddr::V6`. - /// - /// # Examples - /// - /// ``` - /// #![feature(ip)] - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).is_loopback(), false); - /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x7f00, 0x1).to_canonical().is_loopback(), true); - /// ``` - #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] - #[unstable(feature = "ip", issue = "27709")] - #[must_use = "this returns the result of the operation, \ - without modifying the original"] - #[inline] - pub const fn to_canonical(&self) -> IpAddr { - if let Some(mapped) = self.to_ipv4_mapped() { - return IpAddr::V4(mapped); - } - IpAddr::V6(*self) - } - - /// Returns the sixteen eight-bit integers the IPv6 address consists of. - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), - /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - /// ``` - #[rustc_const_stable(feature = "const_ip_32", since = "1.32.0")] - #[stable(feature = "ipv6_to_octets", since = "1.12.0")] - #[must_use] - #[inline] - pub const fn octets(&self) -> [u8; 16] { - self.octets - } -} - -/// Write an Ipv6Addr, conforming to the canonical style described by -/// [RFC 5952](https://tools.ietf.org/html/rfc5952). -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Ipv6Addr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write the IP address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if f.precision().is_none() && f.width().is_none() { - let segments = self.segments(); - - // Special case for :: and ::1; otherwise they get written with the - // IPv4 formatter - if self.is_unspecified() { - f.write_str("::") - } else if self.is_loopback() { - f.write_str("::1") - } else if let Some(ipv4) = self.to_ipv4() { - match segments[5] { - // IPv4 Compatible address - 0 => write!(f, "::{}", ipv4), - // IPv4 Mapped address - 0xffff => write!(f, "::ffff:{}", ipv4), - _ => unreachable!(), - } - } else { - #[derive(Copy, Clone, Default)] - struct Span { - start: usize, - len: usize, - } - - // Find the inner 0 span - let zeroes = { - let mut longest = Span::default(); - let mut current = Span::default(); - - for (i, &segment) in segments.iter().enumerate() { - if segment == 0 { - if current.len == 0 { - current.start = i; - } - - current.len += 1; - - if current.len > longest.len { - longest = current; - } - } else { - current = Span::default(); - } - } - - longest - }; - - /// Write a colon-separated part of the address - #[inline] - fn fmt_subslice(f: &mut fmt::Formatter<'_>, chunk: &[u16]) -> fmt::Result { - if let Some((first, tail)) = chunk.split_first() { - write!(f, "{:x}", first)?; - for segment in tail { - f.write_char(':')?; - write!(f, "{:x}", segment)?; - } - } - Ok(()) - } - - if zeroes.len > 1 { - fmt_subslice(f, &segments[..zeroes.start])?; - f.write_str("::")?; - fmt_subslice(f, &segments[zeroes.start + zeroes.len..]) - } else { - fmt_subslice(f, &segments) - } - } - } else { - const LONGEST_IPV6_ADDR: &str = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV6_ADDR.len() }>::new(); - // Buffer is long enough for the longest possible IPv6 address, so this should never fail. - write!(buf, "{}", self).unwrap(); - - f.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Ipv6Addr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<IpAddr> for Ipv6Addr { - #[inline] - fn eq(&self, other: &IpAddr) -> bool { - match other { - IpAddr::V4(_) => false, - IpAddr::V6(v6) => self == v6, - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialEq<Ipv6Addr> for IpAddr { - #[inline] - fn eq(&self, other: &Ipv6Addr) -> bool { - match self { - IpAddr::V4(_) => false, - IpAddr::V6(v6) => v6 == other, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Ipv6Addr { - #[inline] - fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<Ipv6Addr> for IpAddr { - #[inline] - fn partial_cmp(&self, other: &Ipv6Addr) -> Option<Ordering> { - match self { - IpAddr::V4(_) => Some(Ordering::Less), - IpAddr::V6(v6) => v6.partial_cmp(other), - } - } -} - -#[stable(feature = "ip_cmp", since = "1.16.0")] -impl PartialOrd<IpAddr> for Ipv6Addr { - #[inline] - fn partial_cmp(&self, other: &IpAddr) -> Option<Ordering> { - match other { - IpAddr::V4(_) => Some(Ordering::Greater), - IpAddr::V6(v6) => self.partial_cmp(v6), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Ipv6Addr { - #[inline] - fn cmp(&self, other: &Ipv6Addr) -> Ordering { - self.segments().cmp(&other.segments()) + Ipv4Addr::from(addr.s_addr.to_ne_bytes()) } } impl IntoInner<c::in6_addr> for Ipv6Addr { fn into_inner(self) -> c::in6_addr { - c::in6_addr { s6_addr: self.octets } + c::in6_addr { s6_addr: self.octets() } } } impl FromInner<c::in6_addr> for Ipv6Addr { #[inline] fn from_inner(addr: c::in6_addr) -> Ipv6Addr { - Ipv6Addr { octets: addr.s6_addr } - } -} - -#[stable(feature = "i128", since = "1.26.0")] -impl From<Ipv6Addr> for u128 { - /// Convert an `Ipv6Addr` into a host byte order `u128`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::new( - /// 0x1020, 0x3040, 0x5060, 0x7080, - /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, - /// ); - /// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr)); - /// ``` - #[inline] - fn from(ip: Ipv6Addr) -> u128 { - u128::from_be_bytes(ip.octets) - } -} -#[stable(feature = "i128", since = "1.26.0")] -impl From<u128> for Ipv6Addr { - /// Convert a host byte order `u128` into an `Ipv6Addr`. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from(0x102030405060708090A0B0C0D0E0F00D_u128); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x1020, 0x3040, 0x5060, 0x7080, - /// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D, - /// ), - /// addr); - /// ``` - #[inline] - fn from(ip: u128) -> Ipv6Addr { - Ipv6Addr::from(ip.to_be_bytes()) - } -} - -#[stable(feature = "ipv6_from_octets", since = "1.9.0")] -impl From<[u8; 16]> for Ipv6Addr { - /// Creates an `Ipv6Addr` from a sixteen element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, - /// ]); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a - /// ), - /// addr - /// ); - /// ``` - #[inline] - fn from(octets: [u8; 16]) -> Ipv6Addr { - Ipv6Addr { octets } - } -} - -#[stable(feature = "ipv6_from_segments", since = "1.16.0")] -impl From<[u16; 8]> for Ipv6Addr { - /// Creates an `Ipv6Addr` from an eight element 16-bit array. - /// - /// # Examples - /// - /// ``` - /// use std::net::Ipv6Addr; - /// - /// let addr = Ipv6Addr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, - /// ]); - /// assert_eq!( - /// Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 - /// ), - /// addr - /// ); - /// ``` - #[inline] - fn from(segments: [u16; 8]) -> Ipv6Addr { - let [a, b, c, d, e, f, g, h] = segments; - Ipv6Addr::new(a, b, c, d, e, f, g, h) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u8; 16]> for IpAddr { - /// Creates an `IpAddr::V6` from a sixteen element byte array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = IpAddr::from([ - /// 25u8, 24u8, 23u8, 22u8, 21u8, 20u8, 19u8, 18u8, - /// 17u8, 16u8, 15u8, 14u8, 13u8, 12u8, 11u8, 10u8, - /// ]); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new( - /// 0x1918, 0x1716, - /// 0x1514, 0x1312, - /// 0x1110, 0x0f0e, - /// 0x0d0c, 0x0b0a - /// )), - /// addr - /// ); - /// ``` - #[inline] - fn from(octets: [u8; 16]) -> IpAddr { - IpAddr::V6(Ipv6Addr::from(octets)) - } -} - -#[stable(feature = "ip_from_slice", since = "1.17.0")] -impl From<[u16; 8]> for IpAddr { - /// Creates an `IpAddr::V6` from an eight element 16-bit array. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr}; - /// - /// let addr = IpAddr::from([ - /// 525u16, 524u16, 523u16, 522u16, - /// 521u16, 520u16, 519u16, 518u16, - /// ]); - /// assert_eq!( - /// IpAddr::V6(Ipv6Addr::new( - /// 0x20d, 0x20c, - /// 0x20b, 0x20a, - /// 0x209, 0x208, - /// 0x207, 0x206 - /// )), - /// addr - /// ); - /// ``` - #[inline] - fn from(segments: [u16; 8]) -> IpAddr { - IpAddr::V6(Ipv6Addr::from(segments)) + Ipv6Addr::from(addr.s6_addr) } } diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs index 0eb59d45de7..ab99c0c2fcc 100644 --- a/library/std/src/net/ip_addr/tests.rs +++ b/library/std/src/net/ip_addr/tests.rs @@ -1,1039 +1,8 @@ -use crate::net::test::{sa4, sa6, tsa}; -use crate::net::*; -use crate::str::FromStr; - -#[test] -fn test_from_str_ipv4() { - assert_eq!(Ok(Ipv4Addr::new(127, 0, 0, 1)), "127.0.0.1".parse()); - assert_eq!(Ok(Ipv4Addr::new(255, 255, 255, 255)), "255.255.255.255".parse()); - assert_eq!(Ok(Ipv4Addr::new(0, 0, 0, 0)), "0.0.0.0".parse()); - - // out of range - let none: Option<Ipv4Addr> = "256.0.0.1".parse().ok(); - assert_eq!(None, none); - // too short - let none: Option<Ipv4Addr> = "255.0.0".parse().ok(); - assert_eq!(None, none); - // too long - let none: Option<Ipv4Addr> = "255.0.0.1.2".parse().ok(); - assert_eq!(None, none); - // no number between dots - let none: Option<Ipv4Addr> = "255.0..1".parse().ok(); - assert_eq!(None, none); - // octal - let none: Option<Ipv4Addr> = "255.0.0.01".parse().ok(); - assert_eq!(None, none); - // octal zero - let none: Option<Ipv4Addr> = "255.0.0.00".parse().ok(); - assert_eq!(None, none); - let none: Option<Ipv4Addr> = "255.0.00.0".parse().ok(); - assert_eq!(None, none); -} - -#[test] -fn test_from_str_ipv6() { - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "0:0:0:0:0:0:0:0".parse()); - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "0:0:0:0:0:0:0:1".parse()); - - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), "::1".parse()); - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), "::".parse()); - - assert_eq!(Ok(Ipv6Addr::new(0x2a02, 0x6b8, 0, 0, 0, 0, 0x11, 0x11)), "2a02:6b8::11:11".parse()); - - // too long group - let none: Option<Ipv6Addr> = "::00000".parse().ok(); - assert_eq!(None, none); - // too short - let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7".parse().ok(); - assert_eq!(None, none); - // too long - let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:8:9".parse().ok(); - assert_eq!(None, none); - // triple colon - let none: Option<Ipv6Addr> = "1:2:::6:7:8".parse().ok(); - assert_eq!(None, none); - // two double colons - let none: Option<Ipv6Addr> = "1:2::6::8".parse().ok(); - assert_eq!(None, none); - // `::` indicating zero groups of zeros - let none: Option<Ipv6Addr> = "1:2:3:4::5:6:7:8".parse().ok(); - assert_eq!(None, none); -} - -#[test] -fn test_from_str_ipv4_in_ipv6() { - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 49152, 545)), "::192.0.2.33".parse()); - assert_eq!(Ok(Ipv6Addr::new(0, 0, 0, 0, 0, 0xFFFF, 49152, 545)), "::FFFF:192.0.2.33".parse()); - assert_eq!( - Ok(Ipv6Addr::new(0x64, 0xff9b, 0, 0, 0, 0, 49152, 545)), - "64:ff9b::192.0.2.33".parse() - ); - assert_eq!( - Ok(Ipv6Addr::new(0x2001, 0xdb8, 0x122, 0xc000, 0x2, 0x2100, 49152, 545)), - "2001:db8:122:c000:2:2100:192.0.2.33".parse() - ); - - // colon after v4 - let none: Option<Ipv4Addr> = "::127.0.0.1:".parse().ok(); - assert_eq!(None, none); - // not enough groups - let none: Option<Ipv6Addr> = "1:2:3:4:5:127.0.0.1".parse().ok(); - assert_eq!(None, none); - // too many groups - let none: Option<Ipv6Addr> = "1:2:3:4:5:6:7:127.0.0.1".parse().ok(); - assert_eq!(None, none); -} - -#[test] -fn test_from_str_socket_addr() { - assert_eq!(Ok(sa4(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); - assert_eq!(Ok(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 80)), "77.88.21.11:80".parse()); - assert_eq!( - Ok(sa6(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53)), - "[2a02:6b8:0:1::1]:53".parse() - ); - assert_eq!( - Ok(SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0)), - "[2a02:6b8:0:1::1]:53".parse() - ); - assert_eq!(Ok(sa6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22)), "[::127.0.0.1]:22".parse()); - assert_eq!( - Ok(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x7F00, 1), 22, 0, 0)), - "[::127.0.0.1]:22".parse() - ); - - // without port - let none: Option<SocketAddr> = "127.0.0.1".parse().ok(); - assert_eq!(None, none); - // without port - let none: Option<SocketAddr> = "127.0.0.1:".parse().ok(); - assert_eq!(None, none); - // wrong brackets around v4 - let none: Option<SocketAddr> = "[127.0.0.1]:22".parse().ok(); - assert_eq!(None, none); - // port out of range - let none: Option<SocketAddr> = "127.0.0.1:123456".parse().ok(); - assert_eq!(None, none); -} - -#[test] -fn ipv4_addr_to_string() { - assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_string(), "127.0.0.1"); - // Short address - assert_eq!(Ipv4Addr::new(1, 1, 1, 1).to_string(), "1.1.1.1"); - // Long address - assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127"); - - // Test padding - assert_eq!(format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 "); - assert_eq!(format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1"); -} - -#[test] -fn ipv6_addr_to_string() { - // ipv4-mapped address - let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); - assert_eq!(a1.to_string(), "::ffff:192.0.2.128"); - - // ipv4-compatible address - let a1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0xc000, 0x280); - assert_eq!(a1.to_string(), "::192.0.2.128"); - - // v6 address with no zero segments - assert_eq!(Ipv6Addr::new(8, 9, 10, 11, 12, 13, 14, 15).to_string(), "8:9:a:b:c:d:e:f"); - - // longest possible IPv6 length - assert_eq!( - Ipv6Addr::new(0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888).to_string(), - "1111:2222:3333:4444:5555:6666:7777:8888" - ); - // padding - assert_eq!(format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 "); - assert_eq!(format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8"); - - // reduce a single run of zeros - assert_eq!( - "ae::ffff:102:304", - Ipv6Addr::new(0xae, 0, 0, 0, 0, 0xffff, 0x0102, 0x0304).to_string() - ); - - // don't reduce just a single zero segment - assert_eq!("1:2:3:4:5:6:0:8", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 0, 8).to_string()); - - // 'any' address - assert_eq!("::", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).to_string()); - - // loopback address - assert_eq!("::1", Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_string()); - - // ends in zeros - assert_eq!("1::", Ipv6Addr::new(1, 0, 0, 0, 0, 0, 0, 0).to_string()); - - // two runs of zeros, second one is longer - assert_eq!("1:0:0:4::8", Ipv6Addr::new(1, 0, 0, 4, 0, 0, 0, 8).to_string()); - - // two runs of zeros, equal length - assert_eq!("1::4:5:0:0:8", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8).to_string()); - - // don't prefix `0x` to each segment in `dbg!`. - assert_eq!("1::4:5:0:0:8", &format!("{:#?}", Ipv6Addr::new(1, 0, 0, 4, 5, 0, 0, 8))); -} - -#[test] -fn ipv4_to_ipv6() { - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678), - Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_mapped() - ); - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678), - Ipv4Addr::new(0x12, 0x34, 0x56, 0x78).to_ipv6_compatible() - ); -} - -#[test] -fn ipv6_to_ipv4_mapped() { - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4_mapped(), - Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) - ); - assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4_mapped(), None); -} - -#[test] -fn ipv6_to_ipv4() { - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678).to_ipv4(), - Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) - ); - assert_eq!( - Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), - Some(Ipv4Addr::new(0x12, 0x34, 0x56, 0x78)) - ); - assert_eq!(Ipv6Addr::new(0, 0, 1, 0, 0, 0, 0x1234, 0x5678).to_ipv4(), None); -} - -#[test] -fn ip_properties() { - macro_rules! ip { - ($s:expr) => { - IpAddr::from_str($s).unwrap() - }; - } - - macro_rules! check { - ($s:expr) => { - check!($s, 0); - }; - - ($s:expr, $mask:expr) => {{ - let unspec: u8 = 1 << 0; - let loopback: u8 = 1 << 1; - let global: u8 = 1 << 2; - let multicast: u8 = 1 << 3; - let doc: u8 = 1 << 4; - let benchmarking: u8 = 1 << 5; - - if ($mask & unspec) == unspec { - assert!(ip!($s).is_unspecified()); - } else { - assert!(!ip!($s).is_unspecified()); - } - - if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); - } else { - assert!(!ip!($s).is_loopback()); - } - - if ($mask & global) == global { - assert!(ip!($s).is_global()); - } else { - assert!(!ip!($s).is_global()); - } - - if ($mask & multicast) == multicast { - assert!(ip!($s).is_multicast()); - } else { - assert!(!ip!($s).is_multicast()); - } - - if ($mask & doc) == doc { - assert!(ip!($s).is_documentation()); - } else { - assert!(!ip!($s).is_documentation()); - } - - if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); - } else { - assert!(!ip!($s).is_benchmarking()); - } - }}; - } - - let unspec: u8 = 1 << 0; - let loopback: u8 = 1 << 1; - let global: u8 = 1 << 2; - let multicast: u8 = 1 << 3; - let doc: u8 = 1 << 4; - let benchmarking: u8 = 1 << 5; - - check!("0.0.0.0", unspec); - check!("0.0.0.1"); - check!("0.1.0.0"); - check!("10.9.8.7"); - check!("127.1.2.3", loopback); - check!("172.31.254.253"); - check!("169.254.253.242"); - check!("192.0.2.183", doc); - check!("192.1.2.183", global); - check!("192.168.254.253"); - check!("198.51.100.0", doc); - check!("203.0.113.0", doc); - check!("203.2.113.0", global); - check!("224.0.0.0", global | multicast); - check!("239.255.255.255", global | multicast); - check!("255.255.255.255"); - // make sure benchmarking addresses are not global - check!("198.18.0.0", benchmarking); - check!("198.18.54.2", benchmarking); - check!("198.19.255.255", benchmarking); - // make sure addresses reserved for protocol assignment are not global - check!("192.0.0.0"); - check!("192.0.0.255"); - check!("192.0.0.100"); - // make sure reserved addresses are not global - check!("240.0.0.0"); - check!("251.54.1.76"); - check!("254.255.255.255"); - // make sure shared addresses are not global - check!("100.64.0.0"); - check!("100.127.255.255"); - check!("100.100.100.0"); - - check!("::", unspec); - check!("::1", loopback); - check!("::0.0.0.2", global); - check!("1::", global); - check!("fc00::"); - check!("fdff:ffff::"); - check!("fe80:ffff::"); - check!("febf:ffff::"); - check!("fec0::", global); - check!("ff01::", global | multicast); - check!("ff02::", global | multicast); - check!("ff03::", global | multicast); - check!("ff04::", global | multicast); - check!("ff05::", global | multicast); - check!("ff08::", global | multicast); - check!("ff0e::", global | multicast); - check!("2001:db8:85a3::8a2e:370:7334", doc); - check!("2001:2::ac32:23ff:21", benchmarking); - check!("102:304:506:708:90a:b0c:d0e:f10", global); -} - -#[test] -fn ipv4_properties() { - macro_rules! ip { - ($s:expr) => { - Ipv4Addr::from_str($s).unwrap() - }; - } - - macro_rules! check { - ($s:expr) => { - check!($s, 0); - }; - - ($s:expr, $mask:expr) => {{ - let unspec: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let private: u16 = 1 << 2; - let link_local: u16 = 1 << 3; - let global: u16 = 1 << 4; - let multicast: u16 = 1 << 5; - let broadcast: u16 = 1 << 6; - let documentation: u16 = 1 << 7; - let benchmarking: u16 = 1 << 8; - let reserved: u16 = 1 << 10; - let shared: u16 = 1 << 11; - - if ($mask & unspec) == unspec { - assert!(ip!($s).is_unspecified()); - } else { - assert!(!ip!($s).is_unspecified()); - } - - if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); - } else { - assert!(!ip!($s).is_loopback()); - } - - if ($mask & private) == private { - assert!(ip!($s).is_private()); - } else { - assert!(!ip!($s).is_private()); - } - - if ($mask & link_local) == link_local { - assert!(ip!($s).is_link_local()); - } else { - assert!(!ip!($s).is_link_local()); - } - - if ($mask & global) == global { - assert!(ip!($s).is_global()); - } else { - assert!(!ip!($s).is_global()); - } - - if ($mask & multicast) == multicast { - assert!(ip!($s).is_multicast()); - } else { - assert!(!ip!($s).is_multicast()); - } - - if ($mask & broadcast) == broadcast { - assert!(ip!($s).is_broadcast()); - } else { - assert!(!ip!($s).is_broadcast()); - } - - if ($mask & documentation) == documentation { - assert!(ip!($s).is_documentation()); - } else { - assert!(!ip!($s).is_documentation()); - } - - if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); - } else { - assert!(!ip!($s).is_benchmarking()); - } - - if ($mask & reserved) == reserved { - assert!(ip!($s).is_reserved()); - } else { - assert!(!ip!($s).is_reserved()); - } - - if ($mask & shared) == shared { - assert!(ip!($s).is_shared()); - } else { - assert!(!ip!($s).is_shared()); - } - }}; - } - - let unspec: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let private: u16 = 1 << 2; - let link_local: u16 = 1 << 3; - let global: u16 = 1 << 4; - let multicast: u16 = 1 << 5; - let broadcast: u16 = 1 << 6; - let documentation: u16 = 1 << 7; - let benchmarking: u16 = 1 << 8; - let reserved: u16 = 1 << 10; - let shared: u16 = 1 << 11; - - check!("0.0.0.0", unspec); - check!("0.0.0.1"); - check!("0.1.0.0"); - check!("10.9.8.7", private); - check!("127.1.2.3", loopback); - check!("172.31.254.253", private); - check!("169.254.253.242", link_local); - check!("192.0.2.183", documentation); - check!("192.1.2.183", global); - check!("192.168.254.253", private); - check!("198.51.100.0", documentation); - check!("203.0.113.0", documentation); - check!("203.2.113.0", global); - check!("224.0.0.0", global | multicast); - check!("239.255.255.255", global | multicast); - check!("255.255.255.255", broadcast); - check!("198.18.0.0", benchmarking); - check!("198.18.54.2", benchmarking); - check!("198.19.255.255", benchmarking); - check!("192.0.0.0"); - check!("192.0.0.255"); - check!("192.0.0.100"); - check!("240.0.0.0", reserved); - check!("251.54.1.76", reserved); - check!("254.255.255.255", reserved); - check!("100.64.0.0", shared); - check!("100.127.255.255", shared); - check!("100.100.100.0", shared); -} - -#[test] -fn ipv6_properties() { - macro_rules! ip { - ($s:expr) => { - Ipv6Addr::from_str($s).unwrap() - }; - } - - macro_rules! check { - ($s:expr, &[$($octet:expr),*], $mask:expr) => { - assert_eq!($s, ip!($s).to_string()); - let octets = &[$($octet),*]; - assert_eq!(&ip!($s).octets(), octets); - assert_eq!(Ipv6Addr::from(*octets), ip!($s)); - - let unspecified: u32 = 1 << 0; - let loopback: u32 = 1 << 1; - let unique_local: u32 = 1 << 2; - let global: u32 = 1 << 3; - let unicast_link_local: u32 = 1 << 4; - let unicast_global: u32 = 1 << 7; - let documentation: u32 = 1 << 8; - let benchmarking: u32 = 1 << 16; - let multicast_interface_local: u32 = 1 << 9; - let multicast_link_local: u32 = 1 << 10; - let multicast_realm_local: u32 = 1 << 11; - let multicast_admin_local: u32 = 1 << 12; - let multicast_site_local: u32 = 1 << 13; - let multicast_organization_local: u32 = 1 << 14; - let multicast_global: u32 = 1 << 15; - let multicast: u32 = multicast_interface_local - | multicast_admin_local - | multicast_global - | multicast_link_local - | multicast_realm_local - | multicast_site_local - | multicast_organization_local; - - if ($mask & unspecified) == unspecified { - assert!(ip!($s).is_unspecified()); - } else { - assert!(!ip!($s).is_unspecified()); - } - if ($mask & loopback) == loopback { - assert!(ip!($s).is_loopback()); - } else { - assert!(!ip!($s).is_loopback()); - } - if ($mask & unique_local) == unique_local { - assert!(ip!($s).is_unique_local()); - } else { - assert!(!ip!($s).is_unique_local()); - } - if ($mask & global) == global { - assert!(ip!($s).is_global()); - } else { - assert!(!ip!($s).is_global()); - } - if ($mask & unicast_link_local) == unicast_link_local { - assert!(ip!($s).is_unicast_link_local()); - } else { - assert!(!ip!($s).is_unicast_link_local()); - } - if ($mask & unicast_global) == unicast_global { - assert!(ip!($s).is_unicast_global()); - } else { - assert!(!ip!($s).is_unicast_global()); - } - if ($mask & documentation) == documentation { - assert!(ip!($s).is_documentation()); - } else { - assert!(!ip!($s).is_documentation()); - } - if ($mask & benchmarking) == benchmarking { - assert!(ip!($s).is_benchmarking()); - } else { - assert!(!ip!($s).is_benchmarking()); - } - if ($mask & multicast) != 0 { - assert!(ip!($s).multicast_scope().is_some()); - assert!(ip!($s).is_multicast()); - } else { - assert!(ip!($s).multicast_scope().is_none()); - assert!(!ip!($s).is_multicast()); - } - if ($mask & multicast_interface_local) == multicast_interface_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::InterfaceLocal); - } - if ($mask & multicast_link_local) == multicast_link_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::LinkLocal); - } - if ($mask & multicast_realm_local) == multicast_realm_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::RealmLocal); - } - if ($mask & multicast_admin_local) == multicast_admin_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::AdminLocal); - } - if ($mask & multicast_site_local) == multicast_site_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::SiteLocal); - } - if ($mask & multicast_organization_local) == multicast_organization_local { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::OrganizationLocal); - } - if ($mask & multicast_global) == multicast_global { - assert_eq!(ip!($s).multicast_scope().unwrap(), - Ipv6MulticastScope::Global); - } - } - } - - let unspecified: u32 = 1 << 0; - let loopback: u32 = 1 << 1; - let unique_local: u32 = 1 << 2; - let global: u32 = 1 << 3; - let unicast_link_local: u32 = 1 << 4; - let unicast_global: u32 = 1 << 7; - let documentation: u32 = 1 << 8; - let benchmarking: u32 = 1 << 16; - let multicast_interface_local: u32 = 1 << 9; - let multicast_link_local: u32 = 1 << 10; - let multicast_realm_local: u32 = 1 << 11; - let multicast_admin_local: u32 = 1 << 12; - let multicast_site_local: u32 = 1 << 13; - let multicast_organization_local: u32 = 1 << 14; - let multicast_global: u32 = 1 << 15; - - check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified); - - check!("::1", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], loopback); - - check!("::0.0.0.2", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], global | unicast_global); - - check!("1::", &[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); - - check!( - "::ffff:127.0.0.1", - &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0x7f, 0, 0, 1], - unicast_global - ); - - check!( - "64:ff9b:1::", - &[0, 0x64, 0xff, 0x9b, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_global - ); - - check!("100::", &[0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); - - check!("2001::", &[0x20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); - - check!( - "2001:1::1", - &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - global | unicast_global - ); - - check!( - "2001:1::2", - &[0x20, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], - global | unicast_global - ); - - check!( - "2001:3::", - &[0x20, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!( - "2001:4:112::", - &[0x20, 1, 0, 4, 1, 0x12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!( - "2001:20::", - &[0x20, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); - - check!( - "2001:200::", - &[0x20, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - global | unicast_global - ); - - check!("fc00::", &[0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unique_local); - - check!( - "fdff:ffff::", - &[0xfd, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unique_local - ); - - check!( - "fe80:ffff::", - &[0xfe, 0x80, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!("fe80::", &[0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local); - - check!( - "febf:ffff::", - &[0xfe, 0xbf, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!("febf::", &[0xfe, 0xbf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_link_local); - - check!( - "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - &[ - 0xfe, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff - ], - unicast_link_local - ); - - check!( - "fe80::ffff:ffff:ffff:ffff", - &[ - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff - ], - unicast_link_local - ); - - check!( - "fe80:0:0:1::", - &[0xfe, 0x80, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_link_local - ); - - check!( - "fec0::", - &[0xfe, 0xc0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - unicast_global | global - ); - - check!( - "ff01::", - &[0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_interface_local | global - ); - - check!( - "ff02::", - &[0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_link_local | global - ); - - check!( - "ff03::", - &[0xff, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_realm_local | global - ); - - check!( - "ff04::", - &[0xff, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_admin_local | global - ); - - check!( - "ff05::", - &[0xff, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_site_local | global - ); - - check!( - "ff08::", - &[0xff, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_organization_local | global - ); - - check!( - "ff0e::", - &[0xff, 0xe, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - multicast_global | global - ); - - check!( - "2001:db8:85a3::8a2e:370:7334", - &[0x20, 1, 0xd, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 3, 0x70, 0x73, 0x34], - documentation - ); - - check!( - "2001:2::ac32:23ff:21", - &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], - benchmarking - ); - - check!( - "102:304:506:708:90a:b0c:d0e:f10", - &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - global | unicast_global - ); -} +use crate::net::test::{sa4, tsa}; +use crate::net::Ipv4Addr; #[test] fn to_socket_addr_socketaddr() { let a = sa4(Ipv4Addr::new(77, 88, 21, 11), 12345); assert_eq!(Ok(vec![a]), tsa(a)); } - -#[test] -fn test_ipv4_to_int() { - let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); - assert_eq!(u32::from(a), 0x11223344); -} - -#[test] -fn test_int_to_ipv4() { - let a = Ipv4Addr::new(0x11, 0x22, 0x33, 0x44); - assert_eq!(Ipv4Addr::from(0x11223344), a); -} - -#[test] -fn test_ipv6_to_int() { - let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); - assert_eq!(u128::from(a), 0x112233445566778899aabbccddeeff11u128); -} - -#[test] -fn test_int_to_ipv6() { - let a = Ipv6Addr::new(0x1122, 0x3344, 0x5566, 0x7788, 0x99aa, 0xbbcc, 0xddee, 0xff11); - assert_eq!(Ipv6Addr::from(0x112233445566778899aabbccddeeff11u128), a); -} - -#[test] -fn ipv4_from_constructors() { - assert_eq!(Ipv4Addr::LOCALHOST, Ipv4Addr::new(127, 0, 0, 1)); - assert!(Ipv4Addr::LOCALHOST.is_loopback()); - assert_eq!(Ipv4Addr::UNSPECIFIED, Ipv4Addr::new(0, 0, 0, 0)); - assert!(Ipv4Addr::UNSPECIFIED.is_unspecified()); - assert_eq!(Ipv4Addr::BROADCAST, Ipv4Addr::new(255, 255, 255, 255)); - assert!(Ipv4Addr::BROADCAST.is_broadcast()); -} - -#[test] -fn ipv6_from_constructors() { - assert_eq!(Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - assert!(Ipv6Addr::LOCALHOST.is_loopback()); - assert_eq!(Ipv6Addr::UNSPECIFIED, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)); - assert!(Ipv6Addr::UNSPECIFIED.is_unspecified()); -} - -#[test] -fn ipv4_from_octets() { - assert_eq!(Ipv4Addr::from([127, 0, 0, 1]), Ipv4Addr::new(127, 0, 0, 1)) -} - -#[test] -fn ipv6_from_segments() { - let from_u16s = - Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); - let new = Ipv6Addr::new(0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff); - assert_eq!(new, from_u16s); -} - -#[test] -fn ipv6_from_octets() { - let from_u16s = - Ipv6Addr::from([0x0011, 0x2233, 0x4455, 0x6677, 0x8899, 0xaabb, 0xccdd, 0xeeff]); - let from_u8s = Ipv6Addr::from([ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]); - assert_eq!(from_u16s, from_u8s); -} - -#[test] -fn cmp() { - let v41 = Ipv4Addr::new(100, 64, 3, 3); - let v42 = Ipv4Addr::new(192, 0, 2, 2); - let v61 = "2001:db8:f00::1002".parse::<Ipv6Addr>().unwrap(); - let v62 = "2001:db8:f00::2001".parse::<Ipv6Addr>().unwrap(); - assert!(v41 < v42); - assert!(v61 < v62); - - assert_eq!(v41, IpAddr::V4(v41)); - assert_eq!(v61, IpAddr::V6(v61)); - assert!(v41 != IpAddr::V4(v42)); - assert!(v61 != IpAddr::V6(v62)); - - assert!(v41 < IpAddr::V4(v42)); - assert!(v61 < IpAddr::V6(v62)); - assert!(IpAddr::V4(v41) < v42); - assert!(IpAddr::V6(v61) < v62); - - assert!(v41 < IpAddr::V6(v61)); - assert!(IpAddr::V4(v41) < v61); -} - -#[test] -fn is_v4() { - let ip = IpAddr::V4(Ipv4Addr::new(100, 64, 3, 3)); - assert!(ip.is_ipv4()); - assert!(!ip.is_ipv6()); -} - -#[test] -fn is_v6() { - let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x1234, 0x5678)); - assert!(!ip.is_ipv4()); - assert!(ip.is_ipv6()); -} - -#[test] -fn ipv4_const() { - // test that the methods of `Ipv4Addr` are usable in a const context - - const IP_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1); - assert_eq!(IP_ADDRESS, Ipv4Addr::LOCALHOST); - - const OCTETS: [u8; 4] = IP_ADDRESS.octets(); - assert_eq!(OCTETS, [127, 0, 0, 1]); - - const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); - assert!(!IS_UNSPECIFIED); - - const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); - assert!(IS_LOOPBACK); - - const IS_PRIVATE: bool = IP_ADDRESS.is_private(); - assert!(!IS_PRIVATE); - - const IS_LINK_LOCAL: bool = IP_ADDRESS.is_link_local(); - assert!(!IS_LINK_LOCAL); - - const IS_GLOBAL: bool = IP_ADDRESS.is_global(); - assert!(!IS_GLOBAL); - - const IS_SHARED: bool = IP_ADDRESS.is_shared(); - assert!(!IS_SHARED); - - const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); - assert!(!IS_BENCHMARKING); - - const IS_RESERVED: bool = IP_ADDRESS.is_reserved(); - assert!(!IS_RESERVED); - - const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); - assert!(!IS_MULTICAST); - - const IS_BROADCAST: bool = IP_ADDRESS.is_broadcast(); - assert!(!IS_BROADCAST); - - const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); - assert!(!IS_DOCUMENTATION); - - const IP_V6_COMPATIBLE: Ipv6Addr = IP_ADDRESS.to_ipv6_compatible(); - assert_eq!( - IP_V6_COMPATIBLE, - Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 1]) - ); - - const IP_V6_MAPPED: Ipv6Addr = IP_ADDRESS.to_ipv6_mapped(); - assert_eq!( - IP_V6_MAPPED, - Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1]) - ); -} - -#[test] -fn ipv6_const() { - // test that the methods of `Ipv6Addr` are usable in a const context - - const IP_ADDRESS: Ipv6Addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); - assert_eq!(IP_ADDRESS, Ipv6Addr::LOCALHOST); - - const SEGMENTS: [u16; 8] = IP_ADDRESS.segments(); - assert_eq!(SEGMENTS, [0, 0, 0, 0, 0, 0, 0, 1]); - - const OCTETS: [u8; 16] = IP_ADDRESS.octets(); - assert_eq!(OCTETS, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); - - const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); - assert!(!IS_UNSPECIFIED); - - const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); - assert!(IS_LOOPBACK); - - const IS_GLOBAL: bool = IP_ADDRESS.is_global(); - assert!(!IS_GLOBAL); - - const IS_UNIQUE_LOCAL: bool = IP_ADDRESS.is_unique_local(); - assert!(!IS_UNIQUE_LOCAL); - - const IS_UNICAST_LINK_LOCAL: bool = IP_ADDRESS.is_unicast_link_local(); - assert!(!IS_UNICAST_LINK_LOCAL); - - const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); - assert!(!IS_DOCUMENTATION); - - const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); - assert!(!IS_BENCHMARKING); - - const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global(); - assert!(!IS_UNICAST_GLOBAL); - - const MULTICAST_SCOPE: Option<Ipv6MulticastScope> = IP_ADDRESS.multicast_scope(); - assert_eq!(MULTICAST_SCOPE, None); - - const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); - assert!(!IS_MULTICAST); - - const IP_V4: Option<Ipv4Addr> = IP_ADDRESS.to_ipv4(); - assert_eq!(IP_V4.unwrap(), Ipv4Addr::new(0, 0, 0, 1)); -} - -#[test] -fn ip_const() { - // test that the methods of `IpAddr` are usable in a const context - - const IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); - - const IS_UNSPECIFIED: bool = IP_ADDRESS.is_unspecified(); - assert!(!IS_UNSPECIFIED); - - const IS_LOOPBACK: bool = IP_ADDRESS.is_loopback(); - assert!(IS_LOOPBACK); - - const IS_GLOBAL: bool = IP_ADDRESS.is_global(); - assert!(!IS_GLOBAL); - - const IS_MULTICAST: bool = IP_ADDRESS.is_multicast(); - assert!(!IS_MULTICAST); - - const IS_IP_V4: bool = IP_ADDRESS.is_ipv4(); - assert!(IS_IP_V4); - - const IS_IP_V6: bool = IP_ADDRESS.is_ipv6(); - assert!(!IS_IP_V6); -} - -#[test] -fn structural_match() { - // test that all IP types can be structurally matched upon - - const IPV4: Ipv4Addr = Ipv4Addr::LOCALHOST; - match IPV4 { - Ipv4Addr::LOCALHOST => {} - _ => unreachable!(), - } - - const IPV6: Ipv6Addr = Ipv6Addr::LOCALHOST; - match IPV6 { - Ipv6Addr::LOCALHOST => {} - _ => unreachable!(), - } - - const IP: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); - match IP { - IpAddr::V4(Ipv4Addr::LOCALHOST) => {} - _ => unreachable!(), - } -} diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 19d90e7ec38..bcab15db35b 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -26,8 +26,6 @@ use crate::io::{self, ErrorKind}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::parser::AddrParseError; -#[stable(feature = "rust1", since = "1.0.0")] pub use self::socket_addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[unstable(feature = "tcplistener_into_incoming", issue = "88339")] pub use self::tcp::IntoIncoming; @@ -35,10 +33,10 @@ pub use self::tcp::IntoIncoming; pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; +#[stable(feature = "rust1", since = "1.0.0")] +pub use core::net::AddrParseError; -mod display_buffer; mod ip_addr; -mod parser; mod socket_addr; mod tcp; #[cfg(test)] diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 1264bae809b..421fed9077c 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -1,9 +1,7 @@ +// Tests for this module #[cfg(all(test, not(target_os = "emscripten")))] mod tests; -use crate::cmp::Ordering; -use crate::fmt::{self, Write}; -use crate::hash; use crate::io; use crate::iter; use crate::mem; @@ -15,533 +13,23 @@ use crate::sys_common::net::LookupHost; use crate::sys_common::{FromInner, IntoInner}; use crate::vec; -use super::display_buffer::DisplayBuffer; - -/// An internet socket address, either IPv4 or IPv6. -/// -/// Internet socket addresses consist of an [IP address], a 16-bit port number, as well -/// as possibly some version-dependent additional information. See [`SocketAddrV4`]'s and -/// [`SocketAddrV6`]'s respective documentation for more details. -/// -/// The size of a `SocketAddr` instance may vary depending on the target operating -/// system. -/// -/// [IP address]: IpAddr -/// -/// # Examples -/// -/// ``` -/// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -/// -/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); -/// -/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); -/// assert_eq!(socket.port(), 8080); -/// assert_eq!(socket.is_ipv4(), true); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum SocketAddr { - /// An IPv4 socket address. - #[stable(feature = "rust1", since = "1.0.0")] - V4(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV4), - /// An IPv6 socket address. - #[stable(feature = "rust1", since = "1.0.0")] - V6(#[stable(feature = "rust1", since = "1.0.0")] SocketAddrV6), -} - -/// An IPv4 socket address. -/// -/// IPv4 socket addresses consist of an [`IPv4` address] and a 16-bit port number, as -/// stated in [IETF RFC 793]. -/// -/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. -/// -/// The size of a `SocketAddrV4` struct may vary depending on the target operating -/// system. Do not assume that this type has the same memory layout as the underlying -/// system representation. -/// -/// [IETF RFC 793]: https://tools.ietf.org/html/rfc793 -/// [`IPv4` address]: Ipv4Addr -/// -/// # Examples -/// -/// ``` -/// use std::net::{Ipv4Addr, SocketAddrV4}; -/// -/// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); -/// -/// assert_eq!("127.0.0.1:8080".parse(), Ok(socket)); -/// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); -/// assert_eq!(socket.port(), 8080); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SocketAddrV4 { - ip: Ipv4Addr, - port: u16, -} - -/// An IPv6 socket address. -/// -/// IPv6 socket addresses consist of an [`IPv6` address], a 16-bit port number, as well -/// as fields containing the traffic class, the flow label, and a scope identifier -/// (see [IETF RFC 2553, Section 3.3] for more details). -/// -/// See [`SocketAddr`] for a type encompassing both IPv4 and IPv6 socket addresses. -/// -/// The size of a `SocketAddrV6` struct may vary depending on the target operating -/// system. Do not assume that this type has the same memory layout as the underlying -/// system representation. -/// -/// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 -/// [`IPv6` address]: Ipv6Addr -/// -/// # Examples -/// -/// ``` -/// use std::net::{Ipv6Addr, SocketAddrV6}; -/// -/// let socket = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0); -/// -/// assert_eq!("[2001:db8::1]:8080".parse(), Ok(socket)); -/// assert_eq!(socket.ip(), &Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)); -/// assert_eq!(socket.port(), 8080); -/// ``` -#[derive(Copy, Clone, Eq, PartialEq)] #[stable(feature = "rust1", since = "1.0.0")] -pub struct SocketAddrV6 { - ip: Ipv6Addr, - port: u16, - flowinfo: u32, - scope_id: u32, -} - -impl SocketAddr { - /// Creates a new socket address from an [IP address] and a port number. - /// - /// [IP address]: IpAddr - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[stable(feature = "ip_addr", since = "1.7.0")] - #[must_use] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn new(ip: IpAddr, port: u16) -> SocketAddr { - match ip { - IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)), - IpAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0)), - } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); - /// ``` - #[must_use] - #[stable(feature = "ip_addr", since = "1.7.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn ip(&self) -> IpAddr { - match *self { - SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()), - SocketAddr::V6(ref a) => IpAddr::V6(*a.ip()), - } - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// socket.set_ip(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); - /// assert_eq!(socket.ip(), IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: IpAddr) { - // `match (*self, new_ip)` would have us mutate a copy of self only to throw it away. - match (self, new_ip) { - (&mut SocketAddr::V4(ref mut a), IpAddr::V4(new_ip)) => a.set_ip(new_ip), - (&mut SocketAddr::V6(ref mut a), IpAddr::V6(new_ip)) => a.set_ip(new_ip), - (self_, new_ip) => *self_ = Self::new(new_ip, self_.port()), - } - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn port(&self) -> u16 { - match *self { - SocketAddr::V4(ref a) => a.port(), - SocketAddr::V6(ref a) => a.port(), - } - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let mut socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// socket.set_port(1025); - /// assert_eq!(socket.port(), 1025); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - match *self { - SocketAddr::V4(ref mut a) => a.set_port(new_port), - SocketAddr::V6(ref mut a) => a.set_port(new_port), - } - } - - /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [`IPv4` address], and [`false`] otherwise. - /// - /// [IP address]: IpAddr - /// [`IPv4` address]: IpAddr::V4 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); - /// assert_eq!(socket.is_ipv4(), true); - /// assert_eq!(socket.is_ipv6(), false); - /// ``` - #[must_use] - #[stable(feature = "sockaddr_checker", since = "1.16.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn is_ipv4(&self) -> bool { - matches!(*self, SocketAddr::V4(_)) - } - - /// Returns [`true`] if the [IP address] in this `SocketAddr` is an - /// [`IPv6` address], and [`false`] otherwise. - /// - /// [IP address]: IpAddr - /// [`IPv6` address]: IpAddr::V6 - /// - /// # Examples - /// - /// ``` - /// use std::net::{IpAddr, Ipv6Addr, SocketAddr}; - /// - /// let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 65535, 0, 1)), 8080); - /// assert_eq!(socket.is_ipv4(), false); - /// assert_eq!(socket.is_ipv6(), true); - /// ``` - #[must_use] - #[stable(feature = "sockaddr_checker", since = "1.16.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn is_ipv6(&self) -> bool { - matches!(*self, SocketAddr::V6(_)) - } -} - -impl SocketAddrV4 { - /// Creates a new socket address from an [`IPv4` address] and a port number. - /// - /// [`IPv4` address]: Ipv4Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 { - SocketAddrV4 { ip, port } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn ip(&self) -> &Ipv4Addr { - &self.ip - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1)); - /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1)); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: Ipv4Addr) { - self.ip = new_ip; - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn port(&self) -> u16 { - self.port - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV4, Ipv4Addr}; - /// - /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); - /// socket.set_port(4242); - /// assert_eq!(socket.port(), 4242); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - self.port = new_port; - } -} - -impl SocketAddrV6 { - /// Creates a new socket address from an [`IPv6` address], a 16-bit port number, - /// and the `flowinfo` and `scope_id` fields. - /// - /// For more information on the meaning and layout of the `flowinfo` and `scope_id` - /// parameters, see [IETF RFC 2553, Section 3.3]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// [`IPv6` address]: Ipv6Addr - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 { - SocketAddrV6 { ip, port, flowinfo, scope_id } - } - - /// Returns the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn ip(&self) -> &Ipv6Addr { - &self.ip - } - - /// Changes the IP address associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); - /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_ip(&mut self, new_ip: Ipv6Addr) { - self.ip = new_ip; - } - - /// Returns the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// assert_eq!(socket.port(), 8080); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn port(&self) -> u16 { - self.port - } - - /// Changes the port number associated with this socket address. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); - /// socket.set_port(4242); - /// assert_eq!(socket.port(), 4242); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_port(&mut self, new_port: u16) { - self.port = new_port; - } - - /// Returns the flow information associated with this address. - /// - /// This information corresponds to the `sin6_flowinfo` field in C's `netinet/in.h`, - /// as specified in [IETF RFC 2553, Section 3.3]. - /// It combines information about the flow label and the traffic class as specified - /// in [IETF RFC 2460], respectively [Section 6] and [Section 7]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// [IETF RFC 2460]: https://tools.ietf.org/html/rfc2460 - /// [Section 6]: https://tools.ietf.org/html/rfc2460#section-6 - /// [Section 7]: https://tools.ietf.org/html/rfc2460#section-7 - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); - /// assert_eq!(socket.flowinfo(), 10); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn flowinfo(&self) -> u32 { - self.flowinfo - } - - /// Changes the flow information associated with this socket address. - /// - /// See [`SocketAddrV6::flowinfo`]'s documentation for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); - /// socket.set_flowinfo(56); - /// assert_eq!(socket.flowinfo(), 56); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_flowinfo(&mut self, new_flowinfo: u32) { - self.flowinfo = new_flowinfo; - } - - /// Returns the scope ID associated with this address. - /// - /// This information corresponds to the `sin6_scope_id` field in C's `netinet/in.h`, - /// as specified in [IETF RFC 2553, Section 3.3]. - /// - /// [IETF RFC 2553, Section 3.3]: https://tools.ietf.org/html/rfc2553#section-3.3 - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); - /// assert_eq!(socket.scope_id(), 78); - /// ``` - #[must_use] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")] - pub const fn scope_id(&self) -> u32 { - self.scope_id - } - - /// Changes the scope ID associated with this socket address. - /// - /// See [`SocketAddrV6::scope_id`]'s documentation for more details. - /// - /// # Examples - /// - /// ``` - /// use std::net::{SocketAddrV6, Ipv6Addr}; - /// - /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); - /// socket.set_scope_id(42); - /// assert_eq!(socket.scope_id(), 42); - /// ``` - #[stable(feature = "sockaddr_setters", since = "1.9.0")] - pub fn set_scope_id(&mut self, new_scope_id: u32) { - self.scope_id = new_scope_id; - } -} +pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; impl FromInner<c::sockaddr_in> for SocketAddrV4 { fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 { - SocketAddrV4 { ip: Ipv4Addr::from_inner(addr.sin_addr), port: u16::from_be(addr.sin_port) } + SocketAddrV4::new(Ipv4Addr::from_inner(addr.sin_addr), u16::from_be(addr.sin_port)) } } impl FromInner<c::sockaddr_in6> for SocketAddrV6 { fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 { - SocketAddrV6 { - ip: Ipv6Addr::from_inner(addr.sin6_addr), - port: u16::from_be(addr.sin6_port), - flowinfo: addr.sin6_flowinfo, - scope_id: addr.sin6_scope_id, - } + SocketAddrV6::new( + Ipv6Addr::from_inner(addr.sin6_addr), + u16::from_be(addr.sin6_port), + addr.sin6_flowinfo, + addr.sin6_scope_id, + ) } } @@ -549,8 +37,8 @@ impl IntoInner<c::sockaddr_in> for SocketAddrV4 { fn into_inner(self) -> c::sockaddr_in { c::sockaddr_in { sin_family: c::AF_INET as c::sa_family_t, - sin_port: self.port.to_be(), - sin_addr: self.ip.into_inner(), + sin_port: self.port().to_be(), + sin_addr: self.ip().into_inner(), ..unsafe { mem::zeroed() } } } @@ -560,162 +48,15 @@ impl IntoInner<c::sockaddr_in6> for SocketAddrV6 { fn into_inner(self) -> c::sockaddr_in6 { c::sockaddr_in6 { sin6_family: c::AF_INET6 as c::sa_family_t, - sin6_port: self.port.to_be(), - sin6_addr: self.ip.into_inner(), - sin6_flowinfo: self.flowinfo, - sin6_scope_id: self.scope_id, + sin6_port: self.port().to_be(), + sin6_addr: self.ip().into_inner(), + sin6_flowinfo: self.flowinfo(), + sin6_scope_id: self.scope_id(), ..unsafe { mem::zeroed() } } } } -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<SocketAddrV4> for SocketAddr { - /// Converts a [`SocketAddrV4`] into a [`SocketAddr::V4`]. - fn from(sock4: SocketAddrV4) -> SocketAddr { - SocketAddr::V4(sock4) - } -} - -#[stable(feature = "ip_from_ip", since = "1.16.0")] -impl From<SocketAddrV6> for SocketAddr { - /// Converts a [`SocketAddrV6`] into a [`SocketAddr::V6`]. - fn from(sock6: SocketAddrV6) -> SocketAddr { - SocketAddr::V6(sock6) - } -} - -#[stable(feature = "addr_from_into_ip", since = "1.17.0")] -impl<I: Into<IpAddr>> From<(I, u16)> for SocketAddr { - /// Converts a tuple struct (Into<[`IpAddr`]>, `u16`) into a [`SocketAddr`]. - /// - /// This conversion creates a [`SocketAddr::V4`] for an [`IpAddr::V4`] - /// and creates a [`SocketAddr::V6`] for an [`IpAddr::V6`]. - /// - /// `u16` is treated as port of the newly created [`SocketAddr`]. - fn from(pieces: (I, u16)) -> SocketAddr { - SocketAddr::new(pieces.0.into(), pieces.1) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - SocketAddr::V4(ref a) => a.fmt(f), - SocketAddr::V6(ref a) => a.fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddrV4 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write the socket address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if f.precision().is_none() && f.width().is_none() { - write!(f, "{}:{}", self.ip(), self.port()) - } else { - const LONGEST_IPV4_SOCKET_ADDR: &str = "255.255.255.255:65536"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV4_SOCKET_ADDR.len() }>::new(); - // Buffer is long enough for the longest possible IPv4 socket address, so this should never fail. - write!(buf, "{}:{}", self.ip(), self.port()).unwrap(); - - f.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddrV4 { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SocketAddrV6 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If there are no alignment requirements, write the socket address directly to `f`. - // Otherwise, write it to a local buffer and then use `f.pad`. - if f.precision().is_none() && f.width().is_none() { - match self.scope_id() { - 0 => write!(f, "[{}]:{}", self.ip(), self.port()), - scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()), - } - } else { - const LONGEST_IPV6_SOCKET_ADDR: &str = - "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967296]:65536"; - - let mut buf = DisplayBuffer::<{ LONGEST_IPV6_SOCKET_ADDR.len() }>::new(); - match self.scope_id() { - 0 => write!(buf, "[{}]:{}", self.ip(), self.port()), - scope_id => write!(buf, "[{}%{}]:{}", self.ip(), scope_id, self.port()), - } - // Buffer is long enough for the longest possible IPv6 socket address, so this should never fail. - .unwrap(); - - f.pad(buf.as_str()) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SocketAddrV6 { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self, fmt) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl PartialOrd for SocketAddrV4 { - fn partial_cmp(&self, other: &SocketAddrV4) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl PartialOrd for SocketAddrV6 { - fn partial_cmp(&self, other: &SocketAddrV6) -> Option<Ordering> { - Some(self.cmp(other)) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl Ord for SocketAddrV4 { - fn cmp(&self, other: &SocketAddrV4) -> Ordering { - self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) - } -} - -#[stable(feature = "socketaddr_ordering", since = "1.45.0")] -impl Ord for SocketAddrV6 { - fn cmp(&self, other: &SocketAddrV6) -> Ordering { - self.ip().cmp(other.ip()).then(self.port().cmp(&other.port())) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for SocketAddrV4 { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - (self.port, self.ip).hash(s) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl hash::Hash for SocketAddrV6 { - fn hash<H: hash::Hasher>(&self, s: &mut H) { - (self.port, &self.ip, self.flowinfo, self.scope_id).hash(s) - } -} - /// A trait for objects which can be converted or resolved to one or more /// [`SocketAddr`] values. /// diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 8c0adcfb0eb..e019bc0b67a 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -670,7 +670,10 @@ fn debug() { // FIXME: re-enabled openbsd tests once their socket timeout code // no longer has rounding errors. // VxWorks ignores SO_SNDTIMEO. -#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), + ignore +)] #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[test] fn timeouts() { diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index f82904ffbbf..892fe2ba8ba 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -180,7 +180,10 @@ fn debug() { // FIXME: re-enabled openbsd/netbsd tests once their socket timeout code // no longer has rounding errors. // VxWorks ignores SO_SNDTIMEO. -#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"), + ignore +)] #[test] fn timeouts() { let addr = next_test_ip4(); diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index af137c9bd85..b148d8a0091 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -127,6 +127,8 @@ pub mod l4re; pub mod macos; #[cfg(target_os = "netbsd")] pub mod netbsd; +#[cfg(target_os = "nto")] +pub mod nto; #[cfg(target_os = "openbsd")] pub mod openbsd; #[cfg(target_os = "redox")] diff --git a/library/std/src/os/nto/fs.rs b/library/std/src/os/nto/fs.rs new file mode 100644 index 00000000000..8f915b08c9e --- /dev/null +++ b/library/std/src/os/nto/fs.rs @@ -0,0 +1,92 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_sec as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_sec as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_sec as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/nto/mod.rs b/library/std/src/os/nto/mod.rs new file mode 100644 index 00000000000..3e591dace92 --- /dev/null +++ b/library/std/src/os/nto/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub(super) mod raw; diff --git a/library/std/src/os/nto/raw.rs b/library/std/src/os/nto/raw.rs new file mode 100644 index 00000000000..90e9ad54643 --- /dev/null +++ b/library/std/src/os/nto/raw.rs @@ -0,0 +1,40 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +use crate::os::raw::c_int; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_int; + +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, time_t}; + +mod arch { + use crate::os::raw::c_long; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = i32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = c_long; +} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 3fc6cc44ce4..a0e664acd13 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -17,6 +17,10 @@ use crate::sealed::Sealed; #[allow(unused_imports)] use io::{Read, Write}; +// Tests for this module +#[cfg(test)] +mod tests; + /// Unix-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { @@ -54,6 +58,16 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; + /// Like `read_at`, except that it reads into a slice of buffers. + /// + /// Data is copied to fill each buffer in order, with the final buffer + /// written to possibly being only partially filled. This method must behave + /// equivalently to a single call to read with concatenated buffers. + #[unstable(feature = "unix_file_vectored_at", issue = "89517")] + fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + /// Reads the exact number of byte required to fill `buf` from the given offset. /// /// The offset is relative to the start of the file and thus independent @@ -155,6 +169,16 @@ pub trait FileExt { #[stable(feature = "file_offset", since = "1.15.0")] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>; + /// Like `write_at`, except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer read + /// from possibly being only partially consumed. This method must behave as + /// a call to `write_at` with the buffers concatenated would. + #[unstable(feature = "unix_file_vectored_at", issue = "89517")] + fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + /// Attempts to write an entire buffer starting from a given offset. /// /// The offset is relative to the start of the file and thus independent @@ -218,9 +242,15 @@ impl FileExt for fs::File { fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.as_inner().read_at(buf, offset) } + fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + self.as_inner().read_vectored_at(bufs, offset) + } fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { self.as_inner().write_at(buf, offset) } + fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> { + self.as_inner().write_vectored_at(bufs, offset) + } } /// Unix-specific extensions to [`fs::Permissions`]. diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs new file mode 100644 index 00000000000..67f607bd468 --- /dev/null +++ b/library/std/src/os/unix/fs/tests.rs @@ -0,0 +1,57 @@ +use super::*; + +#[test] +fn read_vectored_at() { + let msg = b"preadv is working!"; + let dir = crate::sys_common::io::test::tmpdir(); + + let filename = dir.join("preadv.txt"); + { + let mut file = fs::File::create(&filename).unwrap(); + file.write_all(msg).unwrap(); + } + { + let file = fs::File::open(&filename).unwrap(); + let mut buf0 = [0; 4]; + let mut buf1 = [0; 3]; + + let mut iovec = [io::IoSliceMut::new(&mut buf0), io::IoSliceMut::new(&mut buf1)]; + + let n = file.read_vectored_at(&mut iovec, 4).unwrap(); + + assert!(n == 4 || n == 7); + assert_eq!(&buf0, b"dv i"); + + if n == 7 { + assert_eq!(&buf1, b"s w"); + } + } +} + +#[test] +fn write_vectored_at() { + let msg = b"pwritev is not working!"; + let dir = crate::sys_common::io::test::tmpdir(); + + let filename = dir.join("preadv.txt"); + { + let mut file = fs::File::create(&filename).unwrap(); + file.write_all(msg).unwrap(); + } + let expected = { + let file = fs::File::options().write(true).open(&filename).unwrap(); + let buf0 = b" "; + let buf1 = b"great "; + + let iovec = [io::IoSlice::new(buf0), io::IoSlice::new(buf1)]; + + let n = file.write_vectored_at(&iovec, 11).unwrap(); + + assert!(n == 4 || n == 11); + + if n == 4 { b"pwritev is working!" } else { b"pwritev is great !" } + }; + + let content = fs::read(&filename).unwrap(); + assert_eq!(&content, expected); +} diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index f97fa0fb06f..eb2d7ce1174 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -65,6 +65,8 @@ mod platform { pub use crate::os::macos::*; #[cfg(target_os = "netbsd")] pub use crate::os::netbsd::*; + #[cfg(target_os = "nto")] + pub use crate::os::nto::*; #[cfg(target_os = "openbsd")] pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] @@ -95,7 +97,8 @@ pub mod thread; target_os = "watchos", target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "nto", ))] pub mod ucred; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index f758f88d0a3..272b4f5dcd5 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -19,7 +19,8 @@ use crate::{fmt, io}; target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "haiku" + target_os = "haiku", + target_os = "nto", ))] use libc::MSG_NOSIGNAL; #[cfg(not(any( @@ -29,7 +30,8 @@ use libc::MSG_NOSIGNAL; target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "haiku" + target_os = "haiku", + target_os = "nto", )))] const MSG_NOSIGNAL: libc::c_int = 0x0; diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 37fcfa8446b..f8c29a6d3a1 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -167,6 +167,7 @@ fn long_path() { } #[test] +#[cfg(not(target_os = "nto"))] fn timeouts() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 09b2bfe39f0..729c63d184f 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -12,15 +12,23 @@ use crate::sealed::Sealed; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))] -type UserId = u32; -#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))] -type GroupId = u32; - -#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] -type UserId = u16; -#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] -type GroupId = u16; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] { + type UserId = u16; + type GroupId = u16; + } else if #[cfg(target_os = "nto")] { + // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP. + // Only positive values should be used, see e.g. + // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html + type UserId = i32; + type GroupId = i32; + } else { + type UserId = u32; + type GroupId = u32; + } +} /// Unix-specific extensions to the [`process::Command`] builder. /// diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs index ae4faf27b4d..95967eac295 100644 --- a/library/std/src/os/unix/ucred.rs +++ b/library/std/src/os/unix/ucred.rs @@ -79,7 +79,8 @@ pub mod impl_linux { target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", - target_os = "netbsd" + target_os = "netbsd", + target_os = "nto", ))] pub mod impl_bsd { use super::UCred; diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index d6e9da187e8..6f78811a186 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -587,8 +587,10 @@ mod prim_pointer {} /// There are two syntactic forms for creating an array: /// /// * A list with each element, i.e., `[x, y, z]`. -/// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`. -/// The type of `x` must be [`Copy`]. +/// * A repeat expression `[expr; N]` where `N` is how many times to repeat `expr` in the array. `expr` must either be: +/// +/// * A value of a type implementing the [`Copy`] trait +/// * A `const` value /// /// Note that `[expr; 0]` is allowed, and produces an empty array. /// This will still evaluate `expr`, however, and immediately drop the resulting value, so diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 62ce2cb33dc..1952e19e607 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1416,7 +1416,7 @@ impl From<fs::File> for Stdio { /// use std::fs::File; /// use std::process::Command; /// - /// // With the `foo.txt` file containing `Hello, world!" + /// // With the `foo.txt` file containing "Hello, world!" /// let file = File::open("foo.txt").unwrap(); /// /// let reverse = Command::new("rev") diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs index 19350b83fab..ae0f718535b 100644 --- a/library/std/src/sys/itron/thread.rs +++ b/library/std/src/sys/itron/thread.rs @@ -247,7 +247,7 @@ impl Thread { // [FINISHED → JOINED] // To synchronize with the child task's memory accesses to // `inner` up to the point of the assignment of `FINISHED`, - // `Ordering::Acquire` must be used for the above `swap` call`. + // `Ordering::Acquire` must be used for the above `swap` call. } _ => unsafe { hint::unreachable_unchecked() }, } diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index a5ce6d5120d..3d79058b320 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -69,7 +69,8 @@ impl DoubleEndedIterator for Args { target_os = "fuchsia", target_os = "redox", target_os = "vxworks", - target_os = "horizon" + target_os = "horizon", + target_os = "nto", ))] mod imp { use super::Args; diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index c9ba661c829..1a9276f1110 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -185,6 +185,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "nto")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nto"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "redox")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 66c33d58d6c..9874af4d3e2 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -53,7 +53,12 @@ const fn max_iov() -> usize { libc::IOV_MAX as usize } -#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +#[cfg(any( + target_os = "android", + target_os = "emscripten", + target_os = "linux", + target_os = "nto", +))] const fn max_iov() -> usize { libc::UIO_MAXIOV as usize } @@ -67,6 +72,7 @@ const fn max_iov() -> usize { target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "nto", target_os = "openbsd", target_os = "horizon", target_os = "watchos", @@ -92,7 +98,7 @@ impl FileDesc { let ret = cvt(unsafe { libc::readv( self.as_raw_fd(), - bufs.as_ptr() as *const libc::iovec, + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, cmp::min(bufs.len(), max_iov()) as libc::c_int, ) })?; @@ -101,7 +107,7 @@ impl FileDesc { #[cfg(any(target_os = "espidf", target_os = "horizon"))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - return crate::io::default_read_vectored(|b| self.read(b), bufs); + io::default_read_vectored(|b| self.read(b), bufs) } #[inline] @@ -147,6 +153,95 @@ impl FileDesc { Ok(()) } + #[cfg(any( + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + )))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `preadv` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `preadv` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + super::weak::syscall! { + fn preadv( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t + ) -> isize + } + + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + // We support old MacOS and iOS versions that do not have `preadv`. There is + // no `syscall` possible in these platform. + #[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "ios", + target_os = "macos", + ))] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match preadv64.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { let ret = cvt(unsafe { libc::write( @@ -172,7 +267,7 @@ impl FileDesc { #[cfg(any(target_os = "espidf", target_os = "horizon"))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { - return crate::io::default_write_vectored(|b| self.write(b), bufs); + io::default_write_vectored(|b| self.write(b), bufs) } #[inline] @@ -197,6 +292,95 @@ impl FileDesc { } } + #[cfg(any( + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + #[cfg(not(any( + target_os = "android", + target_os = "emscripten", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + )))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + + // We support some old Android versions that do not have `pwritev` in libc, + // so we use weak linkage and fallback to a direct syscall if not available. + // + // On 32-bit targets, we don't want to deal with weird ABI issues around + // passing 64-bits parameters to syscalls, so we fallback to the default + // implementation if `pwritev` is not available. + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + super::weak::syscall! { + fn pwritev( + fd: libc::c_int, + iovec: *const libc::iovec, + n_iovec: libc::c_int, + offset: off64_t + ) -> isize + } + + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + + // We support old MacOS and iOS versions that do not have `pwritev`. There is + // no `syscall` possible in these platform. + #[cfg(any( + all(target_os = "android", target_pointer_width = "32"), + target_os = "ios", + target_os = "macos", + ))] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match pwritev64.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } + } + #[cfg(not(any( target_env = "newlib", target_os = "solaris", @@ -207,7 +391,8 @@ impl FileDesc { target_os = "linux", target_os = "haiku", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -225,7 +410,8 @@ impl FileDesc { target_os = "linux", target_os = "haiku", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 8e1f35d6cc9..7566fafda24 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -13,7 +13,8 @@ use crate::mem; target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", ))] use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -54,7 +55,8 @@ use libc::fstatat64; target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", ))] use libc::readdir as readdir64; #[cfg(target_os = "linux")] @@ -69,7 +71,8 @@ use libc::readdir64_r; target_os = "illumos", target_os = "l4re", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -277,7 +280,8 @@ unsafe impl Sync for Dir {} target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", ))] pub struct DirEntry { dir: Arc<InnerReadDir>, @@ -297,11 +301,12 @@ pub struct DirEntry { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", ))] struct dirent64_min { d_ino: u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))] d_type: u8, } @@ -311,7 +316,8 @@ struct dirent64_min { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", )))] pub struct DirEntry { dir: Arc<InnerReadDir>, @@ -438,7 +444,7 @@ impl FileAttr { } } -#[cfg(not(target_os = "netbsd"))] +#[cfg(not(any(target_os = "netbsd", target_os = "nto")))] impl FileAttr { #[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))] pub fn modified(&self) -> io::Result<SystemTime> { @@ -524,6 +530,21 @@ impl FileAttr { } } +#[cfg(target_os = "nto")] +impl FileAttr { + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)) + } +} + impl AsInner<stat64> for FileAttr { fn as_inner(&self) -> &stat64 { &self.stat @@ -603,7 +624,8 @@ impl Iterator for ReadDir { target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", ))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { @@ -686,7 +708,11 @@ impl Iterator for ReadDir { let entry = dirent64_min { d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + )))] d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; @@ -705,7 +731,8 @@ impl Iterator for ReadDir { target_os = "solaris", target_os = "fuchsia", target_os = "redox", - target_os = "illumos" + target_os = "illumos", + target_os = "nto", )))] fn next(&mut self) -> Option<io::Result<DirEntry>> { if self.end_of_stream { @@ -794,7 +821,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "haiku", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", ))] pub fn file_type(&self) -> io::Result<FileType> { self.metadata().map(|m| m.file_type()) @@ -804,7 +832,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "haiku", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nto", )))] pub fn file_type(&self) -> io::Result<FileType> { match self.entry.d_type { @@ -834,7 +863,8 @@ impl DirEntry { target_os = "redox", target_os = "vxworks", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "nto", ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 @@ -887,7 +917,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -898,7 +929,8 @@ impl DirEntry { target_os = "solaris", target_os = "illumos", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "nto", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1051,7 +1083,8 @@ impl File { target_os = "linux", target_os = "android", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "nto", ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) @@ -1065,6 +1098,7 @@ impl File { target_os = "netbsd", target_os = "openbsd", target_os = "watchos", + target_os = "nto", )))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) @@ -1098,6 +1132,10 @@ impl File { self.0.read_buf(cursor) } + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> { + self.0.read_vectored_at(bufs, offset) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } @@ -1115,6 +1153,10 @@ impl File { self.0.write_at(buf, offset) } + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> { + self.0.write_vectored_at(bufs, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } @@ -1750,13 +1792,25 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; // Fallback for REDOX, ESP-ID, Horizon, and Miri -#[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri))] +#[cfg(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nto", + miri +))] mod remove_dir_impl { pub use crate::sys_common::fs::remove_dir_all; } // Modern implementation using openat(), unlinkat() and fdopendir() -#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))] +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nto", + miri +)))] mod remove_dir_impl { use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir}; use crate::ffi::CStr; diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs index 6be1abc2b08..192fa216dfa 100644 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -2,7 +2,10 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed}; use crate::sys::locks::{pthread_mutex, Mutex}; +#[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use crate::time::Duration; @@ -132,10 +135,18 @@ impl Condvar { let mutex = pthread_mutex::raw(mutex); self.verify(mutex); + #[cfg(not(target_os = "nto"))] let timeout = Timespec::now(libc::CLOCK_MONOTONIC) .checked_add_duration(&dur) .and_then(|t| t.to_timespec()) .unwrap_or(TIMESPEC_MAX); + + #[cfg(target_os = "nto")] + let timeout = Timespec::now(libc::CLOCK_MONOTONIC) + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); + let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); assert!(r == libc::ETIMEDOUT || r == 0); r == 0 diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 30a96be1430..68c9520cc9e 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -329,7 +329,7 @@ pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { // do so. In 1003.1-2004 this was fixed. // // glibc's implementation did the flush, unsafely, before glibc commit -// 91e7cf982d01 `abort: Do not flush stdio streams [BZ #15436]' by Florian +// 91e7cf982d01 `abort: Do not flush stdio streams [BZ #15436]` by Florian // Weimer. According to glibc's NEWS: // // The abort function terminates the process immediately, without flushing diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index c86f80972a6..8e05b618daa 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -78,6 +78,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "nto", ))] { // On platforms that support it we pass the SOCK_CLOEXEC // flag to atomically create the socket and set it as @@ -115,6 +116,7 @@ impl Socket { target_os = "linux", target_os = "netbsd", target_os = "openbsd", + target_os = "nto", ))] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 2f2663db607..21b035fb373 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -62,6 +62,7 @@ extern "C" { link_name = "__errno" )] #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] + #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] #[cfg_attr( any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"), link_name = "__error" @@ -361,6 +362,17 @@ pub fn current_exe() -> io::Result<PathBuf> { } } +#[cfg(target_os = "nto")] +pub fn current_exe() -> io::Result<PathBuf> { + let mut e = crate::fs::read("/proc/self/exefile")?; + // Current versions of QNX Neutrino provide a null-terminated path. + // Ensure the trailing null byte is not returned here. + if let Some(0) = e.last() { + e.pop(); + } + Ok(PathBuf::from(OsString::from_vec(e))) +} + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 3bc17b7754d..ceaff596684 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -18,6 +18,7 @@ use crate::sys::weak::raw_syscall; target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), + target_os = "nto", ))] use crate::sys::weak::weak; @@ -30,6 +31,15 @@ use libc::{c_int, pid_t}; #[cfg(not(any(target_os = "vxworks", target_os = "l4re")))] use libc::{gid_t, uid_t}; +cfg_if::cfg_if! { + if #[cfg(all(target_os = "nto", target_env = "nto71"))] { + use crate::thread; + use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; + // arbitrary number of tries: + const MAX_FORKSPAWN_TRIES: u32 = 4; + } +} + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// @@ -140,11 +150,31 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", all(target_os = "nto", target_env = "nto71"))))] unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { cvt(libc::fork()).map(|res| (res, -1)) } + // On QNX Neutrino, fork can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the fork() was occurring". + // Documentation says "... or try calling fork() again". This is what we do here. + // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html + #[cfg(all(target_os = "nto", target_env = "nto71"))] + unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { + use crate::sys::os::errno; + + let mut tries_left = MAX_FORKSPAWN_TRIES; + loop { + let r = libc::fork(); + if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF { + thread::yield_now(); + tries_left -= 1; + } else { + return cvt(r).map(|res| (res, -1)); + } + } + } + // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, child_pidfd)) in the parent. #[cfg(target_os = "linux")] @@ -389,6 +419,7 @@ impl Command { target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), + target_os = "nto", )))] fn posix_spawn( &mut self, @@ -405,6 +436,7 @@ impl Command { target_os = "freebsd", all(target_os = "linux", target_env = "gnu"), all(target_os = "linux", target_env = "musl"), + target_os = "nto", ))] fn posix_spawn( &mut self, @@ -436,6 +468,34 @@ impl Command { } } + // On QNX Neutrino, posix_spawnp can fail with EBADF in case "another thread might have opened + // or closed a file descriptor while the posix_spawn() was occurring". + // Documentation says "... or try calling posix_spawn() again". This is what we do here. + // See also http://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html + #[cfg(all(target_os = "nto", target_env = "nto71"))] + unsafe fn retrying_libc_posix_spawnp( + pid: *mut pid_t, + file: *const c_char, + file_actions: *const posix_spawn_file_actions_t, + attrp: *const posix_spawnattr_t, + argv: *const *mut c_char, + envp: *const *mut c_char, + ) -> i32 { + let mut tries_left = MAX_FORKSPAWN_TRIES; + loop { + match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { + libc::EBADF if tries_left > 0 => { + thread::yield_now(); + tries_left -= 1; + continue; + } + r => { + return r; + } + } + } + } + // Solaris, glibc 2.29+, and musl 1.24+ can set a new working directory, // and maybe others will gain this non-POSIX function too. We'll check // for this weak symbol as soon as it's needed, so we can return early @@ -555,7 +615,12 @@ impl Command { // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::os::env_read_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); - cvt_nz(libc::posix_spawnp( + + #[cfg(not(target_os = "nto"))] + let spawn_fn = libc::posix_spawnp; + #[cfg(target_os = "nto")] + let spawn_fn = retrying_libc_posix_spawnp; + cvt_nz(spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), @@ -760,7 +825,7 @@ fn signal_string(signal: i32) -> &'static str { ) ))] libc::SIGSTKFLT => " (SIGSTKFLT)", - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "nto"))] libc::SIGPWR => " (SIGPWR)", #[cfg(any( target_os = "macos", @@ -769,7 +834,8 @@ fn signal_string(signal: i32) -> &'static str { target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", - target_os = "dragonfly" + target_os = "dragonfly", + target_os = "nto", ))] libc::SIGEMT => " (SIGEMT)", #[cfg(any( diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index cc0e5929569..15070b1f6a7 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -9,7 +9,7 @@ use crate::time::Duration; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; -#[cfg(any(target_os = "solaris", target_os = "illumos"))] +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] use crate::sys::weak::weak; #[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -173,7 +173,7 @@ impl Thread { } } - #[cfg(any(target_os = "solaris", target_os = "illumos"))] + #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] pub fn set_name(name: &CStr) { weak! { fn pthread_setname_np( @@ -381,6 +381,17 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { } Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) + } else if #[cfg(target_os = "nto")] { + unsafe { + use libc::_syspage_ptr; + if _syspage_ptr.is_null() { + Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available")) + } else { + let cpus = (*_syspage_ptr).num_cpu; + NonZeroUsize::new(cpus as usize) + .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) + } + } } else if #[cfg(target_os = "haiku")] { // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus` // `get_system_info` calls then `smp_get_num_cpus` diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs index 082d25e68f5..43046ed07b8 100644 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/unix/thread_parking/pthread.rs @@ -6,7 +6,10 @@ use crate::pin::Pin; use crate::ptr::addr_of_mut; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::SeqCst; +#[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; +#[cfg(target_os = "nto")] +use crate::sys::time::TIMESPEC_MAX_CAPPED; use crate::time::Duration; const EMPTY: usize = 0; @@ -80,8 +83,14 @@ unsafe fn wait_timeout( (Timespec::now(libc::CLOCK_MONOTONIC), dur) }; + #[cfg(not(target_os = "nto"))] let timeout = now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX); + #[cfg(target_os = "nto")] + let timeout = now + .checked_add_duration(&dur) + .and_then(|t| t.to_timespec_capped()) + .unwrap_or(TIMESPEC_MAX_CAPPED); let r = libc::pthread_cond_timedwait(cond, lock, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 2daad981b73..0f11de8f5b8 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -9,6 +9,14 @@ pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; pub const TIMESPEC_MAX: libc::timespec = libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 }; +// This additional constant is only used when calling +// `libc::pthread_cond_timedwait`. +#[cfg(target_os = "nto")] +pub(super) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { + tv_sec: (u64::MAX / NSEC_PER_SEC) as i64, + tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, +}; + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] @@ -144,6 +152,20 @@ impl Timespec { tv_nsec: self.tv_nsec.0.try_into().ok()?, }) } + + // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait + // is 2^64 nanoseconds + #[cfg(target_os = "nto")] + pub(super) fn to_timespec_capped(&self) -> Option<libc::timespec> { + // Check if timeout in nanoseconds would fit into an u64 + if (self.tv_nsec.0 as u64) + .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) + .is_none() + { + return None; + } + self.to_timespec() + } } impl From<libc::timespec> for Timespec { diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 1d0ab772739..5d150eca00e 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -6,13 +6,15 @@ use crate::ffi::CStr; use crate::mem; -use crate::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ushort}; +use crate::os::raw::{c_char, c_long, c_longlong, c_uint, c_ulong, c_ushort}; use crate::os::windows::io::{BorrowedHandle, HandleOrInvalid, HandleOrNull}; use crate::ptr; use core::ffi::NonZero_c_ulong; use libc::{c_void, size_t, wchar_t}; +pub use crate::os::raw::c_int; + #[path = "c/errors.rs"] // c.rs is included from two places so we need to specify this mod errors; pub use errors::*; @@ -47,16 +49,19 @@ pub type ACCESS_MASK = DWORD; pub type LPBOOL = *mut BOOL; pub type LPBYTE = *mut BYTE; +pub type LPCCH = *const CHAR; pub type LPCSTR = *const CHAR; +pub type LPCWCH = *const WCHAR; pub type LPCWSTR = *const WCHAR; +pub type LPCVOID = *const c_void; pub type LPDWORD = *mut DWORD; pub type LPHANDLE = *mut HANDLE; pub type LPOVERLAPPED = *mut OVERLAPPED; pub type LPPROCESS_INFORMATION = *mut PROCESS_INFORMATION; pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES; pub type LPSTARTUPINFO = *mut STARTUPINFO; +pub type LPSTR = *mut CHAR; pub type LPVOID = *mut c_void; -pub type LPCVOID = *const c_void; pub type LPWCH = *mut WCHAR; pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW; pub type LPWSADATA = *mut WSADATA; @@ -132,6 +137,10 @@ pub const MAX_PATH: usize = 260; pub const FILE_TYPE_PIPE: u32 = 3; +pub const CP_UTF8: DWORD = 65001; +pub const MB_ERR_INVALID_CHARS: DWORD = 0x08; +pub const WC_ERR_INVALID_CHARS: DWORD = 0x80; + #[repr(C)] #[derive(Copy)] pub struct WIN32_FIND_DATAW { @@ -1147,6 +1156,25 @@ extern "system" { lpFilePart: *mut LPWSTR, ) -> DWORD; pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD; + + pub fn MultiByteToWideChar( + CodePage: UINT, + dwFlags: DWORD, + lpMultiByteStr: LPCCH, + cbMultiByte: c_int, + lpWideCharStr: LPWSTR, + cchWideChar: c_int, + ) -> c_int; + pub fn WideCharToMultiByte( + CodePage: UINT, + dwFlags: DWORD, + lpWideCharStr: LPCWCH, + cchWideChar: c_int, + lpMultiByteStr: LPSTR, + cbMultiByte: c_int, + lpDefaultChar: LPCCH, + lpUsedDefaultChar: LPBOOL, + ) -> c_int; } #[link(name = "ws2_32")] diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index c2cd48470bd..32c6ccffb7a 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -169,14 +169,27 @@ fn write( } fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usize> { + debug_assert!(!utf8.is_empty()); + let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2]; - let mut len_utf16 = 0; - for (chr, dest) in utf8.encode_utf16().zip(utf16.iter_mut()) { - *dest = MaybeUninit::new(chr); - len_utf16 += 1; - } - // Safety: We've initialized `len_utf16` values. - let utf16: &[u16] = unsafe { MaybeUninit::slice_assume_init_ref(&utf16[..len_utf16]) }; + let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len())]; + + let utf16: &[u16] = unsafe { + // Note that this theoretically checks validity twice in the (most common) case + // where the underlying byte sequence is valid utf-8 (given the check in `write()`). + let result = c::MultiByteToWideChar( + c::CP_UTF8, // CodePage + c::MB_ERR_INVALID_CHARS, // dwFlags + utf8.as_ptr() as c::LPCCH, // lpMultiByteStr + utf8.len() as c::c_int, // cbMultiByte + utf16.as_mut_ptr() as c::LPWSTR, // lpWideCharStr + utf16.len() as c::c_int, // cchWideChar + ); + assert!(result != 0, "Unexpected error in MultiByteToWideChar"); + + // Safety: MultiByteToWideChar initializes `result` values. + MaybeUninit::slice_assume_init_ref(&utf16[..result as usize]) + }; let mut written = write_u16s(handle, &utf16)?; @@ -189,8 +202,8 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz // a missing surrogate can be produced (and also because of the UTF-8 validation above), // write the missing surrogate out now. // Buffering it would mean we have to lie about the number of bytes written. - let first_char_remaining = utf16[written]; - if first_char_remaining >= 0xDCEE && first_char_remaining <= 0xDFFF { + let first_code_unit_remaining = utf16[written]; + if first_code_unit_remaining >= 0xDCEE && first_code_unit_remaining <= 0xDFFF { // low surrogate // We just hope this works, and give up otherwise let _ = write_u16s(handle, &utf16[written..written + 1]); @@ -212,6 +225,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result<usiz } fn write_u16s(handle: c::HANDLE, data: &[u16]) -> io::Result<usize> { + debug_assert!(data.len() < u32::MAX as usize); let mut written = 0; cvt(unsafe { c::WriteConsoleW( @@ -365,26 +379,32 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz Ok(amount as usize) } -#[allow(unused)] fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> { - let mut written = 0; - for chr in char::decode_utf16(utf16.iter().cloned()) { - match chr { - Ok(chr) => { - chr.encode_utf8(&mut utf8[written..]); - written += chr.len_utf8(); - } - Err(_) => { - // We can't really do any better than forget all data and return an error. - return Err(io::const_io_error!( - io::ErrorKind::InvalidData, - "Windows stdin in console mode does not support non-UTF-16 input; \ - encountered unpaired surrogate", - )); - } - } + debug_assert!(utf16.len() <= c::c_int::MAX as usize); + debug_assert!(utf8.len() <= c::c_int::MAX as usize); + + let result = unsafe { + c::WideCharToMultiByte( + c::CP_UTF8, // CodePage + c::WC_ERR_INVALID_CHARS, // dwFlags + utf16.as_ptr(), // lpWideCharStr + utf16.len() as c::c_int, // cchWideChar + utf8.as_mut_ptr() as c::LPSTR, // lpMultiByteStr + utf8.len() as c::c_int, // cbMultiByte + ptr::null(), // lpDefaultChar + ptr::null_mut(), // lpUsedDefaultChar + ) + }; + if result == 0 { + // We can't really do any better than forget all data and return an error. + Err(io::const_io_error!( + io::ErrorKind::InvalidData, + "Windows stdin in console mode does not support non-UTF-16 input; \ + encountered unpaired surrogate", + )) + } else { + Ok(result as usize) } - Ok(written) } impl IncompleteUtf8 { diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index f1d804ef40c..8752f46ff81 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -91,6 +91,19 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: if stop { return false; } + #[cfg(target_os = "nto")] + if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { + if !hit && start { + use crate::backtrace_rs::SymbolName; + res = bt_fmt.frame().print_raw( + frame.ip(), + Some(SymbolName::new("__my_thread_exit".as_bytes())), + None, + None, + ); + } + return false; + } if !hit && start { res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 2c38dfecf97..85ecc1def3a 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -21,7 +21,7 @@ cfg_if::cfg_if! { target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", target_os = "watchos", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", - target_os = "solaris", target_os = "haiku", target_os = "l4re"))] { + target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { @@ -35,7 +35,7 @@ cfg_if::cfg_if! { target_os = "linux", target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "haiku"))] { + target_os = "haiku", target_os = "nto"))] { use libc::MSG_NOSIGNAL; } else { const MSG_NOSIGNAL: c_int = 0x0; @@ -46,7 +46,8 @@ cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", - target_os = "solaris", target_os = "illumos"))] { + target_os = "solaris", target_os = "illumos", + target_os = "nto"))] { use crate::ffi::c_uchar; type IpV4MultiCastType = c_uchar; } else { diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys_common/net/tests.rs index ac75d9ebfc8..fc236b8027b 100644 --- a/library/std/src/sys_common/net/tests.rs +++ b/library/std/src/sys_common/net/tests.rs @@ -6,7 +6,7 @@ fn no_lookup_host_duplicates() { let mut addrs = HashMap::new(); let lh = match LookupHost::try_from(("localhost", 0)) { Ok(lh) => lh, - Err(e) => panic!("couldn't resolve `localhost': {e}"), + Err(e) => panic!("couldn't resolve `localhost`: {e}"), }; for sa in lh { *addrs.entry(sa).or_insert(0) += 1; diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 2672a2a75b0..89360e45601 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -117,9 +117,15 @@ pub struct Key { /// This value specifies no destructor by default. pub const INIT: StaticKey = StaticKey::new(None); -// Define a sentinel value that is unlikely to be returned -// as a TLS key (but it may be returned). +// Define a sentinel value that is likely not to be returned +// as a TLS key. +#[cfg(not(target_os = "nto"))] const KEY_SENTVAL: usize = 0; +// On QNX Neutrino, 0 is always returned when currently not in use. +// Using 0 would mean to always create two keys and remote the first +// one (with value of 0) immediately afterwards. +#[cfg(target_os = "nto")] +const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; impl StaticKey { #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index e202d17e1c2..bc588bdbb3c 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -594,7 +594,7 @@ impl Wtf8 { } /// Returns the code point at `position` if it is in the ASCII range, - /// or `b'\xFF' otherwise. + /// or `b'\xFF'` otherwise. /// /// # Panics /// diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 69fb529d7f5..88d8e5fe97a 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -204,7 +204,7 @@ fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn { } /// Invoked when unit tests terminate. Returns `Result::Err` if the test is -/// considered a failure. By default, invokes `report() and checks for a `0` +/// considered a failure. By default, invokes `report()` and checks for a `0` /// result. pub fn assert_test_result<T: Termination>(result: T) -> Result<(), String> { let code = result.report().to_i32(); diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 3753071d5f0..edc10aa39af 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -127,3 +127,7 @@ extern "C" {} #[cfg(target_os = "haiku")] #[link(name = "gcc_s")] extern "C" {} + +#[cfg(target_os = "nto")] +#[link(name = "gcc_s")] +extern "C" {} diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 036a35869a5..eeeed3afcd3 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -89,7 +89,7 @@ pub type _Unwind_Exception_Cleanup_Fn = // FIXME: The `#[link]` attributes on `extern "C"` block marks those symbols declared in // the block are reexported in dylib build of std. This is needed when build rustc with -// feature `llvm-libunwind', as no other cdylib will provided those _Unwind_* symbols. +// feature `llvm-libunwind`, as no other cdylib will provided those _Unwind_* symbols. // However the `link` attribute is duplicated multiple times and does not just export symbol, // a better way to manually export symbol would be another attribute like `#[export]`. // See the logic in function rustc_codegen_ssa::src::back::exported_symbols, module diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 4a0ba592577..e861d520c53 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ "opener", "pretty_assertions", "serde", + "serde_derive", "serde_json", "sha2", "sysinfo", @@ -564,9 +565,6 @@ name = "serde" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" -dependencies = [ - "serde_derive", -] [[package]] name = "serde_derive" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 22ceeca941e..663987f113c 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -39,7 +39,10 @@ cc = "1.0.69" libc = "0.2" hex = "0.4" object = { version = "0.29.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } -serde = { version = "1.0.8", features = ["derive"] } +serde = "1.0.137" +# Directly use serde_derive rather than through the derive feature of serde to allow building both +# in parallel and to allow serde_json and toml to start building as soon as serde has been built. +serde_derive = "1.0.137" serde_json = "1.0.2" sha2 = "0.10" tar = "0.4" diff --git a/src/bootstrap/bolt.rs b/src/bootstrap/bolt.rs index ea37cd47049..973dc4f602b 100644 --- a/src/bootstrap/bolt.rs +++ b/src/bootstrap/bolt.rs @@ -1,46 +1,40 @@ use std::path::Path; use std::process::Command; -/// Uses the `llvm-bolt` binary to instrument the binary/library at the given `path` with BOLT. +/// Uses the `llvm-bolt` binary to instrument the artifact at the given `path` with BOLT. /// When the instrumented artifact is executed, it will generate BOLT profiles into /// `/tmp/prof.fdata.<pid>.fdata`. -pub fn instrument_with_bolt_inplace(path: &Path) { - let dir = std::env::temp_dir(); - let instrumented_path = dir.join("instrumented.so"); - +/// Creates the instrumented artifact at `output_path`. +pub fn instrument_with_bolt(path: &Path, output_path: &Path) { let status = Command::new("llvm-bolt") .arg("-instrument") .arg(&path) // Make sure that each process will write its profiles into a separate file .arg("--instrumentation-file-append-pid") .arg("-o") - .arg(&instrumented_path) + .arg(output_path) .status() .expect("Could not instrument artifact using BOLT"); if !status.success() { panic!("Could not instrument {} with BOLT, exit code {:?}", path.display(), status.code()); } - - std::fs::copy(&instrumented_path, path).expect("Cannot copy instrumented artifact"); - std::fs::remove_file(instrumented_path).expect("Cannot delete instrumented artifact"); } -/// Uses the `llvm-bolt` binary to optimize the binary/library at the given `path` with BOLT, +/// Uses the `llvm-bolt` binary to optimize the artifact at the given `path` with BOLT, /// using merged profiles from `profile_path`. /// /// The recorded profiles have to be merged using the `merge-fdata` tool from LLVM and the merged /// profile path should be then passed to this function. -pub fn optimize_library_with_bolt_inplace(path: &Path, profile_path: &Path) { - let dir = std::env::temp_dir(); - let optimized_path = dir.join("optimized.so"); - +/// +/// Creates the optimized artifact at `output_path`. +pub fn optimize_with_bolt(path: &Path, profile_path: &Path, output_path: &Path) { let status = Command::new("llvm-bolt") .arg(&path) .arg("-data") .arg(&profile_path) .arg("-o") - .arg(&optimized_path) + .arg(output_path) // Reorder basic blocks within functions .arg("-reorder-blocks=ext-tsp") // Reorder functions within the binary @@ -65,7 +59,4 @@ pub fn optimize_library_with_bolt_inplace(path: &Path, profile_path: &Path) { if !status.success() { panic!("Could not optimize {} with BOLT, exit code {:?}", path.display(), status.code()); } - - std::fs::copy(&optimized_path, path).expect("Cannot copy optimized artifact"); - std::fs::remove_file(optimized_path).expect("Cannot delete optimized artifact"); } diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index b33fc02f49c..3d48db8660a 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -910,14 +910,16 @@ impl<'a> Builder<'a> { /// new artifacts, it can't be used to rely on the presence of a particular /// sysroot. /// - /// See `force_use_stage1` for documentation on what each argument is. + /// See `force_use_stage1` and `force_use_stage2` for documentation on what each argument is. pub fn compiler_for( &self, stage: u32, host: TargetSelection, target: TargetSelection, ) -> Compiler { - if self.build.force_use_stage1(Compiler { stage, host }, target) { + if self.build.force_use_stage2() { + self.compiler(2, self.config.build) + } else if self.build.force_use_stage1(Compiler { stage, host }, target) { self.compiler(1, self.config.build) } else { self.compiler(stage, host) diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index ff7821fb9ff..cd19667139a 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -105,10 +105,15 @@ impl Step for Std { cargo.arg("--lib"); } - builder.info(&format!( - "Checking stage{} library artifacts ({} -> {})", - builder.top_stage, &compiler.host, target - )); + let msg = if compiler.host == target { + format!("Checking stage{} library artifacts ({target})", builder.top_stage) + } else { + format!( + "Checking stage{} library artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -162,10 +167,18 @@ impl Step for Std { cargo.arg("-p").arg(krate.name); } - builder.info(&format!( - "Checking stage{} library test/bench/example targets ({} -> {})", - builder.top_stage, &compiler.host, target - )); + let msg = if compiler.host == target { + format!( + "Checking stage{} library test/bench/example targets ({target})", + builder.top_stage + ) + } else { + format!( + "Checking stage{} library test/bench/example targets ({} -> {})", + builder.top_stage, &compiler.host, target + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -239,10 +252,15 @@ impl Step for Rustc { cargo.arg("-p").arg(krate.name); } - builder.info(&format!( - "Checking stage{} compiler artifacts ({} -> {})", - builder.top_stage, &compiler.host, target - )); + let msg = if compiler.host == target { + format!("Checking stage{} compiler artifacts ({target})", builder.top_stage) + } else { + format!( + "Checking stage{} compiler artifacts ({} -> {})", + builder.top_stage, &compiler.host, target + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -299,10 +317,15 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend))); rustc_cargo_env(builder, &mut cargo, target); - builder.info(&format!( - "Checking stage{} {} artifacts ({} -> {})", - builder.top_stage, backend, &compiler.host.triple, target.triple - )); + let msg = if compiler.host == target { + format!("Checking stage{} {} artifacts ({target})", builder.top_stage, backend) + } else { + format!( + "Checking stage{} {} library ({} -> {})", + builder.top_stage, backend, &compiler.host.triple, target.triple + ) + }; + builder.info(&msg); run_cargo( builder, @@ -362,10 +385,15 @@ impl Step for RustAnalyzer { cargo.arg("--benches"); } - builder.info(&format!( - "Checking stage{} {} artifacts ({} -> {})", - compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple - )); + let msg = if compiler.host == target { + format!("Checking stage{} {} artifacts ({target})", compiler.stage, "rust-analyzer") + } else { + format!( + "Checking stage{} {} artifacts ({} -> {})", + compiler.stage, "rust-analyzer", &compiler.host.triple, target.triple + ) + }; + builder.info(&msg); run_cargo( builder, cargo, @@ -432,14 +460,18 @@ macro_rules! tool_check_step { // NOTE: this doesn't enable lints for any other tools unless they explicitly add `#![warn(rustc::internal)]` // See https://github.com/rust-lang/rust/pull/80573#issuecomment-754010776 cargo.rustflag("-Zunstable-options"); - - builder.info(&format!( - "Checking stage{} {} artifacts ({} -> {})", - builder.top_stage, - stringify!($name).to_lowercase(), - &compiler.host.triple, - target.triple - )); + let msg = if compiler.host == target { + format!("Checking stage{} {} artifacts ({target})", builder.top_stage, stringify!($name).to_lowercase()) + } else { + format!( + "Checking stage{} {} artifacts ({} -> {})", + builder.top_stage, + stringify!($name).to_lowercase(), + &compiler.host.triple, + target.triple + ) + }; + builder.info(&msg); run_cargo( builder, cargo, diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 468efc1114c..7ebd0a8f270 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -62,6 +62,7 @@ macro_rules! clean_crate_tree { let target = compiler.host; let mut cargo = builder.bare_cargo(compiler, $mode, target, "clean"); for krate in &*self.crates { + cargo.arg("-p"); cargo.arg(krate); } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 348d22a9ce6..8b80dfc0f9b 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -16,7 +16,7 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str; -use serde::Deserialize; +use serde_derive::Deserialize; use crate::builder::crate_description; use crate::builder::Cargo; diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 4a563bc396d..05e74254949 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -25,6 +25,7 @@ use crate::flags::{Color, Flags}; use crate::util::{exe, output, t}; use once_cell::sync::OnceCell; use serde::{Deserialize, Deserializer}; +use serde_derive::Deserialize; macro_rules! check_ci_llvm { ($name:expr) => { diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs index 681ecbfeb5b..5a105007f21 100644 --- a/src/bootstrap/config/tests.rs +++ b/src/bootstrap/config/tests.rs @@ -11,6 +11,11 @@ fn parse(config: &str) -> Config { #[test] fn download_ci_llvm() { + if crate::native::is_ci_llvm_modified(&parse("")) { + eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change"); + return; + } + let parse_llvm = |s| parse(s).llvm_from_ci; let if_available = parse_llvm("llvm.download-ci-llvm = \"if-available\""); diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 04e798e3949..c0d382d8a50 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -442,6 +442,8 @@ def to_toml(value): return value else: return "'" + value + "'" + elif isinstance(value, dict): + return "{" + ", ".join(map(lambda a: "{} = {}".format(to_toml(a[0]), to_toml(a[1])), value.items())) + "}" else: raise RuntimeError('no toml') @@ -486,6 +488,22 @@ for section_key, section_config in config.items(): else: configure_section(sections[section_key], section_config) +def write_uncommented(target, f): + block = [] + is_comment = True + + for line in target: + block.append(line) + if len(line) == 0: + if not is_comment: + for l in block: + f.write(l + "\n") + block = [] + is_comment = True + continue + is_comment = is_comment and line.startswith('#') + return f + # Now that we've built up our `config.toml`, write it all out in the same # order that we read it in. p("") @@ -494,11 +512,9 @@ with bootstrap.output('config.toml') as f: for section in section_order: if section == 'target': for target in targets: - for line in targets[target]: - f.write(line + "\n") + f = write_uncommented(targets[target], f) else: - for line in sections[section]: - f.write(line + "\n") + f = write_uncommented(sections[section], f) with bootstrap.output('Makefile') as f: contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in') diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index 9b2b549612d..d7008df4179 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -18,7 +18,9 @@ use std::process::Command; use object::read::archive::ArchiveFile; use object::BinaryFormat; +use sha2::Digest; +use crate::bolt::{instrument_with_bolt, optimize_with_bolt}; use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; use crate::channel; @@ -1904,6 +1906,26 @@ fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) { } } +fn install_llvm_file(builder: &Builder<'_>, source: &Path, destination: &Path) { + if builder.config.dry_run() { + return; + } + + // After LLVM is built, we modify (instrument or optimize) the libLLVM.so library file. + // This is not done in-place so that the built LLVM files are not "tainted" with BOLT. + // We perform the instrumentation/optimization here, on the fly, just before they are being + // packaged into some destination directory. + let postprocessed = if builder.config.llvm_bolt_profile_generate { + builder.ensure(BoltInstrument::new(source.to_path_buf())) + } else if let Some(path) = &builder.config.llvm_bolt_profile_use { + builder.ensure(BoltOptimize::new(source.to_path_buf(), path.into())) + } else { + source.to_path_buf() + }; + + builder.install(&postprocessed, destination, 0o644); +} + /// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking. /// /// Returns whether the files were actually copied. @@ -1955,7 +1977,7 @@ fn maybe_install_llvm(builder: &Builder<'_>, target: TargetSelection, dst_libdir } else { PathBuf::from(file) }; - builder.install(&file, dst_libdir, 0o644); + install_llvm_file(builder, &file, dst_libdir); } !builder.config.dry_run() } else { @@ -1986,6 +2008,117 @@ pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection } } +/// Creates an output path to a BOLT-manipulated artifact for the given `file`. +/// The hash of the file is used to make sure that we don't mix BOLT artifacts amongst different +/// files with the same name. +/// +/// We need to keep the file-name the same though, to make sure that copying the manipulated file +/// to a directory will not change the final file path. +fn create_bolt_output_path(builder: &Builder<'_>, file: &Path, hash: &str) -> PathBuf { + let directory = builder.out.join("bolt").join(hash); + t!(fs::create_dir_all(&directory)); + directory.join(file.file_name().unwrap()) +} + +/// Instrument the provided file with BOLT. +/// Returns a path to the instrumented artifact. +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct BoltInstrument { + file: PathBuf, + hash: String, +} + +impl BoltInstrument { + fn new(file: PathBuf) -> Self { + let mut hasher = sha2::Sha256::new(); + hasher.update(t!(fs::read(&file))); + let hash = hex::encode(hasher.finalize().as_slice()); + + Self { file, hash } + } +} + +impl Step for BoltInstrument { + type Output = PathBuf; + + const ONLY_HOSTS: bool = false; + const DEFAULT: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.never() + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + if builder.build.config.dry_run() { + return self.file.clone(); + } + + if builder.build.config.llvm_from_ci { + println!("warning: trying to use BOLT with LLVM from CI, this will probably not work"); + } + + println!("Instrumenting {} with BOLT", self.file.display()); + + let output_path = create_bolt_output_path(builder, &self.file, &self.hash); + if !output_path.is_file() { + instrument_with_bolt(&self.file, &output_path); + } + output_path + } +} + +/// Optimize the provided file with BOLT. +/// Returns a path to the optimized artifact. +/// +/// The hash is stored in the step to make sure that we don't optimize the same file +/// twice (even under different file paths). +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct BoltOptimize { + file: PathBuf, + profile: PathBuf, + hash: String, +} + +impl BoltOptimize { + fn new(file: PathBuf, profile: PathBuf) -> Self { + let mut hasher = sha2::Sha256::new(); + hasher.update(t!(fs::read(&file))); + hasher.update(t!(fs::read(&profile))); + let hash = hex::encode(hasher.finalize().as_slice()); + + Self { file, profile, hash } + } +} + +impl Step for BoltOptimize { + type Output = PathBuf; + + const ONLY_HOSTS: bool = false; + const DEFAULT: bool = false; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.never() + } + + fn run(self, builder: &Builder<'_>) -> PathBuf { + if builder.build.config.dry_run() { + return self.file.clone(); + } + + if builder.build.config.llvm_from_ci { + println!("warning: trying to use BOLT with LLVM from CI, this will probably not work"); + } + + println!("Optimizing {} with BOLT", self.file.display()); + + let output_path = create_bolt_output_path(builder, &self.file, &self.hash); + if !output_path.is_file() { + optimize_with_bolt(&self.file, &self.profile, &output_path); + } + output_path + } +} + #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct LlvmTools { pub target: TargetSelection, diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 7f8aa2573dd..cc80763ef44 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -62,6 +62,7 @@ macro_rules! book { target: self.target, name: INTERNER.intern_str($book_name), src: INTERNER.intern_path(builder.src.join($path)), + parent: Some(self), }) } } @@ -119,18 +120,20 @@ impl Step for UnstableBook { target: self.target, name: INTERNER.intern_str("unstable-book"), src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")), + parent: Some(self), }) } } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -struct RustbookSrc { +struct RustbookSrc<P: Step> { target: TargetSelection, name: Interned<String>, src: Interned<PathBuf>, + parent: Option<P>, } -impl Step for RustbookSrc { +impl<P: Step> Step for RustbookSrc<P> { type Output = (); fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -152,13 +155,18 @@ impl Step for RustbookSrc { let index = out.join("index.html"); let rustbook = builder.tool_exe(Tool::Rustbook); let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); - if builder.config.dry_run() || up_to_date(&src, &index) && up_to_date(&rustbook, &index) { - return; + + if !builder.config.dry_run() && !(up_to_date(&src, &index) || up_to_date(&rustbook, &index)) + { + builder.info(&format!("Rustbook ({}) - {}", target, name)); + let _ = fs::remove_dir_all(&out); + + builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out)); } - builder.info(&format!("Rustbook ({}) - {}", target, name)); - let _ = fs::remove_dir_all(&out); - builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out)); + if self.parent.is_some() { + builder.maybe_open_in_browser::<P>(index) + } } } @@ -205,6 +213,7 @@ impl Step for TheBook { target, name: INTERNER.intern_str("book"), src: INTERNER.intern_path(builder.src.join(&relative_path)), + parent: Some(self), }); // building older edition redirects @@ -213,6 +222,9 @@ impl Step for TheBook { target, name: INTERNER.intern_string(format!("book/{}", edition)), src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)), + // There should only be one book that is marked as the parent for each target, so + // treat the other editions as not having a parent. + parent: Option::<Self>::None, }); } @@ -228,10 +240,6 @@ impl Step for TheBook { invoke_rustdoc(builder, compiler, &shared_assets, target, path); } - - let out = builder.doc_out(target); - let index = out.join("book").join("index.html"); - builder.maybe_open_in_browser::<Self>(index); } } @@ -1032,10 +1040,7 @@ impl Step for RustcBook { target: self.target, name: INTERNER.intern_str("rustc"), src: INTERNER.intern_path(out_base), + parent: Some(self), }); - - let out = builder.doc_out(self.target); - let index = out.join("rustc").join("index.html"); - builder.maybe_open_in_browser::<Self>(index); } } diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index ae72a42973c..6d5753e8a6d 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -87,7 +87,7 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str get_git_modified_files(Some(&build.config.src), &vec!["rs"]) } -#[derive(serde::Deserialize)] +#[derive(serde_derive::Deserialize)] struct RustfmtConfig { ignore: Vec<String>, } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index f4abdf1cc57..ebd42bcf678 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1209,6 +1209,15 @@ impl Build { && (self.hosts.iter().any(|h| *h == target) || target == self.build) } + /// Checks whether the `compiler` compiling for `target` should be forced to + /// use a stage2 compiler instead. + /// + /// When we download the pre-compiled version of rustc it should be forced to + /// use a stage2 compiler. + fn force_use_stage2(&self) -> bool { + self.config.download_rustc() + } + /// Given `num` in the form "a.b.c" return a "release string" which /// describes the release version number. /// diff --git a/src/bootstrap/metadata.rs b/src/bootstrap/metadata.rs index e193e70a0c4..bba4d65e8c3 100644 --- a/src/bootstrap/metadata.rs +++ b/src/bootstrap/metadata.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::process::Command; -use serde::Deserialize; +use serde_derive::Deserialize; use crate::cache::INTERNER; use crate::util::output; diff --git a/src/bootstrap/metrics.rs b/src/bootstrap/metrics.rs index c823dc79684..2e62c950709 100644 --- a/src/bootstrap/metrics.rs +++ b/src/bootstrap/metrics.rs @@ -7,7 +7,7 @@ use crate::builder::Step; use crate::util::t; use crate::Build; -use serde::{Deserialize, Serialize}; +use serde_derive::{Deserialize, Serialize}; use std::cell::RefCell; use std::fs::File; use std::io::BufWriter; diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 21157b02a78..8c2bece1e62 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -16,7 +16,6 @@ use std::io; use std::path::{Path, PathBuf}; use std::process::Command; -use crate::bolt::{instrument_with_bolt_inplace, optimize_library_with_bolt_inplace}; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::channel; use crate::config::{Config, TargetSelection}; @@ -216,21 +215,24 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { } } - if CiEnv::is_ci() { + if is_ci_llvm_modified(config) { + eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change"); + return false; + } + + true +} + +/// Returns true if we're running in CI with modified LLVM (and thus can't download it) +pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool { + CiEnv::is_ci() && { // We assume we have access to git, so it's okay to unconditionally pass // `true` here. let llvm_sha = detect_llvm_sha(config, true); let head_sha = output(config.git().arg("rev-parse").arg("HEAD")); let head_sha = head_sha.trim(); - if llvm_sha == head_sha { - eprintln!( - "Detected LLVM as non-available: running in CI and modified LLVM in this change" - ); - return false; - } + llvm_sha == head_sha } - - true } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] @@ -520,34 +522,12 @@ impl Step for Llvm { } } - // After LLVM is built, we modify (instrument or optimize) the libLLVM.so library file - // in place. This is fine, because currently we do not support incrementally rebuilding - // LLVM after a configuration change, so to rebuild it the build files have to be removed, - // which will also remove these modified files. - if builder.config.llvm_bolt_profile_generate { - instrument_with_bolt_inplace(&get_built_llvm_lib_path(&res.llvm_config)); - } - if let Some(path) = &builder.config.llvm_bolt_profile_use { - optimize_library_with_bolt_inplace( - &get_built_llvm_lib_path(&res.llvm_config), - &Path::new(path), - ); - } - t!(stamp.write()); res } } -/// Returns path to a built LLVM library (libLLVM.so). -/// Assumes that we have built LLVM into a single library file. -fn get_built_llvm_lib_path(llvm_config_path: &Path) -> PathBuf { - let mut cmd = Command::new(llvm_config_path); - cmd.arg("--libfiles"); - PathBuf::from(output(&mut cmd).trim()) -} - fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { if !builder.config.llvm_version_check { return; diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index 1969e0b6f87..7aab88a1a73 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -1,6 +1,6 @@ use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::util::t; -use serde::{Deserialize, Serialize}; +use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; use std::env; use std::fmt; diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index c3d532c4b27..e7d1d9781d5 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -9,6 +9,7 @@ https://doc.rust-lang.org/stable/rustc/platform-support/fuchsia.html#aarch64-unk import argparse from dataclasses import dataclass +import fcntl import glob import hashlib import json @@ -146,6 +147,9 @@ class TestEnvironment: def zxdb_script_path(self): return os.path.join(self.tmp_dir(), "zxdb_script") + def pm_lockfile_path(self): + return os.path.join(self.tmp_dir(), "pm.lock") + def log_info(self, msg): print(msg) @@ -460,6 +464,9 @@ class TestEnvironment: stderr=self.subprocess_output(), ) + # Create lockfiles + open(self.pm_lockfile_path(), 'a').close() + # Write to file self.write_to_file() @@ -676,19 +683,25 @@ class TestEnvironment: log("Publishing package to repo...") # Publish package to repo - subprocess.check_call( - [ - self.tool_path("pm"), - "publish", - "-a", - "-repo", - self.repo_dir(), - "-f", - far_path, - ], - stdout=log_file, - stderr=log_file, - ) + with open(self.pm_lockfile_path(), 'w') as pm_lockfile: + fcntl.lockf(pm_lockfile.fileno(), fcntl.LOCK_EX) + subprocess.check_call( + [ + self.tool_path("pm"), + "publish", + "-a", + "-repo", + self.repo_dir(), + "-f", + far_path, + ], + stdout=log_file, + stderr=log_file, + ) + # This lock should be released automatically when the pm + # lockfile is closed, but we'll be polite and unlock it now + # since the spec leaves some wiggle room. + fcntl.lockf(pm_lockfile.fileno(), fcntl.LOCK_UN) log("Running ffx test...") diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index ad9c308ad85..11f1532bef5 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -78,7 +78,7 @@ x--expand-yaml-anchors--remove: <<: *base-job - &job-macos-xl - os: macos-latest # We don't have an XL builder for this + os: macos-12-xl <<: *base-job - &job-windows-xl diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh deleted file mode 100755 index cbe32920a74..00000000000 --- a/src/ci/pgo.sh +++ /dev/null @@ -1,230 +0,0 @@ -#!/bin/bash -# ignore-tidy-linelength - -set -euxo pipefail - -ci_dir=`cd $(dirname $0) && pwd` -source "$ci_dir/shared.sh" - -# The root checkout, where the source is located -CHECKOUT=/checkout - -DOWNLOADED_LLVM=/rustroot - -# The main directory where the build occurs, which can be different between linux and windows -BUILD_ROOT=$CHECKOUT/obj - -if isWindows; then - CHECKOUT=$(pwd) - DOWNLOADED_LLVM=$CHECKOUT/citools/clang-rust - BUILD_ROOT=$CHECKOUT -fi - -# The various build artifacts used in other commands: to launch rustc builds, build the perf -# collector, and run benchmarks to gather profiling data -BUILD_ARTIFACTS=$BUILD_ROOT/build/$PGO_HOST -RUSTC_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/rustc -CARGO_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/cargo -RUSTC_STAGE_2=$BUILD_ARTIFACTS/stage2/bin/rustc - -# Windows needs these to have the .exe extension -if isWindows; then - RUSTC_STAGE_0="${RUSTC_STAGE_0}.exe" - CARGO_STAGE_0="${CARGO_STAGE_0}.exe" - RUSTC_STAGE_2="${RUSTC_STAGE_2}.exe" -fi - -# Make sure we have a temporary PGO work folder -PGO_TMP=/tmp/tmp-pgo -mkdir -p $PGO_TMP -rm -rf $PGO_TMP/* - -RUSTC_PERF=$PGO_TMP/rustc-perf - -# Compile several crates to gather execution PGO profiles. -# Arg0 => profiles (Debug, Opt) -# Arg1 => scenarios (Full, IncrFull, All) -# Arg2 => crates (syn, cargo, ...) -gather_profiles () { - cd $BUILD_ROOT - - # Compile libcore, both in opt-level=0 and opt-level=3 - RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \ - --edition=2021 --crate-type=lib $CHECKOUT/library/core/src/lib.rs \ - --out-dir $PGO_TMP - RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \ - --edition=2021 --crate-type=lib -Copt-level=3 $CHECKOUT/library/core/src/lib.rs \ - --out-dir $PGO_TMP - - cd $RUSTC_PERF - - # Run rustc-perf benchmarks - # Benchmark using profile_local with eprintln, which essentially just means - # don't actually benchmark -- just make sure we run rustc a bunch of times. - RUST_LOG=collector=debug \ - RUSTC=$RUSTC_STAGE_0 \ - RUSTC_BOOTSTRAP=1 \ - $CARGO_STAGE_0 run -p collector --bin collector -- \ - profile_local \ - eprintln \ - $RUSTC_STAGE_2 \ - --id Test \ - --profiles $1 \ - --cargo $CARGO_STAGE_0 \ - --scenarios $2 \ - --include $3 - - cd $BUILD_ROOT -} - -# This path has to be absolute -LLVM_PROFILE_DIRECTORY_ROOT=$PGO_TMP/llvm-pgo - -# We collect LLVM profiling information and rustc profiling information in -# separate phases. This increases build time -- though not by a huge amount -- -# but prevents any problems from arising due to different profiling runtimes -# being simultaneously linked in. -# LLVM IR PGO does not respect LLVM_PROFILE_FILE, so we have to set the profiling file -# path through our custom environment variable. We include the PID in the directory path -# to avoid updates to profile files being lost because of race conditions. -LLVM_PROFILE_DIR=${LLVM_PROFILE_DIRECTORY_ROOT}/prof-%p python3 $CHECKOUT/x.py build \ - --target=$PGO_HOST \ - --host=$PGO_HOST \ - --stage 2 library/std \ - --llvm-profile-generate - -# Compile rustc-perf: -# - get the expected commit source code: on linux, the Dockerfile downloads a source archive before -# running this script. On Windows, we do that here. -if isLinux; then - cp -r /tmp/rustc-perf $RUSTC_PERF - chown -R $(whoami): $RUSTC_PERF -else - # rustc-perf version from 2022-07-22 - PERF_COMMIT=3c253134664fdcba862c539d37f0de18557a9a4c - retry curl -LS -o $PGO_TMP/perf.zip \ - https://github.com/rust-lang/rustc-perf/archive/$PERF_COMMIT.zip && \ - cd $PGO_TMP && unzip -q perf.zip && \ - mv rustc-perf-$PERF_COMMIT $RUSTC_PERF && \ - rm perf.zip -fi - -# - build rustc-perf's collector ahead of time, which is needed to make sure the rustc-fake binary -# used by the collector is present. -cd $RUSTC_PERF - -RUSTC=$RUSTC_STAGE_0 \ -RUSTC_BOOTSTRAP=1 \ -$CARGO_STAGE_0 build -p collector - -# Here we're profiling LLVM, so we only care about `Debug` and `Opt`, because we want to stress -# codegen. We also profile some of the most prolific crates. -gather_profiles "Debug,Opt" "Full" \ - "syn-1.0.89,cargo-0.60.0,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18" - -LLVM_PROFILE_MERGED_FILE=$PGO_TMP/llvm-pgo.profdata - -# Merge the profile data we gathered for LLVM -# Note that this uses the profdata from the clang we used to build LLVM, -# which likely has a different version than our in-tree clang. -$DOWNLOADED_LLVM/bin/llvm-profdata merge -o ${LLVM_PROFILE_MERGED_FILE} ${LLVM_PROFILE_DIRECTORY_ROOT} - -echo "LLVM PGO statistics" -du -sh ${LLVM_PROFILE_MERGED_FILE} -du -sh ${LLVM_PROFILE_DIRECTORY_ROOT} -echo "Profile file count" -find ${LLVM_PROFILE_DIRECTORY_ROOT} -type f | wc -l - -# We don't need the individual .profraw files now that they have been merged into a final .profdata -rm -r $LLVM_PROFILE_DIRECTORY_ROOT - -# Rustbuild currently doesn't support rebuilding LLVM when PGO options -# change (or any other llvm-related options); so just clear out the relevant -# directories ourselves. -rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld - -# Okay, LLVM profiling is done, switch to rustc PGO. - -# The path has to be absolute -RUSTC_PROFILE_DIRECTORY_ROOT=$PGO_TMP/rustc-pgo - -python3 $CHECKOUT/x.py build --target=$PGO_HOST --host=$PGO_HOST \ - --stage 2 library/std \ - --rust-profile-generate=${RUSTC_PROFILE_DIRECTORY_ROOT} - -# Here we're profiling the `rustc` frontend, so we also include `Check`. -# The benchmark set includes various stress tests that put the frontend under pressure. -if isLinux; then - # The profile data is written into a single filepath that is being repeatedly merged when each - # rustc invocation ends. Empirically, this can result in some profiling data being lost. That's - # why we override the profile path to include the PID. This will produce many more profiling - # files, but the resulting profile will produce a slightly faster rustc binary. - LLVM_PROFILE_FILE=${RUSTC_PROFILE_DIRECTORY_ROOT}/default_%m_%p.profraw gather_profiles \ - "Check,Debug,Opt" "All" \ - "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" -else - # On windows, we don't do that yet (because it generates a lot of data, hitting disk space - # limits on the builder), and use the default profraw merging behavior. - gather_profiles \ - "Check,Debug,Opt" "All" \ - "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" -fi - -RUSTC_PROFILE_MERGED_FILE=$PGO_TMP/rustc-pgo.profdata - -# Merge the profile data we gathered -$BUILD_ARTIFACTS/llvm/bin/llvm-profdata \ - merge -o ${RUSTC_PROFILE_MERGED_FILE} ${RUSTC_PROFILE_DIRECTORY_ROOT} - -echo "Rustc PGO statistics" -du -sh ${RUSTC_PROFILE_MERGED_FILE} -du -sh ${RUSTC_PROFILE_DIRECTORY_ROOT} -echo "Profile file count" -find ${RUSTC_PROFILE_DIRECTORY_ROOT} -type f | wc -l - -# We don't need the individual .profraw files now that they have been merged into a final .profdata -rm -r $RUSTC_PROFILE_DIRECTORY_ROOT - -# Rustbuild currently doesn't support rebuilding LLVM when PGO options -# change (or any other llvm-related options); so just clear out the relevant -# directories ourselves. -rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld - -if isLinux; then - # Gather BOLT profile (BOLT is currently only available on Linux) - python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ - --stage 2 library/std \ - --llvm-profile-use=${LLVM_PROFILE_MERGED_FILE} \ - --llvm-bolt-profile-generate - - BOLT_PROFILE_MERGED_FILE=/tmp/bolt.profdata - - # Here we're profiling Bolt. - gather_profiles "Check,Debug,Opt" "Full" \ - "syn-1.0.89,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18" - - merge-fdata /tmp/prof.fdata* > ${BOLT_PROFILE_MERGED_FILE} - - echo "BOLT statistics" - du -sh /tmp/prof.fdata* - du -sh ${BOLT_PROFILE_MERGED_FILE} - echo "Profile file count" - find /tmp/prof.fdata* -type f | wc -l - - rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld - - # This produces the actual final set of artifacts, using both the LLVM and rustc - # collected profiling data. - $@ \ - --rust-profile-use=${RUSTC_PROFILE_MERGED_FILE} \ - --llvm-profile-use=${LLVM_PROFILE_MERGED_FILE} \ - --llvm-bolt-profile-use=${BOLT_PROFILE_MERGED_FILE} -else - $@ \ - --rust-profile-use=${RUSTC_PROFILE_MERGED_FILE} \ - --llvm-profile-use=${LLVM_PROFILE_MERGED_FILE} -fi - -echo "Rustc binary size" -ls -la ./build/$PGO_HOST/stage2/bin -ls -la ./build/$PGO_HOST/stage2/lib diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py index bd8fd524a26..fe3083dc31e 100644 --- a/src/ci/stage-build.py +++ b/src/ci/stage-build.py @@ -798,14 +798,16 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L "--llvm-profile-use", pipeline.llvm_profile_merged_file(), "--llvm-bolt-profile-generate", + "--rust-profile-use", + pipeline.rustc_profile_merged_file() ]) record_metrics(pipeline, rustc_build) with stage3.section("Gather profiles"): gather_llvm_bolt_profiles(pipeline) + # LLVM is not being cleared here, we want to reuse the previous build print_free_disk_space(pipeline) - clear_llvm_files(pipeline) final_build_args += [ "--llvm-bolt-profile-use", pipeline.llvm_bolt_profile_merged_file() diff --git a/src/doc/book b/src/doc/book -Subproject d94e03a18a2590ed3f1c67b859cb11528d2a2d5 +Subproject 21a2ed14f4480dab62438dcc1130291bebc6537 diff --git a/src/doc/reference b/src/doc/reference -Subproject e5adb99c04817b7fbe08f4ffce5b36702667345 +Subproject a9afb04b47a84a6753e4dc657348c324c876102 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example -Subproject efe23c4fe12e06351b8dc8c3d18312c76145510 +Subproject af0998b7473839ca75563ba3d3e7fd0160bef23 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide -Subproject 41a96ab971cb45e2a184df20619ad1829765c99 +Subproject b06dab84083390e0ee1e998f466545a8a1a76a9 diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 16057048259..9eafa27e2b7 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -13,6 +13,8 @@ for targets at each tier, see the [Target Tier Policy](target-tier-policy.md). Targets are identified by their "target triple" which is the string to inform the compiler what kind of output should be produced. +Component availability is tracked [here](https://rust-lang.github.io/rustup-components-history/). + ## Tier 1 with Host Tools Tier 1 targets can be thought of as "guaranteed to work". The Rust project @@ -216,7 +218,7 @@ target | std | host | notes [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon [`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | -[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.1 RTOS | +[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD `aarch64-unknown-hermit` | ✓ | | ARM64 HermitCore `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) @@ -308,7 +310,7 @@ target | std | host | notes `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 `x86_64-apple-tvos` | * | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator -[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ? | | x86 64-bit QNX Neutrino 7.1 RTOS | +[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | [`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | `x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md index 63dde2aaedd..4d97b8c6cb9 100644 --- a/src/doc/rustc/src/platform-support/fuchsia.md +++ b/src/doc/rustc/src/platform-support/fuchsia.md @@ -12,7 +12,6 @@ The [Fuchsia team]: - Tyler Mandry ([@tmandry](https://github.com/tmandry)) - Dan Johnson ([@computerdruid](https://github.com/computerdruid)) - David Koloski ([@djkoloski](https://github.com/djkoloski)) -- Andrew Pollack ([@andrewpollack](https://github.com/andrewpollack)) - Joseph Ryan ([@P1n3appl3](https://github.com/P1n3appl3)) As the team evolves over time, the specific members listed here may differ from @@ -717,7 +716,7 @@ run the full `tests/ui` test suite: --stage=2 \ test tests/ui \ --target x86_64-unknown-fuchsia \ - --run=always --jobs 1 \ + --run=always \ --test-args --target-rustcflags \ --test-args -Lnative=${SDK_PATH}/arch/{x64|arm64}/sysroot/lib \ --test-args --target-rustcflags \ @@ -729,9 +728,6 @@ run the full `tests/ui` test suite: ) ``` -*Note: The test suite cannot be run in parallel at the moment, so `x.py` -must be run with `--jobs 1` to ensure only one test runs at a time.* - By default, `x.py` compiles test binaries with `panic=unwind`. If you built your Rust toolchain with `-Cpanic=abort`, you need to tell `x.py` to compile test binaries with `panic=abort` as well: @@ -908,7 +904,7 @@ through our `x.py` invocation. The full invocation is: --stage=2 \ test tests/${TEST} \ --target x86_64-unknown-fuchsia \ - --run=always --jobs 1 \ + --run=always \ --test-args --target-rustcflags \ --test-args -Lnative=${SDK_PATH}/arch/{x64|arm64}/sysroot/lib \ --test-args --target-rustcflags \ diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 37d0c31976c..38198fe6c3a 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -2,9 +2,9 @@ **Tier: 3** -[BlackBerry® QNX®][BlackBerry] Neutrino (nto) Real-time operating system. +[QNX®][BlackBerry] Neutrino (nto) Real-time operating system. The support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit] -and [BlackBerry][BlackBerry]. +and [Blackberry QNX][BlackBerry]. [BlackBerry]: https://blackberry.qnx.com [Elektrobit]: https://www.elektrobit.com @@ -19,19 +19,24 @@ and [BlackBerry][BlackBerry]. Currently, only cross-compilation for QNX Neutrino on AArch64 and x86_64 are supported (little endian). Adding other architectures that are supported by QNX Neutrino is possible. -The standard library does not yet support QNX Neutrino. Therefore, only `no_std` code can -be compiled. +The standard library, including `core` and `alloc` (with default allocator) are supported. -`core` and `alloc` (with default allocator) are supported. +For building or using the Rust toolchain for QNX Neutrino, the +[QNX Software Development Platform (SDP)](https://blackberry.qnx.com/en/products/foundation-software/qnx-software-development-platform) +must be installed and initialized. +Initialization is usually done by sourcing `qnxsdp-env.sh` (this will be installed as part of the SDP, see also installation instruction provided with the SDP). +Afterwards [`qcc`](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.utilities/topic/q/qcc.html) (QNX C/C++ compiler) +should be available (in the `$PATH` variable). +`qcc` will be called e.g. for linking executables. -Applications must link against `libc.so` (see example). This is required because applications -always link against the `crt` library and `crt` depends on `libc.so`. - -The correct version of `qcc` must be available by setting the `$PATH` variable (e.g. by sourcing `qnxsdp-env.sh` of the -QNX Neutrino toolchain). +When linking `no_std` applications, they must link against `libc.so` (see example). This is +required because applications always link against the `crt` library and `crt` depends on `libc.so`. +This is done automatically when using the standard library. ### Small example application +Small `no_std` example is shown below. Applications using the standard library work as well. + ```rust,ignore (platform-specific) #![no_std] #![no_main] @@ -89,30 +94,150 @@ changelog-seen = 2 2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host (for both `aarch64` and `x86_64` targets) -Run the following: +Compiling the Rust toolchain requires the same environment variables used for compiling C binaries. +Refer to the [QNX developer manual](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.prog/topic/devel_OS_version.html). + +To compile for QNX Neutrino (aarch64 and x86_64) and Linux (x86_64): ```bash -env \ - CC_aarch64-unknown-nto-qnx710="qcc" \ - CFLAGS_aarch64-unknown-nto-qnx710="-Vgcc_ntoaarch64le_cxx" \ - CXX_aarch64-unknown-nto-qnx710="qcc" \ - AR_aarch64_unknown_nto_qnx710="ntoaarch64-ar" \ - CC_x86_64-pc-nto-qnx710="qcc" \ - CFLAGS_x86_64-pc-nto-qnx710="-Vgcc_ntox86_64_cxx" \ - CXX_x86_64-pc-nto-qnx710="qcc" \ - AR_x86_64_pc_nto_qnx710="ntox86_64-ar" \ - ./x.py build --target aarch64-unknown-nto-qnx710 --target x86_64-pc-nto-qnx710 --target x86_64-unknown-linux-gnu rustc library/core library/alloc/ +export build_env=' + CC_aarch64-unknown-nto-qnx710=qcc + CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx + CXX_aarch64-unknown-nto-qnx710=qcc + AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar + CC_x86_64-pc-nto-qnx710=qcc + CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx + CXX_x86_64-pc-nto-qnx710=qcc + AR_x86_64_pc_nto_qnx710=ntox86_64-ar' + +env $build_env \ + ./x.py build \ + --target aarch64-unknown-nto-qnx710 \ + --target x86_64-pc-nto-qnx710 \ + --target x86_64-unknown-linux-gnu \ + rustc library/core library/alloc ``` +## Running the Rust test suite + +The test suites of the Rust compiler and standard library can be executed much like other Rust targets. +The environment for testing should match the one used during compiler compilation (refer to `build_env` and `qcc`/`PATH` above) with the +addition of the TEST_DEVICE_ADDR environment variable. +The TEST_DEVICE_ADDR variable controls the remote runner and should point to the target, despite localhost being shown in the following example. +Note that some tests are failing which is why they are currently excluded by the target maintainers which can be seen in the following example. + +To run all tests on a x86_64 QNX Neutrino target: + +```bash +export TEST_DEVICE_ADDR="localhost:12345" # must address the test target, can be a SSH tunnel +export build_env=' + CC_aarch64-unknown-nto-qnx710=qcc + CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx + CXX_aarch64-unknown-nto-qnx710=qcc + AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar + CC_x86_64-pc-nto-qnx710=qcc + CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx + CXX_x86_64-pc-nto-qnx710=qcc + AR_x86_64_pc_nto_qnx710=ntox86_64-ar' + +# Disable tests that only work on the host or don't make sense for this target. +# See also: +# - src/ci/docker/host-x86_64/i686-gnu/Dockerfile +# - https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Running.20tests.20on.20remote.20target +# - .github/workflows/ci.yml +export exclude_tests=' + --exclude src/bootstrap + --exclude src/tools/error_index_generator + --exclude src/tools/linkchecker + --exclude tests/ui-fulldeps + --exclude rustc + --exclude rustdoc + --exclude tests/run-make-fulldeps' + +env $build_env \ + ./x.py test -j 1 \ + $exclude_tests \ + --stage 1 \ + --target x86_64-pc-nto-qnx710 +``` + +Currently, only one thread can be used when testing due to limitations in `libc::fork` and `libc::posix_spawnp`. +See [fork documentation](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html) +(error section) for more information. +This can be achieved by using the `-j 1` parameter in the `x.py` call. +This issue is being researched and we will try to allow parallelism in the future. + ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using -`build-std` or similar. +Rust does not yet ship pre-compiled artifacts for this target. +To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), +or build your own copy of `core` by using `build-std` or similar. ## Testing -Compiled executables can directly be run on QNX Neutrino. +Compiled executables can run directly on QNX Neutrino. + +### Rust std library test suite + +The target needs sufficient resources to execute all tests. The commands below assume that a QEMU image +is used. + +* Ensure that the temporary directory used by `remote-test-server` has enough free space and inodes. + 5GB of free space and 40000 inodes are known to be sufficient (the test will create more than 32k files). + To create a QEMU image in an empty directory, run this command inside the directory: + + ```bash + mkqnximage --type=qemu --ssh-ident=$HOME/.ssh/id_ed25519.pub --data-size=5000 --data-inodes=40000 + ``` + + `/data` should have enough free resources. + Set the `TMPDIR` environment variable accordingly when running `remote-test-server`, e.g.: + ```bash + TMPDIR=/data/tmp/rust remote-test-server --bind 0.0.0.0:12345 + ``` + +* Ensure the TCP stack can handle enough parallel connections (default is 200, should be 300 or higher). + After creating an image (see above), edit the file `output/build/startup.sh`: + 1. Search for `io-pkt-v6-hc` + 2. Add the parameter `-ptcpip threads_max=300`, e.g.: + ```text + io-pkt-v6-hc -U 33:33 -d e1000 -ptcpip threads_max=300 + ``` + 3. Update the image by running `mkqnximage` again with the same parameters as above for creating it. + +* Running and stopping the virtual machine + + To start the virtual machine, run inside the directory of the VM: + + ```bash + mkqnximage --run=-h + ``` + + To stop the virtual machine, run inside the directory of the VM: + + ```bash + mkqnximage --stop + ``` + +* Ensure local networking + + Ensure that 'localhost' is getting resolved to 127.0.0.1. If you can't ping the localhost, some tests may fail. + Ensure it's appended to /etc/hosts (if first `ping` command fails). + Commands have to be executed inside the virtual machine! + + ```bash + $ ping localhost + ping: Cannot resolve "localhost" (Host name lookup failure) + + $ echo "127.0.0.1 localhost" >> /etc/hosts + + $ ping localhost + PING localhost (127.0.0.1): 56 data bytes + 64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=1 ms + ``` ## Cross-compilation toolchains and C code -Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), to ensure `qcc` is used with proper arguments. To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout. +Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), +to ensure `qcc` is used with proper arguments. +To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout. diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md index d666d54b315..28a004a9253 100644 --- a/src/doc/rustdoc/src/how-to-read-rustdoc.md +++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md @@ -84,6 +84,9 @@ When typing in the search bar, you can prefix your search term with a type followed by a colon (such as `mod:`) to restrict the results to just that kind of item. (The available items are listed in the help popup.) +Searching for `println!` will search for a macro named `println`, just like +searching for `macro:println` does. + ### Changing displayed theme You can change the displayed theme by opening the settings menu (the gear diff --git a/src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md b/src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md new file mode 100644 index 00000000000..c7f10afaccc --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/export-executable-symbols.md @@ -0,0 +1,43 @@ +# `export-executable-symbols` + +The tracking issue for this feature is: [#84161](https://github.com/rust-lang/rust/issues/84161). + +------------------------ + +The `-Zexport-executable-symbols` compiler flag makes `rustc` export symbols from executables. The resulting binary is runnable, but can also be used as a dynamic library. This is useful for interoperating with programs written in other languages, in particular languages with a runtime like Java or Lua. + +For example on windows: +```rust +#[no_mangle] +fn my_function() -> usize { + return 42; +} + +fn main() { + println!("Hello, world!"); +} +``` + +A standard `cargo build` will produce a `.exe` without an export directory. When the `export-executable-symbols` flag is added + +```Bash +export RUSTFLAGS="-Zexport-executable-symbols" +cargo build +``` + +the binary has an export directory with the functions: + +```plain +The Export Tables (interpreted .edata section contents) + +... + +[Ordinal/Name Pointer] Table + [ 0] my_function + [ 1] main +``` +(the output of `objdump -x` on the binary) + +Please note that the `#[no_mangle]` attribute is required. Without it, the symbol is not exported. + +The equivalent of this flag in C and C++ compilers is the `__declspec(dllexport)` annotation or the `-rdynamic` linker flag. diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0e8f0cfc518..bbd9f18973a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -25,7 +25,7 @@ use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, AdtKind, DefIdTree, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index fc1396e86f6..6d8380c5fcc 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -22,7 +22,7 @@ use rustc_hir::{BodyId, Mutability}; use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety; use rustc_index::vec::IndexVec; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, DefIdTree, TyCtxt, Visibility}; +use rustc_middle::ty::{self, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; @@ -1017,12 +1017,12 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct ItemLink { /// The original link written in the markdown - pub(crate) link: String, + pub(crate) link: Box<str>, /// The link text displayed in the HTML. /// /// This may not be the same as `link` if there was a disambiguator /// in an intra-doc link (e.g. \[`fn@f`\]) - pub(crate) link_text: String, + pub(crate) link_text: Box<str>, /// The `DefId` of the Item whose **HTML Page** contains the item being /// linked to. This will be different to `item_id` on item's that don't /// have their own page, such as struct fields and enum variants. @@ -1035,9 +1035,9 @@ pub struct RenderedLink { /// The text the link was original written as. /// /// This could potentially include disambiguators and backticks. - pub(crate) original_text: String, + pub(crate) original_text: Box<str>, /// The text to display in the HTML - pub(crate) new_text: String, + pub(crate) new_text: Box<str>, /// The URL to put in the `href` pub(crate) href: String, /// The tooltip. diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index c9c1c2c458a..cafb00df51e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -17,7 +17,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::mir; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; use std::mem; diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8a73d25d3f0..9cf84acc79f 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -769,8 +769,8 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { Ok(p) => p, Err(_) => { - debug!("Cannot build a parser to check mod attr so skipping..."); - return true; + // If there is an unclosed delimiter, an error will be returned by the tokentrees. + return false; } }; // If a parsing error happened, it's very likely that the attribute is incomplete. @@ -778,15 +778,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { e.cancel(); return false; } - // We now check if there is an unclosed delimiter for the attribute. To do so, we look at - // the `unclosed_delims` and see if the opening square bracket was closed. - parser - .unclosed_delims() - .get(0) - .map(|unclosed| { - unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2) - }) - .unwrap_or(true) + true }) }) .unwrap_or(false) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6bdd9db9bfa..f9ea829e388 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -19,7 +19,6 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty; -use rustc_middle::ty::DefIdTree; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::kw; use rustc_span::{sym, Symbol}; @@ -772,14 +771,21 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Cont let Some((fqp, shortty)) = cache.paths.get(&did) .or_else(|| cache.external_paths.get(&did)) else { return String::new() }; - let fqp = fqp.iter().map(|sym| sym.as_str()).join("::"); + let mut buf = Buffer::new(); if let &Some(UrlFragment::Item(id)) = fragment { - let name = cx.tcx().item_name(id); - let descr = cx.tcx().def_descr(id); - format!("{descr} {fqp}::{name}") - } else { - format!("{shortty} {fqp}") + write!(buf, "{} ", cx.tcx().def_descr(id)); + for component in fqp { + write!(buf, "{component}::"); + } + write!(buf, "{}", cx.tcx().item_name(id)); + } else if !fqp.is_empty() { + let mut fqp_it = fqp.into_iter(); + write!(buf, "{shortty} {}", fqp_it.next().unwrap()); + for component in fqp_it { + write!(buf, "::{component}"); + } } + buf.into_inner() } /// Used to render a [`clean::Path`]. diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 89f1ad71134..fe446ae3c16 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -978,7 +978,7 @@ impl Markdown<'_> { let mut replacer = |broken_link: BrokenLink<'_>| { links .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) + .find(|link| &*link.original_text == &*broken_link.reference) .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; @@ -1061,7 +1061,7 @@ impl MarkdownSummaryLine<'_> { let mut replacer = |broken_link: BrokenLink<'_>| { links .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) + .find(|link| &*link.original_text == &*broken_link.reference) .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; @@ -1108,7 +1108,7 @@ fn markdown_summary_with_limit( let mut replacer = |broken_link: BrokenLink<'_>| { link_names .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) + .find(|link| &*link.original_text == &*broken_link.reference) .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; @@ -1189,7 +1189,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin let mut replacer = |broken_link: BrokenLink<'_>| { link_names .iter() - .find(|link| link.original_text.as_str() == &*broken_link.reference) + .find(|link| &*link.original_text == &*broken_link.reference) .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into())) }; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 2869a39613f..08796f10d92 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1839,6 +1839,12 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { } else { let size = layout.size.bytes() - tag_size; write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" },); + if layout.abi.is_uninhabited() { + write!( + w, + " (<a href=\"https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited\">uninhabited</a>)" + ); + } } } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 090ea2cb157..e22ac6ec19b 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -7,9 +7,7 @@ use rustc_span::symbol::Symbol; use serde::ser::{Serialize, SerializeStruct, Serializer}; use crate::clean; -use crate::clean::types::{ - FnRetTy, Function, GenericBound, Generics, ItemId, Type, WherePredicate, -}; +use crate::clean::types::{FnRetTy, Function, Generics, ItemId, Type, WherePredicate}; use crate::formats::cache::{Cache, OrphanImplItem}; use crate::formats::item_type::ItemType; use crate::html::format::join_with_double_colon; @@ -482,29 +480,23 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>( if let Type::Generic(arg_s) = *arg { // First we check if the bounds are in a `where` predicate... if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g { - WherePredicate::BoundPredicate { ty, .. } => ty.def_id(cache) == arg.def_id(cache), + WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s, _ => false, }) { let mut ty_generics = Vec::new(); let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]); for bound in bounds.iter() { - if let GenericBound::TraitBound(poly_trait, _) = bound { - for param_def in poly_trait.generic_params.iter() { - match ¶m_def.kind { - clean::GenericParamDefKind::Type { default: Some(ty), .. } => { - add_generics_and_bounds_as_types( - self_, - generics, - ty, - tcx, - recurse + 1, - &mut ty_generics, - cache, - ) - } - _ => {} - } - } + if let Some(path) = bound.get_trait_path() { + let ty = Type::Path { path }; + add_generics_and_bounds_as_types( + self_, + generics, + &ty, + tcx, + recurse + 1, + &mut ty_generics, + cache, + ); } } insert_ty(res, arg.clone(), ty_generics); diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index fcd925bb358..1a34530c2d1 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -90,7 +90,6 @@ module.exports = { "no-return-assign": "error", "no-script-url": "error", "no-sequences": "error", - "no-throw-literal": "error", "no-div-regex": "error", } }; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index d18b56eb19c..95528e70e35 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1260,6 +1260,11 @@ a.tooltip:hover::after { color: var(--search-tab-title-count-color); } +#search .error code { + border-radius: 3px; + background-color: var(--search-error-code-background-color); +} + #src-sidebar-toggle { position: sticky; top: 0; diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 472a725f053..90cf689ad33 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -43,6 +43,7 @@ Original by Dempfi (https://github.com/dempfi/ayu) --search-result-link-focus-background-color: #3c3c3c; --search-result-border-color: #aaa3; --search-color: #fff; + --search-error-code-background-color: #4f4c4c; --search-results-alias-color: #c5c5c5; --search-results-grey-color: #999; --search-tab-title-count-color: #888; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 5612bde96a8..e8cd0693139 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -38,6 +38,7 @@ --search-result-link-focus-background-color: #616161; --search-result-border-color: #aaa3; --search-color: #111; + --search-error-code-background-color: #484848; --search-results-alias-color: #fff; --search-results-grey-color: #ccc; --search-tab-title-count-color: #888; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 34b35c405a8..5e3f14e483f 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -38,6 +38,7 @@ --search-result-link-focus-background-color: #ccc; --search-result-border-color: #aaa3; --search-color: #000; + --search-error-code-background-color: #d0cccc; --search-results-alias-color: #000; --search-results-grey-color: #999; --search-tab-title-count-color: #888; diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1e6c94d29ba..b98bced4126 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -144,7 +144,7 @@ function initSearch(rawSearchIndex) { function itemTypeFromName(typename) { const index = itemTypes.findIndex(i => i === typename); if (index < 0) { - throw new Error("Unknown type filter `" + typename + "`"); + throw ["Unknown type filter ", typename]; } return index; } @@ -164,21 +164,21 @@ function initSearch(rawSearchIndex) { */ function getStringElem(query, parserState, isInGenerics) { if (isInGenerics) { - throw new Error("`\"` cannot be used in generics"); + throw ["Unexpected ", "\"", " in generics"]; } else if (query.literalSearch) { - throw new Error("Cannot have more than one literal search element"); + throw ["Cannot have more than one literal search element"]; } else if (parserState.totalElems - parserState.genericsElems > 0) { - throw new Error("Cannot use literal search when there is more than one element"); + throw ["Cannot use literal search when there is more than one element"]; } parserState.pos += 1; const start = parserState.pos; const end = getIdentEndPosition(parserState); if (parserState.pos >= parserState.length) { - throw new Error("Unclosed `\"`"); + throw ["Unclosed ", "\""]; } else if (parserState.userQuery[end] !== "\"") { - throw new Error(`Unexpected \`${parserState.userQuery[end]}\` in a string element`); + throw ["Unexpected ", parserState.userQuery[end], " in a string element"]; } else if (start === end) { - throw new Error("Cannot have empty string element"); + throw ["Cannot have empty string element"]; } // To skip the quote at the end. parserState.pos += 1; @@ -257,7 +257,7 @@ function initSearch(rawSearchIndex) { return; } if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) { - throw new Error("You cannot have more than one element if you use quotes"); + throw ["You cannot have more than one element if you use quotes"]; } const pathSegments = name.split("::"); if (pathSegments.length > 1) { @@ -266,17 +266,17 @@ function initSearch(rawSearchIndex) { if (pathSegment.length === 0) { if (i === 0) { - throw new Error("Paths cannot start with `::`"); + throw ["Paths cannot start with ", "::"]; } else if (i + 1 === len) { - throw new Error("Paths cannot end with `::`"); + throw ["Paths cannot end with ", "::"]; } - throw new Error("Unexpected `::::`"); + throw ["Unexpected ", "::::"]; } } } // In case we only have something like `<p>`, there is no name. if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === "")) { - throw new Error("Found generics without a path"); + throw ["Found generics without a path"]; } parserState.totalElems += 1; if (isInGenerics) { @@ -300,22 +300,23 @@ function initSearch(rawSearchIndex) { * @return {integer} */ function getIdentEndPosition(parserState) { + const start = parserState.pos; let end = parserState.pos; - let foundExclamation = false; + let foundExclamation = -1; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; if (!isIdentCharacter(c)) { if (c === "!") { - if (foundExclamation) { - throw new Error("Cannot have more than one `!` in an ident"); + if (foundExclamation !== -1) { + throw ["Cannot have more than one ", "!", " in an ident"]; } else if (parserState.pos + 1 < parserState.length && isIdentCharacter(parserState.userQuery[parserState.pos + 1]) ) { - throw new Error("`!` can only be at the end of an ident"); + throw ["Unexpected ", "!", ": it can only be at the end of an ident"]; } - foundExclamation = true; + foundExclamation = parserState.pos; } else if (isErrorCharacter(c)) { - throw new Error(`Unexpected \`${c}\``); + throw ["Unexpected ", c]; } else if ( isStopCharacter(c) || isSpecialStartCharacter(c) || @@ -326,16 +327,40 @@ function initSearch(rawSearchIndex) { if (!isPathStart(parserState)) { break; } + if (foundExclamation !== -1) { + if (start <= (end - 2)) { + throw ["Cannot have associated items in macros"]; + } else { + // if start == end - 1, we got the never type + // while the never type has no associated macros, we still + // can parse a path like that + foundExclamation = -1; + } + } // Skip current ":". parserState.pos += 1; - foundExclamation = false; } else { - throw new Error(`Unexpected \`${c}\``); + throw ["Unexpected ", c]; } } parserState.pos += 1; end = parserState.pos; } + // if start == end - 1, we got the never type + if (foundExclamation !== -1 && start <= (end - 2)) { + if (parserState.typeFilter === null) { + parserState.typeFilter = "macro"; + } else if (parserState.typeFilter !== "macro") { + throw [ + "Invalid search type: macro ", + "!", + " and ", + parserState.typeFilter, + " both specified", + ]; + } + end = foundExclamation; + } return end; } @@ -362,9 +387,9 @@ function initSearch(rawSearchIndex) { parserState.userQuery[parserState.pos] === "<" ) { if (isInGenerics) { - throw new Error("Unexpected `<` after `<`"); + throw ["Unexpected ", "<", " after ", "<"]; } else if (start >= end) { - throw new Error("Found generics without a path"); + throw ["Found generics without a path"]; } parserState.pos += 1; getItemsBefore(query, parserState, generics, ">"); @@ -408,24 +433,51 @@ function initSearch(rawSearchIndex) { foundStopChar = true; continue; } else if (c === ":" && isPathStart(parserState)) { - throw new Error("Unexpected `::`: paths cannot start with `::`"); + throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; } else if (c === ":" || isEndCharacter(c)) { let extra = ""; if (endChar === ">") { - extra = "`<`"; + extra = "<"; } else if (endChar === "") { - extra = "`->`"; + extra = "->"; + } else { + extra = endChar; } - throw new Error("Unexpected `" + c + "` after " + extra); + throw ["Unexpected ", c, " after ", extra]; } if (!foundStopChar) { if (endChar !== "") { - throw new Error(`Expected \`,\`, \` \` or \`${endChar}\`, found \`${c}\``); + throw [ + "Expected ", + ",", // comma + ", ", + " ", // whitespace + " or ", + endChar, + ", found ", + c, + ]; } - throw new Error(`Expected \`,\` or \` \`, found \`${c}\``); + throw [ + "Expected ", + ",", // comma + " or ", + " ", // whitespace + ", found ", + c, + ]; } const posBefore = parserState.pos; getNextElem(query, parserState, elems, endChar === ">"); + if (endChar !== "") { + if (parserState.pos >= parserState.length) { + throw ["Unclosed ", "<"]; + } + const c2 = parserState.userQuery[parserState.pos]; + if (!isSeparatorCharacter(c2) && c2 !== endChar) { + throw ["Expected ", endChar, ", found ", c2]; + } + } // This case can be encountered if `getNextElem` encountered a "stop character" right // from the start. For example if you have `,,` or `<>`. In this case, we simply move up // the current position to continue the parsing. @@ -434,7 +486,10 @@ function initSearch(rawSearchIndex) { } foundStopChar = false; } - // We are either at the end of the string or on the `endChar`` character, let's move forward + if (parserState.pos >= parserState.length && endChar !== "") { + throw ["Unclosed ", "<"]; + } + // We are either at the end of the string or on the `endChar` character, let's move forward // in any case. parserState.pos += 1; } @@ -450,7 +505,7 @@ function initSearch(rawSearchIndex) { for (let pos = 0; pos < parserState.pos; ++pos) { if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) { - throw new Error(`Unexpected \`${query[pos]}\` in type filter`); + throw ["Unexpected ", query[pos], " in type filter"]; } } } @@ -463,11 +518,10 @@ function initSearch(rawSearchIndex) { * @param {ParserState} parserState */ function parseInput(query, parserState) { - let c, before; let foundStopChar = true; while (parserState.pos < parserState.length) { - c = parserState.userQuery[parserState.pos]; + const c = parserState.userQuery[parserState.pos]; if (isStopCharacter(c)) { foundStopChar = true; if (isSeparatorCharacter(c)) { @@ -477,19 +531,19 @@ function initSearch(rawSearchIndex) { if (isReturnArrow(parserState)) { break; } - throw new Error(`Unexpected \`${c}\` (did you mean \`->\`?)`); + throw ["Unexpected ", c, " (did you mean ", "->", "?)"]; } - throw new Error(`Unexpected \`${c}\``); + throw ["Unexpected ", c]; } else if (c === ":" && !isPathStart(parserState)) { if (parserState.typeFilter !== null) { - throw new Error("Unexpected `:`"); + throw ["Unexpected ", ":"]; } if (query.elems.length === 0) { - throw new Error("Expected type filter before `:`"); + throw ["Expected type filter before ", ":"]; } else if (query.elems.length !== 1 || parserState.totalElems !== 1) { - throw new Error("Unexpected `:`"); + throw ["Unexpected ", ":"]; } else if (query.literalSearch) { - throw new Error("You cannot use quotes on type filter"); + throw ["You cannot use quotes on type filter"]; } checkExtraTypeFilterCharacters(parserState); // The type filter doesn't count as an element since it's a modifier. @@ -502,11 +556,31 @@ function initSearch(rawSearchIndex) { } if (!foundStopChar) { if (parserState.typeFilter !== null) { - throw new Error(`Expected \`,\`, \` \` or \`->\`, found \`${c}\``); + throw [ + "Expected ", + ",", // comma + ", ", + " ", // whitespace + " or ", + "->", // arrow + ", found ", + c, + ]; } - throw new Error(`Expected \`,\`, \` \`, \`:\` or \`->\`, found \`${c}\``); - } - before = query.elems.length; + throw [ + "Expected ", + ",", // comma + ", ", + " ", // whitespace + ", ", + ":", // colon + " or ", + "->", // arrow + ", found ", + c, + ]; + } + const before = query.elems.length; getNextElem(query, parserState, query.elems, false); if (query.elems.length === before) { // Nothing was added, weird... Let's increase the position to not remain stuck. @@ -515,14 +589,13 @@ function initSearch(rawSearchIndex) { foundStopChar = false; } while (parserState.pos < parserState.length) { - c = parserState.userQuery[parserState.pos]; if (isReturnArrow(parserState)) { parserState.pos += 2; // Get returned elements. getItemsBefore(query, parserState, query.returned, ""); // Nothing can come afterward! if (query.returned.length === 0) { - throw new Error("Expected at least one item after `->`"); + throw ["Expected at least one item after ", "->"]; } break; } else { @@ -591,8 +664,8 @@ function initSearch(rawSearchIndex) { * * The supported syntax by this parser is as follow: * - * ident = *(ALPHA / DIGIT / "_") [!] - * path = ident *(DOUBLE-COLON ident) + * ident = *(ALPHA / DIGIT / "_") + * path = ident *(DOUBLE-COLON ident) [!] * arg = path [generics] * arg-without-generic = path * type-sep = COMMA/WS *(COMMA/WS) @@ -676,7 +749,7 @@ function initSearch(rawSearchIndex) { } } catch (err) { query = newParsedQuery(userQuery); - query.error = err.message; + query.error = err; query.typeFilter = -1; return query; } @@ -1742,7 +1815,16 @@ function initSearch(rawSearchIndex) { let output = `<h1 class="search-results-title">Results${crates}</h1>`; if (results.query.error !== null) { - output += `<h3>Query parser error: "${results.query.error}".</h3>`; + const error = results.query.error; + error.forEach((value, index) => { + value = value.split("<").join("<").split(">").join(">"); + if (index % 2 !== 0) { + error[index] = `<code>${value}</code>`; + } else { + error[index] = value; + } + }); + output += `<h3 class="error">Query parser error: "${error.join("")}".</h3>`; output += "<div id=\"search-tabs\">" + makeTabHeader(0, "In Names", ret_others[1]) + "</div>"; @@ -1940,7 +2022,6 @@ function initSearch(rawSearchIndex) { */ const searchWords = []; const charA = "A".charCodeAt(0); - let i, word; let currentIndex = 0; let id = 0; @@ -2035,7 +2116,7 @@ function initSearch(rawSearchIndex) { // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode let len = paths.length; - for (i = 0; i < len; ++i) { + for (let i = 0; i < len; ++i) { lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()}); paths[i] = {ty: paths[i][0], name: paths[i][1]}; } @@ -2049,16 +2130,14 @@ function initSearch(rawSearchIndex) { // faster analysis operations len = itemTypes.length; let lastPath = ""; - for (i = 0; i < len; ++i) { + for (let i = 0; i < len; ++i) { + let word = ""; // This object should have exactly the same set of fields as the "crateRow" // object defined above. if (typeof itemNames[i] === "string") { word = itemNames[i].toLowerCase(); - searchWords.push(word); - } else { - word = ""; - searchWords.push(""); } + searchWords.push(word); const row = { crate: crate, ty: itemTypes.charCodeAt(i) - charA, diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index 0e1c864e62d..6c0f03b5bb0 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -117,8 +117,7 @@ function createSourceSidebar() { sidebar.appendChild(title); Object.keys(sourcesIndex).forEach(key => { sourcesIndex[key][NAME_OFFSET] = key; - hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", - hasFoundFile); + hasFoundFile = createDirEntry(sourcesIndex[key], sidebar, "", hasFoundFile); }); container.appendChild(sidebar); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index d5e9010eb4e..18c45fd6991 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -38,7 +38,7 @@ impl JsonRenderer<'_> { Some(UrlFragment::UserWritten(_)) | None => *page_id, }; - (link.clone(), id_from_item_default(id.into(), self.tcx)) + (String::from(&**link), id_from_item_default(id.into(), self.tcx)) }) .collect(); let docs = item.attrs.collapsed_doc_value(); diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 0b22f943dab..be5286b24d7 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -8,7 +8,6 @@ use crate::visit::DocVisitor; use rustc_hir as hir; use rustc_lint::builtin::MISSING_DOCS; use rustc_middle::lint::LintLevelSource; -use rustc_middle::ty::DefIdTree; use rustc_session::lint; use rustc_span::FileName; use serde::Serialize; diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index a39d57d42b7..6b13e6c9581 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -14,7 +14,6 @@ use crate::visit::DocVisitor; use crate::visit_ast::inherits_doc_hidden; use rustc_hir as hir; use rustc_middle::lint::LintLevelSource; -use rustc_middle::ty::DefIdTree; use rustc_session::lint; pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 920a3b22f25..bcb69d1a4ca 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -13,7 +13,7 @@ use rustc_hir::def::Namespace::*; use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::Mutability; -use rustc_middle::ty::{DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_resolve::rustdoc::MalformedGenerics; use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, strip_generics_from_path}; @@ -228,7 +228,7 @@ struct ResolutionInfo { item_id: ItemId, module_id: DefId, dis: Option<Disambiguator>, - path_str: String, + path_str: Box<str>, extra_fragment: Option<String>, } @@ -849,10 +849,10 @@ impl PreprocessingError { #[derive(Clone)] struct PreprocessingInfo { - path_str: String, + path_str: Box<str>, disambiguator: Option<Disambiguator>, extra_fragment: Option<String>, - link_text: String, + link_text: Box<str>, } // Not a typedef to avoid leaking several private structures from this module. @@ -937,7 +937,7 @@ fn preprocess_link( path_str, disambiguator, extra_fragment: extra_fragment.map(|frag| frag.to_owned()), - link_text: link_text.to_owned(), + link_text: Box::<str>::from(link_text), })) } @@ -993,7 +993,7 @@ impl LinkCollector<'_, '_> { item_id: item.item_id, module_id, dis: disambiguator, - path_str: path_str.to_owned(), + path_str: path_str.clone(), extra_fragment: extra_fragment.clone(), }, diag_info.clone(), // this struct should really be Copy, but Range is not :( @@ -1067,7 +1067,7 @@ impl LinkCollector<'_, '_> { } res.def_id(self.cx.tcx).map(|page_id| ItemLink { - link: ori_link.link.clone(), + link: Box::<str>::from(&*ori_link.link), link_text: link_text.clone(), page_id, fragment, @@ -1091,7 +1091,7 @@ impl LinkCollector<'_, '_> { let page_id = clean::register_res(self.cx, rustc_hir::def::Res::Def(kind, id)); Some(ItemLink { - link: ori_link.link.clone(), + link: Box::<str>::from(&*ori_link.link), link_text: link_text.clone(), page_id, fragment, diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 01ed4a60b3b..d32e8185d3f 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -9,7 +9,7 @@ use crate::visit::DocVisitor; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE}; -use rustc_middle::ty::{self, DefIdTree}; +use rustc_middle::ty; use rustc_span::symbol::sym; pub(crate) const COLLECT_TRAIT_IMPLS: Pass = Pass { diff --git a/src/librustdoc/passes/lint/html_tags.rs b/src/librustdoc/passes/lint/html_tags.rs index eac362b37b2..4f72df5a5cd 100644 --- a/src/librustdoc/passes/lint/html_tags.rs +++ b/src/librustdoc/passes/lint/html_tags.rs @@ -113,7 +113,7 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { if let Some(link) = link_names.iter().find(|link| *link.original_text == *broken_link.reference) { - Some((link.href.as_str().into(), link.new_text.as_str().into())) + Some((link.href.as_str().into(), link.new_text.to_string().into())) } else if matches!( &broken_link.link_type, LinkType::Reference | LinkType::ReferenceUnknown diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index a4bc486900b..f35643af637 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -9,7 +9,6 @@ use crate::fold::DocFolder; use crate::passes::Pass; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::DefIdTree; pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass { name: "propagate-doc-cfg", diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 277201e4de9..5bbbff175cf 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{walk_item, Visitor}; use rustc_hir::{Node, CRATE_HIR_ID}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; diff --git a/src/llvm-project b/src/llvm-project -Subproject 477e7285b12f876ad105188cfcfc8adda7dc29a +Subproject fd949f3034f8a422ecfffa889c2823485dde4bd diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 9d5b32f503fc099c4064298465add14d4bce11e +Subproject 9880b408a3af50c08fab3dbf4aa2a972df71e95 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 659e8aebcd5..765826ed867 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -4430,6 +4430,7 @@ Released 2018-09-13 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return @@ -4494,6 +4495,7 @@ Released 2018-09-13 [`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use +[`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug @@ -4620,6 +4622,7 @@ Released 2018-09-13 [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding +[`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty @@ -4675,6 +4678,7 @@ Released 2018-09-13 [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names [`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark +[`question_mark_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark_used [`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero @@ -4734,6 +4738,7 @@ Released 2018-09-13 [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee +[`significant_drop_tightening`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_tightening [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str [`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names @@ -4764,6 +4769,7 @@ Released 2018-09-13 [`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops [`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl [`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting +[`suspicious_command_arg_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_command_arg_space [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl @@ -4790,6 +4796,7 @@ Released 2018-09-13 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float +[`transmute_int_to_non_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_non_zero [`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn [`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 95f6d2cc45c..3e7379ace7e 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -19,21 +19,35 @@ You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the | `clippy::complexity` | code that does something simple but in a complex way | **warn** | | `clippy::perf` | code that can be written to run faster | **warn** | | `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow | +| `clippy::restriction` | lints which prevent the use of language and library features[^restrict] | allow | | `clippy::nursery` | new lints that are still under development | allow | | `clippy::cargo` | lints for the cargo manifest | allow | More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! -The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are -for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used -very selectively, if at all. +The `restriction` category should, *emphatically*, not be enabled as a whole. The contained +lints may lint against perfectly reasonable code, may not have an alternative suggestion, +and may contradict any other lints (including other categories). Lints should be considered +on a case-by-case basis before enabling. + +[^restrict]: Some use cases for `restriction` lints include: + - Strict coding styles (e.g. [`clippy::else_if_without_else`]). + - Additional restrictions on CI (e.g. [`clippy::todo`]). + - Preventing panicking in certain functions (e.g. [`clippy::unwrap_used`]). + - Running a lint only on a subset of code (e.g. `#[forbid(clippy::float_arithmetic)]` on a module). + +[`clippy::else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`clippy::todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo +[`clippy::unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used + +--- Table of contents: -* [Usage instructions](#usage) -* [Configuration](#configuration) -* [Contributing](#contributing) -* [License](#license) +* [Usage instructions](#usage) +* [Configuration](#configuration) +* [Contributing](#contributing) +* [License](#license) ## Usage @@ -64,6 +78,7 @@ Once you have rustup and the latest stable release (at least Rust 1.29) installe ```terminal rustup component add clippy ``` + If it says that it can't find the `clippy` component, please run `rustup self update`. #### Step 3: Run Clippy @@ -143,16 +158,16 @@ line. (You can swap `clippy::all` with the specific lint category you are target You can add options to your code to `allow`/`warn`/`deny` Clippy lints: -* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`). +* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`). Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). -* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, +* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive lints prone to false positives. -* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) +* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) -* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. +* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny` the lint will emit an error, when @@ -176,12 +191,14 @@ cargo clippy -- -W clippy::lint_name This also works with lint groups. For example, you can run Clippy with warnings for all lints enabled: + ```terminal cargo clippy -- -W clippy::pedantic ``` If you care only about a single lint, you can allow all others and then explicitly warn on the lint(s) you are interested in: + ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` diff --git a/src/tools/clippy/book/src/development/infrastructure/backport.md b/src/tools/clippy/book/src/development/infrastructure/backport.md index 15f3d1f0806..6920c4e4656 100644 --- a/src/tools/clippy/book/src/development/infrastructure/backport.md +++ b/src/tools/clippy/book/src/development/infrastructure/backport.md @@ -28,6 +28,7 @@ repository. You can do this with: ```bash # Assuming the current directory corresponds to the Rust repository $ git checkout beta +# Make sure to change `your-github-name` to your github name in the following command $ git subtree pull -p src/tools/clippy https://github.com/<your-github-name>/rust-clippy backport $ ./x.py test src/tools/clippy ``` diff --git a/src/tools/clippy/book/src/development/infrastructure/sync.md b/src/tools/clippy/book/src/development/infrastructure/sync.md index 5a0f7409a2e..02cfc11b55a 100644 --- a/src/tools/clippy/book/src/development/infrastructure/sync.md +++ b/src/tools/clippy/book/src/development/infrastructure/sync.md @@ -79,8 +79,7 @@ to be run inside the `rust` directory): `rustup check`. 3. Sync the changes to the rust-copy of Clippy to your Clippy fork: ```bash - # Make sure to change `your-github-name` to your github name in the following command. Also be - # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand + # Be sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand # because changes cannot be fast forwarded and you have to run this command again. git subtree push -P src/tools/clippy clippy-local sync-from-rust ``` diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 32e8e218c40..995dd2f04b1 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -53,6 +53,7 @@ Please use that command to update the file and do not edit it by hand. | [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` | | [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` | | [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` | +| [missing-docs-in-crate-items](#missing-docs-in-crate-items) | `false` | ### arithmetic-side-effects-allowed Suppress checking of the passed type names in all types of operations. @@ -305,7 +306,7 @@ The maximum number of lines a function or method can have ### array-size-threshold The maximum allowed size for arrays on the stack -**Default Value:** `512000` (`u128`) +**Default Value:** `512000` (`u64`) * [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays) * [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays) @@ -471,7 +472,7 @@ The maximum size of a file included via `include_bytes!()` or `include_str!()`, ### allow-expect-in-tests -Whether `expect` should be allowed within `#[cfg(test)]` +Whether `expect` should be allowed in test functions or `#[cfg(test)]` **Default Value:** `false` (`bool`) @@ -479,7 +480,7 @@ Whether `expect` should be allowed within `#[cfg(test)]` ### allow-unwrap-in-tests -Whether `unwrap` should be allowed in test cfg +Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` **Default Value:** `false` (`bool`) @@ -487,7 +488,7 @@ Whether `unwrap` should be allowed in test cfg ### allow-dbg-in-tests -Whether `dbg!` should be allowed in test functions +Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` **Default Value:** `false` (`bool`) @@ -495,7 +496,7 @@ Whether `dbg!` should be allowed in test functions ### allow-print-in-tests -Whether print macros (ex. `println!`) should be allowed in test functions +Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` **Default Value:** `false` (`bool`) @@ -540,4 +541,13 @@ if no suggestion can be made. * [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing) +### missing-docs-in-crate-items +Whether to **only** check for missing documentation in items visible within the current +crate. For example, `pub(crate)` items. + +**Default Value:** `false` (`bool`) + +* [missing_docs_in_private_items](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items) + + diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index ec7f1dd0d84..420214d9256 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -1,5 +1,6 @@ use crate::clippy_project_root; use indoc::{formatdoc, writedoc}; +use std::fmt; use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; @@ -256,7 +257,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - let _ = write!(result, "{}", get_lint_declaration(&name_upper, category)); + let _: fmt::Result = write!(result, "{}", get_lint_declaration(&name_upper, category)); result.push_str(&if enable_msrv { formatdoc!( @@ -353,7 +354,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R let mut lint_file_contents = String::new(); if enable_msrv { - let _ = writedoc!( + let _: fmt::Result = writedoc!( lint_file_contents, r#" use clippy_utils::msrvs::{{self, Msrv}}; @@ -373,7 +374,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R name_upper = name_upper, ); } else { - let _ = writedoc!( + let _: fmt::Result = writedoc!( lint_file_contents, r#" use rustc_lint::{{{context_import}, LintContext}}; @@ -521,7 +522,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> .chain(std::iter::once(&*lint_name_upper)) .filter(|s| !s.is_empty()) { - let _ = write!(new_arr_content, "\n {ident},"); + let _: fmt::Result = write!(new_arr_content, "\n {ident},"); } new_arr_content.push('\n'); diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 837618c9294..779e4d0e1e3 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; -use std::fmt::Write; +use std::fmt::{self, Write}; use std::fs::{self, OpenOptions}; use std::io::{self, Read, Seek, SeekFrom, Write as _}; use std::ops::Range; @@ -691,7 +691,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); for lint in lints { - let _ = write!( + let _: fmt::Result = write!( output, concat!( " store.register_removed(\n", @@ -726,7 +726,7 @@ fn gen_declared_lints<'a>( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - let _ = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); + let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,"); } output.push_str("];\n"); diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 9d98a6bab71..dfa949d1af2 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -117,7 +117,8 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ) => { if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) && let Some(sig) = expr_sig(cx, path) && - let Some(input) = sig.input(index) + let Some(input) = sig.input(index) && + !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait() { input.no_bound_vars().is_some() } else { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index f3f8b8d8798..823970e35ab 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -168,7 +168,7 @@ pub(super) fn check( let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})"); span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| { - diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ..."); + diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ..."); diag.span_suggestion_with_style( expr.span, "... or use `try_from` and handle the error accordingly", diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 457a25826e7..cd5dd7a5706 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -179,6 +179,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::IMPL_TRAIT_IN_PARAMS_INFO, crate::functions::MISNAMED_GETTERS_INFO, crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, @@ -224,6 +225,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO, crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, + crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, @@ -378,6 +380,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, + crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, crate::methods::SUSPICIOUS_TO_OWNED_INFO, @@ -447,6 +450,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::no_effect::NO_EFFECT_INFO, crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, crate::no_effect::UNNECESSARY_OPERATION_INFO, + crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO, crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, @@ -506,6 +510,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_use::PUB_USE_INFO, crate::question_mark::QUESTION_MARK_INFO, + crate::question_mark_used::QUESTION_MARK_USED_INFO, crate::ranges::MANUAL_RANGE_CONTAINS_INFO, crate::ranges::RANGE_MINUS_ONE_INFO, crate::ranges::RANGE_PLUS_ONE_INFO, @@ -536,6 +541,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::shadow::SHADOW_REUSE_INFO, crate::shadow::SHADOW_SAME_INFO, crate::shadow::SHADOW_UNRELATED_INFO, + crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO, crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, @@ -573,6 +579,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, crate::transmute::TRANSMUTE_NULL_TO_FN_INFO, crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 644604a2e3f..47501980e66 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; +use clippy_utils::ty::{adt_and_variant_of_res, expr_sig, is_copy, peel_mid_ty_refs, ty_sig}; use clippy_utils::{ fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage, }; @@ -26,8 +26,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::{Rvalue, StatementKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, - ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults, + self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy, + PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults, }; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span, Symbol}; @@ -736,7 +736,7 @@ fn walk_parents<'tcx>( .. }) if span.ctxt() == ctxt => { let ty = cx.tcx.type_of(owner_id.def_id).subst_identity(); - Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx)) + Some(ty_auto_deref_stability(cx.tcx, cx.param_env, ty, precedence).position_for_result(cx)) }, Node::Item(&Item { @@ -760,7 +760,7 @@ fn walk_parents<'tcx>( let output = cx .tcx .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output()); - Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)) + Some(ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx)) }, Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) { @@ -768,10 +768,23 @@ fn walk_parents<'tcx>( hir_id, kind: ExprKind::Struct(path, ..), .. - }) => variant_of_res(cx, cx.qpath_res(path, *hir_id)) - .and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name)) - .map(|field_def| { - ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did).subst_identity(), precedence).position_for_arg() + }) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id)) + .and_then(|(adt, variant)| { + variant + .fields + .iter() + .find(|f| f.name == field.ident.name) + .map(|f| (adt, f)) + }) + .map(|(adt, field_def)| { + ty_auto_deref_stability( + cx.tcx, + // Use the param_env of the target type. + cx.tcx.param_env(adt.did()), + cx.tcx.type_of(field_def.did).subst_identity(), + precedence, + ) + .position_for_arg() }), _ => None, }, @@ -792,7 +805,7 @@ fn walk_parents<'tcx>( let output = cx .tcx .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output()); - ty_auto_deref_stability(cx, output, precedence).position_for_result(cx) + ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx) }, ) }, @@ -835,15 +848,20 @@ fn walk_parents<'tcx>( msrv, ) } else { - ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) - .position_for_arg() + ty_auto_deref_stability( + cx.tcx, + // Use the param_env of the target function. + sig.predicates_id().map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)), + cx.tcx.erase_late_bound_regions(ty), + precedence + ).position_for_arg() } }, } }) }), ExprKind::MethodCall(method, receiver, args, _) => { - let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); + let fn_id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); if receiver.hir_id == child_id { // Check for calls to trait methods where the trait is implemented on a reference. // Two cases need to be handled: @@ -852,13 +870,17 @@ fn walk_parents<'tcx>( // priority. if e.hir_id != child_id { return Some(Position::ReborrowStable(precedence)) - } else if let Some(trait_id) = cx.tcx.trait_of_item(id) + } else if let Some(trait_id) = cx.tcx.trait_of_item(fn_id) && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e)) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let subs = cx .typeck_results() .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default() - && let impl_ty = if cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[0].is_ref() { + && let impl_ty = if cx.tcx.fn_sig(fn_id) + .subst_identity() + .skip_binder() + .inputs()[0].is_ref() + { // Trait methods taking `&self` sub_ty } else { @@ -879,10 +901,13 @@ fn walk_parents<'tcx>( return Some(Position::MethodReceiver); } args.iter().position(|arg| arg.hir_id == child_id).map(|i| { - let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i + 1]; + let ty = cx.tcx.fn_sig(fn_id).subst_identity().input(i + 1); // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782 - if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() { + if e.hir_id == child_id + && method.args.is_none() + && let ty::Param(param_ty) = ty.skip_binder().kind() + { needless_borrow_impl_arg_position( cx, possible_borrowers, @@ -895,8 +920,10 @@ fn walk_parents<'tcx>( ) } else { ty_auto_deref_stability( - cx, - cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().input(i + 1)), + cx.tcx, + // Use the param_env of the target function. + cx.tcx.param_env(fn_id), + cx.tcx.erase_late_bound_regions(ty), precedence, ) .position_for_arg() @@ -1378,11 +1405,18 @@ impl<'tcx> TyPosition<'tcx> { } // Checks whether a type is stable when switching to auto dereferencing, -fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> { +fn ty_auto_deref_stability<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + precedence: i8, +) -> TyPosition<'tcx> { let ty::Ref(_, mut ty, _) = *ty.kind() else { return Position::Other(precedence).into(); }; + ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty); + loop { break match *ty.kind() { ty::Ref(_, ref_ty, _) => { @@ -1423,9 +1457,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc | ty::Closure(..) | ty::Never | ty::Tuple(_) - | ty::Alias(ty::Projection, _) => { - Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into() - }, + | ty::Alias(ty::Projection, _) => Position::DerefStable(precedence, ty.is_sized(tcx, param_env)).into(), }; } } diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index f95b8ccf067..c5f4e943f4f 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -8,7 +8,7 @@ use rustc_hir::{ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{AdtDef, DefIdTree}; +use rustc_middle::ty::AdtDef; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 6fdb7de25cc..384aca7fead 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -6,6 +6,11 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; use itertools::Itertools; +use pulldown_cmark::Event::{ + Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, +}; +use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; @@ -497,7 +502,6 @@ struct DocHeaders { } fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> { - use pulldown_cmark::{BrokenLink, CowStr, Options}; /// We don't want the parser to choke on intra doc links. Since we don't /// actually care about rendering them, just pretend that all broken links are /// point to a fake address. @@ -538,8 +542,6 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter(); // Iterate over all `Events` and combine consecutive events into one let events = parser.coalesce(|previous, current| { - use pulldown_cmark::Event::Text; - let previous_range = previous.1; let current_range = current.1; @@ -564,12 +566,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize spans: &[(usize, Span)], ) -> DocHeaders { // true if a safety header was found - use pulldown_cmark::Event::{ - Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, - }; - use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; - use pulldown_cmark::{CodeBlockKind, CowStr}; - let mut headers = DocHeaders::default(); let mut in_code = false; let mut in_link = None; @@ -660,6 +656,12 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len()); // Adjust for the beginning of the current `Event` let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin)); + if let Some(link) = in_link.as_ref() + && let Ok(url) = Url::parse(link) + && (url.scheme() == "https" || url.scheme() == "http") { + // Don't check the text associated with external URLs + continue; + } text_to_check.push((text, span)); } }, @@ -704,10 +706,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false - ); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = EmitterWriter::new( Box::new(io::sink()), None, diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index b44e6243588..48a54f60253 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -6,7 +6,7 @@ use clippy_utils::{ source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, SpanlessEq, }; -use core::fmt::Write; +use core::fmt::{self, Write}; use rustc_errors::Applicability; use rustc_hir::{ hir_id::HirIdSet, @@ -65,6 +65,10 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else { return }; @@ -532,7 +536,7 @@ impl<'tcx> InsertSearchResults<'tcx> { if is_expr_used_or_unified(cx.tcx, insertion.call) { write_wrapped(&mut res, insertion, ctxt, app); } else { - let _ = write!( + let _: fmt::Result = write!( res, "e.insert({})", snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0 @@ -548,7 +552,7 @@ impl<'tcx> InsertSearchResults<'tcx> { ( self.snippet(cx, span, app, |res, insertion, ctxt, app| { // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value` - let _ = write!( + let _: fmt::Result = write!( res, "Some(e.insert({}))", snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0 @@ -562,7 +566,7 @@ impl<'tcx> InsertSearchResults<'tcx> { ( self.snippet(cx, span, app, |res, insertion, ctxt, app| { // Insertion into a map would return `None`, but the entry returns a mutable reference. - let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) { + let _: fmt::Result = if is_expr_final_block_expr(cx.tcx, insertion.call) { write!( res, "e.insert({});\n{}None", diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 2fdd8a71466..20565e1d232 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -4,12 +4,17 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; use rustc_hir::{ - GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, + BodyId, ExprKind, GenericBound, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, + PredicateOrigin, Ty, TyKind, WherePredicate, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{def_id::DefId, Span}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{ + def_id::{DefId, LocalDefId}, + Span, +}; declare_clippy_lint! { /// ### What it does @@ -21,7 +26,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // unused type parameters /// fn unused_ty<T>(x: u8) { /// // .. /// } @@ -37,13 +41,35 @@ declare_clippy_lint! { complexity, "unused type parameters in function definitions" } -declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); + +pub struct ExtraUnusedTypeParameters { + avoid_breaking_exported_api: bool, +} + +impl ExtraUnusedTypeParameters { + pub fn new(avoid_breaking_exported_api: bool) -> Self { + Self { + avoid_breaking_exported_api, + } + } + + /// Don't lint external macros or functions with empty bodies. Also, don't lint public items if + /// the `avoid_breaking_exported_api` config option is set. + fn check_false_positive(&self, cx: &LateContext<'_>, span: Span, def_id: LocalDefId, body_id: BodyId) -> bool { + let body = cx.tcx.hir().body(body_id).value; + let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none()); + let is_exported = cx.effective_visibilities.is_exported(def_id); + in_external_macro(cx.sess(), span) || (self.avoid_breaking_exported_api && is_exported) || fn_empty + } +} + +impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); /// A visitor struct that walks a given function and gathers generic type parameters, plus any /// trait bounds those parameters have. struct TypeWalker<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, - /// Collection of all the type parameters and their spans. + /// Collection of all the function's type parameters. ty_params: FxHashMap<DefId, Span>, /// Collection of any (inline) trait bounds corresponding to each type parameter. bounds: FxHashMap<DefId, Span>, @@ -64,8 +90,8 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { .params .iter() .filter_map(|param| { - if let GenericParamKind::Type { .. } = param.kind { - Some((param.def_id.into(), param.span)) + if let GenericParamKind::Type { synthetic, .. } = param.kind { + (!synthetic).then_some((param.def_id.into(), param.span)) } else { if !param.is_elided_lifetime() { all_params_unused = false; @@ -74,6 +100,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { } }) .collect(); + Self { cx, ty_params, @@ -83,6 +110,12 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { } } + fn mark_param_used(&mut self, def_id: DefId) { + if self.ty_params.remove(&def_id).is_some() { + self.all_params_unused = false; + } + } + fn emit_lint(&self) { let (msg, help) = match self.ty_params.len() { 0 => return, @@ -96,7 +129,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { ), }; - let source_map = self.cx.tcx.sess.source_map(); + let source_map = self.cx.sess().source_map(); let span = if self.all_params_unused { self.generics.span.into() // Remove the entire list of generics } else { @@ -118,14 +151,18 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { } } +/// Given a generic bound, if the bound is for a trait that's not a `LangItem`, return the +/// `LocalDefId` for that trait. +fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option<LocalDefId> { + bound.trait_ref()?.trait_def_id()?.as_local() +} + impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { if let Some((def_id, _)) = t.peel_refs().as_generic_param() { - if self.ty_params.remove(&def_id).is_some() { - self.all_params_unused = false; - } + self.mark_param_used(def_id); } else if let TyKind::OpaqueDef(id, _, _) = t.kind { // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're @@ -139,12 +176,21 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { if let WherePredicate::BoundPredicate(predicate) = predicate { - // Collect spans for bounds that appear in the list of generics (not in a where-clause) - // for use in forming the help message - if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() - && let PredicateOrigin::GenericParam = predicate.origin - { - self.bounds.insert(def_id, predicate.span); + // Collect spans for any bounds on type parameters. We only keep bounds that appear in + // the list of generics (not in a where-clause). + if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() { + // If the bound contains non-public traits, err on the safe side and don't lint the + // corresponding parameter. + if !predicate + .bounds + .iter() + .filter_map(bound_to_trait_def_id) + .all(|id| self.cx.effective_visibilities.is_exported(id)) + { + self.mark_param_used(def_id); + } else if let PredicateOrigin::GenericParam = predicate.origin { + self.bounds.insert(def_id, predicate.span); + } } // Only walk the right-hand side of where-bounds for bound in predicate.bounds { @@ -160,7 +206,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Fn(_, generics, _) = item.kind { + if let ItemKind::Fn(_, generics, body_id) = item.kind + && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id) + { let mut walker = TypeWalker::new(cx, generics); walk_item(&mut walker, item); walker.emit_lint(); @@ -169,7 +217,10 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { // Only lint on inherent methods, not trait methods. - if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { + if let ImplItemKind::Fn(.., body_id) = item.kind + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + && !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id) + { let mut walker = TypeWalker::new(cx, item.generics); walk_impl_item(&mut walker, item); walker.emit_lint(); diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index ea26b96ee07..c511d85e9cf 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -340,6 +340,7 @@ fn check_one_arg( if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind && let [segment] = path.segments + && segment.args.is_none() && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id) { let replacement = match param.usage { diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs new file mode 100644 index 00000000000..2811a73f6c1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -0,0 +1,50 @@ +use clippy_utils::{diagnostics::span_lint_and_then, is_in_test_function}; + +use rustc_hir::{intravisit::FnKind, Body, HirId}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::IMPL_TRAIT_IN_PARAMS; + +pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) { + if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() && !is_in_test_function(cx.tcx, hir_id) + { + if let FnKind::ItemFn(ident, generics, _) = kind { + for param in generics.params { + if param.is_impl_trait() { + // No generics with nested generics, and no generics like FnMut(x) + span_lint_and_then( + cx, + IMPL_TRAIT_IN_PARAMS, + param.span, + "'`impl Trait` used as a function parameter'", + |diag| { + if let Some(gen_span) = generics.span_for_param_suggestion() { + diag.span_suggestion_with_style( + gen_span, + "add a type paremeter", + format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), + rustc_errors::Applicability::HasPlaceholders, + rustc_errors::SuggestionStyle::ShowAlways, + ); + } else { + diag.span_suggestion_with_style( + Span::new( + body.params[0].span.lo() - rustc_span::BytePos(1), + ident.span.hi(), + ident.span.ctxt(), + ident.span.parent(), + ), + "add a type paremeter", + format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), + rustc_errors::Applicability::HasPlaceholders, + rustc_errors::SuggestionStyle::ShowAlways, + ); + } + }, + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 4399c68e130..d2852b4acad 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -1,3 +1,4 @@ +mod impl_trait_in_params; mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; @@ -327,6 +328,32 @@ declare_clippy_lint! { "getter method returning the wrong field" } +declare_clippy_lint! { + /// ### What it does + /// Lints when `impl Trait` is being used in a function's paremeters. + /// ### Why is this bad? + /// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor. + /// + /// ### Example + /// ```rust + /// trait MyTrait {} + /// fn foo(a: impl MyTrait) { + /// // [...] + /// } + /// ``` + /// Use instead: + /// ```rust + /// trait MyTrait {} + /// fn foo<T: MyTrait>(a: T) { + /// // [...] + /// } + /// ``` + #[clippy::version = "1.68.0"] + pub IMPL_TRAIT_IN_PARAMS, + restriction, + "`impl Trait` is used in the function's parameters" +} + #[derive(Copy, Clone)] pub struct Functions { too_many_arguments_threshold: u64, @@ -354,6 +381,7 @@ impl_lint_pass!(Functions => [ RESULT_UNIT_ERR, RESULT_LARGE_ERR, MISNAMED_GETTERS, + IMPL_TRAIT_IN_PARAMS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -371,6 +399,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id); misnamed_getters::check_fn(cx, kind, decl, body, span); + impl_trait_in_params::check_fn(cx, &kind, body, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs index e2f2d3d42e6..1ad886f2cf3 100644 --- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs @@ -7,7 +7,7 @@ use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; -use std::fmt::Write as _; +use std::fmt::{self, Write as _}; declare_clippy_lint! { /// ### What it does @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - let _ = write!(fields_snippet, "{ident}, "); + let _: fmt::Result = write!(fields_snippet, "{ident}, "); } fields_snippet.push_str(&last_ident.to_string()); diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index f8e35950980..7600777fab9 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -90,7 +90,45 @@ declare_clippy_lint! { "non-binding `let` on a future" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]); +declare_clippy_lint! { + /// ### What it does + /// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one, + /// or remove the `let` keyword altogether. + /// + /// ### Why is this bad? + /// The `let _ = <expr>` expression ignores the value of `<expr>` but will remain doing so even + /// if the type were to change, thus potentially introducing subtle bugs. By supplying a type + /// annotation, one will be forced to re-visit the decision to ignore the value in such cases. + /// + /// ### Known problems + /// The `_ = <expr>` is not properly supported by some tools (e.g. IntelliJ) and may seem odd + /// to many developers. This lint also partially overlaps with the other `let_underscore_*` + /// lints. + /// + /// ### Example + /// ```rust + /// fn foo() -> Result<u32, ()> { + /// Ok(123) + /// } + /// let _ = foo(); + /// ``` + /// Use instead: + /// ```rust + /// fn foo() -> Result<u32, ()> { + /// Ok(123) + /// } + /// // Either provide a type annotation: + /// let _: Result<u32, ()> = foo(); + /// // …or drop the let keyword: + /// _ = foo(); + /// ``` + #[clippy::version = "1.69.0"] + pub LET_UNDERSCORE_UNTYPED, + pedantic, + "non-binding `let` without a type annotation" +} + +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]); const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, @@ -148,6 +186,18 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "consider explicitly using function result", ); } + + if local.pat.default_binding_modes && local.ty.is_none() { + // When `default_binding_modes` is true, the `let` keyword is present. + span_lint_and_help( + cx, + LET_UNDERSCORE_UNTYPED, + local.span, + "non-binding `let` without a type annotation", + None, + "consider adding a type annotation or removing the `let` keyword", + ); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 9011f0896a0..c626e0bd998 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -2,6 +2,7 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] #![feature(drain_filter)] +#![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(lint_reasons)] @@ -219,6 +220,7 @@ mod neg_cmp_op_on_partial_ord; mod neg_multiply; mod new_without_default; mod no_effect; +mod no_mangle_with_rust_abi; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; @@ -243,6 +245,7 @@ mod ptr; mod ptr_offset_with_cast; mod pub_use; mod question_mark; +mod question_mark_used; mod ranges; mod rc_clone_in_vec_init; mod read_zero_byte_vec; @@ -264,6 +267,7 @@ mod semicolon_block; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod significant_drop_tightening; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -559,6 +563,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|_| Box::new(mut_mut::MutMut)); store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); + store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); store.register_late_pass(|_| Box::new(attrs::Attributes)); store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); @@ -665,12 +670,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); + let missing_docs_in_crate_items = conf.missing_docs_in_crate_items; store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(mem_forget::MemForget)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); - store.register_late_pass(|_| Box::new(missing_doc::MissingDoc::new())); + store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); @@ -694,6 +700,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); store.register_late_pass(|_| Box::new(question_mark::QuestionMark)); + store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed)); store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); @@ -770,7 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); - let array_size_threshold = conf.array_size_threshold; + let array_size_threshold = u128::from(conf.array_size_threshold); store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold))); store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); @@ -911,7 +918,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); - store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters)); + store.register_late_pass(move |_| { + Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new( + avoid_breaking_exported_api, + )) + }); + store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 43a1a65a43a..986ffcad883 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -144,6 +144,10 @@ fn check_fn_inner<'tcx>( .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { + if !typ.span.eq_ctxt(span) { + return; + } + for pred in generics.bounds_for_param(typ.def_id) { if pred.origin == PredicateOrigin::WhereClause { // has_where_lifetimes checked that this predicate contains no lifetime. @@ -181,6 +185,10 @@ fn check_fn_inner<'tcx>( } if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { + if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { + return; + } + let lts = elidable_lts .iter() // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 3a7b7835c99..dadcd9c5135 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -210,7 +210,7 @@ impl WarningType { cx, UNUSUAL_BYTE_GROUPINGS, span, - "digits of hex or binary literal not grouped by four", + "digits of hex, binary or octal literal not in groups of equal size", "consider", suggested_format, Applicability::MachineApplicable, @@ -427,8 +427,12 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); - if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) { - return Err(WarningType::UnusualByteGroupings); + if radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal { + if let Some(second_size) = groups.next() { + if !groups.all(|i| i == second_size) || first > second_size { + return Err(WarningType::UnusualByteGroupings); + } + } } if let Some(second) = groups.next() { @@ -484,7 +488,7 @@ impl DecimalLiteralRepresentation { then { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); - let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| { + let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { warning_type.display(num_lit.format(), cx, span); }); } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index 8c27c09404b..1e02a30e35f 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -9,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, DefIdTree}; +use rustc_middle::ty; use rustc_span::source_map::Span; /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index d7e00047312..b1bc10802e1 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -39,6 +39,7 @@ pub(super) fn check( }); }, NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), + NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(), } } @@ -48,6 +49,8 @@ enum NeverLoopResult { AlwaysBreak, // A continue may occur for the main loop. MayContinueMainLoop, + // Ignore everything until the end of the block with this id + IgnoreUntilEnd(HirId), Otherwise, } @@ -56,6 +59,7 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { match arg { NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, + NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id), } } @@ -63,27 +67,26 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { #[must_use] fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult { match first { - NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first, - NeverLoopResult::Otherwise => second, - } -} - -// Combine two results where both parts are called but not necessarily in order. -#[must_use] -fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult { - match (left, right) { - (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { - NeverLoopResult::MayContinueMainLoop + NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => { + first }, - (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, - (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise, + NeverLoopResult::Otherwise => second, } } // Combine two results where only one of the part may have been executed. #[must_use] -fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult { +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult { match (b1, b2) { + (NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => { + if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a { + NeverLoopResult::IgnoreUntilEnd(b) + } else { + NeverLoopResult::IgnoreUntilEnd(a) + } + }, + (i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak) + | (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i, (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak, (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => { NeverLoopResult::MayContinueMainLoop @@ -103,7 +106,7 @@ fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id let e = never_loop_expr(e, ignore_ids, main_loop_id); // els is an else block in a let...else binding els.map_or(e, |els| { - combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id)) + combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id), ignore_ids) }) }) .fold(NeverLoopResult::Otherwise, combine_seq) @@ -139,7 +142,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H ExprKind::Struct(_, fields, base) => { let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); if let Some(base) = base { - combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id)) + combine_seq(fields, never_loop_expr(base, ignore_ids, main_loop_id)) } else { fields } @@ -159,7 +162,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { never_loop_expr(e, ignore_ids, main_loop_id) }); - combine_seq(e1, combine_branches(e2, e3)) + combine_seq(e1, combine_branches(e2, e3, ignore_ids)) }, ExprKind::Match(e, arms, _) => { let e = never_loop_expr(e, ignore_ids, main_loop_id); @@ -175,8 +178,13 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H ignore_ids.push(b.hir_id); } let ret = never_loop_block(b, ignore_ids, main_loop_id); - ignore_ids.pop(); - ret + if l.is_some() { + ignore_ids.pop(); + } + match ret { + NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise, + _ => ret, + } }, ExprKind::Continue(d) => { let id = d @@ -190,8 +198,8 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H }, // checks if break targets a block instead of a loop ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e - .map_or(NeverLoopResult::Otherwise, |e| { - combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) + .map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| { + never_loop_expr(e, ignore_ids, main_loop_id) }), ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { combine_seq( @@ -218,7 +226,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, }) - .fold(NeverLoopResult::Otherwise, combine_both), + .fold(NeverLoopResult::Otherwise, combine_seq), ExprKind::Yield(_, _) | ExprKind::Closure { .. } | ExprKind::Path(_) @@ -234,7 +242,7 @@ fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>( main_loop_id: HirId, ) -> NeverLoopResult { es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) - .fold(NeverLoopResult::Otherwise, combine_both) + .fold(NeverLoopResult::Otherwise, combine_seq) } fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>( @@ -242,8 +250,9 @@ fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>( ignore_ids: &mut Vec<HirId>, main_loop_id: HirId, ) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) - .fold(NeverLoopResult::AlwaysBreak, combine_branches) + e.fold(NeverLoopResult::AlwaysBreak, |a, b| { + combine_branches(a, never_loop_expr(b, ignore_ids, main_loop_id), ignore_ids) + }) } fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String { diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 9c6f8b43c07..98e698c6c2a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -4,11 +4,12 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, Visitable}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -115,6 +116,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { .enumerate() .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. + // However, if it arrives in second position, its pattern may cover some cases already covered + // by the diverging one. + // TODO: accept the non-diverging arm as a second position if patterns are disjointed. + if idx == 0 { + return; + } let pat_arm = &arms[1 - idx]; if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { return; @@ -162,61 +170,102 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: ); } -fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { - fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { - if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) { - return ty.is_never(); - } - false +/// Check whether an expression is divergent. May give false negatives. +fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + res: ControlFlow<(), Descend>, } - // We can't just call is_never on expr and be done, because the type system - // sometimes coerces the ! type to something different before we can get - // our hands on it. So instead, we do a manual search. We do fall back to - // is_never in some places when there is no better alternative. - for_each_expr(expr, |ex| { - match ex.kind { - ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()), - ExprKind::Call(call, _) => { - if is_never(cx, ex) || is_never(cx, call) { - return ControlFlow::Break(()); - } - ControlFlow::Continue(Descend::Yes) - }, - ExprKind::MethodCall(..) => { - if is_never(cx, ex) { - return ControlFlow::Break(()); - } - ControlFlow::Continue(Descend::Yes) - }, - ExprKind::If(if_expr, if_then, if_else) => { - let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex)); - let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then)); - if diverges { - return ControlFlow::Break(()); + impl<'tcx> Visitor<'tcx> for V<'_, '_> { + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) { + return ty.is_never(); } - ControlFlow::Continue(Descend::No) - }, - ExprKind::Match(match_expr, match_arms, _) => { - let diverges = expr_diverges(cx, match_expr) - || match_arms.iter().all(|arm| { - let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body())); - guard_diverges || expr_diverges(cx, arm.body) - }); - if diverges { - return ControlFlow::Break(()); - } - ControlFlow::Continue(Descend::No) - }, + false + } - // Don't continue into loops or labeled blocks, as they are breakable, - // and we'd have to start checking labels. - ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + if self.res.is_break() { + return; + } - // Default: descend - _ => ControlFlow::Continue(Descend::Yes), + // We can't just call is_never on expr and be done, because the type system + // sometimes coerces the ! type to something different before we can get + // our hands on it. So instead, we do a manual search. We do fall back to + // is_never in some places when there is no better alternative. + self.res = match e.kind { + ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()), + ExprKind::Call(call, _) => { + if is_never(self.cx, e) || is_never(self.cx, call) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) + } + }, + ExprKind::MethodCall(..) => { + if is_never(self.cx, e) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::Yes) + } + }, + ExprKind::If(if_expr, if_then, if_else) => { + let else_diverges = if_else.map_or(false, |ex| expr_diverges(self.cx, ex)); + let diverges = + expr_diverges(self.cx, if_expr) || (else_diverges && expr_diverges(self.cx, if_then)); + if diverges { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::No) + } + }, + ExprKind::Match(match_expr, match_arms, _) => { + let diverges = expr_diverges(self.cx, match_expr) + || match_arms.iter().all(|arm| { + let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(self.cx, g.body())); + guard_diverges || expr_diverges(self.cx, arm.body) + }); + if diverges { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(Descend::No) + } + }, + + // Don't continue into loops or labeled blocks, as they are breakable, + // and we'd have to start checking labels. + ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + + // Default: descend + _ => ControlFlow::Continue(Descend::Yes), + }; + if let ControlFlow::Continue(Descend::Yes) = self.res { + walk_expr(self, e); + } + } + + fn visit_local(&mut self, local: &'tcx Local<'_>) { + // Don't visit the else block of a let/else statement as it will not make + // the statement divergent even though the else block is divergent. + if let Some(init) = local.init { + self.visit_expr(init); + } } - }) - .is_some() + + // Avoid unnecessary `walk_*` calls. + fn visit_ty(&mut self, _: &'tcx Ty<'tcx>) {} + fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {} + fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {} + // Avoid monomorphising all `visit_*` functions. + fn visit_nested_item(&mut self, _: ItemId) {} + } + + let mut v = V { + cx, + res: ControlFlow::Continue(Descend::Yes), + }; + expr.visit(&mut v); + v.res.is_break() } fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 9a84068d448..0e22485db2c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -8,7 +8,6 @@ use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::DefIdTree; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 587c926dc01..6447899f2b9 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -10,7 +10,6 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty::DefIdTree; use rustc_span::sym; use super::MANUAL_UNWRAP_OR; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 81bebff34c8..df0ea7f5b86 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -12,7 +12,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; +use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_span::{sym, Symbol}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index 4720a6e6888..8e1130cf8df 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -8,7 +8,6 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{LangItem, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::DefIdTree; use rustc_span::Span; pub(crate) struct OptionAndThenSome; diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index d512cc4eeae..c5fc145b289 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -5,6 +5,8 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; +use crate::methods::method_call; + use super::BYTES_NTH; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) { @@ -16,18 +18,32 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E } else { return; }; + let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_NTH, - expr.span, - &format!("called `.bytes().nth()` on a `{caller_type}`"), - "try", - format!( - "{}.as_bytes().get({})", - snippet_with_applicability(cx, recv.span, "..", &mut applicability), - snippet_with_applicability(cx, n_arg.span, "..", &mut applicability) - ), - applicability, - ); + let receiver = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + let n = snippet_with_applicability(cx, n_arg.span, "..", &mut applicability); + + if let Some(parent) = clippy_utils::get_parent_expr(cx, expr) + && let Some((name, _, _, _, _)) = method_call(parent) + && name == "unwrap" { + span_lint_and_sugg( + cx, + BYTES_NTH, + parent.span, + &format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"), + "try", + format!("{receiver}.as_bytes()[{n}]",), + applicability + ); + } else { + span_lint_and_sugg( + cx, + BYTES_NTH, + expr.span, + &format!("called `.bytes().nth()` on a `{caller_type}`"), + "try", + format!("{receiver}.as_bytes().get({n}).copied()"), + applicability + ); + }; } diff --git a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs index 56b7fbb9d4b..079df2226d1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/methods/chars_cmp.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_lint::Lint; -use rustc_middle::ty::{self, DefIdTree}; +use rustc_middle::ty; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. pub(super) fn check( diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs index cce8f797e98..614610335a1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -27,7 +27,7 @@ pub(super) fn check( let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 374eb29fc52..5a78a416877 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -53,7 +53,9 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir "to_vec" => cx .tcx .impl_of_method(method_def_id) - .filter(|&impl_did| cx.tcx.type_of(impl_did).subst_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()) + .filter(|&impl_did| { + cx.tcx.type_of(impl_did).subst_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() + }) .is_some(), _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 6301b3ded20..702df4b282b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -80,6 +80,7 @@ mod skip_while_next; mod stable_sort_primitive; mod str_splitn; mod string_extend_chars; +mod suspicious_command_arg_space; mod suspicious_map; mod suspicious_splitn; mod suspicious_to_owned; @@ -3162,6 +3163,32 @@ declare_clippy_lint! { "collecting an iterator when collect is not needed" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `Command::arg()` invocations that look like they + /// should be multiple arguments instead, such as `arg("-t ext2")`. + /// + /// ### Why is this bad? + /// + /// `Command::arg()` does not split arguments by space. An argument like `arg("-t ext2")` + /// will be passed as a single argument to the command, + /// which is likely not what was intended. + /// + /// ### Example + /// ```rust + /// std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap(); + /// ``` + #[clippy::version = "1.67.0"] + pub SUSPICIOUS_COMMAND_ARG_SPACE, + suspicious, + "single command line argument that looks like it should be multiple arguments" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3289,6 +3316,7 @@ impl_lint_pass!(Methods => [ SEEK_FROM_CURRENT, SEEK_TO_START_INSTEAD_OF_REWIND, NEEDLESS_COLLECT, + SUSPICIOUS_COMMAND_ARG_SPACE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -3496,6 +3524,9 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, + ("arg", [arg]) => { + suspicious_command_arg_space::check(cx, recv, arg, span); + } ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs index 3a23ecc50dc..41ceef19e3a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -6,7 +6,6 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; -use rustc_middle::ty::DefIdTree; use rustc_span::symbol::sym; use super::OPTION_MAP_OR_NONE; diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs new file mode 100644 index 00000000000..73632c5a357 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths; +use clippy_utils::ty::match_type; +use rustc_ast as ast; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::SUSPICIOUS_COMMAND_ARG_SPACE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + if match_type(cx, ty, &paths::STD_PROCESS_COMMAND) + && let hir::ExprKind::Lit(lit) = &arg.kind + && let ast::LitKind::Str(s, _) = &lit.node + && let Some((arg1, arg2)) = s.as_str().split_once(' ') + && arg1.starts_with('-') + && arg1.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-') + { + span_lint_and_then( + cx, + SUSPICIOUS_COMMAND_ARG_SPACE, + arg.span, + "single argument that looks like it should be multiple arguments", + |diag: &mut Diagnostic| { + diag.multipart_suggestion_verbose( + "consider splitting the argument", + vec![ + (span, "args".to_string()), + (arg.span, format!("[{arg1:?}, {arg2:?}]")), + ], + Applicability::MaybeIncorrect, + ); + } + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs index 90983f249cd..5e4c3daee64 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_cfg_test, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -27,7 +27,7 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { + if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 6fd100762b4..5b1f03fc16c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -8,10 +8,12 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; +use hir::def_id::LocalDefId; +use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::DefIdTree; +use rustc_middle::ty::Visibility; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Span; @@ -34,6 +36,9 @@ declare_clippy_lint! { } pub struct MissingDoc { + /// Whether to **only** check for missing documentation in items visible within the current + /// crate. For example, `pub(crate)` items. + crate_items_only: bool, /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec<bool>, @@ -42,14 +47,15 @@ pub struct MissingDoc { impl Default for MissingDoc { #[must_use] fn default() -> Self { - Self::new() + Self::new(false) } } impl MissingDoc { #[must_use] - pub fn new() -> Self { + pub fn new(crate_items_only: bool) -> Self { Self { + crate_items_only, doc_hidden_stack: vec![false], } } @@ -75,6 +81,7 @@ impl MissingDoc { fn check_missing_docs_attrs( &self, cx: &LateContext<'_>, + def_id: LocalDefId, attrs: &[ast::Attribute], sp: Span, article: &'static str, @@ -95,6 +102,13 @@ impl MissingDoc { return; } + if self.crate_items_only && def_id != CRATE_DEF_ID { + let vis = cx.tcx.visibility(def_id); + if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) { + return; + } + } + let has_doc = attrs .iter() .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); @@ -123,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_crate(&mut self, cx: &LateContext<'tcx>) { let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID); - self.check_missing_docs_attrs(cx, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate"); + self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { @@ -159,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let attrs = cx.tcx.hir().attrs(it.hir_id()); if !is_from_proc_macro(cx, it) { - self.check_missing_docs_attrs(cx, attrs, it.span, article, desc); + self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc); } } @@ -168,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let attrs = cx.tcx.hir().attrs(trait_item.hir_id()); if !is_from_proc_macro(cx, trait_item) { - self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc); + self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc); } } @@ -185,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); let attrs = cx.tcx.hir().attrs(impl_item.hir_id()); if !is_from_proc_macro(cx, impl_item) { - self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc); + self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc); } } @@ -193,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !sf.is_positional() { let attrs = cx.tcx.hir().attrs(sf.hir_id); if !is_from_proc_macro(cx, sf) { - self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field"); + self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } } } @@ -201,7 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { let attrs = cx.tcx.hir().attrs(v.hir_id); if !is_from_proc_macro(cx, v) { - self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant"); + self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant"); } } } diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 0742943dff2..349fcd2274d 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -134,7 +134,7 @@ fn process_paths_for_mod_files<'a>( mod_folders: &mut FxHashSet<&'a OsStr>, ) { let mut comp = path.components().rev().peekable(); - let _ = comp.next(); + let _: Option<_> = comp.next(); if path.ends_with("mod.rs") { mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default()); } diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 5a533261cad..8aa814b7405 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -166,7 +166,8 @@ impl MutableKeyType { Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty), Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty), Array(inner_ty, size) => { - size.try_eval_target_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) + size.try_eval_target_usize(cx.tcx, cx.param_env) + .map_or(true, |u| u != 0) && self.is_interior_mutable_type(cx, inner_ty) }, Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)), diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index da3b6fa9899..1ab81aee7b8 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -149,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { }; let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity(); - let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig); + let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig); for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() { // All spans generated from a proc-macro invocation are the same... diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs index 97c8cfbd3eb..e2a7ba02a04 100644 --- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs @@ -6,7 +6,6 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::DefIdTree; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs new file mode 100644 index 00000000000..bc64ccb295c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_target::spec::abi::Abi; + +declare_clippy_lint! { + /// ### What it does + /// Checks for Rust ABI functions with the `#[no_mangle]` attribute. + /// + /// ### Why is this bad? + /// The Rust ABI is not stable, but in many simple cases matches + /// enough with the C ABI that it is possible to forget to add + /// `extern "C"` to a function called from C. Changes to the + /// Rust ABI can break this at any point. + /// + /// ### Example + /// ```rust + /// #[no_mangle] + /// fn example(arg_one: u32, arg_two: usize) {} + /// ``` + /// + /// Use instead: + /// ```rust + /// #[no_mangle] + /// extern "C" fn example(arg_one: u32, arg_two: usize) {} + /// ``` + #[clippy::version = "1.69.0"] + pub NO_MANGLE_WITH_RUST_ABI, + pedantic, + "convert Rust ABI functions to C ABI" +} +declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); + +impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn(fn_sig, _, _) = &item.kind { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut applicability); + for attr in attrs { + if let Some(ident) = attr.ident() + && ident.name == rustc_span::sym::no_mangle + && fn_sig.header.abi == Abi::Rust + && !snippet.contains("extern") { + + let suggestion = snippet.split_once("fn") + .map_or(String::new(), |(first, second)| format!(r#"{first}extern "C" fn{second}"#)); + + span_lint_and_sugg( + cx, + NO_MANGLE_WITH_RUST_ABI, + fn_sig.span, + "attribute #[no_mangle] set on a Rust ABI function", + "try", + suggestion, + applicability + ); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index d592f6e814c..87a8a2ed12b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,8 +1,8 @@ use super::ARITHMETIC_SIDE_EFFECTS; use clippy_utils::{ - consts::{constant, constant_simple}, + consts::{constant, constant_simple, Constant}, diagnostics::span_lint, - peel_hir_expr_refs, peel_hir_expr_unary, + is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, }; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -97,17 +97,19 @@ impl ArithmeticSideEffects { self.expr_span = Some(expr.span); } - /// If `expr` is not a literal integer like `1`, returns `None`. + /// Returns the numeric value of a literal integer originated from `expr`, if any. /// - /// Returns the absolute value of the expression, if this is an integer literal. - fn literal_integer(expr: &hir::Expr<'_>) -> Option<u128> { + /// Literal integers can be originated from adhoc declarations like `1`, associated constants + /// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`, + fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> { let actual = peel_hir_expr_unary(expr).0; if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node { - Some(n) + return Some(n) } - else { - None + if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) { + return Some(n); } + None } /// Manages when the lint should be triggered. Operations in constant environments, hard coded @@ -143,7 +145,10 @@ impl ArithmeticSideEffects { let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); - match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) { + match ( + Self::literal_integer(cx, actual_lhs), + Self::literal_integer(cx, actual_rhs), + ) { (None, None) => false, (None, Some(n)) | (Some(n), None) => match (&op.node, n) { (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, @@ -180,20 +185,22 @@ impl ArithmeticSideEffects { return; } let actual_un_expr = peel_hir_expr_refs(un_expr).0; - if Self::literal_integer(actual_un_expr).is_some() { + if Self::literal_integer(cx, actual_un_expr).is_some() { return; } self.issue_lint(cx, expr); } - fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool { - self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + fn should_skip_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id) + || self.expr_span.is_some() + || self.const_span.map_or(false, |sp| sp.contains(expr.span)) } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { - if self.should_skip_expr(expr) { + if self.should_skip_expr(cx, expr) { return; } match &expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs new file mode 100644 index 00000000000..9b678e8d753 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_help; + +use clippy_utils::macros::span_is_local; +use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions that use the question mark operator and rejects them. + /// + /// ### Why is this bad? + /// Sometimes code wants to avoid the question mark operator because for instance a local + /// block requires a macro to re-throw errors to attach additional information to the + /// error. + /// + /// ### Example + /// ```ignore + /// let result = expr?; + /// ``` + /// + /// Could be written: + /// + /// ```ignore + /// utility_macro!(expr); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub QUESTION_MARK_USED, + restriction, + "complains if the question mark operator is used" +} + +declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]); + +impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(_, _, MatchSource::TryDesugar) = expr.kind { + if !span_is_local(expr.span) { + return; + } + + span_lint_and_help( + cx, + QUESTION_MARK_USED, + expr.span, + "question mark operator was used", + None, + "consider using a custom macro or match expression", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 84a0c6b9558..f0d7dd23a67 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -14,6 +14,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; use rustc_span::{BytePos, Pos}; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -69,31 +70,41 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } -#[derive(PartialEq, Eq, Copy, Clone)] -enum RetReplacement { +#[derive(PartialEq, Eq, Clone)] +enum RetReplacement<'tcx> { Empty, Block, Unit, + IfSequence(Cow<'tcx, str>, Applicability), + Expr(Cow<'tcx, str>, Applicability), } -impl RetReplacement { +impl<'tcx> RetReplacement<'tcx> { fn sugg_help(self) -> &'static str { match self { - Self::Empty => "remove `return`", + Self::Empty | Self::Expr(..) => "remove `return`", Self::Block => "replace `return` with an empty block", Self::Unit => "replace `return` with a unit value", + Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses", + } + } + fn applicability(&self) -> Option<Applicability> { + match self { + Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap), + _ => None, } } } -impl ToString for RetReplacement { +impl<'tcx> ToString for RetReplacement<'tcx> { fn to_string(&self) -> String { - match *self { - Self::Empty => "", - Self::Block => "{}", - Self::Unit => "()", + match self { + Self::Empty => String::new(), + Self::Block => "{}".to_string(), + Self::Unit => "()".to_string(), + Self::IfSequence(inner, _) => format!("({inner})"), + Self::Expr(inner, _) => inner.to_string(), } - .to_string() } } @@ -204,19 +215,38 @@ fn check_final_expr<'tcx>( expr: &'tcx Expr<'tcx>, semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an * needless return */ - replacement: RetReplacement, + replacement: RetReplacement<'tcx>, ) { let peeled_drop_expr = expr.peel_drop_temps(); match &peeled_drop_expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { - // if desugar of `do yeet`, don't lint - if let Some(inner_expr) = inner - && let ExprKind::Call(path_expr, _) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind - { - return; - } + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + let replacement = if let Some(inner_expr) = inner { + // if desugar of `do yeet`, don't lint + if let ExprKind::Call(path_expr, _) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind + { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); + if expr_contains_conjunctive_ifs(inner_expr) { + RetReplacement::IfSequence(snippet, applicability) + } else { + RetReplacement::Expr(snippet, applicability) + } + } else { + replacement + }; + if !cx.tcx.hir().attrs(expr.hir_id).is_empty() { return; } @@ -224,14 +254,8 @@ fn check_final_expr<'tcx>( if borrows { return; } - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); + emit_return_lint(cx, ret_span, semi_spans, replacement); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -253,29 +277,25 @@ fn check_final_expr<'tcx>( } } -fn emit_return_lint( - cx: &LateContext<'_>, - ret_span: Span, - semi_spans: Vec<Span>, - inner_span: Option<Span>, - replacement: RetReplacement, -) { +fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool { + fn contains_if(expr: &Expr<'_>, on_if: bool) -> bool { + match expr.kind { + ExprKind::If(..) => on_if, + ExprKind::Binary(_, left, right) => contains_if(left, true) || contains_if(right, true), + _ => false, + } + } + + contains_if(expr, false) +} + +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: RetReplacement<'_>) { if ret_span.from_expansion() { return; } - let mut applicability = Applicability::MachineApplicable; - let return_replacement = inner_span.map_or_else( - || replacement.to_string(), - |inner_span| { - let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); - snippet.to_string() - }, - ); - let sugg_help = if inner_span.is_some() { - "remove `return`" - } else { - replacement.sugg_help() - }; + let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable); + let return_replacement = replacement.to_string(); + let sugg_help = replacement.sugg_help(); span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); // for each parent statement, we need to remove the semicolon diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs new file mode 100644 index 00000000000..e2d90edec5a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -0,0 +1,399 @@ +use crate::FxHashSet; +use clippy_utils::{ + diagnostics::span_lint_and_then, + get_attr, + source::{indent_of, snippet}, +}; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::{ + self as hir, + intravisit::{walk_expr, Visitor}, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::{subst::GenericArgKind, Ty, TypeAndMut}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::Ident, Span, DUMMY_SP}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Searches for elements marked with `#[clippy::significant_drop]` that could be early + /// dropped but are in fact dropped at the end of their scopes. In other words, enforces the + /// "tightening" of their possible lifetimes. + /// + /// ### Why is this bad? + /// + /// Elements marked with `#[clippy::has_significant_drop]` are generally synchronizing + /// primitives that manage shared resources, as such, it is desired to release them as soon as + /// possible to avoid unnecessary resource contention. + /// + /// ### Example + /// + /// ```rust,ignore + /// fn main() { + /// let lock = some_sync_resource.lock(); + /// let owned_rslt = lock.do_stuff_with_resource(); + /// // Only `owned_rslt` is needed but `lock` is still held. + /// do_heavy_computation_that_takes_time(owned_rslt); + /// } + /// ``` + /// + /// Use instead: + /// + /// ```rust,ignore + /// fn main() { + /// let owned_rslt = some_sync_resource.lock().do_stuff_with_resource(); + /// do_heavy_computation_that_takes_time(owned_rslt); + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub SIGNIFICANT_DROP_TIGHTENING, + nursery, + "Searches for elements marked with `#[clippy::has_significant_drop]` that could be early dropped but are in fact dropped at the end of their scopes" +} + +impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]); + +#[derive(Default)] +pub struct SignificantDropTightening<'tcx> { + /// Auxiliary structure used to avoid having to verify the same type multiple times. + seen_types: FxHashSet<Ty<'tcx>>, +} + +impl<'tcx> SignificantDropTightening<'tcx> { + /// Unifies the statements of a block with its return expression. + fn all_block_stmts<'ret, 'rslt, 'stmts>( + block_stmts: &'stmts [hir::Stmt<'tcx>], + dummy_ret_stmt: Option<&'ret hir::Stmt<'tcx>>, + ) -> impl Iterator<Item = &'rslt hir::Stmt<'tcx>> + where + 'ret: 'rslt, + 'stmts: 'rslt, + { + block_stmts.iter().chain(dummy_ret_stmt) + } + + /// Searches for at least one statement that could slow down the release of a significant drop. + fn at_least_one_stmt_is_expensive<'stmt>(stmts: impl Iterator<Item = &'stmt hir::Stmt<'tcx>>) -> bool + where + 'tcx: 'stmt, + { + for stmt in stmts { + match stmt.kind { + hir::StmtKind::Expr(expr) if let hir::ExprKind::Path(_) = expr.kind => {} + hir::StmtKind::Local(local) if let Some(expr) = local.init + && let hir::ExprKind::Path(_) = expr.kind => {}, + _ => return true + }; + } + false + } + + /// Verifies if the expression is of type `drop(some_lock_path)` to assert that the temporary + /// is already being dropped before the end of its scope. + fn has_drop(expr: &'tcx hir::Expr<'_>, init_bind_ident: Ident) -> bool { + if let hir::ExprKind::Call(fun, args) = expr.kind + && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind + && let [fun_ident, ..] = fun_path.segments + && fun_ident.ident.name == rustc_span::sym::drop + && let [first_arg, ..] = args + && let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind + && let [first_arg_ps, .. ] = arg_path.segments + { + first_arg_ps.ident == init_bind_ident + } + else { + false + } + } + + /// Tries to find types marked with `#[has_significant_drop]` of an expression `expr` that is + /// originated from `stmt` and then performs common logic on `sdap`. + fn modify_sdap_if_sig_drop_exists( + &mut self, + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + idx: usize, + sdap: &mut SigDropAuxParams, + stmt: &hir::Stmt<'_>, + cb: impl Fn(&mut SigDropAuxParams), + ) { + let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types); + sig_drop_finder.visit_expr(expr); + if sig_drop_finder.has_sig_drop { + cb(sdap); + if sdap.number_of_stmts > 0 { + sdap.last_use_stmt_idx = idx; + sdap.last_use_stmt_span = stmt.span; + if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind { + sdap.last_use_method_span = span; + } + } + sdap.number_of_stmts = sdap.number_of_stmts.wrapping_add(1); + } + } + + /// Shows generic overall messages as well as specialized messages depending on the usage. + fn set_suggestions(cx: &LateContext<'tcx>, block_span: Span, diag: &mut Diagnostic, sdap: &SigDropAuxParams) { + match sdap.number_of_stmts { + 0 | 1 => {}, + 2 => { + let indent = " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0)); + let init_method = snippet(cx, sdap.init_method_span, ".."); + let usage_method = snippet(cx, sdap.last_use_method_span, ".."); + let stmt = if let Some(last_use_bind_span) = sdap.last_use_bind_span { + format!( + "\n{indent}let {} = {init_method}.{usage_method};", + snippet(cx, last_use_bind_span, ".."), + ) + } else { + format!("\n{indent}{init_method}.{usage_method};") + }; + diag.span_suggestion_verbose( + sdap.init_stmt_span, + "merge the temporary construction with its single usage", + stmt, + Applicability::MaybeIncorrect, + ); + diag.span_suggestion( + sdap.last_use_stmt_span, + "remove separated single usage", + "", + Applicability::MaybeIncorrect, + ); + }, + _ => { + diag.span_suggestion( + sdap.last_use_stmt_span.shrink_to_hi(), + "drop the temporary after the end of its last usage", + format!( + "\n{}drop({});", + " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0)), + sdap.init_bind_ident + ), + Applicability::MaybeIncorrect, + ); + }, + } + diag.note("this might lead to unnecessary resource contention"); + diag.span_label( + block_span, + format!( + "temporary `{}` is currently being dropped at the end of its contained scope", + sdap.init_bind_ident + ), + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let dummy_ret_stmt = block.expr.map(|expr| hir::Stmt { + hir_id: hir::HirId::INVALID, + kind: hir::StmtKind::Expr(expr), + span: DUMMY_SP, + }); + let mut sdap = SigDropAuxParams::default(); + for (idx, stmt) in Self::all_block_stmts(block.stmts, dummy_ret_stmt.as_ref()).enumerate() { + match stmt.kind { + hir::StmtKind::Expr(expr) => self.modify_sdap_if_sig_drop_exists( + cx, + expr, + idx, + &mut sdap, + stmt, + |_| {} + ), + hir::StmtKind::Local(local) if let Some(expr) = local.init => self.modify_sdap_if_sig_drop_exists( + cx, + expr, + idx, + &mut sdap, + stmt, + |local_sdap| { + if local_sdap.number_of_stmts == 0 { + if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind { + local_sdap.init_bind_ident = ident; + } + if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr.kind { + local_sdap.init_method_span = local_expr.span.to(span); + } + local_sdap.init_stmt_span = stmt.span; + } + else if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind { + local_sdap.last_use_bind_span = Some(ident.span); + } + } + ), + hir::StmtKind::Semi(expr) => { + if Self::has_drop(expr, sdap.init_bind_ident) { + return; + } + self.modify_sdap_if_sig_drop_exists(cx, expr, idx, &mut sdap, stmt, |_| {}); + }, + _ => {} + }; + } + + let idx = sdap.last_use_stmt_idx.wrapping_add(1); + let stmts_after_last_use = Self::all_block_stmts(block.stmts, dummy_ret_stmt.as_ref()).skip(idx); + if sdap.number_of_stmts > 1 && Self::at_least_one_stmt_is_expensive(stmts_after_last_use) { + span_lint_and_then( + cx, + SIGNIFICANT_DROP_TIGHTENING, + sdap.init_bind_ident.span, + "temporary with significant `Drop` can be early dropped", + |diag| { + Self::set_suggestions(cx, block.span, diag, &sdap); + }, + ); + } + } +} + +/// Auxiliary parameters used on each block check. +struct SigDropAuxParams { + /// The binding or variable that references the initial construction of the type marked with + /// `#[has_significant_drop]`. + init_bind_ident: Ident, + /// Similar to `init_bind_ident` but encompasses the right-hand method call. + init_method_span: Span, + /// Similar to `init_bind_ident` but encompasses the whole contained statement. + init_stmt_span: Span, + + /// The last visited binding or variable span within a block that had any referenced inner type + /// marked with `#[has_significant_drop]`. + last_use_bind_span: Option<Span>, + /// Index of the last visited statement within a block that had any referenced inner type + /// marked with `#[has_significant_drop]`. + last_use_stmt_idx: usize, + /// Similar to `last_use_bind_span` but encompasses the whole contained statement. + last_use_stmt_span: Span, + /// Similar to `last_use_bind_span` but encompasses the right-hand method call. + last_use_method_span: Span, + + /// Total number of statements within a block that have any referenced inner type marked with + /// `#[has_significant_drop]`. + number_of_stmts: usize, +} + +impl Default for SigDropAuxParams { + fn default() -> Self { + Self { + init_bind_ident: Ident::empty(), + init_method_span: DUMMY_SP, + init_stmt_span: DUMMY_SP, + last_use_bind_span: None, + last_use_method_span: DUMMY_SP, + last_use_stmt_idx: 0, + last_use_stmt_span: DUMMY_SP, + number_of_stmts: 0, + } + } +} + +/// Checks the existence of the `#[has_significant_drop]` attribute +struct SigDropChecker<'cx, 'sdt, 'tcx> { + cx: &'cx LateContext<'tcx>, + seen_types: &'sdt mut FxHashSet<Ty<'tcx>>, +} + +impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> { + pub(crate) fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self { + seen_types.clear(); + Self { cx, seen_types } + } + + pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { + if let Some(adt) = ty.ty_adt_def() { + let mut iter = get_attr( + self.cx.sess(), + self.cx.tcx.get_attrs_unchecked(adt.did()), + "has_significant_drop", + ); + if iter.next().is_some() { + return true; + } + } + match ty.kind() { + rustc_middle::ty::Adt(a, b) => { + for f in a.all_fields() { + let ty = f.ty(self.cx.tcx, b); + if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) { + return true; + } + } + for generic_arg in b.iter() { + if let GenericArgKind::Type(ty) = generic_arg.unpack() { + if self.has_sig_drop_attr(ty) { + return true; + } + } + } + false + }, + rustc_middle::ty::Array(ty, _) + | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) + | rustc_middle::ty::Ref(_, ty, _) + | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty), + _ => false, + } + } + + fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool { + !self.seen_types.insert(ty) + } +} + +/// Performs recursive calls to find any inner type marked with `#[has_significant_drop]`. +struct SigDropFinder<'cx, 'sdt, 'tcx> { + cx: &'cx LateContext<'tcx>, + has_sig_drop: bool, + sig_drop_checker: SigDropChecker<'cx, 'sdt, 'tcx>, +} + +impl<'cx, 'sdt, 'tcx> SigDropFinder<'cx, 'sdt, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self { + Self { + cx, + has_sig_drop: false, + sig_drop_checker: SigDropChecker::new(cx, seen_types), + } + } +} + +impl<'cx, 'sdt, 'tcx> Visitor<'tcx> for SigDropFinder<'cx, 'sdt, 'tcx> { + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'_>) { + if self + .sig_drop_checker + .has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex)) + { + self.has_sig_drop = true; + return; + } + + match ex.kind { + hir::ExprKind::MethodCall(_, expr, ..) => { + self.visit_expr(expr); + }, + hir::ExprKind::Array(..) + | hir::ExprKind::Assign(..) + | hir::ExprKind::AssignOp(..) + | hir::ExprKind::Binary(..) + | hir::ExprKind::Box(..) + | hir::ExprKind::Call(..) + | hir::ExprKind::Field(..) + | hir::ExprKind::If(..) + | hir::ExprKind::Index(..) + | hir::ExprKind::Match(..) + | hir::ExprKind::Repeat(..) + | hir::ExprKind::Ret(..) + | hir::ExprKind::Tup(..) + | hir::ExprKind::Unary(..) + | hir::ExprKind::Yield(..) => { + walk_expr(self, ex); + }, + _ => {}, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index d6b336bef94..a13bc7a5188 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::def_id::DefId; use rustc_hir::{def::Res, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::DefIdTree; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, symbol::kw, Span}; diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 17e9cc5f6b7..0f062cecf88 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{sym, symbol::Ident, Span}; declare_clippy_lint! { /// ### What it does @@ -174,53 +174,74 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { /// Implementation of the `ALMOST_SWAPPED` lint. fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { - for w in block.stmts.windows(2) { - if_chain! { - if let StmtKind::Semi(first) = w[0].kind; - if let StmtKind::Semi(second) = w[1].kind; - if first.span.ctxt() == second.span.ctxt(); - if let ExprKind::Assign(lhs0, rhs0, _) = first.kind; - if let ExprKind::Assign(lhs1, rhs1, _) = second.kind; - if eq_expr_value(cx, lhs0, rhs1); - if eq_expr_value(cx, lhs1, rhs0); - then { - let lhs0 = Sugg::hir_opt(cx, lhs0); - let rhs0 = Sugg::hir_opt(cx, rhs0); - let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) { - ( - format!(" `{first}` and `{second}`"), - first.mut_addr().to_string(), - second.mut_addr().to_string(), - ) - } else { - (String::new(), String::new(), String::new()) - }; + for [first, second] in block.stmts.array_windows() { + if let Some((lhs0, rhs0)) = parse(first) + && let Some((lhs1, rhs1)) = parse(second) + && first.span.eq_ctxt(second.span) + && is_same(cx, lhs0, rhs1) + && is_same(cx, lhs1, rhs0) + && let Some(lhs_sugg) = match &lhs0 { + ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr), + ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())), + } + && let Some(rhs_sugg) = Sugg::hir_opt(cx, rhs0) + { + let span = first.span.to(rhs1.span); + let Some(sugg) = std_or_core(cx) else { return }; + span_lint_and_then( + cx, + ALMOST_SWAPPED, + span, + &format!("this looks like you are trying to swap `{lhs_sugg}` and `{rhs_sugg}`"), + |diag| { + diag.span_suggestion( + span, + "try", + format!("{sugg}::mem::swap({}, {})", lhs_sugg.mut_addr(), rhs_sugg.mut_addr()), + Applicability::MaybeIncorrect, + ); + diag.note(format!("or maybe you should use `{sugg}::mem::replace`?")); + }, + ); + } + } +} + +fn is_same(cx: &LateContext<'_>, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool { + match lhs { + ExprOrIdent::Expr(expr) => eq_expr_value(cx, expr, rhs), + ExprOrIdent::Ident(ident) => { + if let ExprKind::Path(QPath::Resolved(None, path)) = rhs.kind + && let [segment] = &path.segments + && segment.ident == ident + { + true + } else { + false + } + } + } +} - let span = first.span.to(second.span); - let Some(sugg) = std_or_core(cx) else { return }; +#[derive(Debug, Clone, Copy)] +enum ExprOrIdent<'a> { + Expr(&'a Expr<'a>), + Ident(Ident), +} - span_lint_and_then(cx, - ALMOST_SWAPPED, - span, - &format!("this looks like you are trying to swap{what}"), - |diag| { - if !what.is_empty() { - diag.span_suggestion( - span, - "try", - format!( - "{sugg}::mem::swap({lhs}, {rhs})", - ), - Applicability::MaybeIncorrect, - ); - diag.note( - format!("or maybe you should use `{sugg}::mem::replace`?") - ); - } - }); +fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<'hir>)> { + if let StmtKind::Semi(expr) = stmt.kind { + if let ExprKind::Assign(lhs, rhs, _) = expr.kind { + return Some((ExprOrIdent::Expr(lhs), rhs)); + } + } else if let StmtKind::Local(expr) = stmt.kind { + if let Some(rhs) = expr.init { + if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind { + return Some((ExprOrIdent::Ident(ident_l), rhs)); } } } + None } /// Implementation of the xor case for `MANUAL_SWAP` lint. diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index c0d290b5adc..c01cbe5090f 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -3,6 +3,7 @@ mod transmute_float_to_int; mod transmute_int_to_bool; mod transmute_int_to_char; mod transmute_int_to_float; +mod transmute_int_to_non_zero; mod transmute_null_to_fn; mod transmute_num_to_bytes; mod transmute_ptr_to_ptr; @@ -255,6 +256,31 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for transmutes from integers to `NonZero*` types, and suggests their `new_unchecked` + /// method instead. + /// + /// ### Why is this bad? + /// Transmutes work on any types and thus might cause unsoundness when those types change + /// elsewhere. `new_unchecked` only works for the appropriate types instead. + /// + /// ### Example + /// ```rust + /// # use core::num::NonZeroU32; + /// let _non_zero: NonZeroU32 = unsafe { std::mem::transmute(123) }; + /// ``` + /// Use instead: + /// ```rust + /// # use core::num::NonZeroU32; + /// let _non_zero = unsafe { NonZeroU32::new_unchecked(123) }; + /// ``` + #[clippy::version = "1.69.0"] + pub TRANSMUTE_INT_TO_NON_ZERO, + complexity, + "transmutes from an integer to a non-zero wrapper" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for transmutes from a float to an integer. /// /// ### Why is this bad? @@ -451,6 +477,7 @@ impl_lint_pass!(Transmute => [ TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_FLOAT, + TRANSMUTE_INT_TO_NON_ZERO, TRANSMUTE_FLOAT_TO_INT, TRANSMUTE_NUM_TO_BYTES, UNSOUND_COLLECTION_TRANSMUTE, @@ -501,6 +528,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) + | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context) | ( diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs new file mode 100644 index 00000000000..5503653253c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -0,0 +1,61 @@ +use super::TRANSMUTE_INT_TO_NON_ZERO; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::{ + query::Key, + ty::{self, Ty}, +}; +use rustc_span::symbol::sym; + +/// Checks for `transmute_int_to_non_zero` lint. +/// Returns `true` if it's triggered, otherwise returns `false`. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + arg: &'tcx Expr<'_>, +) -> bool { + let (ty::Int(_) | ty::Uint(_), Some(to_ty_id)) = (&from_ty.kind(), to_ty.ty_adt_id()) else { + return false; + }; + let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_id) else { + return false; + }; + + if !matches!( + to_type_sym, + sym::NonZeroU8 + | sym::NonZeroU16 + | sym::NonZeroU32 + | sym::NonZeroU64 + | sym::NonZeroU128 + | sym::NonZeroI8 + | sym::NonZeroI16 + | sym::NonZeroI32 + | sym::NonZeroI64 + | sym::NonZeroI128 + ) { + return false; + } + + span_lint_and_then( + cx, + TRANSMUTE_INT_TO_NON_ZERO, + e.span, + &format!("transmute from a `{from_ty}` to a `{to_type_sym}`"), + |diag| { + let arg = sugg::Sugg::hir(cx, arg, ".."); + diag.span_suggestion( + e.span, + "consider using", + format!("{to_type_sym}::{}({arg})", sym::new_unchecked), + Applicability::Unspecified, + ); + }, + ); + true +} diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 06d248204c1..a57bf7ee822 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -400,7 +400,7 @@ fn drain_matching( // If `ThinVec` had the `drain_filter` method, this loop could be rewritten // like so: - // + // // for pat in alternatives.drain_filter(|p| { // // Check if we should extract, but only if `idx >= start`. // idx += 1; @@ -412,12 +412,12 @@ fn drain_matching( while i < alternatives.len() { idx += 1; // Check if we should extract, but only if `idx >= start`. - if idx > start && predicate(&alternatives[i].kind) { - let pat = alternatives.remove(i); + if idx > start && predicate(&alternatives[i].kind) { + let pat = alternatives.remove(i); tail_or.push(extract(pat.into_inner().kind)); - } else { - i += 1; - } + } else { + i += 1; + } } tail_or diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 1d78c7cfae0..1c7f3e96db8 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -334,7 +334,7 @@ define_Conf! { /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. /// /// The maximum allowed size for arrays on the stack - (array_size_threshold: u128 = 512_000), + (array_size_threshold: u64 = 512_000), /// Lint: VEC_BOX. /// /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed @@ -419,19 +419,19 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed within `#[cfg(test)]` + /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test cfg + /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// - /// Whether `dbg!` should be allowed in test functions + /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` (allow_dbg_in_tests: bool = false), /// Lint: PRINT_STDOUT, PRINT_STDERR. /// - /// Whether print macros (ex. `println!`) should be allowed in test functions + /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` (allow_print_in_tests: bool = false), /// Lint: RESULT_LARGE_ERR. /// @@ -454,6 +454,11 @@ define_Conf! { /// configuration will cause restriction lints to trigger even /// if no suggestion can be made. (suppress_restriction_lint_in_const: bool = false), + /// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS. + /// + /// Whether to **only** check for missing documentation in items visible within the current + /// crate. For example, `pub(crate)` items. + (missing_docs_in_crate_items: bool = false), } /// Search for the configuration file. diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 688a8b865f3..f8978e30a8e 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty::{self}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index ee5e42bae0f..14ed1368e03 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -11,7 +11,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{self, DefIdTree, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -44,7 +44,7 @@ impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]); #[derive(Default)] pub struct UnnecessaryDefPath { - array_def_ids: FxHashSet<(DefId, Span)>, + array_def_ids: FxIndexSet<(DefId, Span)>, linted_def_ids: FxHashSet<DefId>, } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 4604ae5c2c7..50bef370930 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -287,15 +287,12 @@ impl<'a> VecArgs<'a> { Some(VecArgs::Repeat(&args[0], &args[1])) } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { // `vec![a, b, c]` case - if_chain! { - if let hir::ExprKind::Box(boxed) = args[0].kind; - if let hir::ExprKind::Array(args) = boxed.kind; - then { - return Some(VecArgs::Vec(args)); - } + if let hir::ExprKind::Call(_, [arg]) = &args[0].kind + && let hir::ExprKind::Array(args) = arg.kind { + Some(VecArgs::Vec(args)) + } else { + None } - - None } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { Some(VecArgs::Vec(&[])) } else { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index f02f8ecb43d..bcfedd07ed1 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -104,7 +104,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType::{ PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType, }; use rustc_middle::ty::{ - layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UpvarCapture, + layout::IntegerExt, BorrowKind, ClosureKind, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UpvarCapture, }; use rustc_middle::ty::{FloatTy, IntTy, UintTy}; use rustc_span::hygiene::{ExpnKind, MacroKind}; diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 63dccbf697c..be6133d3202 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -391,11 +391,18 @@ impl FormatString { }; let mut unescaped = String::with_capacity(inner.len()); + // Sometimes the original string comes from a macro which accepts a malformed string, such as in a + // #[display(""somestring)] attribute (accepted by the `displaythis` crate). Reconstructing the + // string from the span will not be possible, so we will just return None here. + let mut unparsable = false; unescape_literal(inner, mode, &mut |_, ch| match ch { Ok(ch) => unescaped.push(ch), Err(e) if !e.is_fatal() => (), - Err(e) => panic!("{e:?}"), + Err(_) => unparsable = true, }); + if unparsable { + return None; + } let mut parts = Vec::new(); let _: Option<!> = for_each_expr(pieces, |expr| { diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index 42bdfd4827f..c225398ad2a 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -186,7 +186,7 @@ impl<'a> NumericLiteral<'a> { // The exponent may have a sign, output it early, otherwise it will be // treated as a digit if digits.clone().next() == Some('-') { - let _ = digits.next(); + let _: Option<char> = digits.next(); output.push('-'); } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 95eebab7567..4aae0f7284e 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -115,6 +115,7 @@ pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"]; pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"]; pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"]; +pub const STD_PROCESS_COMMAND: [&str; 3] = ["std", "process", "Command"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 11ca81cfe6c..07feadca2b0 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -20,7 +20,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use std::borrow::Cow; -use std::fmt::{Display, Write as _}; +use std::fmt::{self, Display, Write as _}; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parentheses. @@ -932,7 +932,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - let _ = write!(self.suggestion_start, "{start_snip}&{ident_str}"); + let _: fmt::Result = write!(self.suggestion_start, "{start_snip}&{ident_str}"); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -947,7 +947,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => { - let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); + let _: fmt::Result = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}"); self.next_pos = span.hi(); return; }, @@ -1055,7 +1055,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } } - let _ = write!(self.suggestion_start, "{start_snip}{replacement_str}"); + let _: fmt::Result = write!(self.suggestion_start, "{start_snip}{replacement_str}"); } self.next_pos = span.hi(); } diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index f8ec4bb5493..41e34eba0ad 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -16,9 +16,9 @@ use rustc_infer::infer::{ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::{ - self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, - PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, - VariantDef, VariantDiscr, TypeVisitableExt, + self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv, Predicate, + PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, UintTy, VariantDef, VariantDiscr, }; use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::symbol::Ident; @@ -894,16 +894,29 @@ impl AdtVariantInfo { } /// Gets the struct or enum variant from the given `Res` -pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> { +pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<(AdtDef<'tcx>, &'tcx VariantDef)> { match res { - Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()), - Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)), - Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()), + Res::Def(DefKind::Struct, id) => { + let adt = cx.tcx.adt_def(id); + Some((adt, adt.non_enum_variant())) + }, + Res::Def(DefKind::Variant, id) => { + let adt = cx.tcx.adt_def(cx.tcx.parent(id)); + Some((adt, adt.variant_with_id(id))) + }, + Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => { + let adt = cx.tcx.adt_def(cx.tcx.parent(id)); + Some((adt, adt.non_enum_variant())) + }, Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => { let var_id = cx.tcx.parent(id); - Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id)) + let adt = cx.tcx.adt_def(cx.tcx.parent(var_id)); + Some((adt, adt.variant_with_id(var_id))) + }, + Res::SelfCtor(id) => { + let adt = cx.tcx.type_of(id).subst_identity().ty_adt_def().unwrap(); + Some((adt, adt.non_enum_variant())) }, - Res::SelfCtor(id) => Some(cx.tcx.type_of(id).subst_identity().ty_adt_def().unwrap().non_enum_variant()), _ => None, } } diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index b8824024e6c..e0244ddcecb 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -35,7 +35,7 @@ fn get_clap_config() -> ArgMatches { .long("markdown") .help("Change the reports table to use markdown links"), Arg::new("recursive") - .long("--recursive") + .long("recursive") .help("Run clippy on the dependencies of crates specified in crates-toml") .conflicts_with("threads") .conflicts_with("fix"), diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index bd49f096072..23c85298027 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -17,9 +17,9 @@ use crate::recursive::LintcheckServer; use std::collections::{HashMap, HashSet}; use std::env; use std::env::consts::EXE_SUFFIX; -use std::fmt::Write as _; +use std::fmt::{self, Write as _}; use std::fs; -use std::io::ErrorKind; +use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -145,8 +145,8 @@ impl ClippyWarning { } let mut output = String::from("| "); - let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); - let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); + let _: fmt::Result = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line); + let _: fmt::Result = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message); output.push('\n'); output } else { @@ -632,7 +632,7 @@ fn main() { .unwrap(); let server = config.recursive.then(|| { - let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + let _: io::Result<()> = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); LintcheckServer::spawn(recursive_options) }); @@ -689,7 +689,7 @@ fn main() { write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); for (cratename, msg) in &ices { - let _ = write!(text, "{cratename}: '{msg}'"); + let _: fmt::Result = write!(text, "{cratename}: '{msg}'"); } println!("Writing logs to {}", config.lintcheck_results_path.display()); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index adea8c53df2..cfe845ec78f 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-02-10" +channel = "nightly-2023-02-25" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 9ac849aecf1..dd183362f27 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -209,10 +209,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false - ); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs index 4be04f77f5b..837811bdf1e 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs @@ -1,8 +1,9 @@ // rustc-env:RUST_BACKTRACE=0 // normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" -// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" +// normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs" // normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" // normalize-stderr-test: "'rustc'" -> "'<unnamed>'" +// normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" #![deny(clippy::internal)] #![allow(clippy::missing_clippy_version_attribute)] diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index 2ba5890660f..7ed0ef0274f 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread '<unnamed>' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9 +thread '<unnamed>' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic @@ -9,5 +9,3 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy note: Clippy version: foo -query stack during panic: -end of query stack diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr index c1a10ba55ef..3ca45404e44 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr @@ -1,19 +1,11 @@ error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 - | -LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: convert all references to use `sym::deref_method` - = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` - -error: hardcoded path to a diagnostic item --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: convert all references to use `sym::Deref` + = note: `-D clippy::unnecessary-def-path` implied by `-D warnings` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 @@ -23,5 +15,13 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"] | = help: convert all references to use `LangItem::DerefMut` +error: hardcoded path to a diagnostic item + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 + | +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: convert all references to use `sym::deref_method` + error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs index bff97d97df7..89f142a150d 100644 --- a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs +++ b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs @@ -16,6 +16,18 @@ fn main() { expect_result(); } +#[test] +fn test_expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +#[test] +fn test_expect_result() { + let res: Result<u8, ()> = Ok(0); + let _ = res.expect(""); +} + #[cfg(test)] mod issue9612 { // should not lint in `#[cfg(test)]` modules diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/clippy.toml b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/clippy.toml new file mode 100644 index 00000000000..ec210a98783 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/clippy.toml @@ -0,0 +1 @@ +missing-docs-in-crate-items = true diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs new file mode 100644 index 00000000000..830d71f61dd --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.rs @@ -0,0 +1,59 @@ +//! this is crate +#![allow(missing_docs)] +#![warn(clippy::missing_docs_in_private_items)] + +/// this is mod +mod my_mod { + /// some docs + fn priv_with_docs() {} + fn priv_no_docs() {} + /// some docs + pub(crate) fn crate_with_docs() {} + pub(crate) fn crate_no_docs() {} + /// some docs + pub(super) fn super_with_docs() {} + pub(super) fn super_no_docs() {} + + mod my_sub { + /// some docs + fn sub_priv_with_docs() {} + fn sub_priv_no_docs() {} + /// some docs + pub(crate) fn sub_crate_with_docs() {} + pub(crate) fn sub_crate_no_docs() {} + /// some docs + pub(super) fn sub_super_with_docs() {} + pub(super) fn sub_super_no_docs() {} + } + + /// some docs + pub(crate) struct CrateStructWithDocs { + /// some docs + pub(crate) crate_field_with_docs: (), + pub(crate) crate_field_no_docs: (), + /// some docs + priv_field_with_docs: (), + priv_field_no_docs: (), + } + + pub(crate) struct CrateStructNoDocs { + /// some docs + pub(crate) crate_field_with_docs: (), + pub(crate) crate_field_no_docs: (), + /// some docs + priv_field_with_docs: (), + priv_field_no_docs: (), + } +} + +/// some docs +type CrateTypedefWithDocs = String; +type CrateTypedefNoDocs = String; +/// some docs +pub type PubTypedefWithDocs = String; +pub type PubTypedefNoDocs = String; + +fn main() { + my_mod::crate_with_docs(); + my_mod::crate_no_docs(); +} diff --git a/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr new file mode 100644 index 00000000000..a474187050c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_crate_missing_docs/pub_crate_missing_doc.stderr @@ -0,0 +1,52 @@ +error: missing documentation for a function + --> $DIR/pub_crate_missing_doc.rs:12:5 + | +LL | pub(crate) fn crate_no_docs() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings` + +error: missing documentation for a function + --> $DIR/pub_crate_missing_doc.rs:15:5 + | +LL | pub(super) fn super_no_docs() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a function + --> $DIR/pub_crate_missing_doc.rs:23:9 + | +LL | pub(crate) fn sub_crate_no_docs() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a struct field + --> $DIR/pub_crate_missing_doc.rs:33:9 + | +LL | pub(crate) crate_field_no_docs: (), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a struct + --> $DIR/pub_crate_missing_doc.rs:39:5 + | +LL | / pub(crate) struct CrateStructNoDocs { +LL | | /// some docs +LL | | pub(crate) crate_field_with_docs: (), +LL | | pub(crate) crate_field_no_docs: (), +... | +LL | | priv_field_no_docs: (), +LL | | } + | |_____^ + +error: missing documentation for a struct field + --> $DIR/pub_crate_missing_doc.rs:42:9 + | +LL | pub(crate) crate_field_no_docs: (), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: missing documentation for a type alias + --> $DIR/pub_crate_missing_doc.rs:51:1 + | +LL | type CrateTypedefNoDocs = String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index a22c6a5a060..6a246afac76 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -33,6 +33,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie max-struct-bools max-suggested-slice-pattern-length max-trait-bounds + missing-docs-in-crate-items msrv pass-by-value-size-limit single-char-binding-names-threshold diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs index bc8e8c1f070..6525ea5bfc3 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -66,6 +66,12 @@ fn main() { } } +#[test] +fn test() { + let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let _ = boxed_slice.get(1).unwrap(); +} + #[cfg(test)] mod issue9612 { // should not lint in `#[cfg(test)]` modules diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr index 94b5ef663ad..8a32750e3c9 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -188,10 +188,16 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:84:17 + --> $DIR/unwrap_used.rs:72:13 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:90:17 | LL | let _ = Box::new([0]).get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]` -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 918cf81c600..2611e3a785f 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -13,6 +13,9 @@ use core::num::{Saturating, Wrapping}; +const ONE: i32 = 1; +const ZERO: i32 = 0; + #[derive(Clone, Copy)] pub struct Custom; @@ -182,6 +185,10 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n += &0; _n -= 0; _n -= &0; + _n += ZERO; + _n += &ZERO; + _n -= ZERO; + _n -= &ZERO; _n /= 99; _n /= &99; _n %= 99; @@ -190,10 +197,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n *= &0; _n *= 1; _n *= &1; + _n *= ZERO; + _n *= &ZERO; + _n *= ONE; + _n *= &ONE; _n += -0; _n += &-0; _n -= -0; _n -= &-0; + _n += -ZERO; + _n += &-ZERO; + _n -= -ZERO; + _n -= &-ZERO; _n /= -99; _n /= &-99; _n %= -99; @@ -208,10 +223,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = _n + &0; _n = 0 + _n; _n = &0 + _n; + _n = _n + ZERO; + _n = _n + &ZERO; + _n = ZERO + _n; + _n = &ZERO + _n; _n = _n - 0; _n = _n - &0; _n = 0 - _n; _n = &0 - _n; + _n = _n - ZERO; + _n = _n - &ZERO; + _n = ZERO - _n; + _n = &ZERO - _n; _n = _n / 99; _n = _n / &99; _n = _n % 99; @@ -222,6 +245,10 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri _n = &0 * _n; _n = _n * 1; _n = _n * &1; + _n = ZERO * _n; + _n = &ZERO * _n; + _n = _n * ONE; + _n = _n * &ONE; _n = 1 * _n; _n = &1 * _n; _n = 23 + 85; diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 5e349f6b497..17a2448fbfc 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:243:5 + --> $DIR/arithmetic_side_effects.rs:270:5 | LL | _n += 1; | ^^^^^^^ @@ -7,589 +7,589 @@ LL | _n += 1; = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings` error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:244:5 + --> $DIR/arithmetic_side_effects.rs:271:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:245:5 + --> $DIR/arithmetic_side_effects.rs:272:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:246:5 + --> $DIR/arithmetic_side_effects.rs:273:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:247:5 + --> $DIR/arithmetic_side_effects.rs:274:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:248:5 + --> $DIR/arithmetic_side_effects.rs:275:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:249:5 + --> $DIR/arithmetic_side_effects.rs:276:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:250:5 + --> $DIR/arithmetic_side_effects.rs:277:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:251:5 + --> $DIR/arithmetic_side_effects.rs:278:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:252:5 + --> $DIR/arithmetic_side_effects.rs:279:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:253:5 + --> $DIR/arithmetic_side_effects.rs:280:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:254:5 + --> $DIR/arithmetic_side_effects.rs:281:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:255:5 + --> $DIR/arithmetic_side_effects.rs:282:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:256:5 + --> $DIR/arithmetic_side_effects.rs:283:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:257:5 + --> $DIR/arithmetic_side_effects.rs:284:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:258:5 + --> $DIR/arithmetic_side_effects.rs:285:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:259:5 + --> $DIR/arithmetic_side_effects.rs:286:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:260:5 + --> $DIR/arithmetic_side_effects.rs:287:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:261:5 + --> $DIR/arithmetic_side_effects.rs:288:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:262:5 + --> $DIR/arithmetic_side_effects.rs:289:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:263:5 + --> $DIR/arithmetic_side_effects.rs:290:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:264:5 + --> $DIR/arithmetic_side_effects.rs:291:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:265:5 + --> $DIR/arithmetic_side_effects.rs:292:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:266:5 + --> $DIR/arithmetic_side_effects.rs:293:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:267:5 + --> $DIR/arithmetic_side_effects.rs:294:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:268:5 + --> $DIR/arithmetic_side_effects.rs:295:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:269:5 + --> $DIR/arithmetic_side_effects.rs:296:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:270:5 + --> $DIR/arithmetic_side_effects.rs:297:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:271:5 + --> $DIR/arithmetic_side_effects.rs:298:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:272:5 + --> $DIR/arithmetic_side_effects.rs:299:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:273:5 + --> $DIR/arithmetic_side_effects.rs:300:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:274:5 + --> $DIR/arithmetic_side_effects.rs:301:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:275:5 + --> $DIR/arithmetic_side_effects.rs:302:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:276:5 + --> $DIR/arithmetic_side_effects.rs:303:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:277:5 + --> $DIR/arithmetic_side_effects.rs:304:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:278:5 + --> $DIR/arithmetic_side_effects.rs:305:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:279:5 + --> $DIR/arithmetic_side_effects.rs:306:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:280:5 + --> $DIR/arithmetic_side_effects.rs:307:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:281:5 + --> $DIR/arithmetic_side_effects.rs:308:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:282:5 + --> $DIR/arithmetic_side_effects.rs:309:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:285:10 + --> $DIR/arithmetic_side_effects.rs:312:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:286:10 + --> $DIR/arithmetic_side_effects.rs:313:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:287:10 + --> $DIR/arithmetic_side_effects.rs:314:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:288:10 + --> $DIR/arithmetic_side_effects.rs:315:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:289:10 + --> $DIR/arithmetic_side_effects.rs:316:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:290:10 + --> $DIR/arithmetic_side_effects.rs:317:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:291:10 + --> $DIR/arithmetic_side_effects.rs:318:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:292:10 + --> $DIR/arithmetic_side_effects.rs:319:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:293:10 + --> $DIR/arithmetic_side_effects.rs:320:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:294:10 + --> $DIR/arithmetic_side_effects.rs:321:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:295:10 + --> $DIR/arithmetic_side_effects.rs:322:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:296:10 + --> $DIR/arithmetic_side_effects.rs:323:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:297:10 + --> $DIR/arithmetic_side_effects.rs:324:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:298:10 + --> $DIR/arithmetic_side_effects.rs:325:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:299:10 + --> $DIR/arithmetic_side_effects.rs:326:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:300:10 + --> $DIR/arithmetic_side_effects.rs:327:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:301:10 + --> $DIR/arithmetic_side_effects.rs:328:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:302:10 + --> $DIR/arithmetic_side_effects.rs:329:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:303:10 + --> $DIR/arithmetic_side_effects.rs:330:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:304:15 + --> $DIR/arithmetic_side_effects.rs:331:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:305:15 + --> $DIR/arithmetic_side_effects.rs:332:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:306:15 + --> $DIR/arithmetic_side_effects.rs:333:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:307:15 + --> $DIR/arithmetic_side_effects.rs:334:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:308:15 + --> $DIR/arithmetic_side_effects.rs:335:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:309:15 + --> $DIR/arithmetic_side_effects.rs:336:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:310:15 + --> $DIR/arithmetic_side_effects.rs:337:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:311:15 + --> $DIR/arithmetic_side_effects.rs:338:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:312:15 + --> $DIR/arithmetic_side_effects.rs:339:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:313:15 + --> $DIR/arithmetic_side_effects.rs:340:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:314:15 + --> $DIR/arithmetic_side_effects.rs:341:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:315:15 + --> $DIR/arithmetic_side_effects.rs:342:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:316:15 + --> $DIR/arithmetic_side_effects.rs:343:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:317:15 + --> $DIR/arithmetic_side_effects.rs:344:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:318:15 + --> $DIR/arithmetic_side_effects.rs:345:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:319:15 + --> $DIR/arithmetic_side_effects.rs:346:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:320:15 + --> $DIR/arithmetic_side_effects.rs:347:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:321:15 + --> $DIR/arithmetic_side_effects.rs:348:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:322:15 + --> $DIR/arithmetic_side_effects.rs:349:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:325:10 + --> $DIR/arithmetic_side_effects.rs:352:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:326:10 + --> $DIR/arithmetic_side_effects.rs:353:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:327:15 + --> $DIR/arithmetic_side_effects.rs:354:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:328:15 + --> $DIR/arithmetic_side_effects.rs:355:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:337:5 + --> $DIR/arithmetic_side_effects.rs:364:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:338:5 + --> $DIR/arithmetic_side_effects.rs:365:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:340:5 + --> $DIR/arithmetic_side_effects.rs:367:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:341:5 + --> $DIR/arithmetic_side_effects.rs:368:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:342:5 + --> $DIR/arithmetic_side_effects.rs:369:5 | LL | i >> 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:343:5 + --> $DIR/arithmetic_side_effects.rs:370:5 | LL | i << 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:352:5 + --> $DIR/arithmetic_side_effects.rs:379:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:353:5 + --> $DIR/arithmetic_side_effects.rs:380:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:354:5 + --> $DIR/arithmetic_side_effects.rs:381:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:356:5 + --> $DIR/arithmetic_side_effects.rs:383:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:358:5 + --> $DIR/arithmetic_side_effects.rs:385:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:359:5 + --> $DIR/arithmetic_side_effects.rs:386:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:361:5 + --> $DIR/arithmetic_side_effects.rs:388:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:363:5 + --> $DIR/arithmetic_side_effects.rs:390:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:364:5 + --> $DIR/arithmetic_side_effects.rs:391:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:365:5 + --> $DIR/arithmetic_side_effects.rs:392:5 | LL | i <<= 3; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> $DIR/arithmetic_side_effects.rs:366:5 + --> $DIR/arithmetic_side_effects.rs:393:5 | LL | i >>= 2; | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/box_default.fixed b/src/tools/clippy/tests/ui/box_default.fixed index 7e9f074fdca..59c0baf8718 100644 --- a/src/tools/clippy/tests/ui/box_default.fixed +++ b/src/tools/clippy/tests/ui/box_default.fixed @@ -33,6 +33,7 @@ fn main() { let _vec4: Box<_> = Box::<Vec<bool>>::default(); let _more = ret_ty_fn(); call_ty_fn(Box::default()); + issue_10381(); } fn ret_ty_fn() -> Box<bool> { @@ -65,3 +66,20 @@ fn issue_10089() { let _ = Box::<WeirdPathed>::default(); }; } + +fn issue_10381() { + #[derive(Default)] + pub struct Foo {} + pub trait Bar {} + impl Bar for Foo {} + + fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> { + if i % 2 == 0 { + Some(Box::<Foo>::default()) + } else { + None + } + } + + assert!(maybe_get_bar(2).is_some()); +} diff --git a/src/tools/clippy/tests/ui/box_default.rs b/src/tools/clippy/tests/ui/box_default.rs index 5c8d0b8354c..f7d832193a3 100644 --- a/src/tools/clippy/tests/ui/box_default.rs +++ b/src/tools/clippy/tests/ui/box_default.rs @@ -33,6 +33,7 @@ fn main() { let _vec4: Box<_> = Box::new(Vec::from([false; 0])); let _more = ret_ty_fn(); call_ty_fn(Box::new(u8::default())); + issue_10381(); } fn ret_ty_fn() -> Box<bool> { @@ -65,3 +66,20 @@ fn issue_10089() { let _ = Box::new(WeirdPathed::default()); }; } + +fn issue_10381() { + #[derive(Default)] + pub struct Foo {} + pub trait Bar {} + impl Bar for Foo {} + + fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> { + if i % 2 == 0 { + Some(Box::new(Foo::default())) + } else { + None + } + } + + assert!(maybe_get_bar(2).is_some()); +} diff --git a/src/tools/clippy/tests/ui/box_default.stderr b/src/tools/clippy/tests/ui/box_default.stderr index 249eb340f96..78e17b9f035 100644 --- a/src/tools/clippy/tests/ui/box_default.stderr +++ b/src/tools/clippy/tests/ui/box_default.stderr @@ -73,22 +73,28 @@ LL | call_ty_fn(Box::new(u8::default())); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:39:5 + --> $DIR/box_default.rs:40:5 | LL | Box::new(bool::default()) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<bool>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:56:28 + --> $DIR/box_default.rs:57:28 | LL | let _: Box<dyn Read> = Box::new(ImplementsDefault::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()` error: `Box::new(_)` of default value - --> $DIR/box_default.rs:65:17 + --> $DIR/box_default.rs:66:17 | LL | let _ = Box::new(WeirdPathed::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<WeirdPathed>::default()` -error: aborting due to 15 previous errors +error: `Box::new(_)` of default value + --> $DIR/box_default.rs:78:18 + | +LL | Some(Box::new(Foo::default())) + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Foo>::default()` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/bytes_nth.fixed b/src/tools/clippy/tests/ui/bytes_nth.fixed index b1fb2e16bd5..a35c679afb7 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.fixed +++ b/src/tools/clippy/tests/ui/bytes_nth.fixed @@ -5,7 +5,7 @@ fn main() { let s = String::from("String"); - let _ = s.as_bytes().get(3); - let _ = &s.as_bytes().get(3); - let _ = s[..].as_bytes().get(3); + let _ = s.as_bytes().get(3).copied(); + let _ = &s.as_bytes()[3]; + let _ = s[..].as_bytes().get(3).copied(); } diff --git a/src/tools/clippy/tests/ui/bytes_nth.rs b/src/tools/clippy/tests/ui/bytes_nth.rs index 034c54e6a42..1ecffea5303 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.rs +++ b/src/tools/clippy/tests/ui/bytes_nth.rs @@ -6,6 +6,6 @@ fn main() { let s = String::from("String"); let _ = s.bytes().nth(3); - let _ = &s.bytes().nth(3); + let _ = &s.bytes().nth(3).unwrap(); let _ = s[..].bytes().nth(3); } diff --git a/src/tools/clippy/tests/ui/bytes_nth.stderr b/src/tools/clippy/tests/ui/bytes_nth.stderr index 9851d4791d8..e8b15027829 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.stderr +++ b/src/tools/clippy/tests/ui/bytes_nth.stderr @@ -2,21 +2,21 @@ error: called `.bytes().nth()` on a `String` --> $DIR/bytes_nth.rs:8:13 | LL | let _ = s.bytes().nth(3); - | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` + | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3).copied()` | = note: `-D clippy::bytes-nth` implied by `-D warnings` -error: called `.bytes().nth()` on a `String` +error: called `.bytes().nth().unwrap()` on a `String` --> $DIR/bytes_nth.rs:9:14 | -LL | let _ = &s.bytes().nth(3); - | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)` +LL | let _ = &s.bytes().nth(3).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.as_bytes()[3]` error: called `.bytes().nth()` on a `str` --> $DIR/bytes_nth.rs:10:13 | LL | let _ = s[..].bytes().nth(3); - | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3)` + | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3).copied()` error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr index 4af1de9aa38..451078de23b 100644 --- a/src/tools/clippy/tests/ui/cast.stderr +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -42,7 +42,7 @@ error: casting `f32` to `i32` may truncate the value LL | 1f32 as i32; | ^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` help: ... or use `try_from` and handle the error accordingly | @@ -55,7 +55,7 @@ error: casting `f32` to `u32` may truncate the value LL | 1f32 as u32; | ^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u32::try_from(1f32); @@ -75,7 +75,7 @@ error: casting `f64` to `f32` may truncate the value LL | 1f64 as f32; | ^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | f32::try_from(1f64); @@ -87,7 +87,7 @@ error: casting `i32` to `i8` may truncate the value LL | 1i32 as i8; | ^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | i8::try_from(1i32); @@ -99,7 +99,7 @@ error: casting `i32` to `u8` may truncate the value LL | 1i32 as u8; | ^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u8::try_from(1i32); @@ -111,7 +111,7 @@ error: casting `f64` to `isize` may truncate the value LL | 1f64 as isize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | isize::try_from(1f64); @@ -123,7 +123,7 @@ error: casting `f64` to `usize` may truncate the value LL | 1f64 as usize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | usize::try_from(1f64); @@ -141,7 +141,7 @@ error: casting `u32` to `u16` may truncate the value LL | 1f32 as u32 as u16; | ^^^^^^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u16::try_from(1f32 as u32); @@ -153,7 +153,7 @@ error: casting `f32` to `u32` may truncate the value LL | 1f32 as u32 as u16; | ^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u32::try_from(1f32) as u16; @@ -215,7 +215,7 @@ error: casting `i64` to `i8` may truncate the value LL | (-99999999999i64).min(1) as i8; // should be linted because signed | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | i8::try_from((-99999999999i64).min(1)); // should be linted because signed @@ -227,7 +227,7 @@ error: casting `u64` to `u8` may truncate the value LL | 999999u64.clamp(0, 256) as u8; // should still be linted | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted @@ -239,7 +239,7 @@ error: casting `main::E2` to `u8` may truncate the value LL | let _ = self as u8; | ^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let _ = u8::try_from(self); @@ -259,7 +259,7 @@ error: casting `main::E5` to `i8` may truncate the value LL | let _ = self as i8; | ^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let _ = i8::try_from(self); @@ -277,7 +277,7 @@ error: casting `main::E6` to `i16` may truncate the value LL | let _ = self as i16; | ^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let _ = i16::try_from(self); @@ -289,7 +289,7 @@ error: casting `main::E7` to `usize` may truncate the value on targets with 32-b LL | let _ = self as usize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let _ = usize::try_from(self); @@ -301,7 +301,7 @@ error: casting `main::E10` to `u16` may truncate the value LL | let _ = self as u16; | ^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let _ = u16::try_from(self); @@ -313,7 +313,7 @@ error: casting `u32` to `u8` may truncate the value LL | let c = (q >> 16) as u8; | ^^^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let c = u8::try_from((q >> 16)); @@ -325,7 +325,7 @@ error: casting `u32` to `u8` may truncate the value LL | let c = (q / 1000) as u8; | ^^^^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | let c = u8::try_from((q / 1000)); diff --git a/src/tools/clippy/tests/ui/cast_size.stderr b/src/tools/clippy/tests/ui/cast_size.stderr index 8acf26049f4..6d2d49d9ed2 100644 --- a/src/tools/clippy/tests/ui/cast_size.stderr +++ b/src/tools/clippy/tests/ui/cast_size.stderr @@ -4,7 +4,7 @@ error: casting `isize` to `i8` may truncate the value LL | 1isize as i8; | ^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... = note: `-D clippy::cast-possible-truncation` implied by `-D warnings` help: ... or use `try_from` and handle the error accordingly | @@ -43,7 +43,7 @@ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wi LL | 1isize as i32; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | i32::try_from(1isize); @@ -55,7 +55,7 @@ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wi LL | 1isize as u32; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u32::try_from(1isize); @@ -67,7 +67,7 @@ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wi LL | 1usize as u32; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | u32::try_from(1usize); @@ -79,7 +79,7 @@ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wi LL | 1usize as i32; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | i32::try_from(1usize); @@ -99,7 +99,7 @@ error: casting `i64` to `isize` may truncate the value on targets with 32-bit wi LL | 1i64 as isize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | isize::try_from(1i64); @@ -111,7 +111,7 @@ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wi LL | 1i64 as usize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | usize::try_from(1i64); @@ -123,7 +123,7 @@ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wi LL | 1u64 as isize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | isize::try_from(1u64); @@ -141,7 +141,7 @@ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wi LL | 1u64 as usize; | ^^^^^^^^^^^^^ | - = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ... + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... help: ... or use `try_from` and handle the error accordingly | LL | usize::try_from(1u64); diff --git a/src/tools/clippy/tests/ui/crashes/ice-10044.rs b/src/tools/clippy/tests/ui/crashes/ice-10044.rs deleted file mode 100644 index 65f38fe7118..00000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-10044.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - [0; usize::MAX]; -} diff --git a/src/tools/clippy/tests/ui/crashes/ice-10044.stderr b/src/tools/clippy/tests/ui/crashes/ice-10044.stderr deleted file mode 100644 index 731f8265ad6..00000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-10044.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: statement with no effect - --> $DIR/ice-10044.rs:2:5 - | -LL | [0; usize::MAX]; - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::no-effect` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.rs b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.rs new file mode 100644 index 00000000000..dd3d8b8b6d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.rs @@ -0,0 +1,9 @@ +// https://github.com/rust-lang/rust/issues/107147 + +#![warn(clippy::needless_pass_by_value)] + +struct Foo<'a>(&'a [(); 100]); + +fn test(x: Foo<'_>) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr new file mode 100644 index 00000000000..7a0a648974f --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr @@ -0,0 +1,15 @@ +error: this argument is passed by value, but not consumed in the function body + --> $DIR/needless_pass_by_value-w-late-bound.rs:7:12 + | +LL | fn test(x: Foo<'_>) {} + | ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>` + | +help: consider marking this type as `Copy` + --> $DIR/needless_pass_by_value-w-late-bound.rs:5:1 + | +LL | struct Foo<'a>(&'a [(); 100]); + | ^^^^^^^^^^^^^^ + = note: `-D clippy::needless-pass-by-value` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed index 747801b40ee..ecb0bf3644e 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed @@ -78,7 +78,7 @@ fn test_allowed() { /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823. /// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) /// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. -/// It can also be [`inline_link2`]. +/// It can also be [inline_link2]. A link to [StackOverflow](https://stackoverflow.com) is also acceptable. /// /// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example /// [inline_link]: https://foobar diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs index f3cf966157a..11c48dd103d 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs @@ -75,10 +75,10 @@ fn test_units() { fn test_allowed() { } -/// This test has [a link_with_underscores][chunked-example] inside it. See #823. +/// This test has [a `link_with_underscores`][chunked-example] inside it. See #823. /// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) /// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. -/// It can also be [inline_link2]. +/// It can also be [inline_link2]. A link to [StackOverflow](https://stackoverflow.com) is also acceptable. /// /// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example /// [inline_link]: https://foobar diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr index 40345370c04..6c67c903c75 100644 --- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr +++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr @@ -143,28 +143,6 @@ LL | /// `be_sure_we_got_to_the_end_of_it` | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: item in documentation is missing backticks - --> $DIR/doc-fixable.rs:78:22 - | -LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. - | ^^^^^^^^^^^^^^^^^^^^^ - | -help: try - | -LL | /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823. - | ~~~~~~~~~~~~~~~~~~~~~~~ - -error: item in documentation is missing backticks - --> $DIR/doc-fixable.rs:81:21 - | -LL | /// It can also be [inline_link2]. - | ^^^^^^^^^^^^ - | -help: try - | -LL | /// It can also be [`inline_link2`]. - | ~~~~~~~~~~~~~~ - -error: item in documentation is missing backticks --> $DIR/doc-fixable.rs:91:5 | LL | /// be_sure_we_got_to_the_end_of_it @@ -329,5 +307,5 @@ help: try LL | /// An iterator over `mycrate::Collection`'s values. | ~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 30 previous errors +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed index 79c29c04e05..dbe09e0ff3c 100644 --- a/src/tools/clippy/tests/ui/entry.fixed +++ b/src/tools/clippy/tests/ui/entry.fixed @@ -152,4 +152,18 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMa }); } +// Issue 10331 +// do not suggest a bad expansion because the compiler unrolls the first +// occurrence of the loop +pub fn issue_10331() { + let mut m = HashMap::new(); + let mut i = 0; + let mut x = 0; + while !m.contains_key(&x) { + m.insert(x, i); + i += 1; + x += 1; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs index 2d7985457d8..30fed34fc5d 100644 --- a/src/tools/clippy/tests/ui/entry.rs +++ b/src/tools/clippy/tests/ui/entry.rs @@ -156,4 +156,18 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMa } } +// Issue 10331 +// do not suggest a bad expansion because the compiler unrolls the first +// occurrence of the loop +pub fn issue_10331() { + let mut m = HashMap::new(); + let mut i = 0; + let mut x = 0; + while !m.contains_key(&x) { + m.insert(x, i); + i += 1; + x += 1; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed index 475fae5e823..5d40c850424 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed @@ -269,6 +269,9 @@ fn main() { trait WithAssoc { type Assoc: ?Sized; + fn to_assoc(&self) -> &Self::Assoc { + panic!() + } } impl WithAssoc for String { type Assoc = str; @@ -281,4 +284,15 @@ fn main() { // Issue #9901 fn takes_ref(_: &i32) {} takes_ref(*Box::new(&0i32)); + + // Issue #10384 + impl<'a> WithAssoc for &'a u32 { + type Assoc = dyn core::fmt::Display; + fn to_assoc(&self) -> &Self::Assoc { + *self + } + } + fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc { + *x + } } diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs index c1894258f4d..79e03f4d76c 100644 --- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs +++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs @@ -269,6 +269,9 @@ fn main() { trait WithAssoc { type Assoc: ?Sized; + fn to_assoc(&self) -> &Self::Assoc { + panic!() + } } impl WithAssoc for String { type Assoc = str; @@ -281,4 +284,15 @@ fn main() { // Issue #9901 fn takes_ref(_: &i32) {} takes_ref(*Box::new(&0i32)); + + // Issue #10384 + impl<'a> WithAssoc for &'a u32 { + type Assoc = dyn core::fmt::Display; + fn to_assoc(&self) -> &Self::Assoc { + *self + } + } + fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc { + *x + } } diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs index 5cb80cb6233..48017434276 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs @@ -1,11 +1,17 @@ #![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::extra_unused_type_parameters)] -fn unused_ty<T>(x: u8) {} +fn unused_ty<T>(x: u8) { + unimplemented!() +} -fn unused_multi<T, U>(x: u8) {} +fn unused_multi<T, U>(x: u8) { + unimplemented!() +} -fn unused_with_lt<'a, T>(x: &'a u8) {} +fn unused_with_lt<'a, T>(x: &'a u8) { + unimplemented!() +} fn used_ty<T>(x: T, y: u8) {} @@ -15,15 +21,20 @@ fn used_ret<T: Default>(x: u8) -> T { T::default() } -fn unused_bounded<T: Default, U>(x: U) {} +fn unused_bounded<T: Default, U>(x: U) { + unimplemented!(); +} fn unused_where_clause<T, U>(x: U) where T: Default, { + unimplemented!(); } -fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {} +fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) { + unimplemented!(); +} fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize { iter.count() @@ -46,7 +57,9 @@ fn used_closure<T: Default + ToString>() -> impl Fn() { struct S; impl S { - fn unused_ty_impl<T>(&self) {} + fn unused_ty_impl<T>(&self) { + unimplemented!() + } } // Don't lint on trait methods @@ -66,4 +79,32 @@ where .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) } +fn unused_opaque<A, B>(dummy: impl Default) { + unimplemented!() +} + +mod unexported_trait_bounds { + mod private { + pub trait Private {} + } + + fn priv_trait_bound<T: private::Private>() { + unimplemented!(); + } + + fn unused_with_priv_trait_bound<T: private::Private, U>() { + unimplemented!(); + } +} + +mod issue10319 { + fn assert_send<T: Send>() {} + + fn assert_send_where<T>() + where + T: Send, + { + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr index 1c8dd53e638..86c88fc9bf0 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr @@ -1,38 +1,38 @@ error: type parameter goes unused in function definition --> $DIR/extra_unused_type_parameters.rs:4:13 | -LL | fn unused_ty<T>(x: u8) {} +LL | fn unused_ty<T>(x: u8) { | ^^^ | = help: consider removing the parameter = note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings` error: type parameters go unused in function definition - --> $DIR/extra_unused_type_parameters.rs:6:16 + --> $DIR/extra_unused_type_parameters.rs:8:16 | -LL | fn unused_multi<T, U>(x: u8) {} +LL | fn unused_multi<T, U>(x: u8) { | ^^^^^^ | = help: consider removing the parameters error: type parameter goes unused in function definition - --> $DIR/extra_unused_type_parameters.rs:8:23 + --> $DIR/extra_unused_type_parameters.rs:12:23 | -LL | fn unused_with_lt<'a, T>(x: &'a u8) {} +LL | fn unused_with_lt<'a, T>(x: &'a u8) { | ^ | = help: consider removing the parameter error: type parameter goes unused in function definition - --> $DIR/extra_unused_type_parameters.rs:18:19 + --> $DIR/extra_unused_type_parameters.rs:24:19 | -LL | fn unused_bounded<T: Default, U>(x: U) {} +LL | fn unused_bounded<T: Default, U>(x: U) { | ^^^^^^^^^^^ | = help: consider removing the parameter error: type parameter goes unused in function definition - --> $DIR/extra_unused_type_parameters.rs:20:24 + --> $DIR/extra_unused_type_parameters.rs:28:24 | LL | fn unused_where_clause<T, U>(x: U) | ^^ @@ -40,20 +40,36 @@ LL | fn unused_where_clause<T, U>(x: U) = help: consider removing the parameter error: type parameters go unused in function definition - --> $DIR/extra_unused_type_parameters.rs:26:16 + --> $DIR/extra_unused_type_parameters.rs:35:16 | -LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {} +LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) { | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ | = help: consider removing the parameters error: type parameter goes unused in function definition - --> $DIR/extra_unused_type_parameters.rs:49:22 + --> $DIR/extra_unused_type_parameters.rs:60:22 | -LL | fn unused_ty_impl<T>(&self) {} +LL | fn unused_ty_impl<T>(&self) { | ^^^ | = help: consider removing the parameter -error: aborting due to 7 previous errors +error: type parameters go unused in function definition + --> $DIR/extra_unused_type_parameters.rs:82:17 + | +LL | fn unused_opaque<A, B>(dummy: impl Default) { + | ^^^^^^ + | + = help: consider removing the parameters + +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:95:58 + | +LL | fn unused_with_priv_trait_bound<T: private::Private, U>() { + | ^ + | + = help: consider removing the parameter + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed index beedf2c1db2..cd2f70ee8b0 100644 --- a/src/tools/clippy/tests/ui/format.fixed +++ b/src/tools/clippy/tests/ui/format.fixed @@ -1,4 +1,5 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -9,6 +10,8 @@ clippy::uninlined_format_args )] +extern crate proc_macro_with_span; + struct Foo(pub String); macro_rules! foo { @@ -87,4 +90,7 @@ fn main() { let _ = abc.to_string(); let xx = "xx"; let _ = xx.to_string(); + + // Issue #10148 + println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs index e805f181889..c22345a79d4 100644 --- a/src/tools/clippy/tests/ui/format.rs +++ b/src/tools/clippy/tests/ui/format.rs @@ -1,4 +1,5 @@ // run-rustfix +// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -9,6 +10,8 @@ clippy::uninlined_format_args )] +extern crate proc_macro_with_span; + struct Foo(pub String); macro_rules! foo { @@ -89,4 +92,7 @@ fn main() { let _ = format!("{abc}"); let xx = "xx"; let _ = format!("{xx}"); + + // Issue #10148 + println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/src/tools/clippy/tests/ui/format.stderr b/src/tools/clippy/tests/ui/format.stderr index 0ef0ac655d3..a0e5d5c8ad2 100644 --- a/src/tools/clippy/tests/ui/format.stderr +++ b/src/tools/clippy/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> $DIR/format.rs:19:5 + --> $DIR/format.rs:22:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -7,19 +7,19 @@ LL | format!("foo"); = note: `-D clippy::useless-format` implied by `-D warnings` error: useless use of `format!` - --> $DIR/format.rs:20:5 + --> $DIR/format.rs:23:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:21:5 + --> $DIR/format.rs:24:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:22:5 + --> $DIR/format.rs:25:5 | LL | / format!( LL | | r##"foo {{}} @@ -34,67 +34,67 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> $DIR/format.rs:27:13 + --> $DIR/format.rs:30:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> $DIR/format.rs:29:5 + --> $DIR/format.rs:32:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:37:5 + --> $DIR/format.rs:40:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:67:5 + --> $DIR/format.rs:70:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> $DIR/format.rs:69:5 + --> $DIR/format.rs:72:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> $DIR/format.rs:73:18 + --> $DIR/format.rs:76:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> $DIR/format.rs:77:22 + --> $DIR/format.rs:80:22 | LL | let _s: String = format!("{}", &*v.join("/n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` error: useless use of `format!` - --> $DIR/format.rs:83:13 + --> $DIR/format.rs:86:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:85:13 + --> $DIR/format.rs:88:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:89:13 + --> $DIR/format.rs:92:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> $DIR/format.rs:91:13 + --> $DIR/format.rs:94:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()` diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.rs b/src/tools/clippy/tests/ui/impl_trait_in_params.rs new file mode 100644 index 00000000000..07560101a41 --- /dev/null +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.rs @@ -0,0 +1,17 @@ +#![allow(unused)] +#![warn(clippy::impl_trait_in_params)] + +pub trait Trait {} +pub trait AnotherTrait<T> {} + +// Should warn +pub fn a(_: impl Trait) {} +pub fn c<C: Trait>(_: C, _: impl Trait) {} +fn d(_: impl AnotherTrait<u32>) {} + +// Shouldn't warn + +pub fn b<B: Trait>(_: B) {} +fn e<T: AnotherTrait<u32>>(_: T) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/impl_trait_in_params.stderr b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr new file mode 100644 index 00000000000..acfcc21445e --- /dev/null +++ b/src/tools/clippy/tests/ui/impl_trait_in_params.stderr @@ -0,0 +1,25 @@ +error: '`impl Trait` used as a function parameter' + --> $DIR/impl_trait_in_params.rs:8:13 + | +LL | pub fn a(_: impl Trait) {} + | ^^^^^^^^^^ + | + = note: `-D clippy::impl-trait-in-params` implied by `-D warnings` +help: add a type paremeter + | +LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {} + | +++++++++++++++++++++++++++++++ + +error: '`impl Trait` used as a function parameter' + --> $DIR/impl_trait_in_params.rs:9:29 + | +LL | pub fn c<C: Trait>(_: C, _: impl Trait) {} + | ^^^^^^^^^^ + | +help: add a type paremeter + | +LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {} + | +++++++++++++++++++++++++++++++ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/large_digit_groups.fixed b/src/tools/clippy/tests/ui/large_digit_groups.fixed index 3430c137ec2..ea18dac0683 100644 --- a/src/tools/clippy/tests/ui/large_digit_groups.fixed +++ b/src/tools/clippy/tests/ui/large_digit_groups.fixed @@ -11,7 +11,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x0123_4567, + 0x1_234_567, 1_2345_6789, 1234_f32, 1_234.12_f32, @@ -19,7 +19,7 @@ fn main() { 1.123_4_f32, ); let _bad = ( - 0b11_0110_i64, + 0b1_10110_i64, 0xdead_beef_usize, 123_456_f32, 123_456.12_f32, diff --git a/src/tools/clippy/tests/ui/large_digit_groups.stderr b/src/tools/clippy/tests/ui/large_digit_groups.stderr index 13d108b56e0..19c0fae98a6 100644 --- a/src/tools/clippy/tests/ui/large_digit_groups.stderr +++ b/src/tools/clippy/tests/ui/large_digit_groups.stderr @@ -1,22 +1,10 @@ -error: digits of hex or binary literal not grouped by four - --> $DIR/large_digit_groups.rs:14:9 - | -LL | 0x1_234_567, - | ^^^^^^^^^^^ help: consider: `0x0123_4567` - | - = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` - -error: digits of hex or binary literal not grouped by four - --> $DIR/large_digit_groups.rs:22:9 - | -LL | 0b1_10110_i64, - | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64` - -error: digits of hex or binary literal not grouped by four +error: digits of hex, binary or octal literal not in groups of equal size --> $DIR/large_digit_groups.rs:23:9 | LL | 0xd_e_adbee_f_usize, | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` + | + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:24:9 @@ -44,5 +32,5 @@ error: digit groups should be smaller LL | 1_23456.12345_6_f64, | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs index 6790765f803..99787ffd3d3 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.rs +++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs @@ -24,6 +24,7 @@ fn main() { [S { data: [0; 32] }; 5000], [Some(""); 20_000_000], [E::T(0); 5000], + [0u8; usize::MAX], ); let good = ( diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr index c7bf941ad00..24e90094982 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.stderr +++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr @@ -31,5 +31,13 @@ LL | [E::T(0); 5000], | = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` -error: aborting due to 4 previous errors +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:27:9 + | +LL | [0u8; usize::MAX], + | ^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.rs b/src/tools/clippy/tests/ui/let_underscore_untyped.rs new file mode 100644 index 00000000000..bcb33c5c7e3 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.rs @@ -0,0 +1,54 @@ +#![allow(unused)] +#![warn(clippy::let_underscore_untyped)] + +use std::future::Future; +use std::{boxed::Box, fmt::Display}; + +fn a() -> u32 { + 1 +} + +fn b<T>(x: T) -> T { + x +} + +fn c() -> impl Display { + 1 +} + +fn d(x: &u32) -> &u32 { + x +} + +fn e() -> Result<u32, ()> { + Ok(1) +} + +fn f() -> Box<dyn Display> { + Box::new(1) +} + +fn main() { + let _ = a(); + let _ = b(1); + let _ = c(); + let _ = d(&1); + let _ = e(); + let _ = f(); + + _ = a(); + _ = b(1); + _ = c(); + _ = d(&1); + _ = e(); + _ = f(); + + let _: u32 = a(); + let _: u32 = b(1); + let _: &u32 = d(&1); + let _: Result<_, _> = e(); + let _: Box<_> = f(); + + #[allow(clippy::let_underscore_untyped)] + let _ = a(); +} diff --git a/src/tools/clippy/tests/ui/let_underscore_untyped.stderr b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr new file mode 100644 index 00000000000..36c3d1214d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/let_underscore_untyped.stderr @@ -0,0 +1,51 @@ +error: non-binding `let` without a type annotation + --> $DIR/let_underscore_untyped.rs:32:5 + | +LL | let _ = a(); + | ^^^^^^^^^^^^ + | + = help: consider adding a type annotation or removing the `let` keyword + = note: `-D clippy::let-underscore-untyped` implied by `-D warnings` + +error: non-binding `let` without a type annotation + --> $DIR/let_underscore_untyped.rs:33:5 + | +LL | let _ = b(1); + | ^^^^^^^^^^^^^ + | + = help: consider adding a type annotation or removing the `let` keyword + +error: non-binding `let` without a type annotation + --> $DIR/let_underscore_untyped.rs:34:5 + | +LL | let _ = c(); + | ^^^^^^^^^^^^ + | + = help: consider adding a type annotation or removing the `let` keyword + +error: non-binding `let` without a type annotation + --> $DIR/let_underscore_untyped.rs:35:5 + | +LL | let _ = d(&1); + | ^^^^^^^^^^^^^^ + | + = help: consider adding a type annotation or removing the `let` keyword + +error: non-binding `let` without a type annotation + --> $DIR/let_underscore_untyped.rs:36:5 + | +LL | let _ = e(); + | ^^^^^^^^^^^^ + | + = help: consider adding a type annotation or removing the `let` keyword + +error: non-binding `let` without a type annotation + --> $DIR/let_underscore_untyped.rs:37:5 + | +LL | let _ = f(); + | ^^^^^^^^^^^^ + | + = help: consider adding a type annotation or removing the `let` keyword + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr index 603d47bacca..9bc7948c7cc 100644 --- a/src/tools/clippy/tests/ui/literals.stderr +++ b/src/tools/clippy/tests/ui/literals.stderr @@ -121,7 +121,7 @@ error: digits grouped inconsistently by underscores LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` -error: digits of hex or binary literal not grouped by four +error: digits of hex, binary or octal literal not in groups of equal size --> $DIR/literals.rs:38:18 | LL | let fail24 = 0xAB_ABC_AB; @@ -129,12 +129,6 @@ LL | let fail24 = 0xAB_ABC_AB; | = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` -error: digits of hex or binary literal not grouped by four - --> $DIR/literals.rs:39:18 - | -LL | let fail25 = 0b01_100_101; - | ^^^^^^^^^^^^ help: consider: `0b0110_0101` - error: this is a decimal constant --> $DIR/literals.rs:46:13 | @@ -168,5 +162,5 @@ help: if you mean to use a decimal constant, remove the `0` to avoid confusion LL | let _ = 89; | ~~ -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index 48a162c1360..d175597a44a 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -248,4 +248,15 @@ fn not_fire() { Some(value) => value, _ => macro_call!(), }; + + // Issue 10296 + // The let/else block in the else part is not divergent despite the presence of return + let _x = if let Some(x) = Some(1) { + x + } else { + let Some(_z) = Some(3) else { + return + }; + 1 + }; } diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs index 28caed9d79d..73b74679125 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.rs +++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs @@ -42,13 +42,13 @@ fn fire() { loop { // More complex pattern for the identity arm and diverging arm let v = match h() { - (Some(_), Some(_)) | (None, None) => continue, (Some(v), None) | (None, Some(v)) => v, + (Some(_), Some(_)) | (None, None) => continue, }; // Custom enums are supported as long as the "else" arm is a simple _ let v = match build_enum() { - _ => continue, Variant::Bar(v) | Variant::Baz(v) => v, + _ => continue, }; } @@ -71,6 +71,12 @@ fn fire() { Variant::Bar(_) | Variant::Baz(_) => (), _ => return, }; + + let data = [1_u8, 2, 3, 4, 0, 0, 0, 0]; + let data = match data.as_slice() { + [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0] => data, + _ => return, + }; } fn not_fire() { @@ -125,4 +131,23 @@ fn not_fire() { Ok(v) | Err(Variant::Bar(v) | Variant::Baz(v)) => v, Err(Variant::Foo) => return, }; + + // Issue 10241 + // The non-divergent arm arrives in second position and + // may cover values already matched in the first arm. + let v = match h() { + (Some(_), Some(_)) | (None, None) => return, + (Some(v), _) | (None, Some(v)) => v, + }; + + let v = match build_enum() { + _ => return, + Variant::Bar(v) | Variant::Baz(v) => v, + }; + + let data = [1_u8, 2, 3, 4, 0, 0, 0, 0]; + let data = match data.as_slice() { + [] | [0, 0] => return, + [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data, + }; } diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index cd5e9a9ac39..7abaa0b85d2 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -22,8 +22,8 @@ error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:44:9 | LL | / let v = match h() { -LL | | (Some(_), Some(_)) | (None, None) => continue, LL | | (Some(v), None) | (None, Some(v)) => v, +LL | | (Some(_), Some(_)) | (None, None) => continue, LL | | }; | |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };` @@ -31,8 +31,8 @@ error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:49:9 | LL | / let v = match build_enum() { -LL | | _ => continue, LL | | Variant::Bar(v) | Variant::Baz(v) => v, +LL | | _ => continue, LL | | }; | |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };` @@ -63,5 +63,14 @@ LL | | _ => return, LL | | }; | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` -error: aborting due to 7 previous errors +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else_match.rs:76:5 + | +LL | / let data = match data.as_slice() { +LL | | [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0] => data, +LL | | _ => return, +LL | | }; + | |______^ help: consider writing: `let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return };` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed index 53628ef6531..8e2f11389f8 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.fixed +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_untyped)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::redundant_closure)] diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.rs b/src/tools/clippy/tests/ui/map_flatten_fixable.rs index 76016c8ed3c..a783a99c4ff 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.rs +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_untyped)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] #![allow(clippy::redundant_closure)] diff --git a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr index b6b0c4d09c3..c91f0b9ae94 100644 --- a/src/tools/clippy/tests/ui/map_flatten_fixable.stderr +++ b/src/tools/clippy/tests/ui/map_flatten_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:17:47 + --> $DIR/map_flatten_fixable.rs:18:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)` @@ -7,43 +7,43 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:18:47 + --> $DIR/map_flatten_fixable.rs:19:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:19:47 + --> $DIR/map_flatten_fixable.rs:20:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:20:47 + --> $DIR/map_flatten_fixable.rs:21:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:23:47 + --> $DIR/map_flatten_fixable.rs:24:47 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)` error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:26:40 + --> $DIR/map_flatten_fixable.rs:27:40 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Result` - --> $DIR/map_flatten_fixable.rs:29:42 + --> $DIR/map_flatten_fixable.rs:30:42 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)` error: called `map(..).flatten()` on `Iterator` - --> $DIR/map_flatten_fixable.rs:38:10 + --> $DIR/map_flatten_fixable.rs:39:10 | LL | .map(|n| match n { | __________^ @@ -72,7 +72,7 @@ LL ~ }); | error: called `map(..).flatten()` on `Option` - --> $DIR/map_flatten_fixable.rs:58:10 + --> $DIR/map_flatten_fixable.rs:59:10 | LL | .map(|_| { | __________^ diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index 6f22366eab2..1519e4da934 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -4,6 +4,7 @@ #![allow( clippy::disallowed_names, clippy::default_trait_access, + clippy::let_underscore_untyped, clippy::missing_docs_in_private_items, clippy::missing_safety_doc, clippy::non_ascii_literal, diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index b63672dd6fd..4643e09e270 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/methods.rs:104:5 + --> $DIR/methods.rs:105:5 | LL | / fn new() -> i32 { LL | | 0 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> $DIR/methods.rs:125:13 + --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index 04a74a009e0..bbbb3cf621e 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -84,7 +84,7 @@ pub unsafe fn mutates_static() -> usize { } #[no_mangle] -pub fn unmangled(i: bool) -> bool { +pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index f04122f4eea..94d3c83bdb9 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -84,7 +84,7 @@ pub unsafe fn mutates_static() -> usize { } #[no_mangle] -pub fn unmangled(i: bool) -> bool { +pub extern "C" fn unmangled(i: bool) -> bool { !i } diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index d286ef4ba37..f0f1f9298ac 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -516,6 +516,16 @@ mod in_macro { // no lint on external macro macro_rules::needless_lifetime!(); + + macro_rules! expanded_lifetime { + ($l:lifetime) => { + fn f<$l>(arg: &$l str) -> &$l str { + arg + } + } + } + + expanded_lifetime!('a); } mod issue5787 { diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 409528b291d..ddfd1043003 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -516,6 +516,16 @@ mod in_macro { // no lint on external macro macro_rules::needless_lifetime!(); + + macro_rules! expanded_lifetime { + ($l:lifetime) => { + fn f<$l>(arg: &$l str) -> &$l str { + arg + } + } + } + + expanded_lifetime!('a); } mod issue5787 { diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index 079e3531def..0f525dd294c 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -297,4 +297,14 @@ fn issue10051() -> Result<String, String> { } } +mod issue10049 { + fn single() -> u32 { + if true { 1 } else { 2 } + } + + fn multiple(b1: bool, b2: bool, b3: bool) -> u32 { + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }) + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index c1c48284f08..a1db8375d95 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -307,4 +307,14 @@ fn issue10051() -> Result<String, String> { } } +mod issue10049 { + fn single() -> u32 { + return if true { 1 } else { 2 }; + } + + fn multiple(b1: bool, b2: bool, b3: bool) -> u32 { + return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 08b04bfe9d8..87d0cd3e14c 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -418,5 +418,21 @@ LL | return Err(format!("err!")); | = help: remove `return` -error: aborting due to 50 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:312:9 + | +LL | return if true { 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:316:9 + | +LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `return` and wrap the sequence with parentheses + +error: aborting due to 52 previous errors diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs index 28e8f459d44..29821ff96fc 100644 --- a/src/tools/clippy/tests/ui/never_loop.rs +++ b/src/tools/clippy/tests/ui/never_loop.rs @@ -250,6 +250,51 @@ pub fn test20() { } } +pub fn test21() { + loop { + 'a: { + {} + break 'a; + } + } +} + +// Issue 10304: code after break from block was not considered +// unreachable code and was considered for further analysis of +// whether the loop would ever be executed or not. +pub fn test22() { + for _ in 0..10 { + 'block: { + break 'block; + return; + } + println!("looped"); + } +} + +pub fn test23() { + for _ in 0..10 { + 'block: { + for _ in 0..20 { + break 'block; + } + } + println!("looped"); + } +} + +pub fn test24() { + 'a: for _ in 0..10 { + 'b: { + let x = Some(1); + match x { + None => break 'a, + Some(_) => break 'b, + } + } + } +} + fn main() { test1(); test2(); diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr index b7029bf8bed..704d448644e 100644 --- a/src/tools/clippy/tests/ui/never_loop.stderr +++ b/src/tools/clippy/tests/ui/never_loop.stderr @@ -126,5 +126,18 @@ LL | | } LL | | } | |_____^ -error: aborting due to 11 previous errors +error: this loop never actually loops + --> $DIR/never_loop.rs:278:13 + | +LL | / for _ in 0..20 { +LL | | break 'block; +LL | | } + | |_____________^ + | +help: if you need the first element of the iterator, try writing + | +LL | if let Some(_) = (0..20).next() { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed new file mode 100644 index 00000000000..d18dec22a8b --- /dev/null +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.fixed @@ -0,0 +1,48 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::no_mangle_with_rust_abi)] + +#[no_mangle] +extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + arg_one: u32, + arg_two: usize, +) -> u32 { + 0 +} + +// Must not run on functions that explicitly opt in to Rust ABI with `extern "Rust"` +#[no_mangle] +#[rustfmt::skip] +extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} + +fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} + +extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} + +extern "C" { + fn c_abi_in_block(arg_one: u32, arg_two: usize); +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs new file mode 100644 index 00000000000..481e1b6d961 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs @@ -0,0 +1,48 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::no_mangle_with_rust_abi)] + +#[no_mangle] +fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + +/// # Safety +/// This function shouldn't be called unless the horsemen are ready +#[no_mangle] +unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( + arg_one: u32, + arg_two: usize, +) -> u32 { + 0 +} + +// Must not run on functions that explicitly opt in to Rust ABI with `extern "Rust"` +#[no_mangle] +#[rustfmt::skip] +extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {} + +fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {} + +#[no_mangle] +extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {} + +extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {} + +extern "C" { + fn c_abi_in_block(arg_one: u32, arg_two: usize); +} + +fn main() { + // test code goes here +} diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr new file mode 100644 index 00000000000..71517d31809 --- /dev/null +++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr @@ -0,0 +1,45 @@ +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:7:1 + | +LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize)` + | + = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings` + +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:10:1 + | +LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize)` + +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:15:1 + | +LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize)` + +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:20:1 + | +LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize)` + +error: attribute #[no_mangle] set on a Rust ABI function + --> $DIR/no_mangle_with_rust_abi.rs:23:1 + | +LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( +LL | | arg_one: u32, +LL | | arg_two: usize, +LL | | ) -> u32 { + | |________^ + | +help: try + | +LL + extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines( +LL + arg_one: u32, +LL + arg_two: usize, +LL ~ ) -> u32 { + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/question_mark_used.rs b/src/tools/clippy/tests/ui/question_mark_used.rs new file mode 100644 index 00000000000..8c3ef789697 --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark_used.rs @@ -0,0 +1,15 @@ +// non rustfixable +#![allow(unreachable_code)] +#![allow(dead_code)] +#![warn(clippy::question_mark_used)] + +fn other_function() -> Option<i32> { + Some(32) +} + +fn my_function() -> Option<i32> { + other_function()?; + None +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/question_mark_used.stderr b/src/tools/clippy/tests/ui/question_mark_used.stderr new file mode 100644 index 00000000000..8b5fcbcdbfd --- /dev/null +++ b/src/tools/clippy/tests/ui/question_mark_used.stderr @@ -0,0 +1,11 @@ +error: question mark operator was used + --> $DIR/question_mark_used.rs:11:5 + | +LL | other_function()?; + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using a custom macro or match expression + = note: `-D clippy::question-mark-used` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.fixed b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed new file mode 100644 index 00000000000..da998c610bd --- /dev/null +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.fixed @@ -0,0 +1,84 @@ +// run-rustfix + +#![warn(clippy::significant_drop_tightening)] + +use std::sync::Mutex; + +pub fn complex_return_triggers_the_lint() -> i32 { + fn foo() -> i32 { + 1 + } + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let _ = *lock; + let _ = *lock; + drop(lock); + foo() +} + +pub fn path_return_can_be_ignored() -> i32 { + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let rslt = *lock; + let _ = *lock; + rslt +} + +pub fn post_bindings_can_be_ignored() { + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let rslt = *lock; + let another = rslt; + let _ = another; +} + +pub fn unnecessary_contention_with_multiple_owned_results() { + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + let _ = lock.is_positive(); + } + + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let rslt0 = lock.abs(); + let rslt1 = lock.is_positive(); + drop(lock); + do_heavy_computation_that_takes_time((rslt0, rslt1)); + } +} + +pub fn unnecessary_contention_with_single_owned_results() { + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + } + { + let mutex = Mutex::new(vec![1i32]); + let mut lock = mutex.lock().unwrap(); + lock.clear(); + } + + { + let mutex = Mutex::new(1i32); + + let rslt0 = mutex.lock().unwrap().abs(); + + do_heavy_computation_that_takes_time(rslt0); + } + { + let mutex = Mutex::new(vec![1i32]); + + mutex.lock().unwrap().clear(); + + do_heavy_computation_that_takes_time(()); + } +} + +// Marker used for illustration purposes. +pub fn do_heavy_computation_that_takes_time<T>(_: T) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.rs b/src/tools/clippy/tests/ui/significant_drop_tightening.rs new file mode 100644 index 00000000000..83823f95f68 --- /dev/null +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.rs @@ -0,0 +1,80 @@ +// run-rustfix + +#![warn(clippy::significant_drop_tightening)] + +use std::sync::Mutex; + +pub fn complex_return_triggers_the_lint() -> i32 { + fn foo() -> i32 { + 1 + } + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let _ = *lock; + let _ = *lock; + foo() +} + +pub fn path_return_can_be_ignored() -> i32 { + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let rslt = *lock; + let _ = *lock; + rslt +} + +pub fn post_bindings_can_be_ignored() { + let mutex = Mutex::new(1); + let lock = mutex.lock().unwrap(); + let rslt = *lock; + let another = rslt; + let _ = another; +} + +pub fn unnecessary_contention_with_multiple_owned_results() { + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + let _ = lock.is_positive(); + } + + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let rslt0 = lock.abs(); + let rslt1 = lock.is_positive(); + do_heavy_computation_that_takes_time((rslt0, rslt1)); + } +} + +pub fn unnecessary_contention_with_single_owned_results() { + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let _ = lock.abs(); + } + { + let mutex = Mutex::new(vec![1i32]); + let mut lock = mutex.lock().unwrap(); + lock.clear(); + } + + { + let mutex = Mutex::new(1i32); + let lock = mutex.lock().unwrap(); + let rslt0 = lock.abs(); + do_heavy_computation_that_takes_time(rslt0); + } + { + let mutex = Mutex::new(vec![1i32]); + let mut lock = mutex.lock().unwrap(); + lock.clear(); + do_heavy_computation_that_takes_time(()); + } +} + +// Marker used for illustration purposes. +pub fn do_heavy_computation_that_takes_time<T>(_: T) {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_tightening.stderr b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr new file mode 100644 index 00000000000..ab8ce356ec7 --- /dev/null +++ b/src/tools/clippy/tests/ui/significant_drop_tightening.stderr @@ -0,0 +1,94 @@ +error: temporary with significant `Drop` can be early dropped + --> $DIR/significant_drop_tightening.rs:12:9 + | +LL | pub fn complex_return_triggers_the_lint() -> i32 { + | __________________________________________________- +LL | | fn foo() -> i32 { +LL | | 1 +LL | | } +LL | | let mutex = Mutex::new(1); +LL | | let lock = mutex.lock().unwrap(); + | | ^^^^ +... | +LL | | foo() +LL | | } + | |_- temporary `lock` is currently being dropped at the end of its contained scope + | + = note: this might lead to unnecessary resource contention + = note: `-D clippy::significant-drop-tightening` implied by `-D warnings` +help: drop the temporary after the end of its last usage + | +LL ~ let _ = *lock; +LL + drop(lock); + | + +error: temporary with significant `Drop` can be early dropped + --> $DIR/significant_drop_tightening.rs:44:13 + | +LL | / { +LL | | let mutex = Mutex::new(1i32); +LL | | let lock = mutex.lock().unwrap(); + | | ^^^^ +LL | | let rslt0 = lock.abs(); +LL | | let rslt1 = lock.is_positive(); +LL | | do_heavy_computation_that_takes_time((rslt0, rslt1)); +LL | | } + | |_____- temporary `lock` is currently being dropped at the end of its contained scope + | + = note: this might lead to unnecessary resource contention +help: drop the temporary after the end of its last usage + | +LL ~ let rslt1 = lock.is_positive(); +LL + drop(lock); + | + +error: temporary with significant `Drop` can be early dropped + --> $DIR/significant_drop_tightening.rs:65:13 + | +LL | / { +LL | | let mutex = Mutex::new(1i32); +LL | | let lock = mutex.lock().unwrap(); + | | ^^^^ +LL | | let rslt0 = lock.abs(); +LL | | do_heavy_computation_that_takes_time(rslt0); +LL | | } + | |_____- temporary `lock` is currently being dropped at the end of its contained scope + | + = note: this might lead to unnecessary resource contention +help: merge the temporary construction with its single usage + | +LL ~ +LL + let rslt0 = mutex.lock().unwrap().abs(); + | +help: remove separated single usage + | +LL - let rslt0 = lock.abs(); +LL + + | + +error: temporary with significant `Drop` can be early dropped + --> $DIR/significant_drop_tightening.rs:71:17 + | +LL | / { +LL | | let mutex = Mutex::new(vec![1i32]); +LL | | let mut lock = mutex.lock().unwrap(); + | | ^^^^ +LL | | lock.clear(); +LL | | do_heavy_computation_that_takes_time(()); +LL | | } + | |_____- temporary `lock` is currently being dropped at the end of its contained scope + | + = note: this might lead to unnecessary resource contention +help: merge the temporary construction with its single usage + | +LL ~ +LL + mutex.lock().unwrap().clear(); + | +help: remove separated single usage + | +LL - lock.clear(); +LL + + | + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs b/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs new file mode 100644 index 00000000000..bdc6113a250 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs @@ -0,0 +1,10 @@ +fn main() { + // Things it should warn about: + std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); + std::process::Command::new("cat").arg("--number file").spawn().unwrap(); + + // Things it should not warn about: + std::process::Command::new("echo").arg("hello world").spawn().unwrap(); + std::process::Command::new("a").arg("--fmt=%a %b %c").spawn().unwrap(); + std::process::Command::new("b").arg("-ldflags=-s -w").spawn().unwrap(); +} diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr new file mode 100644 index 00000000000..9bc0ca93aec --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr @@ -0,0 +1,25 @@ +error: single argument that looks like it should be multiple arguments + --> $DIR/suspicious_command_arg_space.rs:3:44 + | +LL | std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); + | ^^^^^^^^^^ + | + = note: `-D clippy::suspicious-command-arg-space` implied by `-D warnings` +help: consider splitting the argument + | +LL | std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap(); + | ~~~~ ~~~~~~~~~~~~~~~ + +error: single argument that looks like it should be multiple arguments + --> $DIR/suspicious_command_arg_space.rs:4:43 + | +LL | std::process::Command::new("cat").arg("--number file").spawn().unwrap(); + | ^^^^^^^^^^^^^^^ + | +help: consider splitting the argument + | +LL | std::process::Command::new("cat").args(["--number", "file"]).spawn().unwrap(); + | ~~~~ ~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/swap.fixed b/src/tools/clippy/tests/ui/swap.fixed index 805a2ba5a59..fa89706a815 100644 --- a/src/tools/clippy/tests/ui/swap.fixed +++ b/src/tools/clippy/tests/ui/swap.fixed @@ -7,7 +7,8 @@ clippy::redundant_clone, redundant_semicolons, dead_code, - unused_assignments + unused_assignments, + unused_variables )] struct Foo(u32); @@ -121,6 +122,27 @@ fn main() { std::mem::swap(&mut c.0, &mut a); ; std::mem::swap(&mut c.0, &mut a); + + std::mem::swap(&mut a, &mut b); + + let mut c = 1; + let mut d = 2; + std::mem::swap(&mut d, &mut c); + + let mut b = 1; + std::mem::swap(&mut a, &mut b); + + let b = 1; + let a = 2; + + let t = b; + let b = a; + let a = t; + + let mut b = 1; + let mut a = 2; + + std::mem::swap(&mut b, &mut a); } fn issue_8154() { diff --git a/src/tools/clippy/tests/ui/swap.rs b/src/tools/clippy/tests/ui/swap.rs index a8c87847952..ef8a81c8341 100644 --- a/src/tools/clippy/tests/ui/swap.rs +++ b/src/tools/clippy/tests/ui/swap.rs @@ -7,7 +7,8 @@ clippy::redundant_clone, redundant_semicolons, dead_code, - unused_assignments + unused_assignments, + unused_variables )] struct Foo(u32); @@ -143,6 +144,32 @@ fn main() { ; let t = c.0; c.0 = a; a = t; + + let a = b; + let b = a; + + let mut c = 1; + let mut d = 2; + d = c; + c = d; + + let mut b = 1; + let a = b; + b = a; + + let b = 1; + let a = 2; + + let t = b; + let b = a; + let a = t; + + let mut b = 1; + let mut a = 2; + + let t = b; + b = a; + a = t; } fn issue_8154() { diff --git a/src/tools/clippy/tests/ui/swap.stderr b/src/tools/clippy/tests/ui/swap.stderr index ee4b7a508a5..f0acbfe253f 100644 --- a/src/tools/clippy/tests/ui/swap.stderr +++ b/src/tools/clippy/tests/ui/swap.stderr @@ -1,5 +1,5 @@ error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:24:5 + --> $DIR/swap.rs:25:5 | LL | / let temp = bar.a; LL | | bar.a = bar.b; @@ -10,7 +10,7 @@ LL | | bar.b = temp; = note: `-D clippy::manual-swap` implied by `-D warnings` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:36:5 + --> $DIR/swap.rs:37:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -18,7 +18,7 @@ LL | | foo[1] = temp; | |_________________^ help: try: `foo.swap(0, 1)` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:45:5 + --> $DIR/swap.rs:46:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -26,7 +26,7 @@ LL | | foo[1] = temp; | |_________________^ help: try: `foo.swap(0, 1)` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:64:5 + --> $DIR/swap.rs:65:5 | LL | / let temp = foo[0]; LL | | foo[0] = foo[1]; @@ -34,7 +34,7 @@ LL | | foo[1] = temp; | |_________________^ help: try: `foo.swap(0, 1)` error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:75:5 + --> $DIR/swap.rs:76:5 | LL | / a ^= b; LL | | b ^= a; @@ -42,7 +42,7 @@ LL | | a ^= b; | |___________^ help: try: `std::mem::swap(&mut a, &mut b)` error: this looks like you are swapping `bar.a` and `bar.b` manually - --> $DIR/swap.rs:83:5 + --> $DIR/swap.rs:84:5 | LL | / bar.a ^= bar.b; LL | | bar.b ^= bar.a; @@ -50,7 +50,7 @@ LL | | bar.a ^= bar.b; | |___________________^ help: try: `std::mem::swap(&mut bar.a, &mut bar.b)` error: this looks like you are swapping elements of `foo` manually - --> $DIR/swap.rs:91:5 + --> $DIR/swap.rs:92:5 | LL | / foo[0] ^= foo[1]; LL | | foo[1] ^= foo[0]; @@ -58,7 +58,7 @@ LL | | foo[0] ^= foo[1]; | |_____________________^ help: try: `foo.swap(0, 1)` error: this looks like you are swapping `foo[0][1]` and `bar[1][0]` manually - --> $DIR/swap.rs:120:5 + --> $DIR/swap.rs:121:5 | LL | / let temp = foo[0][1]; LL | | foo[0][1] = bar[1][0]; @@ -68,7 +68,7 @@ LL | | bar[1][0] = temp; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `a` and `b` manually - --> $DIR/swap.rs:134:7 + --> $DIR/swap.rs:135:7 | LL | ; let t = a; | _______^ @@ -79,7 +79,7 @@ LL | | b = t; = note: or maybe you should use `std::mem::replace`? error: this looks like you are swapping `c.0` and `a` manually - --> $DIR/swap.rs:143:7 + --> $DIR/swap.rs:144:7 | LL | ; let t = c.0; | _______^ @@ -89,8 +89,18 @@ LL | | a = t; | = note: or maybe you should use `std::mem::replace`? +error: this looks like you are swapping `b` and `a` manually + --> $DIR/swap.rs:170:5 + | +LL | / let t = b; +LL | | b = a; +LL | | a = t; + | |_________^ help: try: `std::mem::swap(&mut b, &mut a)` + | + = note: or maybe you should use `std::mem::replace`? + error: this looks like you are trying to swap `a` and `b` - --> $DIR/swap.rs:131:5 + --> $DIR/swap.rs:132:5 | LL | / a = b; LL | | b = a; @@ -100,7 +110,7 @@ LL | | b = a; = note: `-D clippy::almost-swapped` implied by `-D warnings` error: this looks like you are trying to swap `c.0` and `a` - --> $DIR/swap.rs:140:5 + --> $DIR/swap.rs:141:5 | LL | / c.0 = a; LL | | a = c.0; @@ -108,8 +118,35 @@ LL | | a = c.0; | = note: or maybe you should use `std::mem::replace`? +error: this looks like you are trying to swap `a` and `b` + --> $DIR/swap.rs:148:5 + | +LL | / let a = b; +LL | | let b = a; + | |_____________^ help: try: `std::mem::swap(&mut a, &mut b)` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are trying to swap `d` and `c` + --> $DIR/swap.rs:153:5 + | +LL | / d = c; +LL | | c = d; + | |_________^ help: try: `std::mem::swap(&mut d, &mut c)` + | + = note: or maybe you should use `std::mem::replace`? + +error: this looks like you are trying to swap `a` and `b` + --> $DIR/swap.rs:157:5 + | +LL | / let a = b; +LL | | b = a; + | |_________^ help: try: `std::mem::swap(&mut a, &mut b)` + | + = note: or maybe you should use `std::mem::replace`? + error: this looks like you are swapping `s.0.x` and `s.0.y` manually - --> $DIR/swap.rs:178:5 + --> $DIR/swap.rs:205:5 | LL | / let t = s.0.x; LL | | s.0.x = s.0.y; @@ -118,5 +155,5 @@ LL | | s.0.y = t; | = note: or maybe you should use `std::mem::replace`? -error: aborting due to 13 previous errors +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs new file mode 100644 index 00000000000..a3840678250 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.rs @@ -0,0 +1,41 @@ +#![warn(clippy::transmute_int_to_non_zero)] + +use core::num::*; + +fn main() { + let int_u8: u8 = 1; + let int_u16: u16 = 1; + let int_u32: u32 = 1; + let int_u64: u64 = 1; + let int_u128: u128 = 1; + + let int_i8: i8 = 1; + let int_i16: i16 = 1; + let int_i32: i32 = 1; + let int_i64: i64 = 1; + let int_i128: i128 = 1; + + let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) }; + let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) }; + let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) }; + let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) }; + let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) }; + + let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) }; + let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) }; + let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) }; + let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) }; + let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) }; + + let _: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(int_u8) }; + let _: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(int_u16) }; + let _: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(int_u32) }; + let _: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(int_u64) }; + let _: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(int_u128) }; + + let _: NonZeroI8 = unsafe { NonZeroI8::new_unchecked(int_i8) }; + let _: NonZeroI16 = unsafe { NonZeroI16::new_unchecked(int_i16) }; + let _: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(int_i32) }; + let _: NonZeroI64 = unsafe { NonZeroI64::new_unchecked(int_i64) }; + let _: NonZeroI128 = unsafe { NonZeroI128::new_unchecked(int_i128) }; +} diff --git a/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr new file mode 100644 index 00000000000..33f8ce79ea7 --- /dev/null +++ b/src/tools/clippy/tests/ui/transmute_int_to_non_zero.stderr @@ -0,0 +1,64 @@ +error: transmute from a `u8` to a `NonZeroU8` + --> $DIR/transmute_int_to_non_zero.rs:18:33 + | +LL | let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU8::new_unchecked(int_u8)` + | + = note: `-D clippy::transmute-int-to-non-zero` implied by `-D warnings` + +error: transmute from a `u16` to a `NonZeroU16` + --> $DIR/transmute_int_to_non_zero.rs:19:34 + | +LL | let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU16::new_unchecked(int_u16)` + +error: transmute from a `u32` to a `NonZeroU32` + --> $DIR/transmute_int_to_non_zero.rs:20:34 + | +LL | let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU32::new_unchecked(int_u32)` + +error: transmute from a `u64` to a `NonZeroU64` + --> $DIR/transmute_int_to_non_zero.rs:21:34 + | +LL | let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU64::new_unchecked(int_u64)` + +error: transmute from a `u128` to a `NonZeroU128` + --> $DIR/transmute_int_to_non_zero.rs:22:35 + | +LL | let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU128::new_unchecked(int_u128)` + +error: transmute from a `i8` to a `NonZeroI8` + --> $DIR/transmute_int_to_non_zero.rs:24:33 + | +LL | let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI8::new_unchecked(int_i8)` + +error: transmute from a `i16` to a `NonZeroI16` + --> $DIR/transmute_int_to_non_zero.rs:25:34 + | +LL | let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI16::new_unchecked(int_i16)` + +error: transmute from a `i32` to a `NonZeroI32` + --> $DIR/transmute_int_to_non_zero.rs:26:34 + | +LL | let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI32::new_unchecked(int_i32)` + +error: transmute from a `i64` to a `NonZeroI64` + --> $DIR/transmute_int_to_non_zero.rs:27:34 + | +LL | let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI64::new_unchecked(int_i64)` + +error: transmute from a `i128` to a `NonZeroI128` + --> $DIR/transmute_int_to_non_zero.rs:28:35 + | +LL | let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI128::new_unchecked(int_i128)` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.fixed b/src/tools/clippy/tests/ui/uninlined_format_args.fixed index 9d08e80cf9a..cbd5cc5fcee 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args.fixed +++ b/src/tools/clippy/tests/ui/uninlined_format_args.fixed @@ -174,3 +174,7 @@ fn _meets_msrv() { let local_i32 = 1; println!("expand='{local_i32}'"); } + +fn _do_not_fire() { + println!("{:?}", None::<()>); +} diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.rs b/src/tools/clippy/tests/ui/uninlined_format_args.rs index 35b3677a896..cf0ea5be481 100644 --- a/src/tools/clippy/tests/ui/uninlined_format_args.rs +++ b/src/tools/clippy/tests/ui/uninlined_format_args.rs @@ -179,3 +179,7 @@ fn _meets_msrv() { let local_i32 = 1; println!("expand='{}'", local_i32); } + +fn _do_not_fire() { + println!("{:?}", None::<()>); +} diff --git a/src/tools/clippy/tests/ui/unreadable_literal.fixed b/src/tools/clippy/tests/ui/unreadable_literal.fixed index a67363b09ea..13e5feb1926 100644 --- a/src/tools/clippy/tests/ui/unreadable_literal.fixed +++ b/src/tools/clippy/tests/ui/unreadable_literal.fixed @@ -23,7 +23,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x0123_4567, + 0x1_234_567, 65536, 1_2345_6789, 1234_f32, diff --git a/src/tools/clippy/tests/ui/unreadable_literal.stderr b/src/tools/clippy/tests/ui/unreadable_literal.stderr index b51130c6a6a..450121b1c5a 100644 --- a/src/tools/clippy/tests/ui/unreadable_literal.stderr +++ b/src/tools/clippy/tests/ui/unreadable_literal.stderr @@ -1,11 +1,3 @@ -error: digits of hex or binary literal not grouped by four - --> $DIR/unreadable_literal.rs:26:9 - | -LL | 0x1_234_567, - | ^^^^^^^^^^^ help: consider: `0x0123_4567` - | - = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` - error: long literal lacking separators --> $DIR/unreadable_literal.rs:34:17 | @@ -68,5 +60,5 @@ error: long literal lacking separators LL | let _fail5 = 1.100300400; | ^^^^^^^^^^^ help: consider: `1.100_300_400` -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 7824ef81d7a..41c23ff86b2 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1432,12 +1432,13 @@ impl<'test> TestCx<'test> { expect_help: bool, expect_note: bool, ) -> bool { - match actual_error.kind { - Some(ErrorKind::Help) => expect_help, - Some(ErrorKind::Note) => expect_note, - Some(ErrorKind::Error) | Some(ErrorKind::Warning) => true, - Some(ErrorKind::Suggestion) | None => false, - } + !actual_error.msg.is_empty() + && match actual_error.kind { + Some(ErrorKind::Help) => expect_help, + Some(ErrorKind::Note) => expect_note, + Some(ErrorKind::Error) | Some(ErrorKind::Warning) => true, + Some(ErrorKind::Suggestion) | None => false, + } } fn should_emit_metadata(&self, pm: Option<PassMode>) -> Emit { diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 8bbf9f87b43..969c81f7e32 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -777,10 +777,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { type Provenance = Provenance; type ProvenanceExtra = ProvenanceExtra; + type Bytes = Box<[u8]>; type MemoryMap = MonoHashMap< AllocId, - (MemoryKind<MiriMemoryKind>, Allocation<Provenance, Self::AllocExtra>), + (MemoryKind<MiriMemoryKind>, Allocation<Provenance, Self::AllocExtra, Self::Bytes>), >; const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global); diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 3da4fed33e1..ea5780f66d7 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -138,8 +138,20 @@ function valueCheck(fullPath, expected, result, error_text, queryName) { error_text.push('==> Unknown key "' + key + '"'); break; } + let result_v = result[key]; + if (result_v !== null && key === "error") { + result_v.forEach((value, index) => { + value = value.split(" ").join(" "); + if (index % 2 === 1) { + result_v[index] = "`" + value + "`"; + } else { + result_v[index] = value; + } + }); + result_v = result_v.join(""); + } const obj_path = fullPath + (fullPath.length > 0 ? '.' : '') + key; - valueCheck(obj_path, expected[key], result[key], error_text, queryName); + valueCheck(obj_path, expected[key], result_v, error_text, queryName); } } else { expectedValue = JSON.stringify(expected); diff --git a/src/version b/src/version index 49349856550..832e9afb6c1 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.69.0 +1.70.0 diff --git a/tests/codegen/array-equality.rs b/tests/codegen/array-equality.rs index cd5e82a9205..abfe295f8b6 100644 --- a/tests/codegen/array-equality.rs +++ b/tests/codegen/array-equality.rs @@ -1,4 +1,4 @@ -// compile-flags: -O +// compile-flags: -O -Z merge-functions=disabled // only-x86_64 #![crate_type = "lib"] @@ -43,6 +43,15 @@ pub fn array_eq_long(a: &[u16; 1234], b: &[u16; 1234]) -> bool { a == b } +// CHECK-LABEL: @array_char_eq +#[no_mangle] +pub fn array_char_eq(a: [char; 2], b: [char; 2]) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: %[[EQ:.+]] = icmp eq i64 %0, %1 + // CHECK-NEXT: ret i1 %[[EQ]] + a == b +} + // CHECK-LABEL: @array_eq_zero_short(i48 #[no_mangle] pub fn array_eq_zero_short(x: [u16; 3]) -> bool { @@ -52,6 +61,25 @@ pub fn array_eq_zero_short(x: [u16; 3]) -> bool { x == [0; 3] } +// CHECK-LABEL: @array_eq_none_short(i40 +#[no_mangle] +pub fn array_eq_none_short(x: [Option<std::num::NonZeroU8>; 5]) -> bool { + // CHECK-NEXT: start: + // CHECK-NEXT: %[[EQ:.+]] = icmp eq i40 %0, 0 + // CHECK-NEXT: ret i1 %[[EQ]] + x == [None; 5] +} + +// CHECK-LABEL: @array_eq_zero_nested( +#[no_mangle] +pub fn array_eq_zero_nested(x: [[u8; 3]; 3]) -> bool { + // CHECK: %[[VAL:.+]] = load i72 + // CHECK-SAME: align 1 + // CHECK: %[[EQ:.+]] = icmp eq i72 %[[VAL]], 0 + // CHECK: ret i1 %[[EQ]] + x == [[0; 3]; 3] +} + // CHECK-LABEL: @array_eq_zero_mid( #[no_mangle] pub fn array_eq_zero_mid(x: [u16; 8]) -> bool { diff --git a/tests/codegen/consts.rs b/tests/codegen/consts.rs index d0418d11142..dd22fd0f7e8 100644 --- a/tests/codegen/consts.rs +++ b/tests/codegen/consts.rs @@ -9,11 +9,11 @@ // CHECK: @STATIC = {{.*}}, align 4 // This checks the constants from inline_enum_const -// CHECK: @alloc12 = {{.*}}, align 2 +// CHECK: @alloc_76bfe2f13a3e3b01074971d122eac57e = {{.*}}, align 2 // This checks the constants from {low,high}_align_const, they share the same // constant, but the alignment differs, so the higher one should be used -// CHECK: [[LOW_HIGH:@alloc[0-9]+]] = {{.*}}, align 4 +// CHECK: [[LOW_HIGH:@alloc_[a-f0-9]+]] = {{.*}}, align 4 #[derive(Copy, Clone)] // repr(i16) is required for the {low,high}_align_const test diff --git a/tests/codegen/option-as-slice.rs b/tests/codegen/option-as-slice.rs new file mode 100644 index 00000000000..d5077dbf6cc --- /dev/null +++ b/tests/codegen/option-as-slice.rs @@ -0,0 +1,28 @@ +// compile-flags: -O +// only-x86_64 + +#![crate_type = "lib"] +#![feature(option_as_slice)] + +extern crate core; + +use core::num::NonZeroU64; +use core::option::Option; + +// CHECK-LABEL: @u64_opt_as_slice +#[no_mangle] +pub fn u64_opt_as_slice(o: &Option<u64>) -> &[u64] { + // CHECK: start: + // CHECK-NOT: select + // CHECK: ret + o.as_slice() +} + +// CHECK-LABEL: @nonzero_u64_opt_as_slice +#[no_mangle] +pub fn nonzero_u64_opt_as_slice(o: &Option<NonZeroU64>) -> &[NonZeroU64] { + // CHECK: start: + // CHECK-NOT: select + // CHECK: ret + o.as_slice() +} diff --git a/tests/codegen/remap_path_prefix/main.rs b/tests/codegen/remap_path_prefix/main.rs index 78ebbccfce1..6c0cd6997d0 100644 --- a/tests/codegen/remap_path_prefix/main.rs +++ b/tests/codegen/remap_path_prefix/main.rs @@ -12,7 +12,7 @@ mod aux_mod; include!("aux_mod.rs"); // Here we check that the expansion of the file!() macro is mapped. -// CHECK: @alloc2 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }> +// CHECK: @alloc_92a59126a55aa3c0019b6c8a007fe001 = private unnamed_addr constant <{ [34 x i8] }> <{ [34 x i8] c"/the/src/remap_path_prefix/main.rs" }> pub static FILE_PATH: &'static str = file!(); fn main() { diff --git a/tests/codegen/slice-ref-equality.rs b/tests/codegen/slice-ref-equality.rs index 47fde12bf30..8f0adab35e7 100644 --- a/tests/codegen/slice-ref-equality.rs +++ b/tests/codegen/slice-ref-equality.rs @@ -1,7 +1,10 @@ -// compile-flags: -C opt-level=3 -Zmerge-functions=disabled +// compile-flags: -O -Zmerge-functions=disabled +// ignore-debug (the extra assertions get in the way) #![crate_type = "lib"] +use std::num::{NonZeroI16, NonZeroU32}; + // #71602 reported a simple array comparison just generating a loop. // This was originally fixed by ensuring it generates a single bcmp, // but we now generate it as a load+icmp instead. `is_zero_slice` was @@ -36,3 +39,54 @@ pub fn is_zero_array(data: &[u8; 4]) -> bool { // CHECK-NEXT: ret i1 %[[EQ]] *data == [0; 4] } + +// The following test the extra specializations to make sure that slice +// equality for non-byte types also just emit a `bcmp`, not a loop. + +// CHECK-LABEL: @eq_slice_of_nested_u8( +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 +#[no_mangle] +fn eq_slice_of_nested_u8(x: &[[u8; 3]], y: &[[u8; 3]]) -> bool { + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = mul nsw [[USIZE]] %1, 3 + // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i8\*|ptr}} + // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) + x == y +} + +// CHECK-LABEL: @eq_slice_of_i32( +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 +#[no_mangle] +fn eq_slice_of_i32(x: &[i32], y: &[i32]) -> bool { + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2 + // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i32\*|ptr}} + // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) + x == y +} + +// CHECK-LABEL: @eq_slice_of_nonzero( +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 +#[no_mangle] +fn eq_slice_of_nonzero(x: &[NonZeroU32], y: &[NonZeroU32]) -> bool { + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 2 + // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i32\*|ptr}} + // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) + x == y +} + +// CHECK-LABEL: @eq_slice_of_option_of_nonzero( +// CHECK-SAME: [[USIZE:i16|i32|i64]] noundef %1 +// CHECK-SAME: [[USIZE]] noundef %3 +#[no_mangle] +fn eq_slice_of_option_of_nonzero(x: &[Option<NonZeroI16>], y: &[Option<NonZeroI16>]) -> bool { + // CHECK: icmp eq [[USIZE]] %1, %3 + // CHECK: %[[BYTES:.+]] = shl nsw [[USIZE]] %1, 1 + // CHECK: tail call{{( noundef)?}} i32 @{{bcmp|memcmp}}({{i16\*|ptr}} + // CHECK-SAME: , [[USIZE]]{{( noundef)?}} %[[BYTES]]) + x == y +} diff --git a/tests/codegen/thread-local.rs b/tests/codegen/thread-local.rs index 0f1b29ca79b..aa7fab7fb17 100644 --- a/tests/codegen/thread-local.rs +++ b/tests/codegen/thread-local.rs @@ -4,6 +4,7 @@ // ignore-wasm globals are used instead of thread locals // ignore-emscripten globals are used instead of thread locals // ignore-android does not use #[thread_local] +// ignore-nto does not use #[thread_local] #![crate_type = "lib"] diff --git a/tests/debuginfo/captured-fields-1.rs b/tests/debuginfo/captured-fields-1.rs index afbf942d404..b71734c2354 100644 --- a/tests/debuginfo/captured-fields-1.rs +++ b/tests/debuginfo/captured-fields-1.rs @@ -1,5 +1,5 @@ // compile-flags:-g - +// edition:2021 // === GDB TESTS =================================================================================== // gdb-command:run @@ -44,7 +44,6 @@ // lldbg-check:(captured_fields_1::main::{closure_env#5}) $5 = { my_var = { my_field1 = 11 my_field2 = 22 } } // lldb-command:continue -#![feature(capture_disjoint_fields)] #![allow(unused)] struct MyStruct { diff --git a/tests/debuginfo/captured-fields-2.rs b/tests/debuginfo/captured-fields-2.rs index c872354a924..8d463fb2451 100644 --- a/tests/debuginfo/captured-fields-2.rs +++ b/tests/debuginfo/captured-fields-2.rs @@ -1,5 +1,5 @@ // compile-flags:-g - +// edition:2021 // === GDB TESTS =================================================================================== // gdb-command:run @@ -20,7 +20,6 @@ // lldbg-check:(unsigned int) $1 = 22 // lldb-command:continue -#![feature(capture_disjoint_fields)] #![allow(unused)] struct MyStruct { @@ -29,10 +28,7 @@ struct MyStruct { } fn main() { - let mut my_var = MyStruct { - my_field1: 11, - my_field2: 22, - }; + let mut my_var = MyStruct { my_field1: 11, my_field2: 22 }; let my_ref = &mut my_var; let test = || { diff --git a/tests/mir-opt/basic_assignment.main.ElaborateDrops.diff b/tests/mir-opt/basic_assignment.main.ElaborateDrops.diff new file mode 100644 index 00000000000..61a934685cd --- /dev/null +++ b/tests/mir-opt/basic_assignment.main.ElaborateDrops.diff @@ -0,0 +1,85 @@ +- // MIR for `main` before ElaborateDrops ++ // MIR for `main` after ElaborateDrops + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/basic_assignment.rs:+0:11: +0:11 + let _1: bool; // in scope 0 at $DIR/basic_assignment.rs:+1:9: +1:17 + let mut _3: bool; // in scope 0 at $DIR/basic_assignment.rs:+6:16: +6:24 + let mut _6: std::option::Option<std::boxed::Box<u32>>; // in scope 0 at $DIR/basic_assignment.rs:+13:14: +13:20 + scope 1 { + debug nodrop_x => _1; // in scope 1 at $DIR/basic_assignment.rs:+1:9: +1:17 + let _2: bool; // in scope 1 at $DIR/basic_assignment.rs:+2:9: +2:17 + scope 2 { + debug nodrop_y => _2; // in scope 2 at $DIR/basic_assignment.rs:+2:9: +2:17 + let _4: std::option::Option<std::boxed::Box<u32>>; // in scope 2 at $DIR/basic_assignment.rs:+8:9: +8:15 + scope 3 { + debug drop_x => _4; // in scope 3 at $DIR/basic_assignment.rs:+8:9: +8:15 + let _5: std::option::Option<std::boxed::Box<u32>>; // in scope 3 at $DIR/basic_assignment.rs:+9:9: +9:15 + scope 4 { + debug drop_y => _5; // in scope 4 at $DIR/basic_assignment.rs:+9:9: +9:15 + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/basic_assignment.rs:+1:9: +1:17 + _1 = const false; // scope 0 at $DIR/basic_assignment.rs:+1:20: +1:25 + StorageLive(_2); // scope 1 at $DIR/basic_assignment.rs:+2:9: +2:17 + StorageLive(_3); // scope 2 at $DIR/basic_assignment.rs:+6:16: +6:24 + _3 = _1; // scope 2 at $DIR/basic_assignment.rs:+6:16: +6:24 + _2 = move _3; // scope 2 at $DIR/basic_assignment.rs:+6:5: +6:24 + StorageDead(_3); // scope 2 at $DIR/basic_assignment.rs:+6:23: +6:24 + StorageLive(_4); // scope 2 at $DIR/basic_assignment.rs:+8:9: +8:15 + _4 = Option::<Box<u32>>::None; // scope 2 at $DIR/basic_assignment.rs:+8:36: +8:40 + StorageLive(_5); // scope 3 at $DIR/basic_assignment.rs:+9:9: +9:15 + StorageLive(_6); // scope 4 at $DIR/basic_assignment.rs:+13:14: +13:20 + _6 = move _4; // scope 4 at $DIR/basic_assignment.rs:+13:14: +13:20 +- drop(_5) -> [return: bb1, unwind: bb2]; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 ++ goto -> bb1; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 + } + + bb1: { + _5 = move _6; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 +- drop(_6) -> [return: bb3, unwind: bb6]; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 ++ goto -> bb3; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 + } + + bb2 (cleanup): { + _5 = move _6; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 + drop(_6) -> bb6; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 + } + + bb3: { + StorageDead(_6); // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 + _0 = const (); // scope 0 at $DIR/basic_assignment.rs:+0:11: +14:2 + drop(_5) -> [return: bb4, unwind: bb7]; // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 + } + + bb4: { + StorageDead(_5); // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 +- drop(_4) -> bb5; // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 ++ goto -> bb5; // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 + } + + bb5: { + StorageDead(_4); // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 + StorageDead(_2); // scope 1 at $DIR/basic_assignment.rs:+14:1: +14:2 + StorageDead(_1); // scope 0 at $DIR/basic_assignment.rs:+14:1: +14:2 + return; // scope 0 at $DIR/basic_assignment.rs:+14:2: +14:2 + } + + bb6 (cleanup): { + drop(_5) -> bb7; // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 + } + + bb7 (cleanup): { +- drop(_4) -> bb8; // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 ++ goto -> bb8; // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 + } + + bb8 (cleanup): { + resume; // scope 0 at $DIR/basic_assignment.rs:+0:1: +14:2 + } + } + diff --git a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir index 1f099cd5e83..f20b534259a 100644 --- a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option<std::boxed::Box<u32>>) }, span: $DIR/basic_assignment.rs:18:17: 18:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> -| 1: user_ty: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option<std::boxed::Box<u32>>) }, span: $DIR/basic_assignment.rs:18:17: 18:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> +| 0: user_ty: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option<std::boxed::Box<u32>>) }, span: $DIR/basic_assignment.rs:20:17: 20:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> +| 1: user_ty: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option<std::boxed::Box<u32>>) }, span: $DIR/basic_assignment.rs:20:17: 20:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> | fn main() -> () { let mut _0: (); // return place in scope 0 at $DIR/basic_assignment.rs:+0:11: +0:11 @@ -41,35 +41,37 @@ fn main() -> () { StorageLive(_5); // scope 3 at $DIR/basic_assignment.rs:+9:9: +9:15 StorageLive(_6); // scope 4 at $DIR/basic_assignment.rs:+13:14: +13:20 _6 = move _4; // scope 4 at $DIR/basic_assignment.rs:+13:14: +13:20 - replace(_5 <- move _6) -> [return: bb1, unwind: bb5]; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 + drop(_5) -> [return: bb1, unwind: bb2]; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 } bb1: { - drop(_6) -> [return: bb2, unwind: bb6]; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 + _5 = move _6; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 + drop(_6) -> [return: bb3, unwind: bb6]; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 } - bb2: { + bb2 (cleanup): { + _5 = move _6; // scope 4 at $DIR/basic_assignment.rs:+13:5: +13:11 + drop(_6) -> bb6; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 + } + + bb3: { StorageDead(_6); // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 _0 = const (); // scope 0 at $DIR/basic_assignment.rs:+0:11: +14:2 - drop(_5) -> [return: bb3, unwind: bb7]; // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 + drop(_5) -> [return: bb4, unwind: bb7]; // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 } - bb3: { + bb4: { StorageDead(_5); // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 - drop(_4) -> [return: bb4, unwind: bb8]; // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 + drop(_4) -> [return: bb5, unwind: bb8]; // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 } - bb4: { + bb5: { StorageDead(_4); // scope 2 at $DIR/basic_assignment.rs:+14:1: +14:2 StorageDead(_2); // scope 1 at $DIR/basic_assignment.rs:+14:1: +14:2 StorageDead(_1); // scope 0 at $DIR/basic_assignment.rs:+14:1: +14:2 return; // scope 0 at $DIR/basic_assignment.rs:+14:2: +14:2 } - bb5 (cleanup): { - drop(_6) -> bb6; // scope 4 at $DIR/basic_assignment.rs:+13:19: +13:20 - } - bb6 (cleanup): { drop(_5) -> bb7; // scope 3 at $DIR/basic_assignment.rs:+14:1: +14:2 } diff --git a/tests/mir-opt/basic_assignment.rs b/tests/mir-opt/basic_assignment.rs index ac350271e9f..92434e44aa9 100644 --- a/tests/mir-opt/basic_assignment.rs +++ b/tests/mir-opt/basic_assignment.rs @@ -1,5 +1,7 @@ +// needs-unwind // this tests move up progration, which is not yet implemented +// EMIT_MIR basic_assignment.main.ElaborateDrops.diff // EMIT_MIR basic_assignment.main.SimplifyCfg-initial.after.mir // Check codegen for assignments (`a = b`) where the left-hand-side is diff --git a/tests/mir-opt/issue_41110.test.ElaborateDrops.diff b/tests/mir-opt/issue_41110.test.ElaborateDrops.diff index 79e3d073be5..3dd1a9bbab5 100644 --- a/tests/mir-opt/issue_41110.test.ElaborateDrops.diff +++ b/tests/mir-opt/issue_41110.test.ElaborateDrops.diff @@ -38,37 +38,39 @@ StorageLive(_5); // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 + _6 = const false; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 _5 = move _1; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 -- replace(_2 <- move _5) -> [return: bb2, unwind: bb6]; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 -+ goto -> bb12; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 +- drop(_2) -> [return: bb2, unwind: bb3]; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 ++ goto -> bb2; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 } bb2: { -- drop(_5) -> [return: bb3, unwind: bb8]; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 -+ goto -> bb3; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 + _2 = move _5; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 +- drop(_5) -> [return: bb4, unwind: bb8]; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 ++ goto -> bb4; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 + } + + bb3 (cleanup): { + _2 = move _5; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 + drop(_5) -> bb8; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 } - bb3: { + bb4: { StorageDead(_5); // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 _0 = const (); // scope 0 at $DIR/issue_41110.rs:+0:15: +5:2 - drop(_2) -> [return: bb4, unwind: bb9]; // scope 1 at $DIR/issue_41110.rs:+5:1: +5:2 + drop(_2) -> [return: bb5, unwind: bb9]; // scope 1 at $DIR/issue_41110.rs:+5:1: +5:2 } - bb4: { + bb5: { StorageDead(_2); // scope 1 at $DIR/issue_41110.rs:+5:1: +5:2 -- drop(_1) -> bb5; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 -+ goto -> bb5; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 +- drop(_1) -> bb6; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 ++ goto -> bb6; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 } - bb5: { + bb6: { + _6 = const false; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 StorageDead(_1); // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 return; // scope 0 at $DIR/issue_41110.rs:+5:2: +5:2 } - bb6 (cleanup): { - drop(_5) -> bb8; // scope 2 at $DIR/issue_41110.rs:+4:9: +4:10 - } - bb7 (cleanup): { - drop(_4) -> bb8; // scope 2 at $DIR/issue_41110.rs:+3:11: +3:12 + goto -> bb8; // scope 2 at $DIR/issue_41110.rs:+3:11: +3:12 @@ -81,7 +83,7 @@ bb9 (cleanup): { - drop(_1) -> bb10; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 -+ goto -> bb14; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 ++ goto -> bb12; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 } bb10 (cleanup): { @@ -89,21 +91,11 @@ + } + + bb11 (cleanup): { -+ _2 = move _5; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 -+ goto -> bb10; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 -+ } -+ -+ bb12: { -+ _2 = move _5; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 -+ goto -> bb2; // scope 2 at $DIR/issue_41110.rs:+4:5: +4:6 -+ } -+ -+ bb13 (cleanup): { + drop(_1) -> bb10; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 + } + -+ bb14 (cleanup): { -+ switchInt(_6) -> [0: bb10, otherwise: bb13]; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 ++ bb12 (cleanup): { ++ switchInt(_6) -> [0: bb10, otherwise: bb11]; // scope 0 at $DIR/issue_41110.rs:+5:1: +5:2 } } diff --git a/tests/mir-opt/issue_41888.main.ElaborateDrops.diff b/tests/mir-opt/issue_41888.main.ElaborateDrops.diff index 257f0b1e6e8..4e38659a90b 100644 --- a/tests/mir-opt/issue_41888.main.ElaborateDrops.diff +++ b/tests/mir-opt/issue_41888.main.ElaborateDrops.diff @@ -34,7 +34,7 @@ } bb1: { - switchInt(move _2) -> [0: bb7, otherwise: bb2]; // scope 1 at $DIR/issue_41888.rs:+2:8: +2:14 + switchInt(move _2) -> [0: bb8, otherwise: bb2]; // scope 1 at $DIR/issue_41888.rs:+2:8: +2:14 } bb2: { @@ -43,47 +43,56 @@ _4 = K; // scope 1 at $DIR/issue_41888.rs:+3:18: +3:19 _3 = E::F(move _4); // scope 1 at $DIR/issue_41888.rs:+3:13: +3:20 StorageDead(_4); // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 -- replace(_1 <- move _3) -> [return: bb3, unwind: bb10]; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ goto -> bb14; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 +- drop(_1) -> [return: bb3, unwind: bb4]; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 ++ goto -> bb3; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 } bb3: { -- drop(_3) -> [return: bb4, unwind: bb11]; // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 -+ goto -> bb4; // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 ++ _7 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 ++ _8 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 ++ _9 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 + _1 = move _3; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 +- drop(_3) -> [return: bb5, unwind: bb11]; // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 ++ goto -> bb5; // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 + } + + bb4 (cleanup): { + _1 = move _3; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 + drop(_3) -> bb11; // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 } - bb4: { + bb5: { StorageDead(_3); // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 _5 = discriminant(_1); // scope 2 at $DIR/issue_41888.rs:+4:16: +4:24 - switchInt(move _5) -> [0: bb5, otherwise: bb6]; // scope 2 at $DIR/issue_41888.rs:+4:16: +4:24 + switchInt(move _5) -> [0: bb6, otherwise: bb7]; // scope 2 at $DIR/issue_41888.rs:+4:16: +4:24 } - bb5: { + bb6: { StorageLive(_6); // scope 2 at $DIR/issue_41888.rs:+4:21: +4:23 + _9 = const false; // scope 2 at $DIR/issue_41888.rs:+4:21: +4:23 _6 = move ((_1 as F).0: K); // scope 2 at $DIR/issue_41888.rs:+4:21: +4:23 _0 = const (); // scope 2 at $DIR/issue_41888.rs:+4:29: +7:10 StorageDead(_6); // scope 1 at $DIR/issue_41888.rs:+7:9: +7:10 - goto -> bb8; // scope 1 at $DIR/issue_41888.rs:+4:9: +7:10 + goto -> bb9; // scope 1 at $DIR/issue_41888.rs:+4:9: +7:10 } - bb6: { + bb7: { _0 = const (); // scope 1 at $DIR/issue_41888.rs:+7:10: +7:10 - goto -> bb8; // scope 1 at $DIR/issue_41888.rs:+4:9: +7:10 + goto -> bb9; // scope 1 at $DIR/issue_41888.rs:+4:9: +7:10 } - bb7: { + bb8: { _0 = const (); // scope 1 at $DIR/issue_41888.rs:+8:6: +8:6 - goto -> bb8; // scope 1 at $DIR/issue_41888.rs:+2:5: +8:6 + goto -> bb9; // scope 1 at $DIR/issue_41888.rs:+2:5: +8:6 } - bb8: { + bb9: { StorageDead(_2); // scope 1 at $DIR/issue_41888.rs:+8:5: +8:6 -- drop(_1) -> bb9; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 -+ goto -> bb20; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 +- drop(_1) -> bb10; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ goto -> bb18; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 } - bb9: { + bb10: { + _7 = const false; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + _8 = const false; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + _9 = const false; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 @@ -91,10 +100,6 @@ return; // scope 0 at $DIR/issue_41888.rs:+9:2: +9:2 } - bb10 (cleanup): { - drop(_3) -> bb11; // scope 1 at $DIR/issue_41888.rs:+3:19: +3:20 - } - bb11 (cleanup): { - drop(_1) -> bb12; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + goto -> bb12; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 @@ -104,55 +109,39 @@ resume; // scope 0 at $DIR/issue_41888.rs:+0:1: +9:2 + } + -+ bb13 (cleanup): { -+ _7 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ _8 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ _9 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ _1 = move _3; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ goto -> bb12; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ } -+ -+ bb14: { -+ _7 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ _8 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ _9 = const true; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ _1 = move _3; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ goto -> bb3; // scope 1 at $DIR/issue_41888.rs:+3:9: +3:10 -+ } -+ -+ bb15: { ++ bb13: { + _7 = const false; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 -+ goto -> bb9; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ goto -> bb10; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb16 (cleanup): { ++ bb14 (cleanup): { + goto -> bb12; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb17: { -+ drop(_1) -> [return: bb15, unwind: bb12]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ bb15: { ++ drop(_1) -> [return: bb13, unwind: bb12]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb18 (cleanup): { ++ bb16 (cleanup): { + drop(_1) -> bb12; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb19: { ++ bb17: { + _10 = discriminant(_1); // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 -+ switchInt(move _10) -> [0: bb15, otherwise: bb17]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ switchInt(move _10) -> [0: bb13, otherwise: bb15]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb20: { -+ switchInt(_7) -> [0: bb15, otherwise: bb19]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ bb18: { ++ switchInt(_7) -> [0: bb13, otherwise: bb17]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb21 (cleanup): { ++ bb19 (cleanup): { + _11 = discriminant(_1); // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 -+ switchInt(move _11) -> [0: bb16, otherwise: bb18]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ switchInt(move _11) -> [0: bb14, otherwise: bb16]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 + } + -+ bb22 (cleanup): { -+ switchInt(_7) -> [0: bb12, otherwise: bb21]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 ++ bb20 (cleanup): { ++ switchInt(_7) -> [0: bb12, otherwise: bb19]; // scope 0 at $DIR/issue_41888.rs:+9:1: +9:2 } } diff --git a/tests/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir b/tests/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir index 210f178a0a9..56cb9166c37 100644 --- a/tests/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir +++ b/tests/mir-opt/packed_struct_drop_aligned.main.SimplifyCfg-elaborate-drops.after.mir @@ -28,21 +28,21 @@ fn main() -> () { StorageDead(_5); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:28: +2:29 StorageLive(_6); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 _6 = move (_1.0: Aligned); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 - drop(_6) -> [return: bb4, unwind: bb3]; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 + drop(_6) -> [return: bb4, unwind: bb1]; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 } - bb1: { - StorageDead(_1); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:1: +3:2 - return; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:2: +3:2 + bb1 (cleanup): { + (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 + drop(_1) -> bb3; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:1: +3:2 } - bb2 (cleanup): { - resume; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+0:1: +3:2 + bb2: { + StorageDead(_1); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:1: +3:2 + return; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:2: +3:2 } bb3 (cleanup): { - (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 - drop(_1) -> bb2; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:1: +3:2 + resume; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+0:1: +3:2 } bb4: { @@ -50,6 +50,6 @@ fn main() -> () { (_1.0: Aligned) = move _4; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8 StorageDead(_4); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:28: +2:29 _0 = const (); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+0:11: +3:2 - drop(_1) -> [return: bb1, unwind: bb2]; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:1: +3:2 + drop(_1) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+3:1: +3:2 } } diff --git a/tests/run-make/coverage-reports/expected_show_coverage.closure.txt b/tests/run-make/coverage-reports/expected_show_coverage.closure.txt index e463099a5ee..002ecec3b91 100644 --- a/tests/run-make/coverage-reports/expected_show_coverage.closure.txt +++ b/tests/run-make/coverage-reports/expected_show_coverage.closure.txt @@ -29,8 +29,8 @@ 29| 1| some_string = Some(String::from("the string content")); 30| 1| let 31| 1| a - 32| 1| = - 33| 1| || + 32| | = + 33| | || 34| 0| { 35| 0| let mut countdown = 0; 36| 0| if is_false { diff --git a/tests/rustdoc-gui/search-error.goml b/tests/rustdoc-gui/search-error.goml new file mode 100644 index 00000000000..2a10a647b53 --- /dev/null +++ b/tests/rustdoc-gui/search-error.goml @@ -0,0 +1,39 @@ +// Checks that the crate search filtering is handled correctly and changes the results. +goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa'%3Bda'%3Bds" +show-text: true + +define-function: ( + "check-colors", + (theme, error_background), + block { + // Setting the theme. + local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} + // We reload the page so the local storage settings are being used. + reload: + + wait-for: "#search .error code" + assert-css: ("#search .error code", {"background-color": |error_background|}) + } +) + +call-function: ( + "check-colors", + { + "theme": "ayu", + "error_background": "rgb(79, 76, 76)", + }, +) +call-function: ( + "check-colors", + { + "theme": "dark", + "error_background": "rgb(72, 72, 72)", + }, +) +call-function: ( + "check-colors", + { + "theme": "light", + "error_background": "rgb(208, 204, 204)", + }, +) diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js new file mode 100644 index 00000000000..dee4819e81a --- /dev/null +++ b/tests/rustdoc-js-std/option-type-signatures.js @@ -0,0 +1,7 @@ +const QUERY = 'option, fnonce -> option'; + +const EXPECTED = { + 'others': [ + { 'path': 'std::option::Option', 'name': 'map' }, + ], +}; diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index dc42031e05f..98c6f27ca61 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -37,6 +37,9 @@ const QUERY = [ "mod : :", "a!a", "a!!", + "mod:a!", + "a!::a", + "a<", ]; const PARSED = [ @@ -65,7 +68,7 @@ const PARSED = [ returned: [], typeFilter: -1, userQuery: "a<\"p\">", - error: "`\"` cannot be used in generics", + error: "Unexpected `\"` in generics", }, { elems: [], @@ -371,7 +374,7 @@ const PARSED = [ returned: [], typeFilter: -1, userQuery: "a!a", - error: '`!` can only be at the end of an ident', + error: 'Unexpected `!`: it can only be at the end of an ident', }, { elems: [], @@ -382,4 +385,31 @@ const PARSED = [ userQuery: "a!!", error: 'Cannot have more than one `!` in an ident', }, + { + elems: [], + foundElems: 0, + original: "mod:a!", + returned: [], + typeFilter: -1, + userQuery: "mod:a!", + error: 'Invalid search type: macro `!` and `mod` both specified', + }, + { + elems: [], + foundElems: 0, + original: "a!::a", + returned: [], + typeFilter: -1, + userQuery: "a!::a", + error: 'Cannot have associated items in macros', + }, + { + elems: [], + foundElems: 0, + original: "a<", + returned: [], + typeFilter: -1, + userQuery: "a<", + error: "Unclosed `<`", + }, ]; diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js index e5a87a415ac..01f65b478f8 100644 --- a/tests/rustdoc-js-std/parser-filter.js +++ b/tests/rustdoc-js-std/parser-filter.js @@ -1,4 +1,4 @@ -const QUERY = ['fn:foo', 'enum : foo', 'macro<f>:foo']; +const QUERY = ['fn:foo', 'enum : foo', 'macro<f>:foo', 'macro!', 'macro:mac!', 'a::mac!']; const PARSED = [ { @@ -40,4 +40,49 @@ const PARSED = [ userQuery: "macro<f>:foo", error: "Unexpected `:`", }, + { + elems: [{ + name: "macro", + fullPath: ["macro"], + pathWithoutLast: [], + pathLast: "macro", + generics: [], + }], + foundElems: 1, + original: "macro!", + returned: [], + typeFilter: 14, + userQuery: "macro!", + error: null, + }, + { + elems: [{ + name: "mac", + fullPath: ["mac"], + pathWithoutLast: [], + pathLast: "mac", + generics: [], + }], + foundElems: 1, + original: "macro:mac!", + returned: [], + typeFilter: 14, + userQuery: "macro:mac!", + error: null, + }, + { + elems: [{ + name: "a::mac", + fullPath: ["a", "mac"], + pathWithoutLast: ["a"], + pathLast: "mac", + generics: [], + }], + foundElems: 1, + original: "a::mac!", + returned: [], + typeFilter: 14, + userQuery: "a::mac!", + error: null, + }, ]; diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js index 4b5ab01ac76..6c17d00f16e 100644 --- a/tests/rustdoc-js-std/parser-ident.js +++ b/tests/rustdoc-js-std/parser-ident.js @@ -3,6 +3,7 @@ const QUERY = [ "!", "a!", "a!::b", + "!::b", "a!::b!", ]; @@ -47,47 +48,50 @@ const PARSED = [ }, { elems: [{ - name: "a!", - fullPath: ["a!"], + name: "a", + fullPath: ["a"], pathWithoutLast: [], - pathLast: "a!", + pathLast: "a", generics: [], }], foundElems: 1, original: "a!", returned: [], - typeFilter: -1, + typeFilter: 14, userQuery: "a!", error: null, }, { - elems: [{ - name: "a!::b", - fullPath: ["a!", "b"], - pathWithoutLast: ["a!"], - pathLast: "b", - generics: [], - }], - foundElems: 1, + elems: [], + foundElems: 0, original: "a!::b", returned: [], typeFilter: -1, userQuery: "a!::b", - error: null, + error: "Cannot have associated items in macros", }, { elems: [{ - name: "a!::b!", - fullPath: ["a!", "b!"], - pathWithoutLast: ["a!"], - pathLast: "b!", + name: "!::b", + fullPath: ["!", "b"], + pathWithoutLast: ["!"], + pathLast: "b", generics: [], }], foundElems: 1, + original: "!::b", + returned: [], + typeFilter: -1, + userQuery: "!::b", + error: null, + }, + { + elems: [], + foundElems: 0, original: "a!::b!", returned: [], typeFilter: -1, userQuery: "a!::b!", - error: null, + error: "Cannot have associated items in macros", }, ]; diff --git a/tests/rustdoc-js/macro-search.js b/tests/rustdoc-js/macro-search.js new file mode 100644 index 00000000000..2b179ce146b --- /dev/null +++ b/tests/rustdoc-js/macro-search.js @@ -0,0 +1,10 @@ +// exact-check + +const QUERY = 'abracadabra!'; + +const EXPECTED = { + 'others': [ + { 'path': 'macro_search', 'name': 'abracadabra' }, + { 'path': 'macro_search', 'name': 'abracadabra_b' }, + ], +}; diff --git a/tests/rustdoc-js/macro-search.rs b/tests/rustdoc-js/macro-search.rs new file mode 100644 index 00000000000..dc397490cf5 --- /dev/null +++ b/tests/rustdoc-js/macro-search.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! abracadabra { + () => {} +} +#[macro_export] +macro_rules! abracadabra_b { + () => {} +} +pub fn abracadabra() {} +pub fn abracadabra_c() {} diff --git a/tests/rustdoc-js/where-clause.js b/tests/rustdoc-js/where-clause.js new file mode 100644 index 00000000000..6cb42a455a3 --- /dev/null +++ b/tests/rustdoc-js/where-clause.js @@ -0,0 +1,19 @@ +const QUERY = ['trait<nested>', '-> trait<nested>', 't1, t2']; + +const EXPECTED = [ + { + 'in_args': [ + { 'path': 'where_clause', 'name': 'abracadabra' }, + ], + }, + { + 'others': [ + { 'path': 'where_clause', 'name': 'alacazam' }, + ], + }, + { + 'others': [ + { 'path': 'where_clause', 'name': 'presto' }, + ], + }, +]; diff --git a/tests/rustdoc-js/where-clause.rs b/tests/rustdoc-js/where-clause.rs new file mode 100644 index 00000000000..808561feee2 --- /dev/null +++ b/tests/rustdoc-js/where-clause.rs @@ -0,0 +1,16 @@ +pub struct Nested; + +pub trait Trait<T> { + fn thank_you(x: T); +} + +pub fn abracadabra<X>(_: X) where X: Trait<Nested> {} + +pub fn alacazam<X>() -> X where X: Trait<Nested> {} + +pub trait T1 {} +pub trait T2<'a, T> { + fn please(_: &'a T); +} + +pub fn presto<A, B>(_: A, _: B) where A: T1, B: for <'b> T2<'b, Nested> {} diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 6aa9785f44e..79e6b94f1ac 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -87,6 +87,7 @@ -Z llvm-plugins=val -- a list LLVM plugins to enable (space separated) -Z llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no) -Z location-detail=val -- what location details should be tracked when using caller_location, either `none`, or a comma separated list of location details, for which valid options are `file`, `line`, and `column` (default: `file,line,column`) + -Z lower-impl-trait-in-trait-to-assoc-ty=val -- modify the lowering strategy for `impl Trait` in traits so that they are lowered to generic associated types -Z ls=val -- list the symbols defined by a library crate (default: no) -Z macro-backtrace=val -- show macro backtraces (default: no) -Z maximal-hir-to-mir-coverage=val -- save as much information as possible about the correspondence between MIR and HIR as source scopes (default: no) diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 5e0a0411a62..bd88e73af5c 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -83,3 +83,11 @@ pub enum WithNiche { None, Some(std::num::NonZeroU32), } + +// @hasraw type_layout/enum.Uninhabited.html 'Size: ' +// @hasraw - '0 bytes (<a href="https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited">uninhabited</a>)' +pub enum Uninhabited {} + +// @hasraw type_layout/struct.Uninhabited2.html 'Size: ' +// @hasraw - '8 bytes (<a href="https://doc.rust-lang.org/stable/reference/glossary.html#uninhabited">uninhabited</a>)' +pub struct Uninhabited2(std::convert::Infallible, u64); diff --git a/tests/ui-toml/array_size_threshold/array_size_threshold.rs b/tests/ui-toml/array_size_threshold/array_size_threshold.rs new file mode 100644 index 00000000000..7f623c7a9ec --- /dev/null +++ b/tests/ui-toml/array_size_threshold/array_size_threshold.rs @@ -0,0 +1,10 @@ +#![allow(unused)] +#![warn(clippy::large_const_arrays, clippy::large_stack_arrays)] + +const ABOVE: [u8; 11] = [0; 11]; +const BELOW: [u8; 10] = [0; 10]; + +fn main() { + let above = [0u8; 11]; + let below = [0u8; 10]; +} diff --git a/tests/ui-toml/array_size_threshold/array_size_threshold.stderr b/tests/ui-toml/array_size_threshold/array_size_threshold.stderr new file mode 100644 index 00000000000..ac017b20916 --- /dev/null +++ b/tests/ui-toml/array_size_threshold/array_size_threshold.stderr @@ -0,0 +1,29 @@ +error: large array defined as const + --> $DIR/array_size_threshold.rs:4:1 + | +LL | const ABOVE: [u8; 11] = [0; 11]; + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: make this a static item: `static` + | + = note: `-D clippy::large-const-arrays` implied by `-D warnings` + +error: allocating a local array larger than 10 bytes + --> $DIR/array_size_threshold.rs:4:25 + | +LL | const ABOVE: [u8; 11] = [0; 11]; + | ^^^^^^^ + | + = help: consider allocating on the heap with `vec![0; 11].into_boxed_slice()` + = note: `-D clippy::large-stack-arrays` implied by `-D warnings` + +error: allocating a local array larger than 10 bytes + --> $DIR/array_size_threshold.rs:8:17 + | +LL | let above = [0u8; 11]; + | ^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![0u8; 11].into_boxed_slice()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/array_size_threshold/clippy.toml b/tests/ui-toml/array_size_threshold/clippy.toml new file mode 100644 index 00000000000..3f1fe9a1209 --- /dev/null +++ b/tests/ui-toml/array_size_threshold/clippy.toml @@ -0,0 +1 @@ +array-size-threshold = 10 diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs index 6d934538f4c..a455eef42ea 100644 --- a/tests/ui/abi/stack-probes-lto.rs +++ b/tests/ui/abi/stack-probes-lto.rs @@ -13,5 +13,6 @@ // ignore-fuchsia no exception handler registered for segfault // compile-flags: -C lto // no-prefer-dynamic +// ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino include!("stack-probes.rs"); diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index e7b91644b3b..8dba54c3f81 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -9,6 +9,7 @@ // ignore-emscripten no processes // ignore-sgx no processes // ignore-fuchsia no exception handler registered for segfault +// ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino use std::env; use std::mem::MaybeUninit; diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs new file mode 100644 index 00000000000..afd3db5e052 --- /dev/null +++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs @@ -0,0 +1,71 @@ +// edition: 2021 +// known-bug: #108309 + +#![feature(async_fn_in_trait)] +#![feature(min_specialization)] + +struct MyStruct; + +trait MyTrait<T> { + async fn foo(_: T) -> &'static str; +} + +impl<T> MyTrait<T> for MyStruct { + default async fn foo(_: T) -> &'static str { + "default" + } +} + +impl MyTrait<i32> for MyStruct { + async fn foo(_: i32) -> &'static str { + "specialized" + } +} + +async fn async_main() { + assert_eq!(MyStruct::foo(42).await, "specialized"); + assert_eq!(indirection(42).await, "specialized"); +} + +async fn indirection<T>(x: T) -> &'static str { + //explicit type coercion is currently necessary + // because of https://github.com/rust-lang/rust/issues/67918 + <MyStruct as MyTrait<T>>::foo(x).await +} + +// ------------------------------------------------------------------------- // +// Implementation Details Below... + +use std::future::Future; +use std::pin::Pin; +use std::task::*; + +pub fn noop_waker() -> Waker { + let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE); + + // SAFETY: the contracts for RawWaker and RawWakerVTable are upheld + unsafe { Waker::from_raw(raw) } +} + +const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop); + +unsafe fn noop_clone(_p: *const ()) -> RawWaker { + RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE) +} + +unsafe fn noop(_p: *const ()) {} + +fn main() { + let mut fut = async_main(); + + // Poll loop, just to test the future... + let waker = noop_waker(); + let ctx = &mut Context::from_waker(&waker); + + loop { + match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } { + Poll::Pending => {} + Poll::Ready(()) => break, + } + } +} diff --git a/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr new file mode 100644 index 00000000000..f71fd9980a2 --- /dev/null +++ b/tests/ui/async-await/in-trait/dont-project-to-specializable-projection.stderr @@ -0,0 +1,19 @@ +warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/dont-project-to-specializable-projection.rs:4:12 + | +LL | #![feature(async_fn_in_trait)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information + = note: `#[warn(incomplete_features)]` on by default + +error: async associated function in trait cannot be specialized + --> $DIR/dont-project-to-specializable-projection.rs:14:5 + | +LL | default async fn foo(_: T) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: specialization behaves in inconsistent and surprising ways with `#![feature(async_fn_in_trait)]`, and for now is disallowed + +error: aborting due to previous error; 1 warning emitted + diff --git a/tests/ui/async-await/issues/issue-107280.rs b/tests/ui/async-await/issues/issue-107280.rs new file mode 100644 index 00000000000..85fce87403a --- /dev/null +++ b/tests/ui/async-await/issues/issue-107280.rs @@ -0,0 +1,15 @@ +// edition:2021 + +async fn foo() { + inner::<false>().await + //~^ ERROR: function takes 2 generic arguments but 1 generic argument was supplied + //~| ERROR: type inside `async fn` body must be known in this context + //~| ERROR: type inside `async fn` body must be known in this context + //~| ERROR: type inside `async fn` body must be known in this context + //~| ERROR: type inside `async fn` body must be known in this context + //~| ERROR: type inside `async fn` body must be known in this context +} + +async fn inner<T, const PING: bool>() {} + +fn main() {} diff --git a/tests/ui/async-await/issues/issue-107280.stderr b/tests/ui/async-await/issues/issue-107280.stderr new file mode 100644 index 00000000000..dd3e10fcc18 --- /dev/null +++ b/tests/ui/async-await/issues/issue-107280.stderr @@ -0,0 +1,82 @@ +error[E0107]: function takes 2 generic arguments but 1 generic argument was supplied + --> $DIR/issue-107280.rs:4:5 + | +LL | inner::<false>().await + | ^^^^^ ----- supplied 1 generic argument + | | + | expected 2 generic arguments + | +note: function defined here, with 2 generic parameters: `T`, `PING` + --> $DIR/issue-107280.rs:13:10 + | +LL | async fn inner<T, const PING: bool>() {} + | ^^^^^ - ---------------- +help: add missing generic argument + | +LL | inner::<false, PING>().await + | ++++++ + +error[E0698]: type inside `async fn` body must be known in this context + --> $DIR/issue-107280.rs:4:5 + | +LL | inner::<false>().await + | ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner` + | +note: the type is part of the `async fn` body because of this `await` + --> $DIR/issue-107280.rs:4:21 + | +LL | inner::<false>().await + | ^^^^^^ + +error[E0698]: type inside `async fn` body must be known in this context + --> $DIR/issue-107280.rs:4:5 + | +LL | inner::<false>().await + | ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner` + | +note: the type is part of the `async fn` body because of this `await` + --> $DIR/issue-107280.rs:4:21 + | +LL | inner::<false>().await + | ^^^^^^ + +error[E0698]: type inside `async fn` body must be known in this context + --> $DIR/issue-107280.rs:4:5 + | +LL | inner::<false>().await + | ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner` + | +note: the type is part of the `async fn` body because of this `await` + --> $DIR/issue-107280.rs:4:21 + | +LL | inner::<false>().await + | ^^^^^^ + +error[E0698]: type inside `async fn` body must be known in this context + --> $DIR/issue-107280.rs:4:5 + | +LL | inner::<false>().await + | ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner` + | +note: the type is part of the `async fn` body because of this `await` + --> $DIR/issue-107280.rs:4:21 + | +LL | inner::<false>().await + | ^^^^^^ + +error[E0698]: type inside `async fn` body must be known in this context + --> $DIR/issue-107280.rs:4:5 + | +LL | inner::<false>().await + | ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner` + | +note: the type is part of the `async fn` body because of this `await` + --> $DIR/issue-107280.rs:4:21 + | +LL | inner::<false>().await + | ^^^^^^ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0107, E0698. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr b/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr index ae4d0d5853c..5ae1d78a92b 100644 --- a/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr +++ b/tests/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr @@ -17,8 +17,9 @@ error[E0700]: hidden type for `impl Trait<'a>` captures lifetime that does not a --> $DIR/ret-impl-trait-one.rs:16:80 | LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { - | ____________________________________--__________________________________________^ - | | | + | ____________________________________--___________________________--------------_^ + | | | | + | | | opaque type defined here | | hidden type `(&'a u8, &'b u8)` captures the lifetime `'b` as defined here LL | | LL | | (a, b) diff --git a/tests/ui/attributes/rustc-box.rs b/tests/ui/attributes/rustc-box.rs new file mode 100644 index 00000000000..b3726fb3867 --- /dev/null +++ b/tests/ui/attributes/rustc-box.rs @@ -0,0 +1,18 @@ +#![feature(rustc_attrs, stmt_expr_attributes)] + +fn foo(_: u32, _: u32) {} +fn bar(_: u32) {} + +fn main() { + #[rustc_box] + Box::new(1); // OK + #[rustc_box] + Box::pin(1); //~ ERROR `#[rustc_box]` attribute used incorrectly + #[rustc_box] + foo(1, 1); //~ ERROR `#[rustc_box]` attribute used incorrectly + #[rustc_box] + bar(1); //~ ERROR `#[rustc_box]` attribute used incorrectly + #[rustc_box] //~ ERROR `#[rustc_box]` attribute used incorrectly + #[rustfmt::skip] + Box::new(1); +} diff --git a/tests/ui/attributes/rustc-box.stderr b/tests/ui/attributes/rustc-box.stderr new file mode 100644 index 00000000000..073a18c7d58 --- /dev/null +++ b/tests/ui/attributes/rustc-box.stderr @@ -0,0 +1,34 @@ +error: `#[rustc_box]` attribute used incorrectly + --> $DIR/rustc-box.rs:10:5 + | +LL | Box::pin(1); + | ^^^^^^^^^^^ + | + = note: `#[rustc_box]` may only be applied to a `Box::new()` call + +error: `#[rustc_box]` attribute used incorrectly + --> $DIR/rustc-box.rs:12:5 + | +LL | foo(1, 1); + | ^^^^^^^^^ + | + = note: `#[rustc_box]` may only be applied to a `Box::new()` call + +error: `#[rustc_box]` attribute used incorrectly + --> $DIR/rustc-box.rs:14:5 + | +LL | bar(1); + | ^^^^^^ + | + = note: `#[rustc_box]` may only be applied to a `Box::new()` call + +error: `#[rustc_box]` attribute used incorrectly + --> $DIR/rustc-box.rs:15:5 + | +LL | #[rustc_box] + | ^^^^^^^^^^^^ + | + = note: no other attributes may be applied + +error: aborting due to 4 previous errors + diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs index 1bda7a49713..127a3f5b2dc 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.rs @@ -8,6 +8,7 @@ fn a() { //~^ NOTE `vec[_]` is borrowed here vec[0] = Box::new(4); //~ ERROR cannot assign //~^ NOTE `vec[_]` is assigned to here + //~| NOTE in this expansion of desugaring of drop and replace _a.use_ref(); //~^ NOTE borrow later used here } @@ -22,6 +23,7 @@ fn b() { //~^ `vec[_]` is borrowed here vec[0] = Box::new(4); //~ ERROR cannot assign //~^ NOTE `vec[_]` is assigned to here + //~| NOTE in this expansion of desugaring of drop and replace _b.use_ref(); //~^ NOTE borrow later used here } diff --git a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr index 70b9e4f4433..5e1251b0590 100644 --- a/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr +++ b/tests/ui/borrowck/borrowck-vec-pattern-nesting.stderr @@ -6,24 +6,24 @@ LL | [box ref _a, _, _] => { LL | LL | vec[0] = Box::new(4); | ^^^^^^ `vec[_]` is assigned to here but it was already borrowed -LL | +... LL | _a.use_ref(); | ------------ borrow later used here error[E0506]: cannot assign to `vec[_]` because it is borrowed - --> $DIR/borrowck-vec-pattern-nesting.rs:23:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:24:13 | LL | &mut [ref _b @ ..] => { | ------ `vec[_]` is borrowed here LL | LL | vec[0] = Box::new(4); | ^^^^^^ `vec[_]` is assigned to here but it was already borrowed -LL | +... LL | _b.use_ref(); | ------------ borrow later used here error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:34:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:36:11 | LL | match vec { | ^^^ cannot move out of here @@ -41,7 +41,7 @@ LL + [_a, | error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:46:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:48:13 | LL | let a = vec[0]; | ^^^^^^ @@ -55,7 +55,7 @@ LL | let a = &vec[0]; | + error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:55:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:57:11 | LL | match vec { | ^^^ cannot move out of here @@ -73,7 +73,7 @@ LL + [ | error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:65:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:67:13 | LL | let a = vec[0]; | ^^^^^^ @@ -87,7 +87,7 @@ LL | let a = &vec[0]; | + error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:74:11 + --> $DIR/borrowck-vec-pattern-nesting.rs:76:11 | LL | match vec { | ^^^ cannot move out of here @@ -106,7 +106,7 @@ LL + [_a, _b, _c] => {} | error[E0508]: cannot move out of type `[Box<isize>]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:85:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:87:13 | LL | let a = vec[0]; | ^^^^^^ diff --git a/tests/ui/borrowck/issue-45199.rs b/tests/ui/borrowck/issue-45199.rs index ded46e56e34..6a6b25541f3 100644 --- a/tests/ui/borrowck/issue-45199.rs +++ b/tests/ui/borrowck/issue-45199.rs @@ -5,6 +5,7 @@ fn test_drop_replace() { b = Box::new(1); //~ NOTE first assignment b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` //~| NOTE cannot assign twice to immutable + //~| NOTE in this expansion of desugaring of drop and replace } fn test_call() { @@ -13,12 +14,14 @@ fn test_call() { //~| SUGGESTION mut b b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` //~| NOTE cannot assign twice to immutable + //~| NOTE in this expansion of desugaring of drop and replace } fn test_args(b: Box<i32>) { //~ HELP consider making this binding mutable //~| SUGGESTION mut b b = Box::new(2); //~ ERROR cannot assign to immutable argument `b` //~| NOTE cannot assign to immutable argument + //~| NOTE in this expansion of desugaring of drop and replace } fn main() {} diff --git a/tests/ui/borrowck/issue-45199.stderr b/tests/ui/borrowck/issue-45199.stderr index 47aa3090827..163f2370ba0 100644 --- a/tests/ui/borrowck/issue-45199.stderr +++ b/tests/ui/borrowck/issue-45199.stderr @@ -10,7 +10,7 @@ LL | b = Box::new(2); | ^ cannot assign twice to immutable variable error[E0384]: cannot assign twice to immutable variable `b` - --> $DIR/issue-45199.rs:14:5 + --> $DIR/issue-45199.rs:15:5 | LL | let b = Box::new(1); | - @@ -22,7 +22,7 @@ LL | b = Box::new(2); | ^ cannot assign twice to immutable variable error[E0384]: cannot assign to immutable argument `b` - --> $DIR/issue-45199.rs:20:5 + --> $DIR/issue-45199.rs:22:5 | LL | fn test_args(b: Box<i32>) { | - help: consider making this binding mutable: `mut b` diff --git a/tests/ui/borrowck/issue-58776-borrowck-scans-children.rs b/tests/ui/borrowck/issue-58776-borrowck-scans-children.rs index efa313a9d23..0b2372d1274 100644 --- a/tests/ui/borrowck/issue-58776-borrowck-scans-children.rs +++ b/tests/ui/borrowck/issue-58776-borrowck-scans-children.rs @@ -5,7 +5,6 @@ fn main() { greeting = "DEALLOCATED".to_string(); //~^ ERROR cannot assign drop(greeting); - //~^ ERROR cannot move println!("thread result: {:?}", res); } diff --git a/tests/ui/borrowck/issue-58776-borrowck-scans-children.stderr b/tests/ui/borrowck/issue-58776-borrowck-scans-children.stderr index 0870b423769..967451c68be 100644 --- a/tests/ui/borrowck/issue-58776-borrowck-scans-children.stderr +++ b/tests/ui/borrowck/issue-58776-borrowck-scans-children.stderr @@ -12,21 +12,6 @@ LL | greeting = "DEALLOCATED".to_string(); LL | println!("thread result: {:?}", res); | --- borrow later used here -error[E0505]: cannot move out of `greeting` because it is borrowed - --> $DIR/issue-58776-borrowck-scans-children.rs:7:10 - | -LL | let res = (|| (|| &greeting)())(); - | -- -------- borrow occurs due to use in closure - | | - | borrow of `greeting` occurs here -... -LL | drop(greeting); - | ^^^^^^^^ move out of `greeting` occurs here -... -LL | println!("thread result: {:?}", res); - | --- borrow later used here - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0505, E0506. -For more information about an error, try `rustc --explain E0505`. +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/closures/2229_closure_analysis/issue_88118.rs b/tests/ui/closures/2229_closure_analysis/issue_88118.rs index 453b7e04a36..bfb487649a3 100644 --- a/tests/ui/closures/2229_closure_analysis/issue_88118.rs +++ b/tests/ui/closures/2229_closure_analysis/issue_88118.rs @@ -1,10 +1,7 @@ // Regression test for #88118. Used to ICE. -// +// edition:2021 // check-pass -#![allow(incomplete_features)] -#![feature(capture_disjoint_fields)] - fn foo<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) { Box::new(move |value| { (|_| handler.clone()())(value); diff --git a/tests/ui/command/command-setgroups.rs b/tests/ui/command/command-setgroups.rs index aff67f91bba..7e321f2f0cd 100644 --- a/tests/ui/command/command-setgroups.rs +++ b/tests/ui/command/command-setgroups.rs @@ -3,6 +3,7 @@ // ignore-emscripten // ignore-sgx // ignore-musl - returns dummy result for _SC_NGROUPS_MAX +// ignore-nto - does not have `/bin/id`, expects groups to be i32 (not u32) #![feature(rustc_private)] #![feature(setgroups)] diff --git a/tests/ui/const-generics/issues/issue-105821.rs b/tests/ui/const-generics/issues/issue-105821.rs new file mode 100644 index 00000000000..cba2e22c460 --- /dev/null +++ b/tests/ui/const-generics/issues/issue-105821.rs @@ -0,0 +1,23 @@ +// check-pass + +#![allow(incomplete_features)] +#![feature(adt_const_params, const_ptr_read, generic_const_exprs)] +#![allow(dead_code)] + +const fn catone<const M: usize>(_a: &[u8; M]) -> [u8; M + 1] +where + [(); M + 1]:, +{ + unimplemented!() +} + +struct Catter<const A: &'static [u8]>; +impl<const A: &'static [u8]> Catter<A> +where + [(); A.len() + 1]:, +{ + const ZEROS: &'static [u8; A.len()] = &[0_u8; A.len()]; + const R: &'static [u8] = &catone(Self::ZEROS); +} + +fn main() {} diff --git a/tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs b/tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs index 5134c672f5f..0fbd851431e 100644 --- a/tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs +++ b/tests/ui/errors/trait-bound-error-spans/blame-trait-error.rs @@ -18,11 +18,85 @@ struct Burrito<F> { filling: F, } +impl<It: Iterator> T1 for Option<It> {} + +impl<'a, A: T1> T1 for &'a A {} + fn want<V: T1>(_x: V) {} +enum ExampleTuple<T> { + ExampleTupleVariant(T), +} +use ExampleDifferentTupleVariantName as ExampleYetAnotherTupleVariantName; +use ExampleTuple as ExampleOtherTuple; +use ExampleTuple::ExampleTupleVariant as ExampleDifferentTupleVariantName; +use ExampleTuple::*; + +impl<A> T1 for ExampleTuple<A> where A: T3 {} + +enum ExampleStruct<T> { + ExampleStructVariant { field: T }, +} +use ExampleDifferentStructVariantName as ExampleYetAnotherStructVariantName; +use ExampleStruct as ExampleOtherStruct; +use ExampleStruct::ExampleStructVariant as ExampleDifferentStructVariantName; +use ExampleStruct::*; + +impl<A> T1 for ExampleStruct<A> where A: T3 {} + +struct ExampleActuallyTupleStruct<T>(T, i32); +use ExampleActuallyTupleStruct as ExampleActuallyTupleStructOther; + +impl<A> T1 for ExampleActuallyTupleStruct<A> where A: T3 {} + fn example<Q>(q: Q) { want(Wrapper { value: Burrito { filling: q } }); //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277] + + want(Some(())); + //~^ ERROR `()` is not an iterator [E0277] + + want(Some(q)); + //~^ ERROR `Q` is not an iterator [E0277] + + want(&Some(q)); + //~^ ERROR `Q` is not an iterator [E0277] + + want(&ExampleTuple::ExampleTupleVariant(q)); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleTupleVariant(q)); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleOtherTuple::ExampleTupleVariant(q)); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleDifferentTupleVariantName(q)); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleYetAnotherTupleVariantName(q)); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleStruct::ExampleStructVariant { field: q }); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleStructVariant { field: q }); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleOtherStruct::ExampleStructVariant { field: q }); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleDifferentStructVariantName { field: q }); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleYetAnotherStructVariantName { field: q }); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleActuallyTupleStruct(q, 0)); + //~^ ERROR `Q: T3` is not satisfied [E0277] + + want(&ExampleActuallyTupleStructOther(q, 0)); + //~^ ERROR `Q: T3` is not satisfied [E0277] } fn main() {} diff --git a/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr b/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr index 27b002db130..9228a047e87 100644 --- a/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr +++ b/tests/ui/errors/trait-bound-error-spans/blame-trait-error.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Q: T3` is not satisfied - --> $DIR/blame-trait-error.rs:24:46 + --> $DIR/blame-trait-error.rs:53:46 | LL | want(Wrapper { value: Burrito { filling: q } }); | ---- ^ the trait `T3` is not implemented for `Q` @@ -21,7 +21,7 @@ LL | impl<B: T2> T1 for Wrapper<B> {} | | | unsatisfied trait bound introduced here note: required by a bound in `want` - --> $DIR/blame-trait-error.rs:21:12 + --> $DIR/blame-trait-error.rs:25:12 | LL | fn want<V: T1>(_x: V) {} | ^^ required by this bound in `want` @@ -30,6 +30,373 @@ help: consider restricting type parameter `Q` LL | fn example<Q: T3>(q: Q) { | ++++ -error: aborting due to previous error +error[E0277]: `()` is not an iterator + --> $DIR/blame-trait-error.rs:56:15 + | +LL | want(Some(())); + | ---- ^^ `()` is not an iterator + | | + | required by a bound introduced by this call + | + = help: the trait `Iterator` is not implemented for `()` + = help: the trait `T1` is implemented for `Option<It>` +note: required for `Option<()>` to implement `T1` + --> $DIR/blame-trait-error.rs:21:20 + | +LL | impl<It: Iterator> T1 for Option<It> {} + | -------- ^^ ^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` + +error[E0277]: `Q` is not an iterator + --> $DIR/blame-trait-error.rs:59:15 + | +LL | want(Some(q)); + | ---- ^ `Q` is not an iterator + | | + | required by a bound introduced by this call + | +note: required for `Option<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:21:20 + | +LL | impl<It: Iterator> T1 for Option<It> {} + | -------- ^^ ^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: std::iter::Iterator>(q: Q) { + | +++++++++++++++++++++ + +error[E0277]: `Q` is not an iterator + --> $DIR/blame-trait-error.rs:62:16 + | +LL | want(&Some(q)); + | ---- ^ `Q` is not an iterator + | | + | required by a bound introduced by this call + | +note: required for `Option<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:21:20 + | +LL | impl<It: Iterator> T1 for Option<It> {} + | -------- ^^ ^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&Option<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: std::iter::Iterator>(q: Q) { + | +++++++++++++++++++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:65:45 + | +LL | want(&ExampleTuple::ExampleTupleVariant(q)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleTuple<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:35:9 + | +LL | impl<A> T1 for ExampleTuple<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleTuple<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:68:31 + | +LL | want(&ExampleTupleVariant(q)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleTuple<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:35:9 + | +LL | impl<A> T1 for ExampleTuple<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleTuple<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:71:50 + | +LL | want(&ExampleOtherTuple::ExampleTupleVariant(q)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleTuple<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:35:9 + | +LL | impl<A> T1 for ExampleTuple<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleTuple<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:74:44 + | +LL | want(&ExampleDifferentTupleVariantName(q)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleTuple<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:35:9 + | +LL | impl<A> T1 for ExampleTuple<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleTuple<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:77:45 + | +LL | want(&ExampleYetAnotherTupleVariantName(q)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleTuple<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:35:9 + | +LL | impl<A> T1 for ExampleTuple<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleTuple<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:80:56 + | +LL | want(&ExampleStruct::ExampleStructVariant { field: q }); + | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q` + | +note: required for `ExampleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:45:9 + | +LL | impl<A> T1 for ExampleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:83:41 + | +LL | want(&ExampleStructVariant { field: q }); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:45:9 + | +LL | impl<A> T1 for ExampleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:86:61 + | +LL | want(&ExampleOtherStruct::ExampleStructVariant { field: q }); + | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q` + | +note: required for `ExampleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:45:9 + | +LL | impl<A> T1 for ExampleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:89:54 + | +LL | want(&ExampleDifferentStructVariantName { field: q }); + | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q` + | +note: required for `ExampleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:45:9 + | +LL | impl<A> T1 for ExampleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:92:55 + | +LL | want(&ExampleYetAnotherStructVariantName { field: q }); + | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q` + | +note: required for `ExampleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:45:9 + | +LL | impl<A> T1 for ExampleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:95:38 + | +LL | want(&ExampleActuallyTupleStruct(q, 0)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleActuallyTupleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:50:9 + | +LL | impl<A> T1 for ExampleActuallyTupleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleActuallyTupleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error[E0277]: the trait bound `Q: T3` is not satisfied + --> $DIR/blame-trait-error.rs:98:43 + | +LL | want(&ExampleActuallyTupleStructOther(q, 0)); + | ---- ^ the trait `T3` is not implemented for `Q` + | | + | required by a bound introduced by this call + | +note: required for `ExampleActuallyTupleStruct<Q>` to implement `T1` + --> $DIR/blame-trait-error.rs:50:9 + | +LL | impl<A> T1 for ExampleActuallyTupleStruct<A> where A: T3 {} + | ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- unsatisfied trait bound introduced here + = note: 1 redundant requirement hidden + = note: required for `&ExampleActuallyTupleStruct<Q>` to implement `T1` +note: required by a bound in `want` + --> $DIR/blame-trait-error.rs:25:12 + | +LL | fn want<V: T1>(_x: V) {} + | ^^ required by this bound in `want` +help: consider restricting type parameter `Q` + | +LL | fn example<Q: T3>(q: Q) { + | ++++ + +error: aborting due to 16 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr b/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr index 6913771f288..b6a24e12bcc 100644 --- a/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr +++ b/tests/ui/errors/traits/blame-trait-error-spans-on-exprs.stderr @@ -91,10 +91,10 @@ LL | fn example<Q: T3>(q: Q) { | ++++ error[E0277]: the trait bound `Q: T3` is not satisfied - --> $DIR/blame-trait-error-spans-on-exprs.rs:93:27 + --> $DIR/blame-trait-error-spans-on-exprs.rs:93:53 | LL | want(Wrapper { value: TacoKinds::OneTaco(false, q) }); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q` + | ---- ^ the trait `T3` is not implemented for `Q` | | | required by a bound introduced by this call | diff --git a/tests/ui/extenv/extenv-not-defined-default.rs b/tests/ui/extenv/extenv-not-defined-default.rs index 30a06a25465..1fb046c78f2 100644 --- a/tests/ui/extenv/extenv-not-defined-default.rs +++ b/tests/ui/extenv/extenv-not-defined-default.rs @@ -1,4 +1,4 @@ fn main() { - env!("__HOPEFULLY_NOT_DEFINED__"); - //~^ ERROR: environment variable `__HOPEFULLY_NOT_DEFINED__` not defined + env!("CARGO__HOPEFULLY_NOT_DEFINED__"); + //~^ ERROR: environment variable `CARGO__HOPEFULLY_NOT_DEFINED__` not defined } diff --git a/tests/ui/extenv/extenv-not-defined-default.stderr b/tests/ui/extenv/extenv-not-defined-default.stderr index 884875dca53..e3dce000530 100644 --- a/tests/ui/extenv/extenv-not-defined-default.stderr +++ b/tests/ui/extenv/extenv-not-defined-default.stderr @@ -1,9 +1,10 @@ -error: environment variable `__HOPEFULLY_NOT_DEFINED__` not defined +error: environment variable `CARGO__HOPEFULLY_NOT_DEFINED__` not defined at compile time --> $DIR/extenv-not-defined-default.rs:2:5 | -LL | env!("__HOPEFULLY_NOT_DEFINED__"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | env!("CARGO__HOPEFULLY_NOT_DEFINED__"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: Cargo sets build script variables at run time. Use `std::env::var("CARGO__HOPEFULLY_NOT_DEFINED__")` instead = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/tests/ui/extenv/issue-55897.stderr b/tests/ui/extenv/issue-55897.stderr index 92e8a44b55f..5752a965e35 100644 --- a/tests/ui/extenv/issue-55897.stderr +++ b/tests/ui/extenv/issue-55897.stderr @@ -1,9 +1,10 @@ -error: environment variable `NON_EXISTENT` not defined +error: environment variable `NON_EXISTENT` not defined at compile time --> $DIR/issue-55897.rs:11:22 | LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs")); | ^^^^^^^^^^^^^^^^^^^^ | + = help: Use `std::env::var("NON_EXISTENT")` to read the variable at run time = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: suffixes on string literals are invalid diff --git a/tests/ui/for/for-else-err.rs b/tests/ui/for/for-else-err.rs new file mode 100644 index 00000000000..16252e980e4 --- /dev/null +++ b/tests/ui/for/for-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + for _ in 0..1 { + //~^ NOTE `else` is attached to this loop + } else { + //~^ ERROR `for...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + } +} diff --git a/tests/ui/for/for-else-err.stderr b/tests/ui/for/for-else-err.stderr new file mode 100644 index 00000000000..b330d107647 --- /dev/null +++ b/tests/ui/for/for-else-err.stderr @@ -0,0 +1,17 @@ +error: `for...else` loops are not supported + --> $DIR/for-else-err.rs:4:7 + | +LL | for _ in 0..1 { + | --- `else` is attached to this loop +LL | +LL | } else { + | _______^ +LL | | +LL | | +LL | | } + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/for/for-else-let-else-err.rs b/tests/ui/for/for-else-let-else-err.rs new file mode 100644 index 00000000000..c0b96f97294 --- /dev/null +++ b/tests/ui/for/for-else-let-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + let _ = for _ in 0..1 { + //~^ NOTE `else` is attached to this loop + } else { + //~^ ERROR `for...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/for/for-else-let-else-err.stderr b/tests/ui/for/for-else-let-else-err.stderr new file mode 100644 index 00000000000..a2396a8fbb1 --- /dev/null +++ b/tests/ui/for/for-else-let-else-err.stderr @@ -0,0 +1,17 @@ +error: `for...else` loops are not supported + --> $DIR/for-else-let-else-err.rs:4:7 + | +LL | let _ = for _ in 0..1 { + | --- `else` is attached to this loop +LL | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/impl-trait/hidden-lifetimes.stderr b/tests/ui/impl-trait/hidden-lifetimes.stderr index 3cc47e1e89d..bc8f559fdee 100644 --- a/tests/ui/impl-trait/hidden-lifetimes.stderr +++ b/tests/ui/impl-trait/hidden-lifetimes.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Swap + 'a` captures lifetime that does not a --> $DIR/hidden-lifetimes.rs:29:5 | LL | fn hide_ref<'a, 'b, T: 'static>(x: &'a mut &'b T) -> impl Swap + 'a { - | -- hidden type `&'a mut &'b T` captures the lifetime `'b` as defined here + | -- -------------- opaque type defined here + | | + | hidden type `&'a mut &'b T` captures the lifetime `'b` as defined here LL | x | ^ | @@ -15,7 +17,9 @@ error[E0700]: hidden type for `impl Swap + 'a` captures lifetime that does not a --> $DIR/hidden-lifetimes.rs:46:5 | LL | fn hide_rc_refcell<'a, 'b: 'a, T: 'static>(x: Rc<RefCell<&'b T>>) -> impl Swap + 'a { - | -- hidden type `Rc<RefCell<&'b T>>` captures the lifetime `'b` as defined here + | -- -------------- opaque type defined here + | | + | hidden type `Rc<RefCell<&'b T>>` captures the lifetime `'b` as defined here LL | x | ^ | diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds-2.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds-2.stderr index 433b76b7afa..d56e1273f24 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds-2.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds-2.stderr @@ -1,6 +1,8 @@ error[E0700]: hidden type for `impl Debug` captures lifetime that does not appear in bounds --> $DIR/impl-fn-hrtb-bounds-2.rs:5:9 | +LL | fn a() -> impl Fn(&u8) -> impl Debug { + | ---------- opaque type defined here LL | |x| x | --- ^ | | diff --git a/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-trait.rs b/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-trait.rs new file mode 100644 index 00000000000..dfce973d770 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/new-lowering-strategy/simple-trait.rs @@ -0,0 +1,11 @@ +// check-pass +// compile-flags: -Zlower-impl-trait-in-trait-to-assoc-ty + +#![feature(return_position_impl_trait_in_trait)] +#![allow(incomplete_features)] + +trait Foo { + fn foo() -> impl Sized; +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/specialization-broken.rs b/tests/ui/impl-trait/in-trait/specialization-broken.rs index 9d27d3710a6..2fcffdf3f9a 100644 --- a/tests/ui/impl-trait/in-trait/specialization-broken.rs +++ b/tests/ui/impl-trait/in-trait/specialization-broken.rs @@ -15,6 +15,7 @@ where { fn bar(&self) -> U { //~^ ERROR method `bar` has an incompatible type for trait + //~| ERROR method with return-position `impl Trait` in trait cannot be specialized *self } } diff --git a/tests/ui/impl-trait/in-trait/specialization-broken.stderr b/tests/ui/impl-trait/in-trait/specialization-broken.stderr index 37cfd74498d..dc621d6b8a8 100644 --- a/tests/ui/impl-trait/in-trait/specialization-broken.stderr +++ b/tests/ui/impl-trait/in-trait/specialization-broken.stderr @@ -18,6 +18,14 @@ LL | fn bar(&self) -> impl Sized; = note: expected signature `fn(&U) -> impl Sized` found signature `fn(&U) -> U` -error: aborting due to previous error +error: method with return-position `impl Trait` in trait cannot be specialized + --> $DIR/specialization-broken.rs:16:5 + | +LL | fn bar(&self) -> U { + | ^^^^^^^^^^^^^^^^^^ + | + = note: specialization behaves in inconsistent and surprising ways with `#![feature(return_position_impl_trait_in_trait)]`, and for now is disallowed + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr index 90875708094..5b0b1cc5e42 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling-2.stderr @@ -1,6 +1,9 @@ error[E0700]: hidden type for `E<'b, 'c>` captures lifetime that does not appear in bounds --> $DIR/error-handling-2.rs:22:5 | +LL | type E<'a, 'b> = impl Sized; + | ---------- opaque type defined here +LL | LL | fn foo<'a: 'b, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { | -- hidden type `*mut &'a i32` captures the lifetime `'a` as defined here ... diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index ec49a61795a..68ac22a05f4 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Trait<'d, 'e>` captures lifetime that does n --> $DIR/ordinary-bounds-unrelated.rs:28:33 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | -- hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here + | -- ------------------ opaque type defined here + | | + | hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here ... LL | if condition() { a } else { b } | ^ diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index c36f9bc6957..493a9e66eaf 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Trait<'a, 'b>` captures lifetime that does n --> $DIR/ordinary-bounds-unsuited.rs:31:33 | LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> - | -- hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here + | -- ------------------ opaque type defined here + | | + | hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here ... LL | if condition() { a } else { b } | ^ diff --git a/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr index 9c81791fbcb..55e3cd95064 100644 --- a/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -2,8 +2,9 @@ error[E0700]: hidden type for `impl Copy` captures lifetime that does not appear --> $DIR/must_outlive_least_region_or_bound.rs:3:35 | LL | fn elided(x: &i32) -> impl Copy { x } - | ---- ^ - | | + | ---- --------- ^ + | | | + | | opaque type defined here | hidden type `&i32` captures the anonymous lifetime defined here | help: to declare that `impl Copy` captures `'_`, you can add an explicit `'_` lifetime bound @@ -15,8 +16,9 @@ error[E0700]: hidden type for `impl Copy` captures lifetime that does not appear --> $DIR/must_outlive_least_region_or_bound.rs:6:44 | LL | fn explicit<'a>(x: &'a i32) -> impl Copy { x } - | -- ^ - | | + | -- --------- ^ + | | | + | | opaque type defined here | hidden type `&'a i32` captures the lifetime `'a` as defined here | help: to declare that `impl Copy` captures `'a`, you can add an explicit `'a` lifetime bound @@ -100,7 +102,9 @@ error[E0700]: hidden type for `impl Fn(&'a u32)` captures lifetime that does not --> $DIR/must_outlive_least_region_or_bound.rs:38:5 | LL | fn move_lifetime_into_fn<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Fn(&'a u32) { - | -- hidden type `[closure@$DIR/must_outlive_least_region_or_bound.rs:38:5: 38:13]` captures the lifetime `'b` as defined here + | -- ---------------- opaque type defined here + | | + | hidden type `[closure@$DIR/must_outlive_least_region_or_bound.rs:38:5: 38:13]` captures the lifetime `'b` as defined here LL | move |_| println!("{}", y) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | diff --git a/tests/ui/impl-trait/nested-return-type4.stderr b/tests/ui/impl-trait/nested-return-type4.stderr index e761a60e79c..907822ebbc3 100644 --- a/tests/ui/impl-trait/nested-return-type4.stderr +++ b/tests/ui/impl-trait/nested-return-type4.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Future<Output = impl Sized>` captures lifeti --> $DIR/nested-return-type4.rs:4:5 | LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> { - | -- hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here + | -- --------------------------------------------- opaque type defined here + | | + | hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here LL | async move { let _s = s; } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | diff --git a/tests/ui/impl-trait/region-escape-via-bound.stderr b/tests/ui/impl-trait/region-escape-via-bound.stderr index 44a790cb1a4..e4556bc21a7 100644 --- a/tests/ui/impl-trait/region-escape-via-bound.stderr +++ b/tests/ui/impl-trait/region-escape-via-bound.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Trait<'y>` captures lifetime that does not a --> $DIR/region-escape-via-bound.rs:17:5 | LL | fn foo<'x, 'y>(x: Cell<&'x u32>) -> impl Trait<'y> - | -- hidden type `Cell<&'x u32>` captures the lifetime `'x` as defined here + | -- -------------- opaque type defined here + | | + | hidden type `Cell<&'x u32>` captures the lifetime `'x` as defined here ... LL | x | ^ diff --git a/tests/ui/impl-trait/static-return-lifetime-infered.stderr b/tests/ui/impl-trait/static-return-lifetime-infered.stderr index c451f8e37c4..488cb821c10 100644 --- a/tests/ui/impl-trait/static-return-lifetime-infered.stderr +++ b/tests/ui/impl-trait/static-return-lifetime-infered.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that --> $DIR/static-return-lifetime-infered.rs:7:9 | LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> { - | ----- hidden type `Map<std::slice::Iter<'_, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:7:27: 7:30]>` captures the anonymous lifetime defined here + | ----- ----------------------- opaque type defined here + | | + | hidden type `Map<std::slice::Iter<'_, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:7:27: 7:30]>` captures the anonymous lifetime defined here LL | self.x.iter().map(|a| a.0) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -15,7 +17,9 @@ error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that --> $DIR/static-return-lifetime-infered.rs:11:9 | LL | fn iter_values<'a>(&'a self) -> impl Iterator<Item=u32> { - | -- hidden type `Map<std::slice::Iter<'a, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:11:27: 11:30]>` captures the lifetime `'a` as defined here + | -- ----------------------- opaque type defined here + | | + | hidden type `Map<std::slice::Iter<'a, (u32, u32)>, [closure@$DIR/static-return-lifetime-infered.rs:11:27: 11:30]>` captures the lifetime `'a` as defined here LL | self.x.iter().map(|a| a.0) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs index c8b1ff1dbce..b99bb39d062 100644 --- a/tests/ui/intrinsics/intrinsic-alignment.rs +++ b/tests/ui/intrinsics/intrinsic-alignment.rs @@ -22,7 +22,9 @@ mod rusti { target_os = "netbsd", target_os = "openbsd", target_os = "solaris", - target_os = "vxworks"))] + target_os = "vxworks", + target_os = "nto", +))] mod m { #[cfg(target_arch = "x86")] pub fn main() { diff --git a/tests/ui/let-else/let-else-brace-before-else.fixed b/tests/ui/let-else/let-else-brace-before-else.fixed index a75c770ddf2..2d85e3878cc 100644 --- a/tests/ui/let-else/let-else-brace-before-else.fixed +++ b/tests/ui/let-else/let-else-brace-before-else.fixed @@ -7,10 +7,6 @@ fn main() { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed return; }; - let Some(1) = (loop { break Some(1) }) else { - //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed - return; - }; let 2 = 1 + (match 1 { n => n }) else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed return; diff --git a/tests/ui/let-else/let-else-brace-before-else.rs b/tests/ui/let-else/let-else-brace-before-else.rs index 5603b946f38..5c3375b3f28 100644 --- a/tests/ui/let-else/let-else-brace-before-else.rs +++ b/tests/ui/let-else/let-else-brace-before-else.rs @@ -7,10 +7,6 @@ fn main() { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed return; }; - let Some(1) = loop { break Some(1) } else { - //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed - return; - }; let 2 = 1 + match 1 { n => n } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed return; diff --git a/tests/ui/let-else/let-else-brace-before-else.stderr b/tests/ui/let-else/let-else-brace-before-else.stderr index cb01e4c18a1..ee53213d8c2 100644 --- a/tests/ui/let-else/let-else-brace-before-else.stderr +++ b/tests/ui/let-else/let-else-brace-before-else.stderr @@ -10,18 +10,7 @@ LL | let Some(1) = ({ Some(1) }) else { | + + error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/let-else-brace-before-else.rs:10:40 - | -LL | let Some(1) = loop { break Some(1) } else { - | ^ - | -help: wrap the expression in parentheses - | -LL | let Some(1) = (loop { break Some(1) }) else { - | + + - -error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/let-else-brace-before-else.rs:14:34 + --> $DIR/let-else-brace-before-else.rs:10:34 | LL | let 2 = 1 + match 1 { n => n } else { | ^ @@ -32,7 +21,7 @@ LL | let 2 = 1 + (match 1 { n => n }) else { | + + error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/let-else-brace-before-else.rs:18:40 + --> $DIR/let-else-brace-before-else.rs:14:40 | LL | let Some(1) = unsafe { unsafe_fn() } else { | ^ @@ -42,5 +31,5 @@ help: wrap the expression in parentheses LL | let Some(1) = (unsafe { unsafe_fn() }) else { | + + -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/lifetimes/issue-105227.stderr b/tests/ui/lifetimes/issue-105227.stderr index d2114593735..b514db461b4 100644 --- a/tests/ui/lifetimes/issue-105227.stderr +++ b/tests/ui/lifetimes/issue-105227.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime tha --> $DIR/issue-105227.rs:7:5 | LL | fn chars0(v :(& str, &str)) -> impl Iterator<Item = char> { - | ----- hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here + | ----- -------------------------- opaque type defined here + | | + | hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here LL | LL | v.0.chars().chain(v.1.chars()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +18,9 @@ error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime tha --> $DIR/issue-105227.rs:13:5 | LL | fn chars1(v0 : & str, v1 : &str) -> impl Iterator<Item = char> { - | ----- hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here + | ----- -------------------------- opaque type defined here + | | + | hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here LL | LL | v0.chars().chain(v1.chars()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +35,10 @@ error[E0700]: hidden type for `impl Iterator<Item = char>` captures lifetime tha | LL | fn chars2<'b>(v0 : &str, v1 : &'_ str, v2 : &'b str) -> | ---- hidden type `std::iter::Chain<Chars<'_>, Chars<'_>>` captures the anonymous lifetime defined here -... +LL | +LL | (impl Iterator<Item = char>, &'b str) + | -------------------------- opaque type defined here +LL | { LL | (v0.chars().chain(v1.chars()), v2) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | diff --git a/tests/ui/lifetimes/issue-107988.rs b/tests/ui/lifetimes/issue-107988.rs new file mode 100644 index 00000000000..92cb60a06a2 --- /dev/null +++ b/tests/ui/lifetimes/issue-107988.rs @@ -0,0 +1,13 @@ +pub trait TraitEngine<'tcx>: 'tcx {} + +pub trait TraitEngineExt<'tcx> { + fn register_predicate_obligations(&mut self); +} + +impl<T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { + //~^ ERROR use of undeclared lifetime name `'tcx` + //~| ERROR use of undeclared lifetime name `'tcx` + fn register_predicate_obligations(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/lifetimes/issue-107988.stderr b/tests/ui/lifetimes/issue-107988.stderr new file mode 100644 index 00000000000..c2d8c7050e9 --- /dev/null +++ b/tests/ui/lifetimes/issue-107988.stderr @@ -0,0 +1,27 @@ +error[E0261]: use of undeclared lifetime name `'tcx` + --> $DIR/issue-107988.rs:7:52 + | +LL | impl<T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { + | - ^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'tcx` here: `'tcx,` + +error[E0261]: use of undeclared lifetime name `'tcx` + --> $DIR/issue-107988.rs:7:30 + | +LL | impl<T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { + | ^^^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'tcx` lifetime + | +LL | impl<T: ?Sized + for<'tcx> TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { + | +++++++++ +help: consider introducing lifetime `'tcx` here + | +LL | impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { + | +++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/lint/issue-104897.rs b/tests/ui/lint/issue-104897.rs index 5fbc658f155..2d298aff9db 100644 --- a/tests/ui/lint/issue-104897.rs +++ b/tests/ui/lint/issue-104897.rs @@ -1,6 +1,5 @@ // error-pattern: this file contains an unclosed delimiter // error-pattern: this file contains an unclosed delimiter // error-pattern: this file contains an unclosed delimiter -// error-pattern: format argument must be a string literal fn f(){(print!(á diff --git a/tests/ui/lint/issue-104897.stderr b/tests/ui/lint/issue-104897.stderr index 817a93c2f3b..728d51f34a4 100644 --- a/tests/ui/lint/issue-104897.stderr +++ b/tests/ui/lint/issue-104897.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-104897.rs:6:18 + --> $DIR/issue-104897.rs:5:18 | LL | fn f(){(print!(á | -- - ^ @@ -8,36 +8,5 @@ LL | fn f(){(print!(á | |unclosed delimiter | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/issue-104897.rs:6:18 - | -LL | fn f(){(print!(á - | -- - ^ - | || | - | || unclosed delimiter - | |unclosed delimiter - | unclosed delimiter - -error: this file contains an unclosed delimiter - --> $DIR/issue-104897.rs:6:18 - | -LL | fn f(){(print!(á - | -- - ^ - | || | - | || unclosed delimiter - | |unclosed delimiter - | unclosed delimiter - -error: format argument must be a string literal - --> $DIR/issue-104897.rs:6:16 - | -LL | fn f(){(print!(á - | ^ - | -help: you might be missing a string literal to format with - | -LL | fn f(){(print!("{}", á - | +++++ - -error: aborting due to 4 previous errors +error: aborting due to previous error diff --git a/tests/ui/lint/unused_parens_multibyte_recovery.rs b/tests/ui/lint/unused_parens_multibyte_recovery.rs index 8fcfae22a3d..bc03faf3fce 100644 --- a/tests/ui/lint/unused_parens_multibyte_recovery.rs +++ b/tests/ui/lint/unused_parens_multibyte_recovery.rs @@ -3,7 +3,6 @@ // error-pattern: this file contains an unclosed delimiter // error-pattern: this file contains an unclosed delimiter // error-pattern: this file contains an unclosed delimiter -// error-pattern: format argument must be a string literal // // Verify that unused parens lint does not try to create a span // which points in the middle of a multibyte character. diff --git a/tests/ui/lint/unused_parens_multibyte_recovery.stderr b/tests/ui/lint/unused_parens_multibyte_recovery.stderr index a0302b17e25..adbf27fcca2 100644 --- a/tests/ui/lint/unused_parens_multibyte_recovery.stderr +++ b/tests/ui/lint/unused_parens_multibyte_recovery.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/unused_parens_multibyte_recovery.rs:11:17 + --> $DIR/unused_parens_multibyte_recovery.rs:10:17 | LL | fn f(){(print!(á | -- - ^ @@ -8,36 +8,5 @@ LL | fn f(){(print!(á | |unclosed delimiter | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/unused_parens_multibyte_recovery.rs:11:17 - | -LL | fn f(){(print!(á - | -- - ^ - | || | - | || unclosed delimiter - | |unclosed delimiter - | unclosed delimiter - -error: this file contains an unclosed delimiter - --> $DIR/unused_parens_multibyte_recovery.rs:11:17 - | -LL | fn f(){(print!(á - | -- - ^ - | || | - | || unclosed delimiter - | |unclosed delimiter - | unclosed delimiter - -error: format argument must be a string literal - --> $DIR/unused_parens_multibyte_recovery.rs:11:16 - | -LL | fn f(){(print!(á - | ^ - | -help: you might be missing a string literal to format with - | -LL | fn f(){(print!("{}", á - | +++++ - -error: aborting due to 4 previous errors +error: aborting due to previous error diff --git a/tests/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs b/tests/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs index c9b16e43910..293fdca1cc9 100644 --- a/tests/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs +++ b/tests/ui/liveness/liveness-assign/liveness-assign-imm-local-with-drop.rs @@ -5,6 +5,7 @@ fn test() { drop(b); b = Box::new(2); //~ ERROR cannot assign twice to immutable variable `b` //~| NOTE cannot assign twice to immutable + //~| NOTE in this expansion of desugaring of drop and replace drop(b); } diff --git a/tests/ui/loops/loop-else-break-with-value.rs b/tests/ui/loops/loop-else-break-with-value.rs new file mode 100644 index 00000000000..670d8a145c0 --- /dev/null +++ b/tests/ui/loops/loop-else-break-with-value.rs @@ -0,0 +1,10 @@ +fn main() { + let Some(1) = loop { + //~^ NOTE `else` is attached to this loop + break Some(1) + } else { + //~^ ERROR `loop...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + return; + }; +} diff --git a/tests/ui/loops/loop-else-break-with-value.stderr b/tests/ui/loops/loop-else-break-with-value.stderr new file mode 100644 index 00000000000..972e2d341ec --- /dev/null +++ b/tests/ui/loops/loop-else-break-with-value.stderr @@ -0,0 +1,18 @@ +error: `loop...else` loops are not supported + --> $DIR/loop-else-break-with-value.rs:5:7 + | +LL | let Some(1) = loop { + | ---- `else` is attached to this loop +... +LL | } else { + | _______^ +LL | | +LL | | +LL | | return; +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/loops/loop-else-err.rs b/tests/ui/loops/loop-else-err.rs new file mode 100644 index 00000000000..202a09c255c --- /dev/null +++ b/tests/ui/loops/loop-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + loop { + //~^ NOTE `else` is attached to this loop + } else { + //~^ ERROR `loop...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + } +} diff --git a/tests/ui/loops/loop-else-err.stderr b/tests/ui/loops/loop-else-err.stderr new file mode 100644 index 00000000000..c2c5c84cded --- /dev/null +++ b/tests/ui/loops/loop-else-err.stderr @@ -0,0 +1,17 @@ +error: `loop...else` loops are not supported + --> $DIR/loop-else-err.rs:4:7 + | +LL | loop { + | ---- `else` is attached to this loop +LL | +LL | } else { + | _______^ +LL | | +LL | | +LL | | } + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/loops/loop-else-let-else-err.rs b/tests/ui/loops/loop-else-let-else-err.rs new file mode 100644 index 00000000000..e828ffef549 --- /dev/null +++ b/tests/ui/loops/loop-else-let-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + let _ = loop { + //~^ NOTE `else` is attached to this loop + } else { + //~^ ERROR `loop...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/loops/loop-else-let-else-err.stderr b/tests/ui/loops/loop-else-let-else-err.stderr new file mode 100644 index 00000000000..a57c784ff6f --- /dev/null +++ b/tests/ui/loops/loop-else-let-else-err.stderr @@ -0,0 +1,17 @@ +error: `loop...else` loops are not supported + --> $DIR/loop-else-let-else-err.rs:4:7 + | +LL | let _ = loop { + | ---- `else` is attached to this loop +LL | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/macros/issue-102878.rs b/tests/ui/macros/issue-102878.rs index aac5891939e..bdd9c256954 100644 --- a/tests/ui/macros/issue-102878.rs +++ b/tests/ui/macros/issue-102878.rs @@ -1,9 +1,5 @@ macro_rules!test{($l:expr,$_:r)=>({const:y y)} //~^ ERROR mismatched closing delimiter: `)` -//~| ERROR invalid fragment specifier `r` -//~| ERROR expected identifier, found keyword `const` -//~| ERROR expected identifier, found keyword `const` -//~| ERROR expected identifier, found `:` fn s(){test!(1,i)} diff --git a/tests/ui/macros/issue-102878.stderr b/tests/ui/macros/issue-102878.stderr index e0b8855a38d..034e3731b87 100644 --- a/tests/ui/macros/issue-102878.stderr +++ b/tests/ui/macros/issue-102878.stderr @@ -7,54 +7,5 @@ LL | macro_rules!test{($l:expr,$_:r)=>({const:y y)} | |unclosed delimiter | closing delimiter possibly meant for this -error: invalid fragment specifier `r` - --> $DIR/issue-102878.rs:1:27 - | -LL | macro_rules!test{($l:expr,$_:r)=>({const:y y)} - | ^^^^ - | - = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis` - -error: expected identifier, found keyword `const` - --> $DIR/issue-102878.rs:1:36 - | -LL | macro_rules!test{($l:expr,$_:r)=>({const:y y)} - | ^^^^^ expected identifier, found keyword -... -LL | fn s(){test!(1,i)} - | ---------- in this macro invocation - | - = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) -help: escape `const` to use it as an identifier - | -LL | macro_rules!test{($l:expr,$_:r)=>({r#const:y y)} - | ++ - -error: expected identifier, found keyword `const` - --> $DIR/issue-102878.rs:1:36 - | -LL | macro_rules!test{($l:expr,$_:r)=>({const:y y)} - | ^^^^^ expected identifier, found keyword -... -LL | fn s(){test!(1,i)} - | ---------- in this macro invocation - | - = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) -help: escape `const` to use it as an identifier - | -LL | macro_rules!test{($l:expr,$_:r)=>({r#const:y y)} - | ++ - -error: expected identifier, found `:` - --> $DIR/issue-102878.rs:1:41 - | -LL | macro_rules!test{($l:expr,$_:r)=>({const:y y)} - | ^ expected identifier -... -LL | fn s(){test!(1,i)} - | ---------- in this macro invocation - | - = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors +error: aborting due to previous error diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index d42f6c179b7..93fbc9c8a44 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -150,18 +150,19 @@ error: expected string literal LL | env!(invalid); | ^^^^^^^ -error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:105:10 +error: env! takes 1 or 2 arguments + --> $DIR/macros-nonfatal-errors.rs:105:5 | LL | env!(foo, abr, baz); - | ^^^ + | ^^^^^^^^^^^^^^^^^^^ -error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined +error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time --> $DIR/macros-nonfatal-errors.rs:106:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: Use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: format argument must be a string literal diff --git a/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr b/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr index 0479035171e..89e0d982eaf 100644 --- a/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr +++ b/tests/ui/malformed/issue-107423-unused-delim-only-one-no-pair.stderr @@ -8,25 +8,5 @@ LL | fn a(){{{ | |unclosed delimiter | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:7:11 - | -LL | fn a(){{{ - | --- ^ - | ||| - | ||unclosed delimiter - | |unclosed delimiter - | unclosed delimiter - -error: this file contains an unclosed delimiter - --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:7:11 - | -LL | fn a(){{{ - | --- ^ - | ||| - | ||unclosed delimiter - | |unclosed delimiter - | unclosed delimiter - -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/methods/method-call-err-msg.stderr b/tests/ui/methods/method-call-err-msg.stderr index c340c2d32b3..bd51378cf1a 100644 --- a/tests/ui/methods/method-call-err-msg.stderr +++ b/tests/ui/methods/method-call-err-msg.stderr @@ -48,14 +48,17 @@ LL | .two(0, /* isize */); error[E0599]: `Foo` is not an iterator --> $DIR/method-call-err-msg.rs:19:7 | -LL | pub struct Foo; - | -------------- - | | - | method `take` not found for this struct - | doesn't satisfy `Foo: Iterator` +LL | pub struct Foo; + | -------------- + | | + | method `take` not found for this struct + | doesn't satisfy `Foo: Iterator` ... -LL | .take() - | ^^^^ `Foo` is not an iterator +LL | / y.zero() +LL | | .take() + | | -^^^^ `Foo` is not an iterator + | |______| + | | = note: the following trait bounds were not satisfied: `Foo: Iterator` diff --git a/tests/ui/mir/validate/storage-live.rs b/tests/ui/mir/validate/storage-live.rs new file mode 100644 index 00000000000..ed3c26ed6da --- /dev/null +++ b/tests/ui/mir/validate/storage-live.rs @@ -0,0 +1,30 @@ +// compile-flags: -Zvalidate-mir -Ztreat-err-as-bug +// failure-status: 101 +// error-pattern: broken MIR in +// error-pattern: StorageLive(_1) which already has storage here +// normalize-stderr-test "note: .*\n\n" -> "" +// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" +// normalize-stderr-test "storage_live\[....\]" -> "storage_live[HASH]" +// rustc-env:RUST_BACKTRACE=0 + +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; +use core::ptr::{addr_of, addr_of_mut}; + +#[custom_mir(dialect = "built")] +fn multiple_storage() { + mir!( + let a: usize; + { + StorageLive(a); + StorageLive(a); + Return() + } + ) +} + +fn main() { + multiple_storage() +} diff --git a/tests/ui/mir/validate/storage-live.stderr b/tests/ui/mir/validate/storage-live.stderr new file mode 100644 index 00000000000..b586a865849 --- /dev/null +++ b/tests/ui/mir/validate/storage-live.stderr @@ -0,0 +1,13 @@ +error: internal compiler error: broken MIR in Item(WithOptConstParam { did: DefId(0:8 ~ storage_live[HASH]::multiple_storage), const_param_did: None }) (before pass CheckPackedRef) at bb0[1]: + StorageLive(_1) which already has storage here + --> $DIR/storage-live.rs:22:13 + | +LL | StorageLive(a); + | ^^^^^^^^^^^^^^ + +error: the compiler unexpectedly panicked. this is a bug. + +query stack during panic: +#0 [mir_const] preparing `multiple_storage` for borrow checking +#1 [mir_promoted] processing MIR for `multiple_storage` +end of query stack diff --git a/tests/ui/nll/issue-73159-rpit-static.stderr b/tests/ui/nll/issue-73159-rpit-static.stderr index 260b9b59772..4d3a9015316 100644 --- a/tests/ui/nll/issue-73159-rpit-static.stderr +++ b/tests/ui/nll/issue-73159-rpit-static.stderr @@ -4,6 +4,7 @@ error[E0700]: hidden type for `impl Iterator<Item = u8>` captures lifetime that LL | impl<'a> Foo<'a> { | -- hidden type `Copied<std::slice::Iter<'a, u8>>` captures the lifetime `'a` as defined here LL | fn make_it(&self) -> impl Iterator<Item = u8> { + | ------------------------ opaque type defined here LL | self.0.iter().copied() | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr b/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr index 1e6ef614dee..e0d476a33b2 100644 --- a/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr +++ b/tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr @@ -24,7 +24,9 @@ error[E0700]: hidden type for `impl Cap<'b> + Cap<'c>` captures lifetime that do --> $DIR/min-choice-reject-ambiguous.rs:39:5 | LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> - | -- hidden type `&'a u8` captures the lifetime `'a` as defined here + | -- ---------------------- opaque type defined here + | | + | hidden type `&'a u8` captures the lifetime `'a` as defined here ... LL | s | ^ diff --git a/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr b/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr index 6824e27ead0..483b5822b9d 100644 --- a/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr +++ b/tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` --> $DIR/nested-impl-trait-fail.rs:17:5 | LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> - | -- hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here + | -- ------------------------------------------------ opaque type defined here + | | + | hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here ... LL | [a] | ^^^ @@ -20,7 +22,9 @@ error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that do --> $DIR/nested-impl-trait-fail.rs:17:5 | LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> - | -- hidden type `&'s u8` captures the lifetime `'s` as defined here + | -- ---------------------- opaque type defined here + | | + | hidden type `&'s u8` captures the lifetime `'s` as defined here ... LL | [a] | ^^^ @@ -40,6 +44,8 @@ error[E0700]: hidden type for `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` LL | fn fail_late_bound<'s, 'a, 'b>( | -- hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here ... +LL | ) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> { + | ------------------------------------------------ opaque type defined here LL | [a] | ^^^ | @@ -58,6 +64,8 @@ error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that do LL | fn fail_late_bound<'s, 'a, 'b>( | -- hidden type `&'s u8` captures the lifetime `'s` as defined here ... +LL | ) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> { + | ---------------------- opaque type defined here LL | [a] | ^^^ | diff --git a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr index 7b9ed171d2d..7fcb68252cf 100644 --- a/tests/ui/nll/ty-outlives/impl-trait-captures.stderr +++ b/tests/ui/nll/ty-outlives/impl-trait-captures.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `Opaque(DefId(0:13 ~ impl_trait_captures[1afc]::fo --> $DIR/impl-trait-captures.rs:11:5 | LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> { - | -- hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[1afc]::foo::'_), '_)) T` captures the anonymous lifetime defined here + | -- ------------ opaque type defined here + | | + | hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:12 ~ impl_trait_captures[1afc]::foo::'_), '_)) T` captures the anonymous lifetime defined here LL | x | ^ | diff --git a/tests/ui/numbers-arithmetic/issue-105626.rs b/tests/ui/numbers-arithmetic/issue-105626.rs new file mode 100644 index 00000000000..f97edd510c9 --- /dev/null +++ b/tests/ui/numbers-arithmetic/issue-105626.rs @@ -0,0 +1,17 @@ +// run-pass +// only-x86 +// min-system-llvm-version: 16 +// compile-flags: -Ctarget-feature=+sse2 + +use std::hint::black_box; + +fn main() { + let n: i64 = black_box(0x3fffffdfffffff); + let r = f32::from_bits(0x5a7fffff); + + assert_ne!((n as f64) as f32, n as f32); + + // FIXME: these assertions fail if only x87 is enabled + assert_eq!(n as i64 as f32, r); + assert_eq!(n as u64 as f32, r); +} diff --git a/tests/ui/parser/deli-ident-issue-1.rs b/tests/ui/parser/deli-ident-issue-1.rs index 54485262a0c..224ee6c09e0 100644 --- a/tests/ui/parser/deli-ident-issue-1.rs +++ b/tests/ui/parser/deli-ident-issue-1.rs @@ -15,8 +15,6 @@ impl dyn Demo { if let Some(b) = val && let Some(c) = num { && b == c { - //~^ ERROR expected struct - //~| ERROR mismatched types } } } diff --git a/tests/ui/parser/deli-ident-issue-1.stderr b/tests/ui/parser/deli-ident-issue-1.stderr index 1119edb199f..eb5073e14cf 100644 --- a/tests/ui/parser/deli-ident-issue-1.stderr +++ b/tests/ui/parser/deli-ident-issue-1.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/deli-ident-issue-1.rs:24:66 + --> $DIR/deli-ident-issue-1.rs:22:66 | LL | impl dyn Demo { | - unclosed delimiter @@ -13,25 +13,5 @@ LL | } LL | fn main() { } | ^ -error[E0574]: expected struct, variant or union type, found local variable `c` - --> $DIR/deli-ident-issue-1.rs:17:17 - | -LL | && b == c { - | ^ not a struct, variant or union type - -error[E0308]: mismatched types - --> $DIR/deli-ident-issue-1.rs:17:9 - | -LL | fn check(&self, val: Option<u32>, num: Option<u32>) { - | - expected `()` because of default return type -... -LL | / && b == c { -LL | | -LL | | -LL | | } - | |_________^ expected `()`, found `bool` - -error: aborting due to 3 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0308, E0574. -For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/deli-ident-issue-2.stderr b/tests/ui/parser/deli-ident-issue-2.stderr index c8f59c9d32b..e0188cdfb4a 100644 --- a/tests/ui/parser/deli-ident-issue-2.stderr +++ b/tests/ui/parser/deli-ident-issue-2.stderr @@ -1,3 +1,11 @@ +error: mismatched closing delimiter: `]` + --> $DIR/deli-ident-issue-2.rs:2:14 + | +LL | if 1 < 2 { + | ^ unclosed delimiter +LL | let _a = vec!]; + | ^ mismatched closing delimiter + error: unexpected closing delimiter: `}` --> $DIR/deli-ident-issue-2.rs:5:1 | @@ -7,13 +15,5 @@ LL | } LL | } | ^ unexpected closing delimiter -error: mismatched closing delimiter: `]` - --> $DIR/deli-ident-issue-2.rs:2:14 - | -LL | if 1 < 2 { - | ^ unclosed delimiter -LL | let _a = vec!]; - | ^ mismatched closing delimiter - error: aborting due to 2 previous errors diff --git a/tests/ui/parser/do-not-suggest-semicolon-before-array.rs b/tests/ui/parser/do-not-suggest-semicolon-before-array.rs index 7ebf3f6b0d8..7eff7f431be 100644 --- a/tests/ui/parser/do-not-suggest-semicolon-before-array.rs +++ b/tests/ui/parser/do-not-suggest-semicolon-before-array.rs @@ -2,7 +2,7 @@ fn foo() {} fn bar() -> [u8; 2] { foo() - [1, 3) //~ ERROR expected one of `.`, `?`, `]`, or an operator, found `,` + [1, 3) //~ ERROR mismatched closing delimiter } fn main() {} diff --git a/tests/ui/parser/do-not-suggest-semicolon-before-array.stderr b/tests/ui/parser/do-not-suggest-semicolon-before-array.stderr index a9dd526321f..7b43c77005e 100644 --- a/tests/ui/parser/do-not-suggest-semicolon-before-array.stderr +++ b/tests/ui/parser/do-not-suggest-semicolon-before-array.stderr @@ -1,8 +1,8 @@ -error: expected one of `.`, `?`, `]`, or an operator, found `,` +error: mismatched closing delimiter: `)` --> $DIR/do-not-suggest-semicolon-before-array.rs:5:5 | LL | [1, 3) - | ^ ^ help: `]` may belong here + | ^ ^ mismatched closing delimiter | | | unclosed delimiter diff --git a/tests/ui/parser/issue-103451.rs b/tests/ui/parser/issue-103451.rs index 1fdb0014881..be33213f3cb 100644 --- a/tests/ui/parser/issue-103451.rs +++ b/tests/ui/parser/issue-103451.rs @@ -1,5 +1,4 @@ // error-pattern: this file contains an unclosed delimiter -// error-pattern: expected value, found struct `R` struct R { } struct S { x: [u8; R diff --git a/tests/ui/parser/issue-103451.stderr b/tests/ui/parser/issue-103451.stderr index eb3c92fb43d..6aacd5012c1 100644 --- a/tests/ui/parser/issue-103451.stderr +++ b/tests/ui/parser/issue-103451.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-103451.rs:5:15 + --> $DIR/issue-103451.rs:4:15 | LL | struct S { | - unclosed delimiter @@ -8,25 +8,5 @@ LL | x: [u8; R | | | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/issue-103451.rs:5:15 - | -LL | struct S { - | - unclosed delimiter -LL | x: [u8; R - | - ^ - | | - | unclosed delimiter - -error[E0423]: expected value, found struct `R` - --> $DIR/issue-103451.rs:5:13 - | -LL | struct R { } - | ------------ `R` defined here -LL | struct S { -LL | x: [u8; R - | ^ help: use struct literal syntax instead: `R {}` - -error: aborting due to 3 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/parser/issue-107705.rs b/tests/ui/parser/issue-107705.rs new file mode 100644 index 00000000000..b80984fcdb0 --- /dev/null +++ b/tests/ui/parser/issue-107705.rs @@ -0,0 +1,3 @@ +// compile-flags: -C debug-assertions + +fn f() {a(b:&, //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/issue-107705.stderr b/tests/ui/parser/issue-107705.stderr new file mode 100644 index 00000000000..d2d61346118 --- /dev/null +++ b/tests/ui/parser/issue-107705.stderr @@ -0,0 +1,10 @@ +error: this file contains an unclosed delimiter + --> $DIR/issue-107705.rs:3:67 + | +LL | fn f() {a(b:&, + | - - unclosed delimiter ^ + | | + | unclosed delimiter + +error: aborting due to previous error + diff --git a/tests/ui/parser/issue-108495-dec.rs b/tests/ui/parser/issue-108495-dec.rs new file mode 100644 index 00000000000..e0816f84e5c --- /dev/null +++ b/tests/ui/parser/issue-108495-dec.rs @@ -0,0 +1,39 @@ +fn test0() { + let mut i = 0; + let _ = i + i--; //~ ERROR Rust has no postfix decrement operator + // won't suggest since we can not handle the precedences +} + +fn test1() { + let mut i = 0; + let _ = i-- + i--; //~ ERROR Rust has no postfix decrement operator +} + +fn test2() { + let mut i = 0; + let _ = --i + i--; //~ ERROR Rust has no postfix decrement operator +} + +fn test3() { + let mut i = 0; + let _ = i-- + --i; //~ ERROR Rust has no postfix decrement operator +} + +fn test4() { + let mut i = 0; + let _ = (1 + 2 + i)--; //~ ERROR Rust has no postfix decrement operator +} + +fn test5() { + let mut i = 0; + let _ = (i-- + 1) + 2; //~ ERROR Rust has no postfix decrement operator +} + +fn test6(){ + let i=10; + while i != 0 { + i--; //~ ERROR Rust has no postfix decrement operator + } +} + +fn main() {} diff --git a/tests/ui/parser/issue-108495-dec.stderr b/tests/ui/parser/issue-108495-dec.stderr new file mode 100644 index 00000000000..85b29038f7c --- /dev/null +++ b/tests/ui/parser/issue-108495-dec.stderr @@ -0,0 +1,69 @@ +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:3:18 + | +LL | let _ = i + i--; + | ^^ not a valid postfix operator + +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:9:14 + | +LL | let _ = i-- + i--; + | ^^ not a valid postfix operator + | +help: use `-= 1` instead + | +LL | let _ = { let tmp = i; i -= 1; tmp } + i--; + | +++++++++++ ~~~~~~~~~~~~~~~ + +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:14:20 + | +LL | let _ = --i + i--; + | ^^ not a valid postfix operator + +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:19:14 + | +LL | let _ = i-- + --i; + | ^^ not a valid postfix operator + | +help: use `-= 1` instead + | +LL | let _ = { let tmp = i; i -= 1; tmp } + --i; + | +++++++++++ ~~~~~~~~~~~~~~~ + +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:24:24 + | +LL | let _ = (1 + 2 + i)--; + | ^^ not a valid postfix operator + | +help: use `-= 1` instead + | +LL | let _ = { let tmp = (1 + 2 + i); (1 + 2 + i) -= 1; tmp }; + | +++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:29:15 + | +LL | let _ = (i-- + 1) + 2; + | ^^ not a valid postfix operator + | +help: use `-= 1` instead + | +LL | let _ = ({ let tmp = i; i -= 1; tmp } + 1) + 2; + | +++++++++++ ~~~~~~~~~~~~~~~ + +error: Rust has no postfix decrement operator + --> $DIR/issue-108495-dec.rs:35:10 + | +LL | i--; + | ^^ not a valid postfix operator + | +help: use `-= 1` instead + | +LL | i -= 1; + | ~~~~ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr index 2c08d41a15f..0ecb748a0a4 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-2.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-2.stderr @@ -1,3 +1,11 @@ +error: mismatched closing delimiter: `)` + --> $DIR/issue-68987-unmatch-issue-2.rs:3:32 + | +LL | async fn obstest() -> Result<> { + | ^ unclosed delimiter +LL | let obs_connect = || -> Result<(), MyError) { + | ^ mismatched closing delimiter + error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-2.rs:14:1 | @@ -7,13 +15,5 @@ LL | let obs_connect = || -> Result<(), MyError) { LL | } | ^ unexpected closing delimiter -error: mismatched closing delimiter: `)` - --> $DIR/issue-68987-unmatch-issue-2.rs:3:32 - | -LL | async fn obstest() -> Result<> { - | ^ unclosed delimiter -LL | let obs_connect = || -> Result<(), MyError) { - | ^ mismatched closing delimiter - error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr index a3fc46a1e88..dfc4407ed65 100644 --- a/tests/ui/parser/issue-68987-unmatch-issue-3.stderr +++ b/tests/ui/parser/issue-68987-unmatch-issue-3.stderr @@ -1,3 +1,11 @@ +error: mismatched closing delimiter: `)` + --> $DIR/issue-68987-unmatch-issue-3.rs:5:19 + | +LL | while cnt < j { + | ^ unclosed delimiter +LL | write!&mut res, " "); + | ^ mismatched closing delimiter + error: unexpected closing delimiter: `}` --> $DIR/issue-68987-unmatch-issue-3.rs:8:1 | @@ -7,13 +15,5 @@ LL | } LL | } | ^ unexpected closing delimiter -error: mismatched closing delimiter: `)` - --> $DIR/issue-68987-unmatch-issue-3.rs:5:19 - | -LL | while cnt < j { - | ^ unclosed delimiter -LL | write!&mut res, " "); - | ^ mismatched closing delimiter - error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issue-81804.rs b/tests/ui/parser/issue-81804.rs index 803bde11e20..ebc4752a142 100644 --- a/tests/ui/parser/issue-81804.rs +++ b/tests/ui/parser/issue-81804.rs @@ -1,8 +1,5 @@ // error-pattern: this file contains an unclosed delimiter // error-pattern: this file contains an unclosed delimiter -// error-pattern: expected pattern, found `=` -// error-pattern: expected one of `)`, `,`, `->`, `where`, or `{`, found `]` -// error-pattern: expected item, found `]` fn main() {} diff --git a/tests/ui/parser/issue-81804.stderr b/tests/ui/parser/issue-81804.stderr index 19c4422c622..de3b33ecd95 100644 --- a/tests/ui/parser/issue-81804.stderr +++ b/tests/ui/parser/issue-81804.stderr @@ -1,14 +1,13 @@ -error: this file contains an unclosed delimiter - --> $DIR/issue-81804.rs:9:11 +error: mismatched closing delimiter: `}` + --> $DIR/issue-81804.rs:6:8 | LL | fn p([=(} - | -- ^ - | || - | |unclosed delimiter - | unclosed delimiter + | ^^ mismatched closing delimiter + | | + | unclosed delimiter error: this file contains an unclosed delimiter - --> $DIR/issue-81804.rs:9:11 + --> $DIR/issue-81804.rs:6:11 | LL | fn p([=(} | -- ^ @@ -16,26 +15,5 @@ LL | fn p([=(} | |unclosed delimiter | unclosed delimiter -error: expected pattern, found `=` - --> $DIR/issue-81804.rs:9:7 - | -LL | fn p([=(} - | ^ expected pattern - -error: expected one of `)`, `,`, `->`, `where`, or `{`, found `]` - --> $DIR/issue-81804.rs:9:8 - | -LL | fn p([=(} - | ^ -^ - | | | - | | help: `)` may belong here - | unclosed delimiter - -error: expected item, found `]` - --> $DIR/issue-81804.rs:9:11 - | -LL | fn p([=(} - | ^ expected item - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issue-81827.rs b/tests/ui/parser/issue-81827.rs index 7ec58159413..91defd12a57 100644 --- a/tests/ui/parser/issue-81827.rs +++ b/tests/ui/parser/issue-81827.rs @@ -1,6 +1,5 @@ // error-pattern: this file contains an unclosed delimiter // error-pattern: mismatched closing delimiter: `]` -// error-pattern: expected one of `)` or `,`, found `{` #![crate_name="0"] diff --git a/tests/ui/parser/issue-81827.stderr b/tests/ui/parser/issue-81827.stderr index 867244b72e8..63d135f73e6 100644 --- a/tests/ui/parser/issue-81827.stderr +++ b/tests/ui/parser/issue-81827.stderr @@ -1,15 +1,14 @@ -error: this file contains an unclosed delimiter - --> $DIR/issue-81827.rs:11:27 +error: mismatched closing delimiter: `]` + --> $DIR/issue-81827.rs:10:23 | LL | fn r()->i{0|{#[cfg(r(0{]0 - | - - - ^ - | | | | - | | | missing open `[` for this delimiter - | | unclosed delimiter - | unclosed delimiter + | - ^^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this error: this file contains an unclosed delimiter - --> $DIR/issue-81827.rs:11:27 + --> $DIR/issue-81827.rs:10:27 | LL | fn r()->i{0|{#[cfg(r(0{]0 | - - - ^ @@ -18,20 +17,5 @@ LL | fn r()->i{0|{#[cfg(r(0{]0 | | unclosed delimiter | unclosed delimiter -error: mismatched closing delimiter: `]` - --> $DIR/issue-81827.rs:11:23 - | -LL | fn r()->i{0|{#[cfg(r(0{]0 - | - ^^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: expected one of `)` or `,`, found `{` - --> $DIR/issue-81827.rs:11:23 - | -LL | fn r()->i{0|{#[cfg(r(0{]0 - | ^ expected one of `)` or `,` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-104367.rs b/tests/ui/parser/issues/issue-104367.rs new file mode 100644 index 00000000000..861761b64f4 --- /dev/null +++ b/tests/ui/parser/issues/issue-104367.rs @@ -0,0 +1,6 @@ +#[derive(A)] +struct S { + d: [u32; { + #![cfg] { + #![w,) //~ ERROR mismatched closing delimiter + //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/issues/issue-104367.stderr b/tests/ui/parser/issues/issue-104367.stderr new file mode 100644 index 00000000000..e6e76535761 --- /dev/null +++ b/tests/ui/parser/issues/issue-104367.stderr @@ -0,0 +1,26 @@ +error: mismatched closing delimiter: `)` + --> $DIR/issue-104367.rs:5:15 + | +LL | #![w,) + | ^ ^ mismatched closing delimiter + | | + | unclosed delimiter + +error: this file contains an unclosed delimiter + --> $DIR/issue-104367.rs:6:71 + | +LL | struct S { + | - unclosed delimiter +LL | d: [u32; { + | - - unclosed delimiter + | | + | unclosed delimiter +LL | #![cfg] { + | - unclosed delimiter +LL | #![w,) + | - missing open `(` for this delimiter +LL | + | ^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issues/issue-105209.rs b/tests/ui/parser/issues/issue-105209.rs new file mode 100644 index 00000000000..6146b795de1 --- /dev/null +++ b/tests/ui/parser/issues/issue-105209.rs @@ -0,0 +1,3 @@ +// compile-flags: -Zunpretty=ast-tree +#![c={#![c[)x //~ ERROR mismatched closing delimiter + //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/issues/issue-105209.stderr b/tests/ui/parser/issues/issue-105209.stderr new file mode 100644 index 00000000000..c75eafa1833 --- /dev/null +++ b/tests/ui/parser/issues/issue-105209.stderr @@ -0,0 +1,22 @@ +error: mismatched closing delimiter: `)` + --> $DIR/issue-105209.rs:2:11 + | +LL | #![c={#![c[)x + | ^^ mismatched closing delimiter + | | + | unclosed delimiter + +error: this file contains an unclosed delimiter + --> $DIR/issue-105209.rs:3:68 + | +LL | #![c={#![c[)x + | - - - - missing open `(` for this delimiter + | | | | + | | | unclosed delimiter + | | unclosed delimiter + | unclosed delimiter +LL | + | ^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issues/issue-10636-2.rs b/tests/ui/parser/issues/issue-10636-2.rs index 6fb63639d5f..80d8ef65a69 100644 --- a/tests/ui/parser/issues/issue-10636-2.rs +++ b/tests/ui/parser/issues/issue-10636-2.rs @@ -1,11 +1,11 @@ +// error-pattern: mismatched closing delimiter: `}` // FIXME(31528) we emit a bunch of silly errors here due to continuing past the // first one. This would be easy-ish to address by better recovery in tokenisation. pub fn trace_option(option: Option<isize>) { option.map(|some| 42; - //~^ ERROR: expected one of + } -//~^ ERROR: expected expression, found `)` fn main() {} diff --git a/tests/ui/parser/issues/issue-10636-2.stderr b/tests/ui/parser/issues/issue-10636-2.stderr index d4f2da9e3ab..4cd4be1803e 100644 --- a/tests/ui/parser/issues/issue-10636-2.stderr +++ b/tests/ui/parser/issues/issue-10636-2.stderr @@ -1,16 +1,13 @@ -error: expected one of `)`, `,`, `.`, `?`, or an operator, found `;` - --> $DIR/issue-10636-2.rs:5:15 +error: mismatched closing delimiter: `}` + --> $DIR/issue-10636-2.rs:6:15 | +LL | pub fn trace_option(option: Option<isize>) { + | - closing delimiter possibly meant for this LL | option.map(|some| 42; - | ^ ^ help: `)` may belong here - | | - | unclosed delimiter - -error: expected expression, found `)` - --> $DIR/issue-10636-2.rs:8:1 - | + | ^ unclosed delimiter +... LL | } - | ^ expected expression + | ^ mismatched closing delimiter -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs index 25699f9fe11..a596a9f2de3 100644 --- a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs +++ b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.rs @@ -1,4 +1,5 @@ // Fixed in #66054. // ignore-tidy-trailing-newlines -// error-pattern: aborting due to 2 previous errors +// error-pattern: this file contains an unclosed delimiter +// error-pattern: aborting due to previous error #[Ѕ \ No newline at end of file diff --git a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr index 8a44ee761ed..c79e8b4fb70 100644 --- a/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr +++ b/tests/ui/parser/issues/issue-58094-missing-right-square-bracket.stderr @@ -1,16 +1,10 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-58094-missing-right-square-bracket.rs:4:4 + --> $DIR/issue-58094-missing-right-square-bracket.rs:5:4 | LL | #[Ѕ | - ^ | | | unclosed delimiter -error: expected item after attributes - --> $DIR/issue-58094-missing-right-square-bracket.rs:4:1 - | -LL | #[Ѕ - | ^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-58856-1.rs b/tests/ui/parser/issues/issue-58856-1.rs index ea80eb8714f..799243d545a 100644 --- a/tests/ui/parser/issues/issue-58856-1.rs +++ b/tests/ui/parser/issues/issue-58856-1.rs @@ -1,8 +1,5 @@ impl A { - //~^ ERROR cannot find type `A` in this scope fn b(self> - //~^ ERROR expected one of `)`, `,`, or `:`, found `>` - //~| ERROR expected one of `->`, `where`, or `{`, found `>` -} +} //~ ERROR mismatched closing delimiter fn main() {} diff --git a/tests/ui/parser/issues/issue-58856-1.stderr b/tests/ui/parser/issues/issue-58856-1.stderr index 96151f3fe07..77ad8acbd43 100644 --- a/tests/ui/parser/issues/issue-58856-1.stderr +++ b/tests/ui/parser/issues/issue-58856-1.stderr @@ -1,29 +1,12 @@ -error: expected one of `)`, `,`, or `:`, found `>` - --> $DIR/issue-58856-1.rs:3:9 - | -LL | fn b(self> - | ^ ^ help: `)` may belong here - | | - | unclosed delimiter - -error: expected one of `->`, `where`, or `{`, found `>` - --> $DIR/issue-58856-1.rs:3:14 +error: mismatched closing delimiter: `}` + --> $DIR/issue-58856-1.rs:2:9 | LL | impl A { - | - while parsing this item list starting here -LL | + | - closing delimiter possibly meant for this LL | fn b(self> - | ^ expected one of `->`, `where`, or `{` -... + | ^ unclosed delimiter LL | } - | - the item list ends here - -error[E0412]: cannot find type `A` in this scope - --> $DIR/issue-58856-1.rs:1:6 - | -LL | impl A { - | ^ not found in this scope + | ^ mismatched closing delimiter -error: aborting due to 3 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/parser/issues/issue-58856-2.rs b/tests/ui/parser/issues/issue-58856-2.rs index 9356d57b0e5..5edd7263905 100644 --- a/tests/ui/parser/issues/issue-58856-2.rs +++ b/tests/ui/parser/issues/issue-58856-2.rs @@ -4,11 +4,8 @@ trait Howness {} impl Howness for () { fn how_are_you(&self -> Empty { - //~^ ERROR expected one of `)` or `,`, found `->` - //~| ERROR method `how_are_you` is not a member of trait `Howness` Empty } -} -//~^ ERROR non-item in item list +} //~ ERROR mismatched closing delimiter fn main() {} diff --git a/tests/ui/parser/issues/issue-58856-2.stderr b/tests/ui/parser/issues/issue-58856-2.stderr index 627dd389059..5fcf5bcc17e 100644 --- a/tests/ui/parser/issues/issue-58856-2.stderr +++ b/tests/ui/parser/issues/issue-58856-2.stderr @@ -1,34 +1,13 @@ -error: expected one of `)` or `,`, found `->` +error: mismatched closing delimiter: `}` --> $DIR/issue-58856-2.rs:6:19 | -LL | fn how_are_you(&self -> Empty { - | ^ -^^ - | | | - | | help: `)` may belong here - | unclosed delimiter - -error: non-item in item list - --> $DIR/issue-58856-2.rs:11:1 - | LL | impl Howness for () { - | - item list starts here + | - closing delimiter possibly meant for this +LL | fn how_are_you(&self -> Empty { + | ^ unclosed delimiter ... LL | } - | ^ - | | - | non-item starts here - | item list ends here - -error[E0407]: method `how_are_you` is not a member of trait `Howness` - --> $DIR/issue-58856-2.rs:6:5 - | -LL | / fn how_are_you(&self -> Empty { -LL | | -LL | | -LL | | Empty -LL | | } - | |_____^ not a member of trait `Howness` + | ^ mismatched closing delimiter -error: aborting due to 3 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0407`. diff --git a/tests/ui/parser/issues/issue-60075.rs b/tests/ui/parser/issues/issue-60075.rs index e89d78ee8a6..3c36e4d3ea7 100644 --- a/tests/ui/parser/issues/issue-60075.rs +++ b/tests/ui/parser/issues/issue-60075.rs @@ -3,9 +3,6 @@ fn main() {} trait T { fn qux() -> Option<usize> { let _ = if true { - }); -//~^ ERROR non-item in item list -//~| ERROR mismatched closing delimiter: `)` -//~| ERROR expected one of `.`, `;` + }); //~ ERROR mismatched closing delimiter Some(4) } diff --git a/tests/ui/parser/issues/issue-60075.stderr b/tests/ui/parser/issues/issue-60075.stderr index 210ef700cd4..cd8f1231fad 100644 --- a/tests/ui/parser/issues/issue-60075.stderr +++ b/tests/ui/parser/issues/issue-60075.stderr @@ -1,21 +1,3 @@ -error: expected one of `.`, `;`, `?`, `else`, or an operator, found `}` - --> $DIR/issue-60075.rs:6:10 - | -LL | }); - | ^ expected one of `.`, `;`, `?`, `else`, or an operator - -error: non-item in item list - --> $DIR/issue-60075.rs:6:11 - | -LL | trait T { - | - item list starts here -... -LL | }); - | ^ non-item starts here -... -LL | } - | - item list ends here - error: mismatched closing delimiter: `)` --> $DIR/issue-60075.rs:4:31 | @@ -25,5 +7,5 @@ LL | let _ = if true { LL | }); | ^ mismatched closing delimiter -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-62524.rs b/tests/ui/parser/issues/issue-62524.rs index 5259dfe2e65..fa7c626f5cc 100644 --- a/tests/ui/parser/issues/issue-62524.rs +++ b/tests/ui/parser/issues/issue-62524.rs @@ -1,5 +1,5 @@ // ignore-tidy-trailing-newlines -// error-pattern: aborting due to 3 previous errors +// error-pattern: aborting due to previous error #![allow(uncommon_codepoints)] y![ diff --git a/tests/ui/parser/issues/issue-62524.stderr b/tests/ui/parser/issues/issue-62524.stderr index 55eed0402a4..0cbaacd4c64 100644 --- a/tests/ui/parser/issues/issue-62524.stderr +++ b/tests/ui/parser/issues/issue-62524.stderr @@ -6,28 +6,5 @@ LL | y![ LL | Ϥ, | ^ -error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-62524.rs:5:3 - | -LL | y![ - | ___^ -LL | | Ϥ, - | |__^ - | -help: change the delimiters to curly braces - | -LL | y! { /* items */ } - | ~~~~~~~~~~~~~~~ -help: add a semicolon - | -LL | Ϥ,; - | + - -error: cannot find macro `y` in this scope - --> $DIR/issue-62524.rs:5:1 - | -LL | y![ - | ^ - -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-62546.rs b/tests/ui/parser/issues/issue-62546.rs index f06b6505859..bb30d68eabd 100644 --- a/tests/ui/parser/issues/issue-62546.rs +++ b/tests/ui/parser/issues/issue-62546.rs @@ -1,3 +1 @@ -pub t(# -//~^ ERROR missing `fn` or `struct` for function or struct definition -//~ ERROR this file contains an unclosed delimiter +pub t(# //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/issues/issue-62546.stderr b/tests/ui/parser/issues/issue-62546.stderr index 32c61391e16..80c1c71689d 100644 --- a/tests/ui/parser/issues/issue-62546.stderr +++ b/tests/ui/parser/issues/issue-62546.stderr @@ -1,17 +1,8 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62546.rs:3:52 + --> $DIR/issue-62546.rs:1:60 | LL | pub t(# - | - unclosed delimiter -LL | -LL | - | ^ + | - unclosed delimiter ^ -error: missing `fn` or `struct` for function or struct definition - --> $DIR/issue-62546.rs:1:4 - | -LL | pub t(# - | ---^- help: if you meant to call a macro, try: `t!` - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-62554.rs b/tests/ui/parser/issues/issue-62554.rs index cfd02183cb4..4b463a17333 100644 --- a/tests/ui/parser/issues/issue-62554.rs +++ b/tests/ui/parser/issues/issue-62554.rs @@ -1,5 +1,4 @@ // error-pattern:this file contains an unclosed delimiter -// error-pattern:xpected `{`, found `macro_rules` fn main() {} diff --git a/tests/ui/parser/issues/issue-62554.stderr b/tests/ui/parser/issues/issue-62554.stderr index 9e62572e388..4637c795ae5 100644 --- a/tests/ui/parser/issues/issue-62554.stderr +++ b/tests/ui/parser/issues/issue-62554.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62554.rs:6:89 + --> $DIR/issue-62554.rs:5:89 | LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { | - - - - - ^ @@ -9,65 +9,5 @@ LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s | | | unclosed delimiter | unclosed delimiter unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/issue-62554.rs:6:89 - | -LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { - | - - - - - ^ - | | | | | | - | | | | | unclosed delimiter - | | | | unclosed delimiter - | | | unclosed delimiter - | unclosed delimiter unclosed delimiter - -error: this file contains an unclosed delimiter - --> $DIR/issue-62554.rs:6:89 - | -LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { - | - - - - - ^ - | | | | | | - | | | | | unclosed delimiter - | | | | unclosed delimiter - | | | unclosed delimiter - | unclosed delimiter unclosed delimiter - -error: this file contains an unclosed delimiter - --> $DIR/issue-62554.rs:6:89 - | -LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { - | - - - - - ^ - | | | | | | - | | | | | unclosed delimiter - | | | | unclosed delimiter - | | | unclosed delimiter - | unclosed delimiter unclosed delimiter - -error: this file contains an unclosed delimiter - --> $DIR/issue-62554.rs:6:89 - | -LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { - | - - - - - ^ - | | | | | | - | | | | | unclosed delimiter - | | | | unclosed delimiter - | | | unclosed delimiter - | unclosed delimiter unclosed delimiter - -error: expected `{`, found `macro_rules` - --> $DIR/issue-62554.rs:6:23 - | -LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { - | ^^^^^^^^^^^ expected `{` - | -note: the `if` expression is missing a block after this condition - --> $DIR/issue-62554.rs:6:20 - | -LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { - | ^^ -help: try placing this code inside a block - | -LL | fn foo(u: u8) { if u8 { macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { } - | + + - -error: aborting due to 6 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-62881.rs b/tests/ui/parser/issues/issue-62881.rs index b9204595fb9..cc80d449930 100644 --- a/tests/ui/parser/issues/issue-62881.rs +++ b/tests/ui/parser/issues/issue-62881.rs @@ -1,6 +1,3 @@ fn main() {} -fn f() -> isize { fn f() -> isize {} pub f< -//~^ ERROR missing `fn` or `struct` for function or struct definition -//~| ERROR mismatched types -//~ ERROR this file contains an unclosed delimiter +fn f() -> isize { fn f() -> isize {} pub f< //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/issues/issue-62881.stderr b/tests/ui/parser/issues/issue-62881.stderr index 87be69baadd..e57cbd1810a 100644 --- a/tests/ui/parser/issues/issue-62881.stderr +++ b/tests/ui/parser/issues/issue-62881.stderr @@ -1,26 +1,8 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62881.rs:6:52 + --> $DIR/issue-62881.rs:3:96 | LL | fn f() -> isize { fn f() -> isize {} pub f< - | - unclosed delimiter -... -LL | - | ^ + | - unclosed delimiter ^ -error: missing `fn` or `struct` for function or struct definition - --> $DIR/issue-62881.rs:3:41 - | -LL | fn f() -> isize { fn f() -> isize {} pub f< - | ^ - -error[E0308]: mismatched types - --> $DIR/issue-62881.rs:3:29 - | -LL | fn f() -> isize { fn f() -> isize {} pub f< - | - ^^^^^ expected `isize`, found `()` - | | - | implicitly returns `()` as its body has no tail or `return` expression - -error: aborting due to 3 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/issues/issue-62894.rs b/tests/ui/parser/issues/issue-62894.rs index b9c0bf834dd..4dfa406ea2d 100644 --- a/tests/ui/parser/issues/issue-62894.rs +++ b/tests/ui/parser/issues/issue-62894.rs @@ -1,6 +1,5 @@ // Regression test for #62894, shouldn't crash. // error-pattern: this file contains an unclosed delimiter -// error-pattern: expected one of `(`, `[`, or `{`, found keyword `fn` fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! diff --git a/tests/ui/parser/issues/issue-62894.stderr b/tests/ui/parser/issues/issue-62894.stderr index 07a203bf416..700479076df 100644 --- a/tests/ui/parser/issues/issue-62894.stderr +++ b/tests/ui/parser/issues/issue-62894.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-62894.rs:7:14 + --> $DIR/issue-62894.rs:6:14 | LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! | - - - unclosed delimiter @@ -10,41 +10,5 @@ LL | LL | fn main() {} | ^ -error: this file contains an unclosed delimiter - --> $DIR/issue-62894.rs:7:14 - | -LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! - | - - - unclosed delimiter - | | | - | | unclosed delimiter - | unclosed delimiter -LL | -LL | fn main() {} - | ^ - -error: this file contains an unclosed delimiter - --> $DIR/issue-62894.rs:7:14 - | -LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! - | - - - unclosed delimiter - | | | - | | unclosed delimiter - | unclosed delimiter -LL | -LL | fn main() {} - | ^ - -error: expected one of `(`, `[`, or `{`, found keyword `fn` - --> $DIR/issue-62894.rs:7:1 - | -LL | fn f() { assert_eq!(f(), (), assert_eq!(assert_eq! - | - expected one of `(`, `[`, or `{` -LL | -LL | fn main() {} - | ^^ unexpected token - --> $SRC_DIR/core/src/macros/mod.rs:LL:COL - | - = note: while parsing argument for this `expr` macro fragment - -error: aborting due to 4 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-62895.rs b/tests/ui/parser/issues/issue-62895.rs index 53f17405d79..33511dee93e 100644 --- a/tests/ui/parser/issues/issue-62895.rs +++ b/tests/ui/parser/issues/issue-62895.rs @@ -1,11 +1,11 @@ fn main() {} -fn v() -> isize { //~ ERROR mismatched types -mod _ { //~ ERROR expected identifier -pub fn g() -> isizee { //~ ERROR cannot find type `isizee` in this scope -mod _ { //~ ERROR expected identifier -pub g() -> is //~ ERROR missing `fn` for function definition -(), w20); +fn v() -> isize { +mod _ { +pub fn g() -> isizee { +mod _ { +pub g() -> is +(), w20); //~ ERROR mismatched closing delimiter } -(), w20); //~ ERROR expected item, found `;` +(), w20); //~ ERROR mismatched closing delimiter } diff --git a/tests/ui/parser/issues/issue-62895.stderr b/tests/ui/parser/issues/issue-62895.stderr index 2e7e500f478..0ad9ac63ebd 100644 --- a/tests/ui/parser/issues/issue-62895.stderr +++ b/tests/ui/parser/issues/issue-62895.stderr @@ -1,47 +1,20 @@ -error: expected identifier, found reserved identifier `_` - --> $DIR/issue-62895.rs:4:5 +error: mismatched closing delimiter: `)` + --> $DIR/issue-62895.rs:6:7 | LL | mod _ { - | ^ expected identifier, found reserved identifier - -error: expected identifier, found reserved identifier `_` - --> $DIR/issue-62895.rs:6:5 - | -LL | mod _ { - | ^ expected identifier, found reserved identifier - -error: missing `fn` for function definition - --> $DIR/issue-62895.rs:7:4 - | + | ^ unclosed delimiter LL | pub g() -> is - | ^^^^ - | -help: add `fn` here to parse `g` as a public function - | -LL | pub fn g() -> is - | ++ - -error: expected item, found `;` - --> $DIR/issue-62895.rs:10:9 - | LL | (), w20); - | ^ help: remove this semicolon - -error[E0412]: cannot find type `isizee` in this scope - --> $DIR/issue-62895.rs:5:15 - | -LL | pub fn g() -> isizee { - | ^^^^^^ help: a builtin type with a similar name exists: `isize` + | ^ mismatched closing delimiter -error[E0308]: mismatched types - --> $DIR/issue-62895.rs:3:11 +error: mismatched closing delimiter: `)` + --> $DIR/issue-62895.rs:4:7 | -LL | fn v() -> isize { - | - ^^^^^ expected `isize`, found `()` - | | - | implicitly returns `()` as its body has no tail or `return` expression +LL | mod _ { + | ^ unclosed delimiter +... +LL | (), w20); + | ^ mismatched closing delimiter -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0308, E0412. -For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/issues/issue-62973.rs b/tests/ui/parser/issues/issue-62973.rs index 1c5d0c6f8ab..22d75457702 100644 --- a/tests/ui/parser/issues/issue-62973.rs +++ b/tests/ui/parser/issues/issue-62973.rs @@ -1,5 +1,5 @@ // ignore-tidy-trailing-newlines -// error-pattern: aborting due to 7 previous errors +// error-pattern: aborting due to 3 previous errors fn main() {} diff --git a/tests/ui/parser/issues/issue-62973.stderr b/tests/ui/parser/issues/issue-62973.stderr index 3cb6d75a675..14411a8cb78 100644 --- a/tests/ui/parser/issues/issue-62973.stderr +++ b/tests/ui/parser/issues/issue-62973.stderr @@ -1,72 +1,3 @@ -error: this file contains an unclosed delimiter - --> $DIR/issue-62973.rs:8:2 - | -LL | fn p() { match s { v, E { [) {) } - | - - - - missing open `(` for this delimiter - | | | | - | | | missing open `(` for this delimiter - | | unclosed delimiter - | unclosed delimiter -LL | -LL | - | ^ - -error: this file contains an unclosed delimiter - --> $DIR/issue-62973.rs:8:2 - | -LL | fn p() { match s { v, E { [) {) } - | - - - - missing open `(` for this delimiter - | | | | - | | | missing open `(` for this delimiter - | | unclosed delimiter - | unclosed delimiter -LL | -LL | - | ^ - -error: expected one of `,`, `:`, or `}`, found `{` - --> $DIR/issue-62973.rs:6:8 - | -LL | fn p() { match s { v, E { [) {) } - | ^ - ^ expected one of `,`, `:`, or `}` - | | | - | | while parsing this struct - | unclosed delimiter - | -help: `}` may belong here - | -LL | fn p() { match s { v, E} { [) {) } - | + -help: try naming a field - | -LL | fn p() { match s { v, E: E { [) {) } - | ++ - -error: struct literals are not allowed here - --> $DIR/issue-62973.rs:6:16 - | -LL | fn p() { match s { v, E { [) {) } - | ________________^ -LL | | -LL | | - | |_^ - | -help: surround the struct literal with parentheses - | -LL ~ fn p() { match (s { v, E { [) {) } -LL | -LL ~ ) - | - -error: expected one of `.`, `?`, `{`, or an operator, found `}` - --> $DIR/issue-62973.rs:8:2 - | -LL | fn p() { match s { v, E { [) {) } - | ----- while parsing this `match` expression -LL | -LL | - | ^ expected one of `.`, `?`, `{`, or an operator - error: mismatched closing delimiter: `)` --> $DIR/issue-62973.rs:6:27 | @@ -83,5 +14,18 @@ LL | fn p() { match s { v, E { [) {) } | | | unclosed delimiter -error: aborting due to 7 previous errors +error: this file contains an unclosed delimiter + --> $DIR/issue-62973.rs:8:2 + | +LL | fn p() { match s { v, E { [) {) } + | - - - - missing open `(` for this delimiter + | | | | + | | | missing open `(` for this delimiter + | | unclosed delimiter + | unclosed delimiter +LL | +LL | + | ^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/parser/issues/issue-63116.rs b/tests/ui/parser/issues/issue-63116.rs index 430bc1d716c..6b9d9cdbeb1 100644 --- a/tests/ui/parser/issues/issue-63116.rs +++ b/tests/ui/parser/issues/issue-63116.rs @@ -1,3 +1,3 @@ // fixed by #66361 -// error-pattern: aborting due to 3 previous errors +// error-pattern: aborting due to 2 previous errors impl W <s(f;Y(;] diff --git a/tests/ui/parser/issues/issue-63116.stderr b/tests/ui/parser/issues/issue-63116.stderr index a1f8a77ffa7..27c94f337bd 100644 --- a/tests/ui/parser/issues/issue-63116.stderr +++ b/tests/ui/parser/issues/issue-63116.stderr @@ -1,3 +1,11 @@ +error: mismatched closing delimiter: `]` + --> $DIR/issue-63116.rs:3:14 + | +LL | impl W <s(f;Y(;] + | ^ ^ mismatched closing delimiter + | | + | unclosed delimiter + error: this file contains an unclosed delimiter --> $DIR/issue-63116.rs:3:18 | @@ -7,19 +15,5 @@ LL | impl W <s(f;Y(;] | | missing open `[` for this delimiter | unclosed delimiter -error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;` - --> $DIR/issue-63116.rs:3:12 - | -LL | impl W <s(f;Y(;] - | ^ expected one of 7 possible tokens - -error: mismatched closing delimiter: `]` - --> $DIR/issue-63116.rs:3:14 - | -LL | impl W <s(f;Y(;] - | ^ ^ mismatched closing delimiter - | | - | unclosed delimiter - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-63135.rs b/tests/ui/parser/issues/issue-63135.rs index a5a8de85466..d61197dc566 100644 --- a/tests/ui/parser/issues/issue-63135.rs +++ b/tests/ui/parser/issues/issue-63135.rs @@ -1,3 +1,3 @@ -// error-pattern: aborting due to 5 previous errors - +// error-pattern: this file contains an unclosed delimiter +// error-pattern: aborting due to previous error fn i(n{...,f # diff --git a/tests/ui/parser/issues/issue-63135.stderr b/tests/ui/parser/issues/issue-63135.stderr index e0dc356d546..ff9d99c28fe 100644 --- a/tests/ui/parser/issues/issue-63135.stderr +++ b/tests/ui/parser/issues/issue-63135.stderr @@ -7,42 +7,5 @@ LL | fn i(n{...,f # | | unclosed delimiter | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/issue-63135.rs:3:16 - | -LL | fn i(n{...,f # - | - - ^ - | | | - | | unclosed delimiter - | unclosed delimiter - -error: expected field pattern, found `...` - --> $DIR/issue-63135.rs:3:8 - | -LL | fn i(n{...,f # - | ^^^ - | -help: to omit remaining fields, use `..` - | -LL | fn i(n{..,f # - | ~~ - -error: expected `}`, found `,` - --> $DIR/issue-63135.rs:3:11 - | -LL | fn i(n{...,f # - | ---^ - | | | - | | expected `}` - | `..` must be at the end and cannot have a trailing comma - -error: expected one of `!` or `[`, found `}` - --> $DIR/issue-63135.rs:3:16 - | -LL | fn i(n{...,f # - | - ^ expected one of `!` or `[` - | | - | while parsing the fields for this pattern - -error: aborting due to 5 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-66357-unexpected-unreachable.rs b/tests/ui/parser/issues/issue-66357-unexpected-unreachable.rs index aed428bfc2a..69a2dfe6cbd 100644 --- a/tests/ui/parser/issues/issue-66357-unexpected-unreachable.rs +++ b/tests/ui/parser/issues/issue-66357-unexpected-unreachable.rs @@ -9,6 +9,4 @@ // // ended up bubbling up `Ok(true)` to `unexpected` which then used `unreachable!()`. -fn f() { |[](* } -//~^ ERROR expected one of `,` or `:`, found `(` -//~| ERROR expected one of `&`, `(`, `)`, `-`, `...`, `..=`, `..`, `[`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*` +fn f() { |[](* } //~ ERROR mismatched closing delimiter diff --git a/tests/ui/parser/issues/issue-66357-unexpected-unreachable.stderr b/tests/ui/parser/issues/issue-66357-unexpected-unreachable.stderr index 6cbab855c76..079fff37ea4 100644 --- a/tests/ui/parser/issues/issue-66357-unexpected-unreachable.stderr +++ b/tests/ui/parser/issues/issue-66357-unexpected-unreachable.stderr @@ -1,16 +1,11 @@ -error: expected one of `,` or `:`, found `(` +error: mismatched closing delimiter: `}` --> $DIR/issue-66357-unexpected-unreachable.rs:12:13 | LL | fn f() { |[](* } - | ^ expected one of `,` or `:` + | - ^ ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this -error: expected one of `&`, `(`, `)`, `-`, `...`, `..=`, `..`, `[`, `_`, `box`, `mut`, `ref`, `|`, identifier, or path, found `*` - --> $DIR/issue-66357-unexpected-unreachable.rs:12:13 - | -LL | fn f() { |[](* } - | ^^ help: `)` may belong here - | | - | unclosed delimiter - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.rs b/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.rs index 87222ef4b59..e712160fcf8 100644 --- a/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.rs +++ b/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.rs @@ -4,9 +4,6 @@ mod a { enum Bug { V = [PhantomData; { [ () ].len() ].len() as isize, //~^ ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` } } @@ -14,10 +11,6 @@ mod b { enum Bug { V = [Vec::new; { [].len() ].len() as isize, //~^ ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR type annotations needed } } @@ -25,11 +18,6 @@ mod c { enum Bug { V = [Vec::new; { [0].len() ].len() as isize, //~^ ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR mismatched closing delimiter: `]` - //~| ERROR type annotations needed - } } -fn main() {} +fn main() {} //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr b/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr index a00f37ed606..9f631edf680 100644 --- a/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr +++ b/tests/ui/parser/issues/issue-67377-invalid-syntax-in-enum-discriminant.stderr @@ -8,7 +8,7 @@ LL | V = [PhantomData; { [ () ].len() ].len() as isize, | closing delimiter possibly meant for this error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:24 + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:12:24 | LL | V = [Vec::new; { [].len() ].len() as isize, | - ^ ^ mismatched closing delimiter @@ -17,7 +17,7 @@ LL | V = [Vec::new; { [].len() ].len() as isize, | closing delimiter possibly meant for this error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:24 + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:19:24 | LL | V = [Vec::new; { [0].len() ].len() as isize, | - ^ ^ mismatched closing delimiter @@ -25,104 +25,23 @@ LL | V = [Vec::new; { [0].len() ].len() as isize, | | unclosed delimiter | closing delimiter possibly meant for this -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:27 - | -LL | V = [PhantomData; { [ () ].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:24 - | -LL | V = [Vec::new; { [].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:24 - | -LL | V = [Vec::new; { [0].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:27 - | -LL | V = [PhantomData; { [ () ].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:24 - | -LL | V = [Vec::new; { [].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:24 - | -LL | V = [Vec::new; { [0].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:5:27 +error: this file contains an unclosed delimiter + --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:23:65 | LL | V = [PhantomData; { [ () ].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:24 - | + | - missing open `[` for this delimiter +... LL | V = [Vec::new; { [].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error: mismatched closing delimiter: `]` - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:24 - | + | - missing open `[` for this delimiter +... +LL | mod c { + | - unclosed delimiter +LL | enum Bug { LL | V = [Vec::new; { [0].len() ].len() as isize, - | - ^ ^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error[E0282]: type annotations needed - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:15:26 - | -LL | V = [Vec::new; { [].len() ].len() as isize, - | ^^ cannot infer type for type parameter `T` - -error[E0282]: type annotations needed - --> $DIR/issue-67377-invalid-syntax-in-enum-discriminant.rs:26:14 - | -LL | V = [Vec::new; { [0].len() ].len() as isize, - | ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Vec` - | -help: consider specifying the generic argument - | -LL | V = [Vec::<T>::new; { [0].len() ].len() as isize, - | +++++ + | - missing open `[` for this delimiter +... +LL | fn main() {} + | ^ -error: aborting due to 14 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/parser/issues/issue-68629.rs b/tests/ui/parser/issues/issue-68629.rs index 672a31f12c8..af89bb58699 100644 --- a/tests/ui/parser/issues/issue-68629.rs +++ b/tests/ui/parser/issues/issue-68629.rs Binary files differdiff --git a/tests/ui/parser/issues/issue-68629.stderr b/tests/ui/parser/issues/issue-68629.stderr index 43a903e6c46..2562baa1c49 100644 --- a/tests/ui/parser/issues/issue-68629.stderr +++ b/tests/ui/parser/issues/issue-68629.stderr Binary files differdiff --git a/tests/ui/parser/issues/issue-84104.rs b/tests/ui/parser/issues/issue-84104.rs index 998949b94a4..962eb69bd83 100644 --- a/tests/ui/parser/issues/issue-84104.rs +++ b/tests/ui/parser/issues/issue-84104.rs @@ -1,3 +1,2 @@ // error-pattern: this file contains an unclosed delimiter -// error-pattern: expected one of #[i=i::<ښܖ< diff --git a/tests/ui/parser/issues/issue-84104.stderr b/tests/ui/parser/issues/issue-84104.stderr index aff31f2c971..7ad59f8450e 100644 --- a/tests/ui/parser/issues/issue-84104.stderr +++ b/tests/ui/parser/issues/issue-84104.stderr @@ -1,16 +1,10 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-84104.rs:3:13 + --> $DIR/issue-84104.rs:2:13 | LL | #[i=i::<ښܖ< | - ^ | | | unclosed delimiter -error: expected one of `>`, a const expression, lifetime, or type, found `]` - --> $DIR/issue-84104.rs:3:13 - | -LL | #[i=i::<ښܖ< - | ^ expected one of `>`, a const expression, lifetime, or type - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-84148-2.rs b/tests/ui/parser/issues/issue-84148-2.rs index 2f6a7facfb2..e677abde5f6 100644 --- a/tests/ui/parser/issues/issue-84148-2.rs +++ b/tests/ui/parser/issues/issue-84148-2.rs @@ -1,3 +1,2 @@ // error-pattern: this file contains an unclosed delimiter -// error-pattern: invalid `?` in type fn f(t:for<>t? diff --git a/tests/ui/parser/issues/issue-84148-2.stderr b/tests/ui/parser/issues/issue-84148-2.stderr index 71d543f9b73..20761180e77 100644 --- a/tests/ui/parser/issues/issue-84148-2.stderr +++ b/tests/ui/parser/issues/issue-84148-2.stderr @@ -1,27 +1,10 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-84148-2.rs:3:16 + --> $DIR/issue-84148-2.rs:2:16 | LL | fn f(t:for<>t? | - ^ | | | unclosed delimiter -error: invalid `?` in type - --> $DIR/issue-84148-2.rs:3:14 - | -LL | fn f(t:for<>t? - | ^ `?` is only allowed on expressions, not types - | -help: if you meant to express that the type might not contain a value, use the `Option` wrapper type - | -LL | fn f(t:Option<for<>t> - | +++++++ ~ - -error: expected one of `->`, `where`, or `{`, found `<eof>` - --> $DIR/issue-84148-2.rs:3:16 - | -LL | fn f(t:for<>t? - | ^ expected one of `->`, `where`, or `{` - -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/issues/issue-88770.rs b/tests/ui/parser/issues/issue-88770.rs index bb69951c7b4..9341415b2d9 100644 --- a/tests/ui/parser/issues/issue-88770.rs +++ b/tests/ui/parser/issues/issue-88770.rs @@ -1,9 +1,6 @@ // Regression test for the ICE described in #88770. // error-pattern:this file contains an unclosed delimiter -// error-pattern:expected one of -// error-pattern:missing `in` in `for` loop -// error-pattern:expected one of `!`, `)`, `,`, `.`, `::`, `;`, `?`, `{`, or an operator, found `e` fn m(){print!("",(c for&g u diff --git a/tests/ui/parser/issues/issue-88770.stderr b/tests/ui/parser/issues/issue-88770.stderr index 4e3a21613ec..836f44953d4 100644 --- a/tests/ui/parser/issues/issue-88770.stderr +++ b/tests/ui/parser/issues/issue-88770.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-88770.rs:11:3 + --> $DIR/issue-88770.rs:8:3 | LL | fn m(){print!("",(c for&g | - - - unclosed delimiter @@ -10,51 +10,5 @@ LL | fn m(){print!("",(c for&g LL | e | ^ -error: this file contains an unclosed delimiter - --> $DIR/issue-88770.rs:11:3 - | -LL | fn m(){print!("",(c for&g - | - - - unclosed delimiter - | | | - | | unclosed delimiter - | unclosed delimiter -... -LL | e - | ^ - -error: this file contains an unclosed delimiter - --> $DIR/issue-88770.rs:11:3 - | -LL | fn m(){print!("",(c for&g - | - - - unclosed delimiter - | | | - | | unclosed delimiter - | unclosed delimiter -... -LL | e - | ^ - -error: missing `in` in `for` loop - --> $DIR/issue-88770.rs:8:26 - | -LL | fn m(){print!("",(c for&g - | __________________________^ -LL | | u - | |_ help: try adding `in` here - -error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found keyword `for` - --> $DIR/issue-88770.rs:8:21 - | -LL | fn m(){print!("",(c for&g - | ^^^ expected one of 8 possible tokens - -error: expected one of `!`, `)`, `,`, `.`, `::`, `;`, `?`, `{`, or an operator, found `e` - --> $DIR/issue-88770.rs:11:1 - | -LL | e - | - expected one of 9 possible tokens -LL | e - | ^ unexpected token - -error: aborting due to 6 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr index 689ce1eb6b7..34217e21ae9 100644 --- a/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr +++ b/tests/ui/parser/macro-mismatched-delim-paren-brace.stderr @@ -1,3 +1,12 @@ +error: mismatched closing delimiter: `}` + --> $DIR/macro-mismatched-delim-paren-brace.rs:2:10 + | +LL | foo! ( + | ^ unclosed delimiter +LL | bar, "baz", 1, 2.0 +LL | } + | ^ mismatched closing delimiter + error: unexpected closing delimiter: `}` --> $DIR/macro-mismatched-delim-paren-brace.rs:5:1 | @@ -9,14 +18,5 @@ LL | } LL | } | ^ unexpected closing delimiter -error: mismatched closing delimiter: `}` - --> $DIR/macro-mismatched-delim-paren-brace.rs:2:10 - | -LL | foo! ( - | ^ unclosed delimiter -LL | bar, "baz", 1, 2.0 -LL | } - | ^ mismatched closing delimiter - error: aborting due to 2 previous errors diff --git a/tests/ui/parser/match-arm-without-braces.stderr b/tests/ui/parser/match-arm-without-braces.stderr index 37d55aa53f8..ee1c8e562fc 100644 --- a/tests/ui/parser/match-arm-without-braces.stderr +++ b/tests/ui/parser/match-arm-without-braces.stderr @@ -2,10 +2,14 @@ error: `match` arm body without braces --> $DIR/match-arm-without-braces.rs:26:27 | LL | Some(Val::Foo) => 3; - | -- ^- help: use a comma to end a `match` arm expression: `,` - | | | - | | this statement is not surrounded by a body + | -- ^ this statement is not surrounded by a body + | | | while parsing the `match` arm starting here + | +help: replace `;` with `,` to end a `match` arm expression + | +LL | Some(Val::Foo) => 3, + | ~ error: `match` arm body without braces --> $DIR/match-arm-without-braces.rs:31:11 diff --git a/tests/ui/parser/mbe_missing_right_paren.rs b/tests/ui/parser/mbe_missing_right_paren.rs index 689176b3eb7..9a92e67da4d 100644 --- a/tests/ui/parser/mbe_missing_right_paren.rs +++ b/tests/ui/parser/mbe_missing_right_paren.rs @@ -1,3 +1,3 @@ // ignore-tidy-trailing-newlines -// error-pattern: aborting due to 3 previous errors +// error-pattern: this file contains an unclosed delimiter macro_rules! abc(ؼ \ No newline at end of file diff --git a/tests/ui/parser/mbe_missing_right_paren.stderr b/tests/ui/parser/mbe_missing_right_paren.stderr index ccaf77d3995..d2af94683ef 100644 --- a/tests/ui/parser/mbe_missing_right_paren.stderr +++ b/tests/ui/parser/mbe_missing_right_paren.stderr @@ -6,26 +6,5 @@ LL | macro_rules! abc(ؼ | | | unclosed delimiter -error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/mbe_missing_right_paren.rs:3:17 - | -LL | macro_rules! abc(ؼ - | ^^ - | -help: change the delimiters to curly braces - | -LL | macro_rules! abc { /* items */ } - | ~~~~~~~~~~~~~~~ -help: add a semicolon - | -LL | macro_rules! abc(ؼ; - | + - -error: unexpected end of macro invocation - --> $DIR/mbe_missing_right_paren.rs:3:19 - | -LL | macro_rules! abc(ؼ - | ^ missing tokens in macro arguments - -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs b/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs index 8f46970b1af..79de98d8b8c 100644 --- a/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs +++ b/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.rs @@ -1,13 +1,13 @@ fn main() {} -impl T for () { //~ ERROR cannot find trait `T` in this scope +impl T for () { fn foo(&self) {} -trait T { //~ ERROR trait is not supported in `trait`s or `impl`s +trait T { fn foo(&self); } -pub(crate) struct Bar<T>(); //~ ERROR struct is not supported in `trait`s or `impl`s +pub(crate) struct Bar<T>(); //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr b/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr index cc7cc0c55d5..d91a7f0542d 100644 --- a/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr +++ b/tests/ui/parser/mismatched-braces/missing-close-brace-in-impl-trait.stderr @@ -7,28 +7,5 @@ LL | impl T for () { LL | | ^ -error: trait is not supported in `trait`s or `impl`s - --> $DIR/missing-close-brace-in-impl-trait.rs:7:1 - | -LL | trait T { - | ^^^^^^^ - | - = help: consider moving the trait out to a nearby module scope - -error: struct is not supported in `trait`s or `impl`s - --> $DIR/missing-close-brace-in-impl-trait.rs:11:1 - | -LL | pub(crate) struct Bar<T>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider moving the struct out to a nearby module scope - -error[E0405]: cannot find trait `T` in this scope - --> $DIR/missing-close-brace-in-impl-trait.rs:3:6 - | -LL | impl T for () { - | ^ not found in this scope - -error: aborting due to 4 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0405`. diff --git a/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs b/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs index 090a17b413d..88bc7257687 100644 --- a/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs +++ b/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.rs @@ -1,7 +1,7 @@ pub(crate) struct Bar<T> { foo: T, -trait T { //~ ERROR expected identifier, found keyword `trait` +trait T { fn foo(&self); } diff --git a/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr b/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr index ad1e90e43ec..d01d9ed60e4 100644 --- a/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr +++ b/tests/ui/parser/mismatched-braces/missing-close-brace-in-struct.stderr @@ -7,14 +7,5 @@ LL | pub(crate) struct Bar<T> { LL | fn main() {} | ^ -error: expected identifier, found keyword `trait` - --> $DIR/missing-close-brace-in-struct.rs:4:1 - | -LL | pub(crate) struct Bar<T> { - | --- while parsing this struct -... -LL | trait T { - | ^^^^^ expected identifier, found keyword - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs b/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs index b6932deb5c0..a05d6aa8edc 100644 --- a/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs +++ b/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.rs @@ -2,10 +2,9 @@ trait T { fn foo(&self); pub(crate) struct Bar<T>(); -//~^ ERROR struct is not supported in `trait`s or `impl`s impl T for Bar<usize> { -//~^ ERROR implementation is not supported in `trait`s or `impl`s + fn foo(&self) {} } diff --git a/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr b/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr index 7c6254356e0..7418dd64c9e 100644 --- a/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr +++ b/tests/ui/parser/mismatched-braces/missing-close-brace-in-trait.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/missing-close-brace-in-trait.rs:12:65 + --> $DIR/missing-close-brace-in-trait.rs:11:65 | LL | trait T { | - unclosed delimiter @@ -7,21 +7,5 @@ LL | trait T { LL | fn main() {} | ^ -error: struct is not supported in `trait`s or `impl`s - --> $DIR/missing-close-brace-in-trait.rs:4:1 - | -LL | pub(crate) struct Bar<T>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider moving the struct out to a nearby module scope - -error: implementation is not supported in `trait`s or `impl`s - --> $DIR/missing-close-brace-in-trait.rs:7:1 - | -LL | impl T for Bar<usize> { - | ^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider moving the implementation out to a nearby module scope - -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/missing_right_paren.rs b/tests/ui/parser/missing_right_paren.rs index 810dee9571d..e240f8c6739 100644 --- a/tests/ui/parser/missing_right_paren.rs +++ b/tests/ui/parser/missing_right_paren.rs @@ -1,3 +1,4 @@ // ignore-tidy-trailing-newlines -// error-pattern: aborting due to 4 previous errors +// error-pattern: this file contains an unclosed delimiter +// error-pattern: aborting due to previous error fn main((ؼ \ No newline at end of file diff --git a/tests/ui/parser/missing_right_paren.stderr b/tests/ui/parser/missing_right_paren.stderr index 3fe0d0f4273..994ce4d8541 100644 --- a/tests/ui/parser/missing_right_paren.stderr +++ b/tests/ui/parser/missing_right_paren.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/missing_right_paren.rs:3:11 + --> $DIR/missing_right_paren.rs:4:11 | LL | fn main((ؼ | -- ^ @@ -7,26 +7,5 @@ LL | fn main((ؼ | |unclosed delimiter | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/missing_right_paren.rs:3:11 - | -LL | fn main((ؼ - | -- ^ - | || - | |unclosed delimiter - | unclosed delimiter - -error: expected one of `:` or `|`, found `)` - --> $DIR/missing_right_paren.rs:3:11 - | -LL | fn main((ؼ - | ^ expected one of `:` or `|` - -error: expected one of `->`, `where`, or `{`, found `<eof>` - --> $DIR/missing_right_paren.rs:3:11 - | -LL | fn main((ؼ - | ^ expected one of `->`, `where`, or `{` - -error: aborting due to 4 previous errors +error: aborting due to previous error diff --git a/tests/ui/parser/parser-recovery-1.rs b/tests/ui/parser/parser-recovery-1.rs index 7e26b4f2b6a..5f729665cb8 100644 --- a/tests/ui/parser/parser-recovery-1.rs +++ b/tests/ui/parser/parser-recovery-1.rs @@ -3,11 +3,8 @@ trait Foo { fn bar() { let x = foo(); - //~^ ERROR cannot find function `foo` in this scope } fn main() { let x = y.; - //~^ ERROR unexpected token - //~| ERROR cannot find value `y` in this scope } //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/parser-recovery-1.stderr b/tests/ui/parser/parser-recovery-1.stderr index 0cb771ea39c..7045b6f5b78 100644 --- a/tests/ui/parser/parser-recovery-1.stderr +++ b/tests/ui/parser/parser-recovery-1.stderr @@ -1,35 +1,16 @@ error: this file contains an unclosed delimiter - --> $DIR/parser-recovery-1.rs:13:54 + --> $DIR/parser-recovery-1.rs:10:54 | LL | trait Foo { | - unclosed delimiter LL | fn bar() { | - this delimiter might not be properly closed... -... +LL | let x = foo(); LL | } | - ...as it matches this but it has different indentation ... LL | } | ^ -error: unexpected token: `;` - --> $DIR/parser-recovery-1.rs:10:15 - | -LL | let x = y.; - | ^ - -error[E0425]: cannot find value `y` in this scope - --> $DIR/parser-recovery-1.rs:10:13 - | -LL | let x = y.; - | ^ not found in this scope - -error[E0425]: cannot find function `foo` in this scope - --> $DIR/parser-recovery-1.rs:5:17 - | -LL | let x = foo(); - | ^^^ not found in this scope - -error: aborting due to 4 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/parser-recovery-2.rs b/tests/ui/parser/parser-recovery-2.rs index 48b22afffe7..203d8aac66c 100644 --- a/tests/ui/parser/parser-recovery-2.rs +++ b/tests/ui/parser/parser-recovery-2.rs @@ -2,11 +2,10 @@ trait Foo { fn bar() { - let x = foo(); //~ ERROR cannot find function `foo` in this scope + let x = foo(); ) //~ ERROR mismatched closing delimiter: `)` } fn main() { - let x = y.; //~ ERROR unexpected token - //~^ ERROR cannot find value `y` in this scope + let x = y.; } diff --git a/tests/ui/parser/parser-recovery-2.stderr b/tests/ui/parser/parser-recovery-2.stderr index 8829cf4c1e1..f396e5fde5b 100644 --- a/tests/ui/parser/parser-recovery-2.stderr +++ b/tests/ui/parser/parser-recovery-2.stderr @@ -1,9 +1,3 @@ -error: unexpected token: `;` - --> $DIR/parser-recovery-2.rs:10:15 - | -LL | let x = y.; - | ^ - error: mismatched closing delimiter: `)` --> $DIR/parser-recovery-2.rs:4:14 | @@ -13,18 +7,5 @@ LL | let x = foo(); LL | ) | ^ mismatched closing delimiter -error[E0425]: cannot find value `y` in this scope - --> $DIR/parser-recovery-2.rs:10:13 - | -LL | let x = y.; - | ^ not found in this scope - -error[E0425]: cannot find function `foo` in this scope - --> $DIR/parser-recovery-2.rs:5:17 - | -LL | let x = foo(); - | ^^^ not found in this scope - -error: aborting due to 4 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/unclosed-delimiter-in-dep.rs b/tests/ui/parser/unclosed-delimiter-in-dep.rs index 6db1b66e9f7..4de83ee640a 100644 --- a/tests/ui/parser/unclosed-delimiter-in-dep.rs +++ b/tests/ui/parser/unclosed-delimiter-in-dep.rs @@ -2,5 +2,4 @@ mod unclosed_delim_mod; fn main() { let _: usize = unclosed_delim_mod::new(); - //~^ ERROR mismatched types } diff --git a/tests/ui/parser/unclosed-delimiter-in-dep.stderr b/tests/ui/parser/unclosed-delimiter-in-dep.stderr index d1725c60dbb..a46d020b967 100644 --- a/tests/ui/parser/unclosed-delimiter-in-dep.stderr +++ b/tests/ui/parser/unclosed-delimiter-in-dep.stderr @@ -9,17 +9,5 @@ LL | } LL | } | ^ mismatched closing delimiter -error[E0308]: mismatched types - --> $DIR/unclosed-delimiter-in-dep.rs:4:20 - | -LL | let _: usize = unclosed_delim_mod::new(); - | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Result<Value, ()>` - | | - | expected due to this - | - = note: expected type `usize` - found enum `Result<Value, ()>` - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/parser/use-unclosed-brace.rs b/tests/ui/parser/use-unclosed-brace.rs index 41742f37f3c..fcfe95b26f9 100644 --- a/tests/ui/parser/use-unclosed-brace.rs +++ b/tests/ui/parser/use-unclosed-brace.rs @@ -1,6 +1,4 @@ -// error-pattern: expected one of `,`, `::`, `as`, or `}`, found `;` // error-pattern: this file contains an unclosed delimiter -// error-pattern: expected item, found `}` use foo::{bar, baz; use std::fmt::Display; diff --git a/tests/ui/parser/use-unclosed-brace.stderr b/tests/ui/parser/use-unclosed-brace.stderr index 438fe9c47ea..ad5bb2de1b2 100644 --- a/tests/ui/parser/use-unclosed-brace.stderr +++ b/tests/ui/parser/use-unclosed-brace.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/use-unclosed-brace.rs:12:14 + --> $DIR/use-unclosed-brace.rs:10:14 | LL | use foo::{bar, baz; | - unclosed delimiter @@ -7,21 +7,5 @@ LL | use foo::{bar, baz; LL | fn main() {} | ^ -error: expected one of `,`, `::`, `as`, or `}`, found `;` - --> $DIR/use-unclosed-brace.rs:4:10 - | -LL | use foo::{bar, baz; - | ^ ^ - | | | - | | expected one of `,`, `::`, `as`, or `}` - | | help: `}` may belong here - | unclosed delimiter - -error: expected item, found `}` - --> $DIR/use-unclosed-brace.rs:12:14 - | -LL | fn main() {} - | ^ expected item - -error: aborting due to 3 previous errors +error: aborting due to previous error diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs index 107eba45dc2..7ae14c6b84d 100644 --- a/tests/ui/process/process-sigpipe.rs +++ b/tests/ui/process/process-sigpipe.rs @@ -15,6 +15,7 @@ // ignore-emscripten no threads support // ignore-vxworks no 'sh' // ignore-fuchsia no 'sh' +// ignore-nto no 'yes' use std::process; use std::thread; diff --git a/tests/ui/resolve/issue-108529.rs b/tests/ui/resolve/issue-108529.rs new file mode 100644 index 00000000000..8e3aafab6aa --- /dev/null +++ b/tests/ui/resolve/issue-108529.rs @@ -0,0 +1,8 @@ +#![allow(nonstandard_style)] +use f::f::f; //~ ERROR + +trait f { + extern "C" fn f(); +} + +fn main() {} diff --git a/tests/ui/resolve/issue-108529.stderr b/tests/ui/resolve/issue-108529.stderr new file mode 100644 index 00000000000..cf4e4759c37 --- /dev/null +++ b/tests/ui/resolve/issue-108529.stderr @@ -0,0 +1,9 @@ +error[E0432]: unresolved import `f::f` + --> $DIR/issue-108529.rs:2:8 + | +LL | use f::f::f; + | ^ expected type, found associated function `f` in `f` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/resolve/token-error-correct-2.rs b/tests/ui/resolve/token-error-correct-2.rs index f7c7d908c78..a38755dae08 100644 --- a/tests/ui/resolve/token-error-correct-2.rs +++ b/tests/ui/resolve/token-error-correct-2.rs @@ -2,6 +2,5 @@ fn main() { if foo { - //~^ ERROR: cannot find value `foo` ) //~ ERROR: mismatched closing delimiter: `)` } diff --git a/tests/ui/resolve/token-error-correct-2.stderr b/tests/ui/resolve/token-error-correct-2.stderr index cca9f2dc88c..be5fb18a5d8 100644 --- a/tests/ui/resolve/token-error-correct-2.stderr +++ b/tests/ui/resolve/token-error-correct-2.stderr @@ -3,16 +3,8 @@ error: mismatched closing delimiter: `)` | LL | if foo { | ^ unclosed delimiter -LL | LL | ) | ^ mismatched closing delimiter -error[E0425]: cannot find value `foo` in this scope - --> $DIR/token-error-correct-2.rs:4:8 - | -LL | if foo { - | ^^^ not found in this scope - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/token-error-correct-3.rs b/tests/ui/resolve/token-error-correct-3.rs index 52934085fa1..2793f1b90ff 100644 --- a/tests/ui/resolve/token-error-correct-3.rs +++ b/tests/ui/resolve/token-error-correct-3.rs @@ -9,12 +9,9 @@ pub mod raw { callback: F) -> io::Result<bool> { if !is_directory(path.as_ref()) { - //~^ ERROR cannot find function `is_directory` callback(path.as_ref(); - //~^ ERROR expected one of fs::create_dir_all(path.as_ref()).map(|()| true) - } else { - //~^ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `)` + } else { //~ ERROR mismatched closing delimiter Ok(false); } diff --git a/tests/ui/resolve/token-error-correct-3.stderr b/tests/ui/resolve/token-error-correct-3.stderr index 77c87c78466..79d1d4883a1 100644 --- a/tests/ui/resolve/token-error-correct-3.stderr +++ b/tests/ui/resolve/token-error-correct-3.stderr @@ -1,25 +1,13 @@ -error: expected one of `)`, `,`, `.`, `?`, or an operator, found `;` - --> $DIR/token-error-correct-3.rs:13:21 +error: mismatched closing delimiter: `}` + --> $DIR/token-error-correct-3.rs:12:21 | +LL | if !is_directory(path.as_ref()) { + | - closing delimiter possibly meant for this LL | callback(path.as_ref(); - | ^ ^ help: `)` may belong here - | | - | unclosed delimiter - -error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)` - --> $DIR/token-error-correct-3.rs:16:9 - | + | ^ unclosed delimiter LL | fs::create_dir_all(path.as_ref()).map(|()| true) - | - expected one of `.`, `;`, `?`, `}`, or an operator LL | } else { - | ^ unexpected token - -error[E0425]: cannot find function `is_directory` in this scope - --> $DIR/token-error-correct-3.rs:11:13 - | -LL | if !is_directory(path.as_ref()) { - | ^^^^^^^^^^^^ not found in this scope + | ^ mismatched closing delimiter -error: aborting due to 3 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/resolve/token-error-correct-4.fixed b/tests/ui/resolve/token-error-correct-4.fixed deleted file mode 100644 index 064b9e74e24..00000000000 --- a/tests/ui/resolve/token-error-correct-4.fixed +++ /dev/null @@ -1,10 +0,0 @@ -// run-rustfix -// Test that we do some basic error correction in the tokeniser and apply suggestions. - -fn setsuna(_: ()) {} - -fn kazusa() {} - -fn main() { - setsuna(kazusa()); //~ ERROR: expected one of -} //~ ERROR: expected expression diff --git a/tests/ui/resolve/token-error-correct-4.rs b/tests/ui/resolve/token-error-correct-4.rs index 5e31d71e7bf..49fad4bd97f 100644 --- a/tests/ui/resolve/token-error-correct-4.rs +++ b/tests/ui/resolve/token-error-correct-4.rs @@ -1,4 +1,3 @@ -// run-rustfix // Test that we do some basic error correction in the tokeniser and apply suggestions. fn setsuna(_: ()) {} @@ -6,5 +5,5 @@ fn setsuna(_: ()) {} fn kazusa() {} fn main() { - setsuna(kazusa(); //~ ERROR: expected one of -} //~ ERROR: expected expression + setsuna(kazusa(); +} //~ ERROR mismatched closing delimiter diff --git a/tests/ui/resolve/token-error-correct-4.stderr b/tests/ui/resolve/token-error-correct-4.stderr index 81e5a133691..3ec97171fd3 100644 --- a/tests/ui/resolve/token-error-correct-4.stderr +++ b/tests/ui/resolve/token-error-correct-4.stderr @@ -1,16 +1,12 @@ -error: expected one of `)`, `,`, `.`, `?`, or an operator, found `;` - --> $DIR/token-error-correct-4.rs:9:12 +error: mismatched closing delimiter: `}` + --> $DIR/token-error-correct-4.rs:8:12 | +LL | fn main() { + | - closing delimiter possibly meant for this LL | setsuna(kazusa(); - | ^ ^ help: `)` may belong here - | | - | unclosed delimiter - -error: expected expression, found `)` - --> $DIR/token-error-correct-4.rs:10:1 - | + | ^ unclosed delimiter LL | } - | ^ expected expression + | ^ mismatched closing delimiter -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/resolve/token-error-correct.rs b/tests/ui/resolve/token-error-correct.rs index 4f74df0bf1f..84ee90e3685 100644 --- a/tests/ui/resolve/token-error-correct.rs +++ b/tests/ui/resolve/token-error-correct.rs @@ -2,7 +2,6 @@ fn main() { foo(bar(; - //~^ ERROR cannot find function `bar` in this scope } //~^ ERROR: mismatched closing delimiter: `}` diff --git a/tests/ui/resolve/token-error-correct.stderr b/tests/ui/resolve/token-error-correct.stderr index ca0c4c18ad4..35b2d0b323b 100644 --- a/tests/ui/resolve/token-error-correct.stderr +++ b/tests/ui/resolve/token-error-correct.stderr @@ -5,16 +5,8 @@ LL | fn main() { | - closing delimiter possibly meant for this LL | foo(bar(; | ^ unclosed delimiter -LL | LL | } | ^ mismatched closing delimiter -error[E0425]: cannot find function `bar` in this scope - --> $DIR/token-error-correct.rs:4:9 - | -LL | foo(bar(; - | ^^^ not found in this scope - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index 6873abc49b2..ff45ace7857 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -6,6 +6,7 @@ // ignore-emscripten no processes // ignore-sgx no processes // ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590) +// ignore-nto no stack overflow handler used (no alternate stack available) #![feature(core_intrinsics)] #![feature(rustc_private)] diff --git a/tests/ui/runtime/signal-alternate-stack-cleanup.rs b/tests/ui/runtime/signal-alternate-stack-cleanup.rs index 8a6d738959e..37c602ae0b0 100644 --- a/tests/ui/runtime/signal-alternate-stack-cleanup.rs +++ b/tests/ui/runtime/signal-alternate-stack-cleanup.rs @@ -7,6 +7,7 @@ // ignore-windows // ignore-sgx no libc // ignore-vxworks no SIGWINCH in user space +// ignore-nto no SA_ONSTACK #![feature(rustc_private)] extern crate libc; diff --git a/tests/ui/rust-2018/remove-extern-crate.fixed b/tests/ui/rust-2018/remove-extern-crate.fixed index 832632268fb..15e0ccc5256 100644 --- a/tests/ui/rust-2018/remove-extern-crate.fixed +++ b/tests/ui/rust-2018/remove-extern-crate.fixed @@ -23,6 +23,7 @@ extern crate alloc; fn main() { another_name::mem::drop(3); another::foo(); + with_visibility::foo(); remove_extern_crate::foo!(); bar!(); alloc::vec![5]; @@ -37,3 +38,12 @@ mod another { remove_extern_crate::foo!(); } } + +mod with_visibility { + pub use core; //~ WARNING `extern crate` is not idiomatic + + pub fn foo() { + core::mem::drop(4); + remove_extern_crate::foo!(); + } +} diff --git a/tests/ui/rust-2018/remove-extern-crate.rs b/tests/ui/rust-2018/remove-extern-crate.rs index bbb84cd462d..aec0bc7c374 100644 --- a/tests/ui/rust-2018/remove-extern-crate.rs +++ b/tests/ui/rust-2018/remove-extern-crate.rs @@ -23,6 +23,7 @@ extern crate alloc; fn main() { another_name::mem::drop(3); another::foo(); + with_visibility::foo(); remove_extern_crate::foo!(); bar!(); alloc::vec![5]; @@ -37,3 +38,12 @@ mod another { remove_extern_crate::foo!(); } } + +mod with_visibility { + pub extern crate core; //~ WARNING `extern crate` is not idiomatic + + pub fn foo() { + core::mem::drop(4); + remove_extern_crate::foo!(); + } +} diff --git a/tests/ui/rust-2018/remove-extern-crate.stderr b/tests/ui/rust-2018/remove-extern-crate.stderr index bde4c180811..d07358e471b 100644 --- a/tests/ui/rust-2018/remove-extern-crate.stderr +++ b/tests/ui/rust-2018/remove-extern-crate.stderr @@ -12,10 +12,26 @@ LL | #![warn(rust_2018_idioms)] = note: `#[warn(unused_extern_crates)]` implied by `#[warn(rust_2018_idioms)]` warning: `extern crate` is not idiomatic in the new edition - --> $DIR/remove-extern-crate.rs:32:5 + --> $DIR/remove-extern-crate.rs:33:5 | LL | extern crate core; - | ^^^^^^^^^^^^^^^^^^ help: convert it to a `use` + | ^^^^^^^^^^^^^^^^^^ + | +help: convert it to a `use` + | +LL | use core; + | ~~~ + +warning: `extern crate` is not idiomatic in the new edition + --> $DIR/remove-extern-crate.rs:43:5 + | +LL | pub extern crate core; + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: convert it to a `use` + | +LL | pub use core; + | ~~~ -warning: 2 warnings emitted +warning: 3 warnings emitted diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr b/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr index eb3d3e4a67a..944cdc5f55d 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait-async.stderr @@ -2,8 +2,9 @@ error[E0700]: hidden type for `impl Clone` captures lifetime that does not appea --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait-async.rs:8:48 | LL | async fn f(self: Pin<&Self>) -> impl Clone { self } - | ----- ^^^^^^^^ - | | + | ----- ---------- ^^^^^^^^ + | | | + | | opaque type defined here | hidden type `Pin<&Foo>` captures the anonymous lifetime defined here | help: to declare that `impl Clone` captures `'_`, you can add an explicit `'_` lifetime bound diff --git a/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr index 2c0b2a0d919..8a9b397ca70 100644 --- a/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr +++ b/tests/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -2,8 +2,9 @@ error[E0700]: hidden type for `impl Clone` captures lifetime that does not appea --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:6:44 | LL | fn f(self: Pin<&Self>) -> impl Clone { self } - | ----- ^^^^ - | | + | ----- ---------- ^^^^ + | | | + | | opaque type defined here | hidden type `Pin<&Foo>` captures the anonymous lifetime defined here | help: to declare that `impl Clone` captures `'_`, you can add an explicit `'_` lifetime bound diff --git a/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs b/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs index 4559da91e47..f20024e759a 100644 --- a/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs +++ b/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs @@ -3,16 +3,10 @@ pub struct A {} impl A { - async fn create(path: impl AsRef<std::path::Path>) { //~ ERROR `async fn` is not permitted in Rust 2015 - //~^ WARN changes to closure capture in Rust 2021 will affect drop order [rust_2021_incompatible_closure_captures] + async fn create(path: impl AsRef<std::path::Path>) { ; - crate(move || {} ).await //~ ERROR expected function, found module `crate` + crate(move || {} ).await } } -trait C{async fn new(val: T) {} //~ ERROR `async fn` is not permitted in Rust 2015 -//~^ ERROR functions in traits cannot be declared `async` -//~| ERROR cannot find type `T` in this scope -//~| WARN changes to closure capture in Rust 2021 will affect drop order [rust_2021_incompatible_closure_captures] - -//~ ERROR this file contains an unclosed delimiter +trait C{async fn new(val: T) {} //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr b/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr index df1cafdb7d3..1ec8ca4275b 100644 --- a/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr +++ b/tests/ui/span/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.stderr @@ -1,95 +1,8 @@ error: this file contains an unclosed delimiter - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:18:53 + --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:12:85 | LL | trait C{async fn new(val: T) {} - | - unclosed delimiter -... -LL | - | ^ + | - unclosed delimiter ^ -error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:6:5 - | -LL | async fn create(path: impl AsRef<std::path::Path>) { - | ^^^^^ to use `async fn`, switch to Rust 2018 or later - | - = help: pass `--edition 2021` to `rustc` - = note: for more on editions, read https://doc.rust-lang.org/edition-guide - -error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:9 - | -LL | trait C{async fn new(val: T) {} - | ^^^^^ to use `async fn`, switch to Rust 2018 or later - | - = help: pass `--edition 2021` to `rustc` - = note: for more on editions, read https://doc.rust-lang.org/edition-guide - -error[E0412]: cannot find type `T` in this scope - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:27 - | -LL | pub struct A {} - | ------------ similarly named struct `A` defined here -... -LL | trait C{async fn new(val: T) {} - | ^ help: a struct with a similar name exists: `A` - -error[E0706]: functions in traits cannot be declared `async` - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:9 - | -LL | trait C{async fn new(val: T) {} - | -----^^^^^^^^^^^^^^^ - | | - | `async` because of this - | - = note: `async` trait functions are not currently supported - = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait - = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information - = help: add `#![feature(async_fn_in_trait)]` to the crate attributes to enable - -error[E0423]: expected function, found module `crate` - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:9:5 - | -LL | crate(move || {} ).await - | ^^^^^ not a function - -warning: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:6:57 - | -LL | async fn create(path: impl AsRef<std::path::Path>) { - | _____________________----_____________________________-__^ - | | | | - | | | in Rust 2018, `path` is dropped here along with the closure, but in Rust 2021 `path` is not part of the closure - | | in Rust 2018, this causes the closure to capture `path`, but in Rust 2021, it has no effect -LL | | -LL | | ; -LL | | crate(move || {} ).await -LL | | } - | |_____^ - | - = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> - = note: requested on the command line with `-W rust-2021-incompatible-closure-captures` -help: add a dummy let to cause `path` to be fully captured - | -LL | async fn create(path: impl AsRef<std::path::Path>) { let _ = &path; - | ++++++++++++++ - -warning: changes to closure capture in Rust 2021 will affect drop order - --> $DIR/drop-location-span-error-rust-2021-incompatible-closure-captures-93117.rs:13:30 - | -LL | trait C{async fn new(val: T) {} - | --- - ^^ - | | | - | | in Rust 2018, `val` is dropped here along with the closure, but in Rust 2021 `val` is not part of the closure - | in Rust 2018, this causes the closure to capture `val`, but in Rust 2021, it has no effect - | - = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> -help: add a dummy let to cause `val` to be fully captured - | -LL | trait C{async fn new(val: T) { let _ = &val;} - | +++++++++++++ - -error: aborting due to 6 previous errors; 2 warnings emitted +error: aborting due to previous error -Some errors have detailed explanations: E0412, E0423, E0670, E0706. -For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/structs-enums/rec-align-u64.rs b/tests/ui/structs-enums/rec-align-u64.rs index 40ede9705f1..f21c9b2c808 100644 --- a/tests/ui/structs-enums/rec-align-u64.rs +++ b/tests/ui/structs-enums/rec-align-u64.rs @@ -43,7 +43,9 @@ struct Outer { target_os = "netbsd", target_os = "openbsd", target_os = "solaris", - target_os = "vxworks"))] + target_os = "vxworks", + target_os = "nto", +))] mod m { #[cfg(target_arch = "x86")] pub mod m { diff --git a/tests/ui/suggestions/constrain-suggest-ice.rs b/tests/ui/suggestions/constrain-suggest-ice.rs index 69b874bed1b..d6e5263ffe0 100644 --- a/tests/ui/suggestions/constrain-suggest-ice.rs +++ b/tests/ui/suggestions/constrain-suggest-ice.rs @@ -1,11 +1,9 @@ -struct Bug<S>{ //~ ERROR parameter `S` is never used [E0392] +struct Bug<S>{ A: [(); { - let x: [u8; Self::W] = [0; Self::W]; //~ ERROR generic `Self` types are currently not permitted in anonymous constants - //~^ ERROR generic `Self` types are currently not permitted in anonymous constants - //~^^ ERROR the size for values of type `S` cannot be known at compilation time [E0277] - F //~ ERROR cannot find value `F` in this scope [E0425] + let x: [u8; Self::W] = [0; Self::W]; + F } -} //~ ERROR mismatched closing delimiter: `}` +} //~^ ERROR mismatched closing delimiter: `}` fn main() {} diff --git a/tests/ui/suggestions/constrain-suggest-ice.stderr b/tests/ui/suggestions/constrain-suggest-ice.stderr index 2af7c2f6971..9b92091de9f 100644 --- a/tests/ui/suggestions/constrain-suggest-ice.stderr +++ b/tests/ui/suggestions/constrain-suggest-ice.stderr @@ -9,64 +9,5 @@ LL | A: [(); { LL | } | ^ mismatched closing delimiter -error: mismatched closing delimiter: `}` - --> $DIR/constrain-suggest-ice.rs:2:8 - | -LL | struct Bug<S>{ - | - closing delimiter possibly meant for this -LL | A: [(); { - | ^ unclosed delimiter -... -LL | } - | ^ mismatched closing delimiter - -error[E0425]: cannot find value `F` in this scope - --> $DIR/constrain-suggest-ice.rs:6:9 - | -LL | F - | ^ help: a local variable with a similar name exists: `x` - -error: generic `Self` types are currently not permitted in anonymous constants - --> $DIR/constrain-suggest-ice.rs:3:21 - | -LL | let x: [u8; Self::W] = [0; Self::W]; - | ^^^^ - -error: generic `Self` types are currently not permitted in anonymous constants - --> $DIR/constrain-suggest-ice.rs:3:36 - | -LL | let x: [u8; Self::W] = [0; Self::W]; - | ^^^^ - -error[E0277]: the size for values of type `S` cannot be known at compilation time - --> $DIR/constrain-suggest-ice.rs:3:36 - | -LL | struct Bug<S>{ - | - this type parameter needs to be `std::marker::Sized` -LL | A: [(); { -LL | let x: [u8; Self::W] = [0; Self::W]; - | ^^^^^^^ doesn't have a size known at compile-time - | -note: required by a bound in `Bug` - --> $DIR/constrain-suggest-ice.rs:1:12 - | -LL | struct Bug<S>{ - | ^ required by this bound in `Bug` -help: consider relaxing the implicit `Sized` restriction - | -LL | struct Bug<S: ?Sized>{ - | ++++++++ - -error[E0392]: parameter `S` is never used - --> $DIR/constrain-suggest-ice.rs:1:12 - | -LL | struct Bug<S>{ - | ^ unused parameter - | - = help: consider removing `S`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `S` to be a const parameter, use `const S: usize` instead - -error: aborting due to 7 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0392, E0425. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr index 679ebd61ead..da72c8ebf19 100644 --- a/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr +++ b/tests/ui/suggestions/impl-on-dyn-trait-with-implicit-static-bound.stderr @@ -17,7 +17,7 @@ note: the used `impl` has a `'static` requirement LL | impl<T> MyTrait<T> for dyn ObjectTrait<T> { | ^^^^^^^^^^^^^^ this has an implicit `'static` lifetime requirement LL | fn use_self<K>(&self) -> &() { panic!() } - | -------- calling this method introduces the `impl`'s 'static` requirement + | -------- calling this method introduces the `impl`'s `'static` requirement help: consider relaxing the implicit `'static` requirement | LL | impl<T> MyTrait<T> for dyn ObjectTrait<T> + '_ { @@ -42,7 +42,7 @@ note: the used `impl` has a `'static` requirement LL | impl dyn ObjectTrait { | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement LL | fn use_self(&self) -> &() { panic!() } - | -------- calling this method introduces the `impl`'s 'static` requirement + | -------- calling this method introduces the `impl`'s `'static` requirement help: consider relaxing the implicit `'static` requirement | LL | impl dyn ObjectTrait + '_ { @@ -65,7 +65,7 @@ note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:87:26 | LL | fn use_self(&self) -> &() { panic!() } - | -------- calling this method introduces the `impl`'s 'static` requirement + | -------- calling this method introduces the `impl`'s `'static` requirement ... LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement @@ -91,7 +91,7 @@ note: the used `impl` has a `'static` requirement --> $DIR/impl-on-dyn-trait-with-implicit-static-bound.rs:106:26 | LL | fn use_self(&self) -> &() { panic!() } - | -------- calling this method introduces the `impl`'s 'static` requirement + | -------- calling this method introduces the `impl`'s `'static` requirement ... LL | impl MyTrait for dyn ObjectTrait {} | ^^^^^^^^^^^ this has an implicit `'static` lifetime requirement diff --git a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index c5c3f7b468c..93cfa60b5ab 100644 --- a/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/tests/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -10,7 +10,9 @@ error[E0700]: hidden type for `impl FnOnce()` captures lifetime that does not ap --> $DIR/missing-lifetimes-in-signature.rs:19:5 | LL | fn foo<G, T>(g: G, dest: &mut T) -> impl FnOnce() - | ------ hidden type `[closure@$DIR/missing-lifetimes-in-signature.rs:19:5: 19:12]` captures the anonymous lifetime defined here + | ------ ------------- opaque type defined here + | | + | hidden type `[closure@$DIR/missing-lifetimes-in-signature.rs:19:5: 19:12]` captures the anonymous lifetime defined here ... LL | / move || { LL | | diff --git a/tests/ui/suggestions/multiline-multipart-suggestion.rs b/tests/ui/suggestions/multiline-multipart-suggestion.rs new file mode 100644 index 00000000000..77d0322d05f --- /dev/null +++ b/tests/ui/suggestions/multiline-multipart-suggestion.rs @@ -0,0 +1,19 @@ +// compile-flags: --error-format=human --color=always +// ignore-windows + +fn short(foo_bar: &Vec<&i32>) -> &i32 { //~ ERROR missing lifetime specifier + &12 +} + +fn long( //~ ERROR missing lifetime specifier + foo_bar: &Vec<&i32>, + something_very_long_so_that_the_line_will_wrap_around__________: i32, +) -> &i32 { + &12 +} + +fn long2( //~ ERROR missing lifetime specifier + foo_bar: &Vec<&i32>) -> &i32 { + &12 +} +fn main() {} diff --git a/tests/ui/suggestions/multiline-multipart-suggestion.stderr b/tests/ui/suggestions/multiline-multipart-suggestion.stderr new file mode 100644 index 00000000000..045a86b4f54 --- /dev/null +++ b/tests/ui/suggestions/multiline-multipart-suggestion.stderr @@ -0,0 +1,46 @@ +[0m[1m[38;5;9merror[E0106][0m[0m[1m: missing lifetime specifier[0m +[0m [0m[0m[1m[38;5;12m--> [0m[0m$DIR/multiline-multipart-suggestion.rs:4:34[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m|[0m[0m [0m[0mfn short(foo_bar: &Vec<&i32>) -> &i32 { +[0m [0m[0m[1m[38;5;12m| [0m[0m [0m[0m[1m[38;5;12m----------[0m[0m [0m[0m[1m[38;5;9m^[0m[0m [0m[0m[1m[38;5;9mexpected named lifetime parameter[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m [0m[0m[1m[38;5;12m= [0m[0m[1mhelp[0m[0m: this function's return type contains a borrowed value, but the signature does not say which one of `foo_bar`'s 2 lifetimes it is borrowed from[0m +[0m[1m[38;5;14mhelp[0m[0m: consider introducing a named lifetime parameter[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m| [0m[0mfn short[0m[0m[38;5;10m<'a>[0m[0m(foo_bar: &[0m[0m[38;5;10m'a [0m[0mVec<&[0m[0m[38;5;10m'a [0m[0mi32>) -> &[0m[0m[38;5;10m'a [0m[0mi32 { +[0m [0m[0m[1m[38;5;12m|[0m[0m [0m[0m[38;5;10m++++[0m[0m [0m[0m[38;5;10m++[0m[0m [0m[0m[38;5;10m++[0m[0m [0m[0m[38;5;10m++[0m + +[0m[1m[38;5;9merror[E0106][0m[0m[1m: missing lifetime specifier[0m +[0m [0m[0m[1m[38;5;12m--> [0m[0m$DIR/multiline-multipart-suggestion.rs:11:6[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m|[0m[0m [0m[0m foo_bar: &Vec<&i32>,[0m +[0m [0m[0m[1m[38;5;12m| [0m[0m [0m[0m[1m[38;5;12m----------[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m|[0m[0m [0m[0m something_very_long_so_that_the_line_will_wrap_around__________: i32,[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m|[0m[0m [0m[0m) -> &i32 {[0m +[0m [0m[0m[1m[38;5;12m| [0m[0m [0m[0m[1m[38;5;9m^[0m[0m [0m[0m[1m[38;5;9mexpected named lifetime parameter[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m [0m[0m[1m[38;5;12m= [0m[0m[1mhelp[0m[0m: this function's return type contains a borrowed value, but the signature does not say which one of `foo_bar`'s 2 lifetimes it is borrowed from[0m +[0m[1m[38;5;14mhelp[0m[0m: consider introducing a named lifetime parameter[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[38;5;10m~ [0m[0mfn long[0m[0m[38;5;10m<'a>[0m[0m( +[0m[1m[38;5;12mLL[0m[0m [0m[0m[38;5;10m~ [0m[0m foo_bar: &[0m[0m[38;5;10m'a [0m[0mVec<&[0m[0m[38;5;10m'a [0m[0mi32>,[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m| [0m[0m something_very_long_so_that_the_line_will_wrap_around__________: i32,[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[38;5;10m~ [0m[0m) -> &[0m[0m[38;5;10m'a [0m[0mi32 {[0m +[0m [0m[0m[1m[38;5;12m|[0m + +[0m[1m[38;5;9merror[E0106][0m[0m[1m: missing lifetime specifier[0m +[0m [0m[0m[1m[38;5;12m--> [0m[0m$DIR/multiline-multipart-suggestion.rs:16:29[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[1m[38;5;12m|[0m[0m [0m[0m foo_bar: &Vec<&i32>) -> &i32 {[0m +[0m [0m[0m[1m[38;5;12m| [0m[0m [0m[0m[1m[38;5;12m----------[0m[0m [0m[0m[1m[38;5;9m^[0m[0m [0m[0m[1m[38;5;9mexpected named lifetime parameter[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m [0m[0m[1m[38;5;12m= [0m[0m[1mhelp[0m[0m: this function's return type contains a borrowed value, but the signature does not say which one of `foo_bar`'s 2 lifetimes it is borrowed from[0m +[0m[1m[38;5;14mhelp[0m[0m: consider introducing a named lifetime parameter[0m +[0m [0m[0m[1m[38;5;12m|[0m +[0m[1m[38;5;12mLL[0m[0m [0m[0m[38;5;10m~ [0m[0mfn long2[0m[0m[38;5;10m<'a>[0m[0m( +[0m[1m[38;5;12mLL[0m[0m [0m[0m[38;5;10m~ [0m[0m foo_bar: &[0m[0m[38;5;10m'a [0m[0mVec<&[0m[0m[38;5;10m'a [0m[0mi32>) -> &[0m[0m[38;5;10m'a [0m[0mi32 {[0m +[0m [0m[0m[1m[38;5;12m|[0m + +[0m[1m[38;5;9merror[0m[0m[1m: aborting due to 3 previous errors[0m + +[0m[1mFor more information about this error, try `rustc --explain E0106`.[0m diff --git a/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.rs b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.rs new file mode 100644 index 00000000000..fbaf41245e9 --- /dev/null +++ b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.rs @@ -0,0 +1,8 @@ +pub struct Bar<S>(S); + +pub trait Foo {} + +impl<S> Foo for Bar<S> where for<'a> <&'a S>::Item: Foo {} +//~^ ERROR ambiguous associated type + +fn main() {} diff --git a/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr new file mode 100644 index 00000000000..0ca5b9b9207 --- /dev/null +++ b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr @@ -0,0 +1,9 @@ +error[E0223]: ambiguous associated type + --> $DIR/suggest-trait-in-ufcs-in-hrtb.rs:5:38 + | +LL | impl<S> Foo for Bar<S> where for<'a> <&'a S>::Item: Foo {} + | ^^^^^^^^^^^^^ help: use the fully-qualified path: `<&'a S as IntoIterator>::Item` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/thir-print/thir-flat.stdout b/tests/ui/thir-print/thir-flat.stdout index c399fa66b6a..910c0da2737 100644 --- a/tests/ui/thir-print/thir-flat.stdout +++ b/tests/ui/thir-print/thir-flat.stdout @@ -1,5 +1,8 @@ DefId(0:3 ~ thir_flat[45a6]::main): Thir { + body_type: Fn( + ([]; c_variadic: false)->(), + ), arms: [], blocks: [ Block { @@ -14,21 +17,16 @@ Thir { ], exprs: [ Expr { - ty: (), - temp_lifetime: Some( - Node(2), - ), - span: $DIR/thir-flat.rs:4:15: 4:17 (#0), kind: Block { block: b0, }, - }, - Expr { ty: (), temp_lifetime: Some( Node(2), ), span: $DIR/thir-flat.rs:4:15: 4:17 (#0), + }, + Expr { kind: Scope { region_scope: Node(2), lint_level: Explicit( @@ -36,18 +34,23 @@ Thir { ), value: e0, }, - }, - Expr { ty: (), temp_lifetime: Some( Node(2), ), span: $DIR/thir-flat.rs:4:15: 4:17 (#0), + }, + Expr { kind: Scope { region_scope: Destruction(2), lint_level: Inherited, value: e1, }, + ty: (), + temp_lifetime: Some( + Node(2), + ), + span: $DIR/thir-flat.rs:4:15: 4:17 (#0), }, ], stmts: [], diff --git a/tests/ui/thread-local/tls.rs b/tests/ui/thread-local/tls.rs index fbd3413885f..f03bd3f991b 100644 --- a/tests/ui/thread-local/tls.rs +++ b/tests/ui/thread-local/tls.rs @@ -1,6 +1,7 @@ // run-pass // ignore-emscripten no threads support // compile-flags: -O +// ignore-nto Doesn't work without emulated TLS enabled (in LLVM) #![feature(thread_local)] diff --git a/tests/ui/tool-attributes/duplicate-diagnostic.rs b/tests/ui/tool-attributes/duplicate-diagnostic.rs index 39c2ca1cb86..e2cf9508757 100644 --- a/tests/ui/tool-attributes/duplicate-diagnostic.rs +++ b/tests/ui/tool-attributes/duplicate-diagnostic.rs @@ -9,5 +9,5 @@ extern crate p1; extern crate p2; #[rustc_diagnostic_item = "Foo"] -pub struct Foo {} //~ ERROR duplicate diagnostic item found +pub struct Foo {} //~ ERROR duplicate diagnostic item in crate `duplicate_diagnostic`: `Foo` fn main() {} diff --git a/tests/ui/tool-attributes/duplicate-diagnostic.stderr b/tests/ui/tool-attributes/duplicate-diagnostic.stderr index e315fdc7d84..26bd6a82e34 100644 --- a/tests/ui/tool-attributes/duplicate-diagnostic.stderr +++ b/tests/ui/tool-attributes/duplicate-diagnostic.stderr @@ -2,11 +2,13 @@ error: duplicate diagnostic item in crate `p2`: `Foo`. | = note: the diagnostic item is first defined in crate `p1`. -error: duplicate diagnostic item found: `Foo`. +error: duplicate diagnostic item in crate `duplicate_diagnostic`: `Foo`. --> $DIR/duplicate-diagnostic.rs:12:1 | LL | pub struct Foo {} | ^^^^^^^^^^^^^^ + | + = note: the diagnostic item is first defined in crate `p2`. error: aborting due to 2 previous errors diff --git a/tests/ui/traits/issue-77982.stderr b/tests/ui/traits/issue-77982.stderr index a397b0accc8..d4fea05fe4b 100644 --- a/tests/ui/traits/issue-77982.stderr +++ b/tests/ui/traits/issue-77982.stderr @@ -43,7 +43,7 @@ LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(0u32.into())).collect( | | | required by a bound introduced by this call | - = note: multiple `impl`s satisfying `u32: From<_>` found in the following crates: `core`, `std`: + = note: multiple `impl`s satisfying `u32: From<_>` found in the `core` crate: - impl From<Ipv4Addr> for u32; - impl From<NonZeroU32> for u32; - impl From<bool> for u32; diff --git a/tests/ui/traits/new-solver/try-example.rs b/tests/ui/traits/new-solver/try-example.rs new file mode 100644 index 00000000000..e826f3a0059 --- /dev/null +++ b/tests/ui/traits/new-solver/try-example.rs @@ -0,0 +1,28 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +use std::error::Error; + +fn main() -> Result<(), Box<dyn Error>> { + let x: i32 = parse()?; + Ok(()) +} + +trait Parse {} + +impl Parse for i32 {} + +#[derive(Debug)] +struct ParseError; + +impl std::fmt::Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ParseError") + } +} + +impl Error for ParseError {} + +fn parse<T: Parse>() -> Result<T, ParseError> { + todo!() +} diff --git a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs new file mode 100644 index 00000000000..3903bfe9bcf --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.rs @@ -0,0 +1,11 @@ +#![feature(non_lifetime_binders, generic_const_exprs)] +//~^ WARN the feature `non_lifetime_binders` is incomplete +//~| WARN the feature `generic_const_exprs` is incomplete + +fn foo() -> usize +where + for<T> [i32; { let _: T = todo!(); 0 }]:, + //~^ ERROR cannot capture late-bound type parameter in a constant +{} + +fn main() {} diff --git a/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr new file mode 100644 index 00000000000..fafff02dea6 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/late-bound-in-anon-ct.stderr @@ -0,0 +1,27 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/late-bound-in-anon-ct.rs:1:12 + | +LL | #![feature(non_lifetime_binders, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/late-bound-in-anon-ct.rs:1:34 + | +LL | #![feature(non_lifetime_binders, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information + +error: cannot capture late-bound type parameter in a constant + --> $DIR/late-bound-in-anon-ct.rs:7:27 + | +LL | for<T> [i32; { let _: T = todo!(); 0 }]:, + | - ^ + | | + | parameter defined here + +error: aborting due to previous error; 2 warnings emitted + diff --git a/tests/ui/traits/non_lifetime_binders/object-lifetime-default-for-late.rs b/tests/ui/traits/non_lifetime_binders/object-lifetime-default-for-late.rs new file mode 100644 index 00000000000..9830241c377 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/object-lifetime-default-for-late.rs @@ -0,0 +1,7 @@ +// check-pass +// compile-flags: --crate-type=lib + +#![feature(non_lifetime_binders)] +//~^ WARN the feature `non_lifetime_binders` is incomplete + +pub fn f<T>() where for<U> (T, U): Copy {} diff --git a/tests/ui/traits/non_lifetime_binders/object-lifetime-default-for-late.stderr b/tests/ui/traits/non_lifetime_binders/object-lifetime-default-for-late.stderr new file mode 100644 index 00000000000..667575b72d4 --- /dev/null +++ b/tests/ui/traits/non_lifetime_binders/object-lifetime-default-for-late.stderr @@ -0,0 +1,11 @@ +warning: the feature `non_lifetime_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/object-lifetime-default-for-late.rs:4:12 + | +LL | #![feature(non_lifetime_binders)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr index 0ed8a703b6d..e52d5f9de69 100644 --- a/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr +++ b/tests/ui/type-alias-impl-trait/imply_bounds_from_bounds_param.stderr @@ -2,7 +2,9 @@ error[E0700]: hidden type for `impl PlusOne` captures lifetime that does not app --> $DIR/imply_bounds_from_bounds_param.rs:24:5 | LL | fn test<'a>(y: &'a mut i32) -> impl PlusOne { - | -- hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here + | -- ------------ opaque type defined here + | | + | hidden type `<&'a mut i32 as Callable>::Output` captures the lifetime `'a` as defined here LL | <&'a mut i32 as Callable>::call(y) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | diff --git a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr index 65a0af0d22f..d666e668d36 100644 --- a/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr +++ b/tests/ui/type-alias-impl-trait/missing_lifetime_bound.stderr @@ -1,6 +1,8 @@ error[E0700]: hidden type for `Opaque<'a, T>` captures lifetime that does not appear in bounds --> $DIR/missing_lifetime_bound.rs:4:47 | +LL | type Opaque<'a, T> = impl Sized; + | ---------- opaque type defined here LL | fn defining<'a, T>(x: &'a i32) -> Opaque<T> { x } | -- ^ | | diff --git a/tests/ui/type/issue-91268.rs b/tests/ui/type/issue-91268.rs index f1e16bc7bd3..274ea839e8b 100644 --- a/tests/ui/type/issue-91268.rs +++ b/tests/ui/type/issue-91268.rs @@ -1,8 +1,4 @@ // error-pattern: this file contains an unclosed delimiter -// error-pattern: cannot find type `ţ` in this scope -// error-pattern: parenthesized type parameters may only be used with a `Fn` trait -// error-pattern: type arguments are not allowed on builtin type `u8` -// error-pattern: mismatched types // ignore-tidy-trailing-newlines // `ţ` must be the last character in this file, it cannot be followed by a newline fn main() { diff --git a/tests/ui/type/issue-91268.stderr b/tests/ui/type/issue-91268.stderr index 6c9ee994584..a3619d863e2 100644 --- a/tests/ui/type/issue-91268.stderr +++ b/tests/ui/type/issue-91268.stderr @@ -1,5 +1,5 @@ error: this file contains an unclosed delimiter - --> $DIR/issue-91268.rs:9:12 + --> $DIR/issue-91268.rs:5:12 | LL | fn main() { | - unclosed delimiter @@ -8,56 +8,5 @@ LL | 0: u8(ţ | | | unclosed delimiter -error: this file contains an unclosed delimiter - --> $DIR/issue-91268.rs:9:12 - | -LL | fn main() { - | - unclosed delimiter -LL | 0: u8(ţ - | - ^ - | | - | unclosed delimiter - -error[E0412]: cannot find type `ţ` in this scope - --> $DIR/issue-91268.rs:9:11 - | -LL | 0: u8(ţ - | ^ expecting a type here because of type ascription - -error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/issue-91268.rs:9:8 - | -LL | 0: u8(ţ - | ^^^^ only `Fn` traits may use parentheses - | -help: use angle brackets instead - | -LL | 0: u8<ţ> - | ~ + - -error[E0109]: type arguments are not allowed on builtin type `u8` - --> $DIR/issue-91268.rs:9:11 - | -LL | 0: u8(ţ - | -- ^ type argument not allowed - | | - | not allowed on builtin type `u8` - | -help: primitive type `u8` doesn't have generic parameters - | -LL - 0: u8(ţ -LL + 0: u8 - | - -error[E0308]: mismatched types - --> $DIR/issue-91268.rs:9:5 - | -LL | fn main() { - | - expected `()` because of default return type -LL | 0: u8(ţ - | ^^^^^^^ expected `()`, found `u8` - -error: aborting due to 6 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0109, E0214, E0308, E0412. -For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/typeck/bad-type-in-vec-contains.rs b/tests/ui/typeck/bad-type-in-vec-contains.rs new file mode 100644 index 00000000000..4433047b75a --- /dev/null +++ b/tests/ui/typeck/bad-type-in-vec-contains.rs @@ -0,0 +1,7 @@ +// The error message here still is pretty confusing. + +fn main() { + let primes = Vec::new(); + primes.contains(3); + //~^ ERROR mismatched types +} diff --git a/tests/ui/typeck/bad-type-in-vec-contains.stderr b/tests/ui/typeck/bad-type-in-vec-contains.stderr new file mode 100644 index 00000000000..0e03388d2d5 --- /dev/null +++ b/tests/ui/typeck/bad-type-in-vec-contains.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/bad-type-in-vec-contains.rs:5:21 + | +LL | primes.contains(3); + | -------- ^ + | | | + | | expected `&_`, found integer + | | help: consider borrowing here: `&3` + | arguments to this method are incorrect + | here the type of `primes` is inferred to be `[_]` + | + = note: expected reference `&_` + found type `{integer}` +note: method defined here + --> $SRC_DIR/core/src/slice/mod.rs:LL:COL + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-31173.stderr b/tests/ui/typeck/issue-31173.stderr index 8346c9a0aae..b622122f33e 100644 --- a/tests/ui/typeck/issue-31173.stderr +++ b/tests/ui/typeck/issue-31173.stderr @@ -24,8 +24,17 @@ note: required by a bound in `cloned` error[E0599]: the method `collect` exists for struct `Cloned<TakeWhile<&mut IntoIter<u8>, [closure@issue-31173.rs:7:21]>>`, but its trait bounds were not satisfied --> $DIR/issue-31173.rs:12:10 | -LL | .collect(); - | ^^^^^^^ method cannot be called due to unsatisfied trait bounds +LL | let temp: Vec<u8> = it + | _________________________- +LL | | .take_while(|&x| { +LL | | found_e = true; +LL | | false +LL | | }) +LL | | .cloned() +LL | | .collect(); + | | -^^^^^^^ method cannot be called due to unsatisfied trait bounds + | |_________| + | --> $SRC_DIR/core/src/iter/adapters/take_while.rs:LL:COL | = note: doesn't satisfy `<_ as Iterator>::Item = &_` diff --git a/tests/ui/typeck/issue-91334.rs b/tests/ui/typeck/issue-91334.rs index bf9a5a62620..29204276bb3 100644 --- a/tests/ui/typeck/issue-91334.rs +++ b/tests/ui/typeck/issue-91334.rs @@ -1,9 +1,6 @@ // Regression test for the ICE described in issue #91334. // error-pattern: this file contains an unclosed delimiter -// error-pattern: expected one of -// error-pattern: mismatched closing delimiter -// error-pattern: mismatched types #![feature(generators)] diff --git a/tests/ui/typeck/issue-91334.stderr b/tests/ui/typeck/issue-91334.stderr index 78f392c9a8a..7cb30eea530 100644 --- a/tests/ui/typeck/issue-91334.stderr +++ b/tests/ui/typeck/issue-91334.stderr @@ -1,15 +1,14 @@ -error: this file contains an unclosed delimiter - --> $DIR/issue-91334.rs:10:23 +error: mismatched closing delimiter: `)` + --> $DIR/issue-91334.rs:7:19 | LL | fn f(){||yield(((){), - | - - - ^ - | | | | - | | | missing open `(` for this delimiter - | | unclosed delimiter - | unclosed delimiter + | - ^^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this error: this file contains an unclosed delimiter - --> $DIR/issue-91334.rs:10:23 + --> $DIR/issue-91334.rs:7:23 | LL | fn f(){||yield(((){), | - - - ^ @@ -18,35 +17,5 @@ LL | fn f(){||yield(((){), | | unclosed delimiter | unclosed delimiter -error: expected one of `)`, `,`, `.`, `?`, or an operator, found `{` - --> $DIR/issue-91334.rs:10:19 - | -LL | fn f(){||yield(((){), - | ^ - | | - | expected one of `)`, `,`, `.`, `?`, or an operator - | help: missing `,` - -error: mismatched closing delimiter: `)` - --> $DIR/issue-91334.rs:10:19 - | -LL | fn f(){||yield(((){), - | - ^^ mismatched closing delimiter - | | | - | | unclosed delimiter - | closing delimiter possibly meant for this - -error[E0308]: mismatched types - --> $DIR/issue-91334.rs:10:8 - | -LL | fn f(){||yield(((){), - | -^^^^^^^^^^^^^^^ expected `()`, found generator - | | - | help: a return type might be missing here: `-> _` - | - = note: expected unit type `()` - found generator `[generator@$DIR/issue-91334.rs:10:8: 10:10]` - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-92481.rs b/tests/ui/typeck/issue-92481.rs index 0a6b1843d99..f752400bbcb 100644 --- a/tests/ui/typeck/issue-92481.rs +++ b/tests/ui/typeck/issue-92481.rs @@ -2,13 +2,8 @@ #![crate_type="lib"] -fn r({) { - Ok { //~ ERROR mismatched types [E0308] +fn r({) { //~ ERROR mismatched closing delimiter + Ok { d..||_=m } } -//~^^^^^ ERROR expected parameter name, found `{` -//~| ERROR expected one of `,`, `:`, or `}`, found `..` -//~^^^^^ ERROR cannot find value `d` in this scope [E0425] -//~| ERROR cannot find value `m` in this scope [E0425] -//~| ERROR variant `Result<_, _>::Ok` has no field named `d` [E0559] diff --git a/tests/ui/typeck/issue-92481.stderr b/tests/ui/typeck/issue-92481.stderr index c3acbd2c067..d87d3277d56 100644 --- a/tests/ui/typeck/issue-92481.stderr +++ b/tests/ui/typeck/issue-92481.stderr @@ -1,60 +1,11 @@ -error: expected parameter name, found `{` +error: mismatched closing delimiter: `)` --> $DIR/issue-92481.rs:5:6 | LL | fn r({) { - | ^ expected parameter name + | -^^ mismatched closing delimiter + | || + | |unclosed delimiter + | closing delimiter possibly meant for this -error: expected one of `,`, `:`, or `}`, found `..` - --> $DIR/issue-92481.rs:5:6 - | -LL | fn r({) { - | ^ unclosed delimiter -LL | Ok { -LL | d..||_=m - | -^ - | | - | help: `}` may belong here - -error[E0425]: cannot find value `d` in this scope - --> $DIR/issue-92481.rs:7:9 - | -LL | d..||_=m - | ^ not found in this scope - -error[E0425]: cannot find value `m` in this scope - --> $DIR/issue-92481.rs:7:16 - | -LL | d..||_=m - | ^ not found in this scope - -error[E0559]: variant `Result<_, _>::Ok` has no field named `d` - --> $DIR/issue-92481.rs:7:9 - | -LL | d..||_=m - | ^ field does not exist - --> $SRC_DIR/core/src/result.rs:LL:COL - | - = note: `Result<_, _>::Ok` defined here - | -help: `Result<_, _>::Ok` is a tuple variant, use the appropriate syntax - | -LL | Result<_, _>::Ok(/* fields */) - | - -error[E0308]: mismatched types - --> $DIR/issue-92481.rs:6:5 - | -LL | fn r({) { - | - help: a return type might be missing here: `-> _` -LL | / Ok { -LL | | d..||_=m -LL | | } - | |_____^ expected `()`, found `Result<_, _>` - | - = note: expected unit type `()` - found enum `Result<_, _>` - -error: aborting due to 6 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0308, E0425, E0559. -For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs b/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs new file mode 100644 index 00000000000..019c6e81c50 --- /dev/null +++ b/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs @@ -0,0 +1,24 @@ +// compile-flags: -Ztrait-solver=next +// known-bug: unknown + +trait Test { + type Assoc; +} + +fn transform<T: Test>(x: T) -> T::Assoc { + todo!() +} + +impl Test for i32 { + type Assoc = i32; +} + +impl Test for String { + type Assoc = String; +} + +fn main() { + let mut x = Default::default(); + x = transform(x); + x = 1i32; +} diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr b/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr new file mode 100644 index 00000000000..57cbc65a17a --- /dev/null +++ b/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/equating-projection-cyclically.rs:22:19 + | +LL | x = transform(x); + | ^ expected inferred type, found associated type + | + = note: expected type `_` + found associated type `<_ as Test>::Assoc` + = help: consider constraining the associated type `<_ as Test>::Assoc` to `_` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs index 674c26a43f2..82a1dd63713 100644 --- a/tests/ui/wait-forked-but-failed-child.rs +++ b/tests/ui/wait-forked-but-failed-child.rs @@ -3,6 +3,7 @@ // ignore-sgx no processes // ignore-vxworks no 'ps' // ignore-fuchsia no 'ps' +// ignore-nto no 'ps' #![feature(rustc_private)] diff --git a/tests/ui/while/while-else-err.rs b/tests/ui/while/while-else-err.rs new file mode 100644 index 00000000000..36b60fbd4be --- /dev/null +++ b/tests/ui/while/while-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + while false { + //~^ NOTE `else` is attached to this loop + } else { + //~^ ERROR `while...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/while/while-else-err.stderr b/tests/ui/while/while-else-err.stderr new file mode 100644 index 00000000000..88f715d5666 --- /dev/null +++ b/tests/ui/while/while-else-err.stderr @@ -0,0 +1,17 @@ +error: `while...else` loops are not supported + --> $DIR/while-else-err.rs:4:7 + | +LL | while false { + | ----- `else` is attached to this loop +LL | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/tests/ui/while/while-else-let-else-err.rs b/tests/ui/while/while-else-let-else-err.rs new file mode 100644 index 00000000000..6d9909347c3 --- /dev/null +++ b/tests/ui/while/while-else-let-else-err.rs @@ -0,0 +1,8 @@ +fn main() { + let _ = while false { + //~^ NOTE `else` is attached to this loop + } else { + //~^ ERROR `while...else` loops are not supported + //~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + }; +} diff --git a/tests/ui/while/while-else-let-else-err.stderr b/tests/ui/while/while-else-let-else-err.stderr new file mode 100644 index 00000000000..431d37c007c --- /dev/null +++ b/tests/ui/while/while-else-let-else-err.stderr @@ -0,0 +1,17 @@ +error: `while...else` loops are not supported + --> $DIR/while-else-let-else-err.rs:4:7 + | +LL | let _ = while false { + | ----- `else` is attached to this loop +LL | +LL | } else { + | _______^ +LL | | +LL | | +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: aborting due to previous error + diff --git a/triagebot.toml b/triagebot.toml index 7a26457ab04..f7607d522c1 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -482,7 +482,6 @@ compiler-team = [ "@davidtwco", "@oli-obk", "@lcnr", - "@nagisa", "@wesleywiser", "@michaelwoerister", ] @@ -552,7 +551,6 @@ mir = [ "@oli-obk", ] mir-opt = [ - "@nagisa", "@oli-obk", "@wesleywiser", ] |
