diff options
458 files changed, 7529 insertions, 3606 deletions
diff --git a/Cargo.lock b/Cargo.lock index ad01ef5e41f..a130f49b0be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,7 @@ dependencies = [ name = "cargo-miri" version = "0.1.0" dependencies = [ - "cargo_metadata 0.15.0", + "cargo_metadata 0.15.3", "directories", "rustc-build-sysroot", "rustc-workspace-hack", @@ -540,15 +540,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.0" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" +checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07" dependencies = [ "camino", "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver", "serde", "serde_json", + "thiserror", ] [[package]] @@ -732,6 +733,7 @@ dependencies = [ name = "clippy" version = "0.1.69" dependencies = [ + "clap 4.1.4", "clippy_lints", "clippy_utils", "compiletest_rs", @@ -762,7 +764,7 @@ name = "clippy_dev" version = "0.0.1" dependencies = [ "aho-corasick", - "clap 3.2.20", + "clap 4.1.4", "indoc", "itertools", "opener", @@ -774,7 +776,7 @@ dependencies = [ name = "clippy_lints" version = "0.1.69" dependencies = [ - "cargo_metadata 0.14.0", + "cargo_metadata 0.15.3", "clippy_utils", "declare_clippy_lint", "if_chain", @@ -893,6 +895,7 @@ dependencies = [ name = "compiletest" version = "0.0.0" dependencies = [ + "build_helper", "colored", "diff", "getopts", @@ -4613,6 +4616,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "bitflags", + "pulldown-cmark 0.9.2", "rustc_arena", "rustc_ast", "rustc_ast_pretty", @@ -4773,6 +4777,7 @@ checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ + "itertools", "rustc_ast", "rustc_attr", "rustc_data_structures", @@ -4878,7 +4883,6 @@ dependencies = [ "itertools", "minifier", "once_cell", - "pulldown-cmark 0.9.2", "rayon", "regex", "rustdoc-json-types", @@ -5864,7 +5868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f" dependencies = [ "bstr 1.0.1", - "cargo_metadata 0.15.0", + "cargo_metadata 0.15.3", "color-eyre", "colored", "crossbeam-channel", diff --git a/RELEASES.md b/RELEASES.md index a63d4e8a043..00d0171de6d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,10 @@ +Version 1.67.1 (2023-02-09) +=========================== + +- [Fix interoperability with thin archives.](https://github.com/rust-lang/rust/pull/107360) +- [Fix an internal error in the compiler build process.](https://github.com/rust-lang/rust/pull/105624) +- [Downgrade `clippy::uninlined_format_args` to pedantic.](https://github.com/rust-lang/rust-clippy/pull/10265) + Version 1.67.0 (2023-01-26) ========================== diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 2440f20502a..237e063d8d1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -343,11 +343,11 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let note = match closure_kind_ty.to_opt_closure_kind() { Some(ty::ClosureKind::Fn) => { "closure implements `Fn`, so references to captured variables \ - can't escape the closure" + can't escape the closure" } Some(ty::ClosureKind::FnMut) => { "closure implements `FnMut`, so references to captured variables \ - can't escape the closure" + can't escape the closure" } Some(ty::ClosureKind::FnOnce) => { bug!("BrEnv in a `FnOnce` closure"); @@ -364,7 +364,11 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty::BoundRegionKind::BrAnon(..) => None, }, - ty::ReLateBound(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => None, + ty::ReLateBound(..) + | ty::ReVar(..) + | ty::RePlaceholder(..) + | ty::ReErased + | ty::ReError(_) => None, } } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index e0e814cfc0a..c7b22d5f2e6 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -91,11 +91,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { } None => { subst_regions.push(vid); - infcx.tcx.sess.delay_span_bug( + infcx.tcx.re_error_with_message( concrete_type.span, "opaque type with non-universal region substs", - ); - infcx.tcx.lifetimes.re_static + ) } } }; diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index c3dfeedc205..6a3748fded5 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -187,6 +187,7 @@ pub(crate) struct PlaceholderIndices { } impl PlaceholderIndices { + /// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion` pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex { let (index, _) = self.indices.insert_full(placeholder); index.into() diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 82ff862479e..2dd24fe0340 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -8,6 +8,7 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty}; +use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; @@ -217,8 +218,27 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.inverse_outlives.add(fr_b, fr_a); } + #[instrument(level = "debug", skip(self))] pub(crate) fn create(mut self) -> CreateResult<'tcx> { let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id()); + + // Insert the facts we know from the predicates. Why? Why not. + let param_env = self.param_env; + self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); + + // - outlives is reflexive, so `'r: 'r` for every region `'r` + // - `'static: 'r` for every region `'r` + // - `'r: 'fn_body` for every (other) universally quantified + // region `'r`, all of which are provided by our caller + let fr_static = self.universal_regions.fr_static; + let fr_fn_body = self.universal_regions.fr_fn_body; + for fr in self.universal_regions.universal_regions() { + debug!("build: relating free region {:?} to itself and to 'static", fr); + self.relate_universal_regions(fr, fr); + self.relate_universal_regions(fr_static, fr); + self.relate_universal_regions(fr, fr_fn_body); + } + let unnormalized_input_output_tys = self .universal_regions .unnormalized_input_tys @@ -236,78 +256,58 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // the `relations` is built. let mut normalized_inputs_and_output = Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); - let constraint_sets: Vec<_> = unnormalized_input_output_tys - .flat_map(|ty| { - debug!("build: input_or_output={:?}", ty); - // We add implied bounds from both the unnormalized and normalized ty. - // See issue #87748 - let constraints_implied1 = self.add_implied_bounds(ty); - let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self - .param_env - .and(type_op::normalize::Normalize::new(ty)) - .fully_perform(self.infcx) - .unwrap_or_else(|_| { - let reported = self - .infcx - .tcx - .sess - .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); - TypeOpOutput { - output: self.infcx.tcx.ty_error_with_guaranteed(reported), - constraints: None, - error_info: None, - } - }); - // Note: we need this in examples like - // ``` - // trait Foo { - // type Bar; - // fn foo(&self) -> &Self::Bar; - // } - // impl Foo for () { - // type Bar = (); - // fn foo(&self) -> &() {} - // } - // ``` - // Both &Self::Bar and &() are WF - let constraints_implied2 = - if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None }; - normalized_inputs_and_output.push(norm_ty); - constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2) - }) - .collect(); + let mut constraints = vec![]; + for ty in unnormalized_input_output_tys { + debug!("build: input_or_output={:?}", ty); + // We add implied bounds from both the unnormalized and normalized ty. + // See issue #87748 + let constraints_unnorm = self.add_implied_bounds(ty); + if let Some(c) = constraints_unnorm { + constraints.push(c) + } + let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self + .param_env + .and(type_op::normalize::Normalize::new(ty)) + .fully_perform(self.infcx) + .unwrap_or_else(|_| { + self.infcx + .tcx + .sess + .delay_span_bug(span, &format!("failed to normalize {:?}", ty)); + TypeOpOutput { + output: self.infcx.tcx.ty_error(), + constraints: None, + error_info: None, + } + }); + if let Some(c) = constraints_normalize { + constraints.push(c) + } - // Insert the facts we know from the predicates. Why? Why not. - let param_env = self.param_env; - self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env)); + // Note: we need this in examples like + // ``` + // trait Foo { + // type Bar; + // fn foo(&self) -> &Self::Bar; + // } + // impl Foo for () { + // type Bar = (); + // fn foo(&self) ->&() {} + // } + // ``` + // Both &Self::Bar and &() are WF + if ty != norm_ty { + let constraints_norm = self.add_implied_bounds(norm_ty); + if let Some(c) = constraints_norm { + constraints.push(c) + } + } - // Finally: - // - outlives is reflexive, so `'r: 'r` for every region `'r` - // - `'static: 'r` for every region `'r` - // - `'r: 'fn_body` for every (other) universally quantified - // region `'r`, all of which are provided by our caller - let fr_static = self.universal_regions.fr_static; - let fr_fn_body = self.universal_regions.fr_fn_body; - for fr in self.universal_regions.universal_regions() { - debug!("build: relating free region {:?} to itself and to 'static", fr); - self.relate_universal_regions(fr, fr); - self.relate_universal_regions(fr_static, fr); - self.relate_universal_regions(fr, fr_fn_body); + normalized_inputs_and_output.push(norm_ty); } - for data in &constraint_sets { - constraint_conversion::ConstraintConversion::new( - self.infcx, - &self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.param_env, - Locations::All(span), - span, - ConstraintCategory::Internal, - &mut self.constraints, - ) - .convert_all(data); + for c in constraints { + self.push_region_constraints(c, span); } CreateResult { @@ -321,6 +321,24 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } + #[instrument(skip(self, data), level = "debug")] + fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) { + debug!("constraints generated: {:#?}", data); + + constraint_conversion::ConstraintConversion::new( + self.infcx, + &self.universal_regions, + &self.region_bound_pairs, + self.implicit_region_bound, + self.param_env, + Locations::All(span), + span, + ConstraintCategory::Internal, + &mut self.constraints, + ) + .convert_all(data); + } + /// Update the type of a single local, which should represent /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come @@ -332,6 +350,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) .fully_perform(self.infcx) .unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty)); + debug!(?bounds, ?constraints); self.add_outlives_bounds(bounds); constraints } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5b52846562f..64c96281ed9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -910,6 +910,8 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> { } impl<'tcx> MirTypeckRegionConstraints<'tcx> { + /// Creates a `Region` for a given `PlaceholderRegion`, or returns the + /// region that corresponds to a previously created one. fn placeholder_region( &mut self, infcx: &InferCtxt<'tcx>, diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index b2702eafd33..8dd06187877 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -1,4 +1,4 @@ -use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; +use rustc_infer::infer::nll_relate::{TypeRelating, TypeRelatingDelegate}; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_infer::traits::PredicateObligations; use rustc_middle::mir::ConstraintCategory; @@ -140,10 +140,6 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> ); } - fn normalization() -> NormalizationStrategy { - NormalizationStrategy::Eager - } - fn forbid_inference_vars() -> bool { true } diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 5380913f5c8..56930c89b2c 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -167,6 +167,9 @@ struct UniversalRegionIndices<'tcx> { /// contains an entry for `ReStatic` -- it might be nice to just /// use a substs, and then handle `ReStatic` another way. indices: FxHashMap<ty::Region<'tcx>, RegionVid>, + + /// The vid assigned to `'static`. Used only for diagnostics. + pub fr_static: RegionVid, } #[derive(Debug, PartialEq)] @@ -609,7 +612,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let subst_mapping = iter::zip(identity_substs.regions(), fr_substs.regions().map(|r| r.to_region_vid())); - UniversalRegionIndices { indices: global_mapping.chain(subst_mapping).collect() } + UniversalRegionIndices { indices: global_mapping.chain(subst_mapping).collect(), fr_static } } fn compute_inputs_and_output( @@ -821,6 +824,11 @@ impl<'tcx> UniversalRegionIndices<'tcx> { pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid { if let ty::ReVar(..) = *r { r.to_region_vid() + } else if r.is_error() { + // We use the `'static` `RegionVid` because `ReError` doesn't actually exist in the + // `UniversalRegionIndices`. This is fine because 1) it is a fallback only used if + // errors are being emitted and 2) it leaves the happy path unaffected. + self.fr_static } else { *self .indices diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 54ac7a46cf2..7a4ec494c8e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -118,7 +118,8 @@ pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attr /// Tell LLVM what instrument function to insert. #[inline] -fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> { + let mut attrs = SmallVec::new(); if cx.sess().opts.unstable_opts.instrument_mcount { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. @@ -127,14 +128,41 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribu // See test/CodeGen/mcount.c in clang. let mcount_name = cx.sess().target.mcount.as_ref(); - Some(llvm::CreateAttrStringValue( + attrs.push(llvm::CreateAttrStringValue( cx.llcx, "instrument-function-entry-inlined", &mcount_name, - )) - } else { - None + )); + } + if let Some(options) = &cx.sess().opts.unstable_opts.instrument_xray { + // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. + // Function prologue and epilogue are instrumented with NOP sleds, + // a runtime library later replaces them with detours into tracing code. + if options.always { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-always")); + } + if options.never { + attrs.push(llvm::CreateAttrStringValue(cx.llcx, "function-instrument", "xray-never")); + } + if options.ignore_loops { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-ignore-loops")); + } + // LLVM will not choose the default for us, but rather requires specific + // threshold in absence of "xray-always". Use the same default as Clang. + let threshold = options.instruction_threshold.unwrap_or(200); + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "xray-instruction-threshold", + &threshold.to_string(), + )); + if options.skip_entry { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-entry")); + } + if options.skip_exit { + attrs.push(llvm::CreateAttrString(cx.llcx, "xray-skip-exit")); + } } + attrs } fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index d3cd085cfb6..66ec8f5f57d 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -203,7 +203,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { } } - self.src_archives.push((archive_path.to_owned(), archive_map)); + self.src_archives.push((archive_path, archive_map)); Ok(()) } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4ac7bea03eb..6fe8527ada6 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -270,10 +270,9 @@ pub fn each_linked_rlib( /// Create an 'rlib'. /// -/// An rlib in its current incarnation is essentially a renamed .a file. The rlib primarily contains -/// the object file of the crate, but it also contains all of the object files from native -/// libraries. This is done by unzipping native libraries and inserting all of the contents into -/// this archive. +/// An rlib in its current incarnation is essentially a renamed .a file (with "dummy" object files). +/// The rlib primarily contains the object file of the crate, but it also some of the object files +/// from native libraries. fn link_rlib<'a>( sess: &'a Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, @@ -347,44 +346,23 @@ fn link_rlib<'a>( // loaded from the libraries found here and then encode that into the // metadata of the rlib we're generating somehow. for lib in codegen_results.crate_info.used_libraries.iter() { - match lib.kind { - NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) } - if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.packed_bundled_libs => {} - NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) } - if flavor == RlibFlavor::Normal => - { - // Don't allow mixing +bundle with +whole_archive since an rlib may contain - // multiple native libs, some of which are +whole-archive and some of which are - // -whole-archive and it isn't clear how we can currently handle such a - // situation correctly. - // See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897 - sess.emit_err(errors::IncompatibleLinkingModifiers); - } - NativeLibKind::Static { bundle: None | Some(true), .. } => {} - NativeLibKind::Static { bundle: Some(false), .. } - | NativeLibKind::Dylib { .. } - | NativeLibKind::Framework { .. } - | NativeLibKind::RawDylib - | NativeLibKind::LinkArg - | NativeLibKind::Unspecified => continue, - } - if let Some(name) = lib.name { - let location = + let NativeLibKind::Static { bundle: None | Some(true), whole_archive } = lib.kind else { + continue; + }; + if whole_archive == Some(true) && !codegen_results.crate_info.feature_packed_bundled_libs { + sess.emit_err(errors::IncompatibleLinkingModifiers); + } + if flavor == RlibFlavor::Normal && let Some(filename) = lib.filename { + let path = find_native_static_library(filename.as_str(), true, &lib_search_paths, sess); + let src = read(path).map_err(|e| sess.emit_fatal(errors::ReadFileError {message: e }))?; + let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src); + let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); + packed_bundled_libs.push(wrapper_file); + } else if let Some(name) = lib.name { + let path = find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess); - if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal { - let filename = lib.filename.unwrap(); - let lib_path = - find_native_static_library(filename.as_str(), true, &lib_search_paths, sess); - let src = read(lib_path) - .map_err(|e| sess.emit_fatal(errors::ReadFileError { message: e }))?; - let (data, _) = create_wrapper_file(sess, b".bundled_lib".to_vec(), &src); - let wrapper_file = emit_wrapper_file(sess, &data, tmpdir, filename.as_str()); - packed_bundled_libs.push(wrapper_file); - continue; - } - ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| { - sess.emit_fatal(errors::AddNativeLibrary { library_path: location, error }); - }); + ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| { + sess.emit_fatal(errors::AddNativeLibrary { library_path: path, error })}); } } @@ -516,36 +494,14 @@ fn link_staticlib<'a>( &codegen_results.crate_info, Some(CrateType::Staticlib), &mut |cnum, path| { - let name = codegen_results.crate_info.crate_name[&cnum]; - let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; - - // Here when we include the rlib into our staticlib we need to make a - // decision whether to include the extra object files along the way. - // These extra object files come from statically included native - // libraries, but they may be cfg'd away with #[link(cfg(..))]. - // - // This unstable feature, though, only needs liblibc to work. The only - // use case there is where musl is statically included in liblibc.rlib, - // so if we don't want the included version we just need to skip it. As - // a result the logic here is that if *any* linked library is cfg'd away - // we just skip all object files. - // - // Clearly this is not sufficient for a general purpose feature, and - // we'd want to read from the library's metadata to determine which - // object files come from where and selectively skip them. - let skip_object_files = native_libs.iter().any(|lib| { - matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) - && !relevant_lib(sess, lib) - }); - let lto = are_upstream_rust_objects_already_included(sess) && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); - // Ignoring obj file starting with the crate name - // as simple comparison is not enough - there - // might be also an extra name suffix - let obj_start = name.as_str().to_owned(); + let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter(); + let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, &lib)); + let relevant_libs: FxHashSet<_> = relevant.filter_map(|lib| lib.filename).collect(); + let bundled_libs: FxHashSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); ab.add_archive( path, Box::new(move |fname: &str| { @@ -559,20 +515,25 @@ fn link_staticlib<'a>( return true; } - // Otherwise if this is *not* a rust object and we're skipping - // objects then skip this file - if skip_object_files - && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) - { + // Skip objects for bundled libs. + if bundled_libs.contains(&Symbol::intern(fname)) { return true; } - // ok, don't skip this false }), ) .unwrap(); + archive_builder_builder + .extract_bundled_libs(path, tempdir.as_ref(), &relevant_libs) + .unwrap_or_else(|e| sess.emit_fatal(e)); + for filename in relevant_libs { + let joined = tempdir.as_ref().join(filename.as_str()); + let path = joined.as_path(); + ab.add_archive(path, Box::new(|_| false)).unwrap(); + } + all_native_libs .extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); }, @@ -2590,18 +2551,8 @@ fn add_static_crate<'a>( cmd.link_rlib(&fix_windows_verbatim_for_gcc(path)); }; - // See the comment above in `link_staticlib` and `link_rlib` for why if - // there's a static library that's not relevant we skip all object - // files. - let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; - let skip_native = native_libs.iter().any(|lib| { - matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) - && !relevant_lib(sess, lib) - }); - - if (!are_upstream_rust_objects_already_included(sess) - || ignored_for_lto(sess, &codegen_results.crate_info, cnum)) - && !skip_native + if !are_upstream_rust_objects_already_included(sess) + || ignored_for_lto(sess, &codegen_results.crate_info, cnum) { link_upstream(cratepath); return; @@ -2632,17 +2583,13 @@ fn add_static_crate<'a>( let is_rust_object = canonical.starts_with(&canonical_name) && looks_like_rust_object_file(&f); - // If we've been requested to skip all native object files - // (those not generated by the rust compiler) then we can skip - // this file. See above for why we may want to do this. - let skip_because_cfg_say_so = skip_native && !is_rust_object; - // If we're performing LTO and this is a rust-generated object // file, then we don't need the object file as it's part of the // LTO module. Note that `#![no_builtins]` is excluded from LTO, // though, so we let that object file slide. - let skip_because_lto = - upstream_rust_objects_already_included && is_rust_object && is_builtins; + if upstream_rust_objects_already_included && is_rust_object && is_builtins { + return true; + } // We skip native libraries because: // 1. This native libraries won't be used from the generated rlib, @@ -2653,10 +2600,6 @@ fn add_static_crate<'a>( return true; } - if skip_because_cfg_say_so || skip_because_lto { - return true; - } - false }), ) { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 02b502d948c..de2727c8a5d 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -858,6 +858,7 @@ impl CrateInfo { dependency_formats: tcx.dependency_formats(()).clone(), windows_subsystem, natvis_debugger_visualizers: Default::default(), + feature_packed_bundled_libs: tcx.features().packed_bundled_libs, }; let crates = tcx.crates(()); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 0e6596d4ba7..d5530c47680 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -159,6 +159,7 @@ pub struct CrateInfo { pub dependency_formats: Lrc<Dependencies>, pub windows_subsystem: Option<String>, pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>, + pub feature_packed_bundled_libs: bool, // unstable feature flag. } #[derive(Encodable, Decodable)] diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index e9bc40c3310..708f3bc0c78 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -385,10 +385,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { calculate_debuginfo_offset(bx, local, &var, base); // Create a variable which will be a pointer to the actual value - let ptr_ty = bx.tcx().mk_ty(ty::RawPtr(ty::TypeAndMut { - mutbl: mir::Mutability::Mut, - ty: place.layout.ty, - })); + let ptr_ty = bx + .tcx() + .mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty }); let ptr_layout = bx.layout_of(ptr_ty); let alloca = PlaceRef::alloca(bx, ptr_layout); bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill")); diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 18e01567ca3..b4a49e1df61 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -54,7 +54,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( trace!( "eval_body_using_ecx: pushing stack frame for global: {}{}", - with_no_trimmed_paths!(ty::tls::with(|tcx| tcx.def_path_str(cid.instance.def_id()))), + with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())), cid.promoted.map_or_else(String::new, |p| format!("::promoted[{:?}]", p)) ); diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index c52886b77e6..fc546e4de0e 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -193,7 +193,7 @@ fn get_info_on_unsized_field<'tcx>( // Have to adjust type for ty::Str let unsized_inner_ty = match unsized_inner_ty.kind() { - ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)), + ty::Str => tcx.types.u8, _ => unsized_inner_ty, }; @@ -216,7 +216,7 @@ fn create_pointee_place<'tcx>( let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx); let unsized_inner_ty = match unsized_inner_ty.kind() { - ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)), + ty::Str => tcx.types.u8, _ => unsized_inner_ty, }; let unsized_inner_ty_size = diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 8ca3fdf400e..bb4b7ad50b8 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -217,10 +217,10 @@ impl Qualif for CustomEq { fn in_adt_inherently<'tcx>( cx: &ConstCx<'_, 'tcx>, - adt: AdtDef<'tcx>, + def: AdtDef<'tcx>, substs: SubstsRef<'tcx>, ) -> bool { - let ty = cx.tcx.mk_ty(ty::Adt(adt, substs)); + let ty = cx.tcx.mk_adt(def, substs); !ty.is_structural_eq_shallow(cx.tcx) } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 76b316cdf0c..56c60d59d28 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::{ RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; -use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; @@ -231,6 +231,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return true; } + // We sometimes have to use `defining_opaque_types` for subtyping + // to succeed here and figuring out how exactly that should work + // is annoying. It is harmless enough to just not validate anything + // in that case. We still check this after analysis as all opque + // types have been revealed at this point. + if (src, dest).has_opaque_types() { + return true; + } + crate::util::is_subtype(self.tcx, self.param_env, src, dest) } } diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index ae4836645fa..e0d77cdaebb 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -486,6 +486,14 @@ impl<HCX> ToStableHashKey<HCX> for String { } } +impl<HCX, T1: ToStableHashKey<HCX>, T2: ToStableHashKey<HCX>> ToStableHashKey<HCX> for (T1, T2) { + type KeyType = (T1::KeyType, T2::KeyType); + #[inline] + fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType { + (self.0.to_stable_hash_key(hcx), self.1.to_stable_hash_key(hcx)) + } +} + impl<CTX> HashStable<CTX> for bool { #[inline] fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index bdf2978cee2..1067fcebcf3 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -23,7 +23,7 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::SeqCst; use rustc_errors::registry::{InvalidErrorCode, Registry}; -use rustc_errors::{ErrorGuaranteed, PResult}; +use rustc_errors::{ErrorGuaranteed, PResult, TerminalUrl}; use rustc_feature::find_gated_cfg; use rustc_hir::def_id::LOCAL_CRATE; use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; @@ -624,7 +624,10 @@ fn print_crate_info( println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } FileNames | CrateName => { - let attrs = attrs.as_ref().unwrap(); + let Some(attrs) = attrs.as_ref() else { + // no crate attributes, print out an error and exit + return Compilation::Continue; + }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); if *req == PrintRequest::CrateName { @@ -1188,6 +1191,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { None, false, false, + TerminalUrl::No, )); let handler = rustc_errors::Handler::with_emitter(true, None, emitter); diff --git a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl index fe77cf23e8f..a3b6b5e8138 100644 --- a/compiler/rustc_error_messages/locales/en-US/borrowck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/borrowck.ftl @@ -18,7 +18,7 @@ borrowck_generic_does_not_live_long_enough = `{$kind}` does not live long enough borrowck_move_borrowed = - cannot move out of `{$desc}` beacause it is borrowed + cannot move out of `{$desc}` because it is borrowed borrowck_var_does_not_need_mut = variable does not need to be mutable @@ -87,10 +87,10 @@ borrowck_use_due_to_use_closure = use occurs due to use in closure borrowck_assign_due_to_use_closure = - assign occurs due to use in closure + assignment occurs due to use in closure borrowck_assign_part_due_to_use_closure = - assign to part occurs due to use in closure + assignment to part occurs due to use in closure borrowck_capture_immute = capture is immutable because of use here diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl index 4924105128d..8fe5f8d50ab 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl @@ -22,7 +22,7 @@ codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files w codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} -codegen_ssa_incompatible_linking_modifiers = the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs +codegen_ssa_incompatible_linking_modifiers = link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs codegen_ssa_add_native_library = failed to add native library {$library_path}: {$error} diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index 5984c201af0..fe553edab42 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -25,6 +25,8 @@ session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C pr session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` +session_instrumentation_not_supported = {$us} instrumentation is not supported for this target + session_sanitizer_not_supported = {$us} sanitizer is not supported for this target session_sanitizers_not_supported = {$us} sanitizers are not supported for this target diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9768526a2f4..4f2cc8b0351 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -18,7 +18,7 @@ use crate::translation::{to_fluent_args, Translate}; use crate::{ diagnostic::DiagnosticLocation, CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, FluentBundle, Handler, LazyFallbackBundle, Level, MultiSpan, SubDiagnostic, - SubstitutionHighlight, SuggestionStyle, + SubstitutionHighlight, SuggestionStyle, TerminalUrl, }; use rustc_lint_defs::pluralize; @@ -66,6 +66,7 @@ impl HumanReadableErrorType { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); @@ -80,6 +81,7 @@ impl HumanReadableErrorType { diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, ) } } @@ -652,6 +654,7 @@ pub struct EmitterWriter { macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, } #[derive(Debug)] @@ -672,6 +675,7 @@ impl EmitterWriter { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, ) -> EmitterWriter { let dst = Destination::from_stderr(color_config); EmitterWriter { @@ -685,6 +689,7 @@ impl EmitterWriter { diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, } } @@ -699,6 +704,7 @@ impl EmitterWriter { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, ) -> EmitterWriter { EmitterWriter { dst: Raw(dst, colored), @@ -711,6 +717,7 @@ impl EmitterWriter { diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, } } @@ -1378,7 +1385,13 @@ impl EmitterWriter { // only render error codes, not lint codes if let Some(DiagnosticId::Error(ref code)) = *code { buffer.append(0, "[", Style::Level(*level)); - buffer.append(0, code, Style::Level(*level)); + let code = if let TerminalUrl::Yes = self.terminal_url { + let path = "https://doc.rust-lang.org/error_codes"; + format!("\x1b]8;;{path}/{code}.html\x07{code}\x1b]8;;\x07") + } else { + code.clone() + }; + buffer.append(0, &code, Style::Level(*level)); buffer.append(0, "]", Style::Level(*level)); label_width += 2 + code.len(); } @@ -1796,17 +1809,17 @@ impl EmitterWriter { // telling users to make a change but not clarifying *where*. let loc = sm.lookup_char_pos(parts[0].span.lo()); if loc.file.name != sm.span_to_filename(span) && loc.file.name.is_real() { - buffer.puts(row_num - 1, 0, "--> ", Style::LineNumber); - buffer.append( - row_num - 1, - &format!( - "{}:{}:{}", - sm.filename_for_diagnostics(&loc.file.name), - sm.doctest_offset_line(&loc.file.name, loc.line), - loc.col.0 + 1, - ), - Style::LineAndColumn, - ); + let arrow = "--> "; + buffer.puts(row_num - 1, 0, arrow, Style::LineNumber); + let filename = sm.filename_for_diagnostics(&loc.file.name); + let offset = sm.doctest_offset_line(&loc.file.name, loc.line); + let message = format!("{}:{}:{}", filename, offset, loc.col.0 + 1); + if row_num == 2 { + let col = usize::max(max_line_num_len + 1, arrow.len()); + buffer.puts(1, col, &message, Style::LineAndColumn); + } else { + buffer.append(row_num - 1, &message, Style::LineAndColumn); + } for _ in 0..max_line_num_len { buffer.prepend(row_num - 1, " ", Style::NoStyle); } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index dc38b8725ad..e475fc725c3 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -17,6 +17,7 @@ use crate::translation::{to_fluent_args, Translate}; use crate::DiagnosticId; use crate::{ CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, SubDiagnostic, + TerminalUrl, }; use rustc_lint_defs::Applicability; @@ -47,6 +48,7 @@ pub struct JsonEmitter { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, } impl JsonEmitter { @@ -60,6 +62,7 @@ impl JsonEmitter { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, ) -> JsonEmitter { JsonEmitter { dst: Box::new(io::BufWriter::new(io::stderr())), @@ -73,6 +76,7 @@ impl JsonEmitter { diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, } } @@ -84,6 +88,7 @@ impl JsonEmitter { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, ) -> JsonEmitter { let file_path_mapping = FilePathMapping::empty(); JsonEmitter::stderr( @@ -96,6 +101,7 @@ impl JsonEmitter { diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, ) } @@ -110,6 +116,7 @@ impl JsonEmitter { diagnostic_width: Option<usize>, macro_backtrace: bool, track_diagnostics: bool, + terminal_url: TerminalUrl, ) -> JsonEmitter { JsonEmitter { dst, @@ -123,6 +130,7 @@ impl JsonEmitter { diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, } } @@ -360,6 +368,7 @@ impl Diagnostic { je.diagnostic_width, je.macro_backtrace, je.track_diagnostics, + je.terminal_url, ) .ui_testing(je.ui_testing) .emit_diagnostic(diag); diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index f131468971b..f161532d3b7 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -4,7 +4,7 @@ use crate::json::JsonEmitter; use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{ColorConfig, HumanReadableErrorType}; -use crate::Handler; +use crate::{Handler, TerminalUrl}; use rustc_span::{BytePos, Span}; use std::str; @@ -60,6 +60,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { None, false, false, + TerminalUrl::No, ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ec04e865d53..83b733d4c06 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -573,6 +573,7 @@ impl Handler { None, flags.macro_backtrace, flags.track_diagnostics, + TerminalUrl::No, )); Self::with_emitter_and_flags(emitter, flags) } @@ -1838,6 +1839,13 @@ pub fn add_elided_lifetime_in_path_suggestion( ); } +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum TerminalUrl { + No, + Yes, + Auto, +} + /// Useful type to use with `Result<>` indicate that an error has already /// been reported to the user, so no need to continue checking. #[derive(Clone, Copy, Debug, Encodable, Decodable, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 8f3bea29ffd..f80141403bf 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -8,7 +8,7 @@ use rustc_span::{BytePos, Span}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Handler, MultiSpan, PResult}; +use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl}; use std::io; use std::io::prelude::*; @@ -152,6 +152,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: & None, false, false, + TerminalUrl::No, ); let handler = Handler::with_emitter(true, None, Box::new(emitter)); #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 323f5a368fc..21d211eefbe 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -164,6 +164,8 @@ declare_features! ( (active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), + /// Allows using `+bundled,+whole-archive` native libs. + (active, packed_bundled_libs, "1.67.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. (active, prelude_import, "1.2.0", None, None), /// Used to identify crates that contain the profiler runtime. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index cca5ead0f83..f1801a0f844 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -2,6 +2,8 @@ use crate::hir; use rustc_ast as ast; use rustc_ast::NodeId; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_macros::HashStable_Generic; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::MacroKind; @@ -472,7 +474,8 @@ impl PartialRes { /// Different kinds of symbols can coexist even if they share the same textual name. /// Therefore, they each have a separate universe (known as a "namespace"). -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] pub enum Namespace { /// The type namespace includes `struct`s, `enum`s, `union`s, `trait`s, and `mod`s /// (and, by extension, crates). @@ -499,6 +502,15 @@ impl Namespace { } } +impl<CTX: crate::HashStableContext> ToStableHashKey<CTX> for Namespace { + type KeyType = Namespace; + + #[inline] + fn to_stable_hash_key(&self, _: &CTX) -> Namespace { + *self + } +} + /// Just a helper ‒ separate structure for each namespace. #[derive(Copy, Clone, Default, Debug)] pub struct PerNS<T> { @@ -760,3 +772,5 @@ pub enum LifetimeRes { /// HACK: This is used to recover the NodeId of an elided lifetime. ElidedAnchor { start: NodeId, end: NodeId }, } + +pub type DocLinkResMap = FxHashMap<(Symbol, Namespace), Option<Res<NodeId>>>; diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3d5f189e233..8c753a99a09 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -263,11 +263,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // elision. `resolve_lifetime` should have // reported an error in this case -- but if // not, let's error out. - tcx.sess.delay_span_bug(lifetime.ident.span, "unelided lifetime in signature"); - - // Supply some dummy value. We don't have an - // `re_error`, annoyingly, so use `'static`. - tcx.lifetimes.re_static + tcx.re_error_with_message(lifetime.ident.span, "unelided lifetime in signature") }) } } @@ -481,11 +477,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!(?param, "unelided lifetime in signature"); // This indicates an illegal lifetime in a non-assoc-trait position - tcx.sess.delay_span_bug(self.span, "unelided lifetime in signature"); - - // Supply some dummy value. We don't have an - // `re_error`, annoyingly, so use `'static`. - tcx.lifetimes.re_static + tcx.re_error_with_message(self.span, "unelided lifetime in signature") }) .into(), GenericParamDefKind::Type { has_default, .. } => { @@ -1258,7 +1250,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` // parameter to have a skipped binder. - let param_ty = tcx.mk_ty(ty::Alias(ty::Projection, projection_ty.skip_binder())); + let param_ty = tcx.mk_alias(ty::Projection, projection_ty.skip_binder()); self.add_bounds(param_ty, ast_bounds.iter(), bounds, candidate.bound_vars()); } } @@ -1328,6 +1320,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Clause::RegionOutlives(_) => bug!(), }, ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(_, _, _) | ty::PredicateKind::Subtype(_) @@ -1622,14 +1615,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "the lifetime bound for this object type cannot be deduced \ from context; please supply an explicit bound" ); - if borrowed { + let e = if borrowed { // We will have already emitted an error E0106 complaining about a // missing named lifetime in `&dyn Trait`, so we elide this one. - err.delay_as_bug(); + err.delay_as_bug() } else { - err.emit(); - } - tcx.lifetimes.re_static + err.emit() + }; + tcx.re_error(e) }) } }) @@ -2937,7 +2930,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length)) + tcx.mk_array_with_const_len(self.ast_ty_to_ty(ty), length) } hir::TyKind::Typeof(e) => { let ty_erased = tcx.type_of(e.def_id); 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 236e36f28ca..c86af6a379b 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -786,13 +786,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( } let Some(ty::ReEarlyBound(e)) = map.get(®ion.into()).map(|r| r.expect_region().kind()) else { - tcx - .sess - .delay_span_bug( - return_span, - "expected ReFree to map to ReEarlyBound" - ); - return tcx.lifetimes.re_static; + return tcx.re_error_with_message(return_span, "expected ReFree to map to ReEarlyBound") }; tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: e.def_id, @@ -1933,10 +1927,10 @@ pub(super) fn check_type_bounds<'tcx>( let kind = ty::BoundTyKind::Param(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Ty(kind); bound_vars.push(bound_var); - tcx.mk_ty(ty::Bound( + tcx.mk_bound( ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, - )) + ) .into() } GenericParamDefKind::Lifetime => { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5b9b57da382..5f95622883b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -603,8 +603,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>( // our example, the type was `Self`, which will also be // `Self` in the GAT. let ty_param = gat_generics.param_at(*ty_idx, tcx); - let ty_param = tcx - .mk_ty(ty::Param(ty::ParamTy { index: ty_param.index, name: ty_param.name })); + let ty_param = tcx.mk_ty_param(ty_param.index, ty_param.name); // Same for the region. In our example, 'a corresponds // to the 'me parameter. let region_param = gat_generics.param_at(*region_a_idx, tcx); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index a5dcfab9be8..02f77f9d6af 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -517,6 +517,7 @@ fn trait_predicate_kind<'tcx>( ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) | ty::PredicateKind::Clause(ty::Clause::Projection(_)) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 663f1c49db7..ecd6849426d 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -55,6 +55,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 9459c5f54ab..c5c5f63a108 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -170,6 +170,8 @@ fn is_free_region(region: Region<'_>) -> bool { // ignore it. We can't put it on the struct header anyway. ty::ReLateBound(..) => false, + ty::ReError(_) => false, + // These regions don't appear in types from type declarations: ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => { bug!("unexpected region in outlives inference: {:?}", region); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 165782f209a..b0cf0387f87 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -409,6 +409,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { // way early-bound regions do, so we skip them here. } + ty::ReError(_) => {} + ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => { // We don't expect to see anything but 'static or bound // regions when visiting member types or method types. diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 88fb2653586..e19ef2ff3bf 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut)); } // Now typecheck the blocks. diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 1c70c1b71e7..05f6d8e6072 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>( for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // Check the pattern. let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); + fcx.check_pat_top(¶m.pat, param_ty, ty_span, None); // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings @@ -264,9 +264,7 @@ fn check_lang_start_fn<'tcx>( // for example `start`'s generic should be a type parameter let generics = tcx.generics_of(def_id); let fn_generic = generics.param_at(0, tcx); - let generic_tykind = - ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name }); - let generic_ty = tcx.mk_ty(generic_tykind); + let generic_ty = tcx.mk_ty_param(fn_generic.index, fn_generic.name); let expected_fn_sig = tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust); let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig)); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index c4905a934cb..ae00042eae7 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -273,12 +273,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ct_op: |c| c, ty_op: |t| match *t.kind() { ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)), - ty::Infer(ty::IntVar(_)) => { - self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 })) - } - ty::Infer(ty::FloatVar(_)) => { - self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 })) - } + ty::Infer(ty::IntVar(_)) => self.tcx.mk_int_var(ty::IntVid { index: 0 }), + ty::Infer(ty::FloatVar(_)) => self.tcx.mk_float_var(ty::FloatVid { index: 0 }), _ => t, }, }; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index bb235a48361..12850c733e8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1429,7 +1429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_repeat_element_needs_copy_bound(element, count, element_ty); - tcx.mk_ty(ty::Array(t, count)) + tcx.mk_array_with_const_len(t, count) } fn check_repeat_element_needs_copy_bound( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e84b3de124c..52c2dabee29 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -669,6 +669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) // N.B., this predicate is created by breaking down a diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2a1265600de..9c7a84ce198 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does the expected pattern type originate from an expression and what is the span? let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) } // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. }; // Type check the pattern. Override if necessary to avoid knock-on errors. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4ce401b52bd..16b0d48002e 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -837,6 +837,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } }); @@ -951,24 +952,38 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let trait_ref = self.tcx.mk_trait_ref(trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { - // For trait aliases, assume all supertraits are relevant. - let bounds = iter::once(ty::Binder::dummy(trait_ref)); - self.elaborate_bounds(bounds, |this, new_trait_ref, item| { - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); + // For trait aliases, recursively assume all explicitly named traits are relevant + for expansion in traits::expand_trait_aliases( + self.tcx, + iter::once((ty::Binder::dummy(trait_ref), self.span)), + ) { + let bound_trait_ref = expansion.trait_ref(); + for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { + if !self.has_applicable_self(&item) { + self.record_static_candidate(CandidateSource::Trait( + bound_trait_ref.def_id(), + )); + } else { + let new_trait_ref = self.erase_late_bound_regions(bound_trait_ref); - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - import_ids: import_ids.clone(), - kind: TraitCandidate(new_trait_ref), - }, - false, - ); - }); + let (xform_self_ty, xform_ret_ty) = self.xform_self_ty( + &item, + new_trait_ref.self_ty(), + new_trait_ref.substs, + ); + self.push_candidate( + Candidate { + xform_self_ty, + xform_ret_ty, + item, + import_ids: import_ids.clone(), + kind: TraitCandidate(new_trait_ref), + }, + false, + ); + } + } + } } else { debug_assert!(self.tcx.is_trait(trait_def_id)); if self.tcx.trait_is_auto(trait_def_id) { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 46799245222..3201035bdd8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -46,7 +46,7 @@ struct TopInfo<'tcx> { /// Was the origin of the `span` from a scrutinee expression? /// /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, /// The span giving rise to the `expected` type, if one could be provided. /// /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: @@ -74,7 +74,8 @@ struct TopInfo<'tcx> { impl<'tcx> FnCtxt<'_, 'tcx> { fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { - let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; + let code = + Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() }; self.cause(cause_span, code) } @@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> { actual: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) + let mut diag = + self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?; + if let Some(expr) = ti.origin_expr { + self.suggest_fn_call(&mut diag, expr, expected, |output| { + self.can_eq(self.param_env, output, actual).is_ok() + }); + } + Some(diag) } fn demand_eqtype_pat( @@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option<Span>, - origin_expr: bool, + origin_expr: Option<&'tcx hir::Expr<'tcx>>, ) { let info = TopInfo { expected, origin_expr, span }; self.check_pat(pat, expected, INITIAL_BM, info); @@ -1288,7 +1296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); let element_tys = tcx.mk_type_list(element_tys_iter); - let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); + let pat_ty = tcx.intern_tup(element_tys); if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence @@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("the semantics of slice patterns changed recently; see issue #62254"); } else if self.autoderef(span, expected_ty) .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) - && let (Some(span), true) = (ti.span, ti.origin_expr) + && let Some(span) = ti.span + && let Some(_) = ti.origin_expr && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let ty = self.resolve_vars_if_possible(ti.expected); diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7aaa5ce2f42..5d861a78af8 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -31,6 +31,8 @@ impl<'a> DescriptionCtx<'a> { ty::RePlaceholder(_) => return None, + ty::ReError(_) => return None, + // FIXME(#13998) RePlaceholder should probably print like // ReFree rather than dumping Debug output on the user. // diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 87c6dfad5fa..c11dcc8587b 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -203,12 +203,10 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { // rust-lang/rust#57464: `impl Trait` can leak local // scopes (in manner violating typeck). Therefore, use // `delay_span_bug` to allow type error over an ICE. - ty::tls::with(|tcx| { - tcx.sess.delay_span_bug( - rustc_span::DUMMY_SP, - &format!("unexpected region in query response: `{:?}`", r), - ); - }); + canonicalizer.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("unexpected region in query response: `{:?}`", r), + ); r } } @@ -371,6 +369,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { ty::ReStatic | ty::ReEarlyBound(..) + | ty::ReError(_) | ty::ReFree(_) | ty::RePlaceholder(..) | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), @@ -753,7 +752,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.fold_ty(bound_to) } else { let var = self.canonical_var(info, ty_var.into()); - self.tcx().mk_ty(ty::Bound(self.binder_index, var.into())) + self.tcx().mk_bound(self.binder_index, var.into()) } } diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index f7e3e4a1cc0..d5cb3fb2498 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -124,7 +124,7 @@ impl<'tcx> InferCtxt<'tcx> { CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, name }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = ty::PlaceholderType { universe: universe_mapped, name }; - self.tcx.mk_ty(ty::Placeholder(placeholder_mapped)).into() + self.tcx.mk_placeholder(placeholder_mapped).into() } CanonicalVarKind::Region(ui) => self diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 7cc9e49b1b6..0c97217bd6a 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -12,7 +12,7 @@ use crate::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues, QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, }; -use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; +use crate::infer::nll_relate::{TypeRelating, TypeRelatingDelegate}; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin}; use crate::traits::query::{Fallible, NoSolution}; @@ -717,10 +717,6 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { }); } - fn normalization() -> NormalizationStrategy { - NormalizationStrategy::Eager - } - fn forbid_inference_vars() -> bool { true } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index a567b6acdbe..4da2a674144 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -38,8 +38,8 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{ - self, FallibleTypeFolder, InferConst, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, - TypeVisitable, + self, AliasKind, FallibleTypeFolder, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable, + TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{IntType, UintType}; use rustc_span::{Span, DUMMY_SP}; @@ -74,7 +74,7 @@ impl<'tcx> InferCtxt<'tcx> { b: Ty<'tcx>, ) -> RelateResult<'tcx, Ty<'tcx>> where - R: TypeRelation<'tcx>, + R: ObligationEmittingRelation<'tcx>, { let a_is_expected = relation.a_is_expected(); @@ -122,6 +122,15 @@ impl<'tcx> InferCtxt<'tcx> { Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) } + (ty::Alias(AliasKind::Projection, _), _) if self.tcx.trait_solver_next() => { + relation.register_type_equate_obligation(a.into(), b.into()); + Ok(b) + } + (_, ty::Alias(AliasKind::Projection, _)) if self.tcx.trait_solver_next() => { + relation.register_type_equate_obligation(b.into(), a.into()); + Ok(a) + } + _ => ty::relate::super_relate_tys(relation, a, b), } } @@ -133,7 +142,7 @@ impl<'tcx> InferCtxt<'tcx> { b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> where - R: ConstEquateRelation<'tcx>, + R: ObligationEmittingRelation<'tcx>, { debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); if a == b { @@ -169,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#59490): Need to remove the leak check to accommodate // escaping bound variables here. if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { - relation.const_equate_obligation(a, b); + relation.register_const_equate_obligation(a, b); } return Ok(b); } @@ -177,7 +186,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#59490): Need to remove the leak check to accommodate // escaping bound variables here. if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { - relation.const_equate_obligation(a, b); + relation.register_const_equate_obligation(a, b); } return Ok(a); } @@ -435,32 +444,21 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { Ok(Generalization { ty, needs_wf }) } - pub fn add_const_equate_obligation( + pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.obligations.extend(obligations.into_iter()); + } + + pub fn register_predicates( &mut self, - a_is_expected: bool, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, + obligations: impl IntoIterator<Item = impl ToPredicate<'tcx>>, ) { - let predicate = if a_is_expected { - ty::PredicateKind::ConstEquate(a, b) - } else { - ty::PredicateKind::ConstEquate(b, a) - }; - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::Binder::dummy(predicate), - )); + self.obligations.extend(obligations.into_iter().map(|to_pred| { + Obligation::new(self.infcx.tcx, self.trace.cause.clone(), self.param_env, to_pred) + })) } pub fn mark_ambiguous(&mut self) { - self.obligations.push(Obligation::new( - self.tcx(), - self.trace.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Ambiguous), - )); + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]); } } @@ -705,6 +703,10 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { return Ok(r); } + ty::ReError(_) => { + return Ok(r); + } + ty::RePlaceholder(..) | ty::ReVar(..) | ty::ReStatic @@ -775,11 +777,42 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } } -pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { +pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { + /// Register obligations that must hold in order for this relation to hold + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); + + /// Register predicates that must hold in order for this relation to hold. Uses + /// a default obligation cause, [`ObligationEmittingRelation::register_obligations`] should + /// be used if control over the obligaton causes is required. + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item = impl ToPredicate<'tcx>>, + ); + /// Register an obligation that both constants must be equal to each other. /// /// If they aren't equal then the relation doesn't hold. - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); + fn register_const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + + self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() { + ty::PredicateKind::AliasEq(a.into(), b.into()) + } else { + ty::PredicateKind::ConstEquate(a, b) + })]); + } + + /// Register an obligation that both types must be equal to each other. + /// + /// If they aren't equal then the relation doesn't hold. + fn register_type_equate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + + self.register_predicates([ty::Binder::dummy(ty::PredicateKind::AliasEq( + a.into(), + b.into(), + ))]); + } } fn int_unification_error<'tcx>( @@ -861,7 +894,7 @@ impl<'tcx> FallibleTypeFolder<'tcx> for ConstInferUnifier<'_, 'tcx> { match *r { // Never make variables for regions bound within the type itself, // nor for erased regions. - ty::ReLateBound(..) | ty::ReErased => { + ty::ReLateBound(..) | ty::ReErased | ty::ReError(_) => { return Ok(r); } diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 7db4d92a177..742c01efff6 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -1,4 +1,6 @@ -use super::combine::{CombineFields, ConstEquateRelation, RelationDir}; +use crate::traits::PredicateObligations; + +use super::combine::{CombineFields, ObligationEmittingRelation, RelationDir}; use super::Subtype; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; @@ -198,8 +200,15 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { } } -impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); +impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item = impl ty::ToPredicate<'tcx>>, + ) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 86f3174b7b2..88a0d6def5e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -134,6 +134,8 @@ pub(super) fn note_and_explain_region<'tcx>( ty::RePlaceholder(_) => return, + ty::ReError(_) => return, + // FIXME(#13998) RePlaceholder should probably print like // ReFree rather than dumping Debug output on the user. // @@ -313,6 +315,9 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ) } } + ty::ReError(_) => { + err.delay_as_bug(); + } _ => { // Ugh. This is a painful case: the hidden region is not one // that we can easily summarize or explain. This can happen @@ -2546,7 +2551,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note_expected_found(&"", sup_expected, &"", sup_found); - err.emit(); + if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug(); + } else { + err.emit(); + } return; } @@ -2562,7 +2571,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); self.note_region_origin(&mut err, &sub_origin); - err.emit(); + if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug(); + } else { + err.emit(); + } } /// Determine whether an error associated with the given span and definition 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 b8c843a8a5a..c092efbb557 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 @@ -158,8 +158,12 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte if infcx.probe_ty_var(ty_vid).is_ok() { warn!("resolved ty var in error message"); } - if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = - infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind + + let mut infcx_inner = infcx.inner.borrow_mut(); + let ty_vars = infcx_inner.type_variables(); + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind + && !var_origin.span.from_expansion() { Some(name) } else { @@ -254,7 +258,7 @@ impl<'tcx> InferCtxt<'tcx> { if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind { - if name != kw::SelfUpper { + if name != kw::SelfUpper && !var_origin.span.from_expansion() { return InferenceDiagnosticsData { name: name.to_string(), span: Some(var_origin.span), @@ -780,7 +784,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // The sources are listed in order of preference here. let tcx = self.infcx.tcx; let ctx = CostCtxt { tcx }; - let base_cost = match source.kind { + match source.kind { InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty), InferSourceKind::GenericArg { def_id, generic_args, .. } => { @@ -797,17 +801,17 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => { 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } } - }; - - let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 }; - - base_cost + suggestion_may_apply + } } /// Uses `fn source_cost` to determine whether this inference source is preferable to /// previous sources. We generally prefer earlier sources. #[instrument(level = "debug", skip(self))] fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) { + if new_source.from_expansion() { + return; + } + let cost = self.source_cost(&new_source) + self.attempt; debug!(?cost); self.attempt += 1; @@ -819,6 +823,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // `let x: _ = iter.collect();`, as this is a very common case. *def_id = Some(did); } + if cost < self.infer_source_cost { self.infer_source_cost = cost; self.infer_source = Some(new_source); diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index b18cbd404d4..bdd09a995dc 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -78,7 +78,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { sub: Region<'tcx>, sup: Region<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - match origin { + let mut err = match origin { infer::Subtype(box trace) => { let terr = TypeError::RegionsDoesNotOutlive(sup, sub); let mut err = self.report_and_explain_type_error(trace, terr); @@ -299,7 +299,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err } + }; + if sub.is_error() || sup.is_error() { + err.delay_as_bug(); } + err } pub fn suggest_copy_trait_method_bounds( diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 2355234637c..073a2b0753d 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -58,14 +58,9 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { } } - fn freshen_ty<F>( - &mut self, - opt_ty: Option<Ty<'tcx>>, - key: ty::InferTy, - freshener: F, - ) -> Ty<'tcx> + fn freshen_ty<F>(&mut self, opt_ty: Option<Ty<'tcx>>, key: ty::InferTy, mk_fresh: F) -> Ty<'tcx> where - F: FnOnce(u32) -> ty::InferTy, + F: FnOnce(u32) -> Ty<'tcx>, { if let Some(ty) = opt_ty { return ty.fold_with(self); @@ -76,7 +71,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { Entry::Vacant(entry) => { let index = self.ty_freshen_count; self.ty_freshen_count += 1; - let t = self.infcx.tcx.mk_ty_infer(freshener(index)); + let t = mk_fresh(index); entry.insert(t); t } @@ -126,6 +121,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { | ty::ReFree(_) | ty::ReVar(_) | ty::RePlaceholder(..) + | ty::ReError(_) | ty::ReErased => { // replace all free regions with 'erased self.tcx().lifetimes.re_erased @@ -203,7 +199,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { match v { ty::TyVar(v) => { let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); - Some(self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)) + Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| self.infcx.tcx.mk_fresh_ty(n))) } ty::IntVar(v) => Some( @@ -215,7 +211,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { .probe_value(v) .map(|v| v.to_type(self.infcx.tcx)), ty::IntVar(v), - ty::FreshIntTy, + |n| self.infcx.tcx.mk_fresh_int_ty(n), ), ), @@ -228,7 +224,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { .probe_value(v) .map(|v| v.to_type(self.infcx.tcx)), ty::FloatVar(v), - ty::FreshFloatTy, + |n| self.infcx.tcx.mk_fresh_float_ty(n), ), ), diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs index b92b162a978..74abca7bbea 100644 --- a/compiler/rustc_infer/src/infer/glb.rs +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -1,12 +1,11 @@ //! Greatest lower bound. See [`lattice`]. -use super::combine::CombineFields; +use super::combine::{CombineFields, ObligationEmittingRelation}; use super::lattice::{self, LatticeDir}; use super::InferCtxt; use super::Subtype; -use crate::infer::combine::ConstEquateRelation; -use crate::traits::{ObligationCause, PredicateObligation}; +use crate::traits::{ObligationCause, PredicateObligations}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -136,10 +135,6 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, &self.fields.trace.cause } - fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) { - self.fields.obligations.extend(obligations) - } - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let mut sub = self.fields.sub(self.a_is_expected); sub.relate(v, a)?; @@ -152,8 +147,15 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, } } -impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); +impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item = impl ty::ToPredicate<'tcx>>, + ) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index 412e52d8fd7..39940f4592d 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -88,10 +88,10 @@ impl<'tcx> InferCtxt<'tcx> { })) }, types: &mut |bound_ty: ty::BoundTy| { - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + self.tcx.mk_placeholder(ty::PlaceholderType { universe: next_universe, name: bound_ty.kind, - })) + }) }, consts: &mut |bound_var: ty::BoundVar, ty| { self.tcx diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 4dbb4b4d7b4..f377ac1d19e 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -17,11 +17,12 @@ //! //! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) +use super::combine::ObligationEmittingRelation; use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use super::InferCtxt; -use crate::traits::{ObligationCause, PredicateObligation}; -use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use crate::traits::ObligationCause; +use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::TyVar; use rustc_middle::ty::{self, Ty}; @@ -30,13 +31,11 @@ use rustc_middle::ty::{self, Ty}; /// /// GLB moves "down" the lattice (to smaller values); LUB moves /// "up" the lattice (to bigger values). -pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> { +pub trait LatticeDir<'f, 'tcx>: ObligationEmittingRelation<'tcx> { fn infcx(&self) -> &'f InferCtxt<'tcx>; fn cause(&self) -> &ObligationCause<'tcx>; - fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>); - fn define_opaque_types(&self) -> bool; // Relates the type `v` to `a` and `b` such that `v` represents @@ -113,7 +112,7 @@ where | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if this.define_opaque_types() && def_id.is_local() => { - this.add_obligations( + this.register_obligations( infcx .handle_opaque_type(a, b, this.a_is_expected(), this.cause(), this.param_env())? .obligations, 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 ce8aec8044b..4a2210bdb68 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -17,7 +17,7 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::PlaceholderRegion; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{ReEarlyBound, ReErased, ReFree, ReStatic}; +use rustc_middle::ty::{ReEarlyBound, ReErased, ReError, ReFree, ReStatic}; use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; use rustc_middle::ty::{Region, RegionVid}; use rustc_span::Span; @@ -216,6 +216,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { Ok(self.tcx().lifetimes.re_static) } + ReError(_) => Ok(a_region), + ReEarlyBound(_) | ReFree(_) => { // All empty regions are less than early-bound, free, // and scope regions. @@ -436,7 +438,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Value(a), VarValue::Empty(_)) => { match *a { - ReLateBound(..) | ReErased => { + ReLateBound(..) | ReErased | ReError(_) => { bug!("cannot relate region: {:?}", a); } @@ -465,7 +467,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } (VarValue::Empty(a_ui), VarValue::Value(b)) => { match *b { - ReLateBound(..) | ReErased => { + ReLateBound(..) | ReErased | ReError(_) => { bug!("cannot relate region: {:?}", b); } @@ -546,6 +548,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ); } + (ReError(_), _) => a, + + (_, ReError(_)) => b, + (ReStatic, _) | (_, ReStatic) => { // nothing lives longer than `'static` self.tcx().lifetimes.re_static @@ -1040,7 +1046,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { ty::ReVar(rid) => match self.values[rid] { VarValue::Empty(_) => r, VarValue::Value(r) => r, - VarValue::ErrorValue => tcx.lifetimes.re_static, + VarValue::ErrorValue => tcx.re_error_misc(), }, _ => r, }; diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs index f6e0554fd1f..f997171b97f 100644 --- a/compiler/rustc_infer/src/infer/lub.rs +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -1,12 +1,11 @@ //! Least upper bound. See [`lattice`]. -use super::combine::CombineFields; +use super::combine::{CombineFields, ObligationEmittingRelation}; use super::lattice::{self, LatticeDir}; use super::InferCtxt; use super::Subtype; -use crate::infer::combine::ConstEquateRelation; -use crate::traits::{ObligationCause, PredicateObligation}; +use crate::traits::{ObligationCause, PredicateObligations}; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -127,12 +126,6 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { } } -impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); - } -} - impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { fn infcx(&self) -> &'infcx InferCtxt<'tcx> { self.fields.infcx @@ -142,10 +135,6 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, &self.fields.trace.cause } - fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) { - self.fields.obligations.extend(obligations) - } - fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { let mut sub = self.fields.sub(self.a_is_expected); sub.relate(a, v)?; @@ -157,3 +146,16 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, self.fields.define_opaque_types } } + +impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item = impl ty::ToPredicate<'tcx>>, + ) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations) + } +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 35918b8bae1..ae196a7133c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -4,6 +4,7 @@ pub use self::LateBoundRegionConversionTime::*; pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; +pub use combine::ObligationEmittingRelation; use self::opaque_types::OpaqueTypeStorage; pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; @@ -2070,14 +2071,14 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>( fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if let ty::Infer(_) = t.kind() { - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + self.tcx.mk_placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, name: ty::BoundTyKind::Anon({ let idx = self.idx; self.idx += 1; idx }), - })) + }) } else { t.super_fold_with(self) } diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index a2cfe8d8881..1dd5062acaf 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -21,11 +21,10 @@ //! thing we relate in chalk are basically domain goals and their //! constituents) -use crate::infer::combine::ConstEquateRelation; use crate::infer::InferCtxt; use crate::infer::{ConstVarValue, ConstVariableValue}; use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::traits::{Obligation, PredicateObligation}; +use crate::traits::{Obligation, PredicateObligations}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::TypeError; @@ -36,11 +35,7 @@ use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; -#[derive(PartialEq)] -pub enum NormalizationStrategy { - Lazy, - Eager, -} +use super::combine::ObligationEmittingRelation; pub struct TypeRelating<'me, 'tcx, D> where @@ -92,7 +87,7 @@ pub trait TypeRelatingDelegate<'tcx> { info: ty::VarianceDiagInfo<'tcx>, ); - fn register_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>); + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>); /// Creates a new universe index. Used when instantiating placeholders. fn create_next_universe(&mut self) -> ty::UniverseIndex; @@ -125,9 +120,6 @@ pub trait TypeRelatingDelegate<'tcx> { /// relation stating that `'?0: 'a`). fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; - /// Define the normalization strategy to use, eager or lazy. - fn normalization() -> NormalizationStrategy; - /// Enables some optimizations if we do not expect inference variables /// in the RHS of the relation. fn forbid_inference_vars() -> bool; @@ -265,38 +257,6 @@ where self.delegate.push_outlives(sup, sub, info); } - /// Relate a projection type and some value type lazily. This will always - /// succeed, but we push an additional `ProjectionEq` goal depending - /// on the value type: - /// - if the value type is any type `T` which is not a projection, we push - /// `ProjectionEq(projection = T)`. - /// - if the value type is another projection `other_projection`, we create - /// a new inference variable `?U` and push the two goals - /// `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`. - fn relate_projection_ty( - &mut self, - projection_ty: ty::AliasTy<'tcx>, - value_ty: Ty<'tcx>, - ) -> Ty<'tcx> { - use rustc_span::DUMMY_SP; - - match *value_ty.kind() { - ty::Alias(ty::Projection, other_projection_ty) => { - let var = self.infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: DUMMY_SP, - }); - // FIXME(lazy-normalization): This will always ICE, because the recursive - // call will end up in the _ arm below. - self.relate_projection_ty(projection_ty, var); - self.relate_projection_ty(other_projection_ty, var); - var - } - - _ => bug!("should never be invoked with eager normalization"), - } - } - /// Relate a type inference variable with a value type. This works /// by creating a "generalization" G of the value where all the /// lifetimes are replaced with fresh inference values. This @@ -335,12 +295,6 @@ where return Ok(value_ty); } - ty::Alias(ty::Projection, projection_ty) - if D::normalization() == NormalizationStrategy::Lazy => - { - return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid))); - } - _ => (), } @@ -627,18 +581,6 @@ where self.relate_opaques(a, b) } - (&ty::Alias(ty::Projection, projection_ty), _) - if D::normalization() == NormalizationStrategy::Lazy => - { - Ok(self.relate_projection_ty(projection_ty, b)) - } - - (_, &ty::Alias(ty::Projection, projection_ty)) - if D::normalization() == NormalizationStrategy::Lazy => - { - Ok(self.relate_projection_ty(projection_ty, a)) - } - _ => { debug!(?a, ?b, ?self.ambient_variance); @@ -813,17 +755,26 @@ where } } -impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> +impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D> where D: TypeRelatingDelegate<'tcx>, { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.delegate.register_obligations(vec![Obligation::new( - self.tcx(), - ObligationCause::dummy(), - self.param_env(), - ty::Binder::dummy(ty::PredicateKind::ConstEquate(a, b)), - )]); + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item = impl ty::ToPredicate<'tcx>>, + ) { + self.delegate.register_obligations( + obligations + .into_iter() + .map(|to_pred| { + Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred) + }) + .collect(), + ); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.delegate.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 4daa257672c..a8e668d81ea 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -21,6 +21,7 @@ pub fn explicit_outlives_bounds<'tcx>( .filter_map(move |kind| match kind { ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::WellFormed(..) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 94de9bc2d02..bae246418b0 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -207,6 +207,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// /// In some cases, such as when `erased_ty` represents a `ty::Param`, however, /// the result is precise. + #[instrument(level = "debug", skip(self))] fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index 4667d99ff00..f795047709e 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -21,16 +21,28 @@ impl<'tcx> InferCtxt<'tcx> { recursion_depth: usize, obligations: &mut Vec<PredicateObligation<'tcx>>, ) -> Ty<'tcx> { - let def_id = projection_ty.def_id; - let ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::NormalizeProjectionType, - span: self.tcx.def_span(def_id), - }); - let projection = - ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, term: ty_var.into() }); - let obligation = - Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection); - obligations.push(obligation); - ty_var + if self.tcx.trait_solver_next() { + // FIXME(-Ztrait-solver=next): Instead of branching here, + // completely change the normalization routine with the new solver. + // + // The new solver correctly handles projection equality so this hack + // is not necessary. if re-enabled it should emit `PredicateKind::AliasEq` + // not `PredicateKind::Clause(Clause::Projection(..))` as in the new solver + // `Projection` is used as `normalizes-to` which will fail for `<T as Trait>::Assoc eq ?0`. + return projection_ty.to_ty(self.tcx); + } else { + let def_id = projection_ty.def_id; + let ty_var = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.tcx.def_span(def_id), + }); + let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( + ty::ProjectionPredicate { projection_ty, term: ty_var.into() }, + ))); + let obligation = + Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection); + obligations.push(obligation); + ty_var + } } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0428481b7ff..cb24375c7a3 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -696,9 +696,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex { match *region { - ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => { - ty::UniverseIndex::ROOT - } + ty::ReStatic + | ty::ReErased + | ty::ReFree(..) + | ty::ReEarlyBound(..) + | ty::ReError(_) => ty::UniverseIndex::ROOT, ty::RePlaceholder(placeholder) => placeholder.universe, ty::ReVar(vid) => self.var_universe(vid), ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index 532fbd0ffe4..bf1b3441547 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -1,8 +1,7 @@ use super::combine::{CombineFields, RelationDir}; -use super::SubregionOrigin; +use super::{ObligationEmittingRelation, SubregionOrigin}; -use crate::infer::combine::ConstEquateRelation; -use crate::traits::Obligation; +use crate::traits::{Obligation, PredicateObligations}; use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::TyVar; @@ -228,8 +227,15 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { } } -impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> { - fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - self.fields.add_const_equate_obligation(self.a_is_expected, a, b); +impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn register_predicates( + &mut self, + obligations: impl IntoIterator<Item = impl ty::ToPredicate<'tcx>>, + ) { + self.fields.register_predicates(obligations); + } + + fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) { + self.fields.register_obligations(obligations); } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 18a966449aa..e617eb68d47 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -294,6 +294,9 @@ impl<'tcx> Elaborator<'tcx> { // Nothing to elaborate } ty::PredicateKind::Ambiguous => {} + ty::PredicateKind::AliasEq(..) => { + // No + } } } } diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index ee0552d77ce..bc6d7c20997 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -38,7 +38,7 @@ fn track_diagnostic(diagnostic: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnost // Diagnostics are tracked, we can ignore the dependency. let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; - return tls::enter_context(&icx, move |_| (*f)(diagnostic)); + return tls::enter_context(&icx, move || (*f)(diagnostic)); } // In any other case, invoke diagnostics anyway. diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2a373ebc132..33ebbb411ce 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -738,30 +738,16 @@ pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock<ExternProviders> = LazyLock: extern_providers }); -pub struct QueryContext<'tcx> { - gcx: &'tcx GlobalCtxt<'tcx>, -} - -impl<'tcx> QueryContext<'tcx> { - pub fn enter<F, R>(&mut self, f: F) -> R - where - F: FnOnce(TyCtxt<'tcx>) -> R, - { - let icx = ty::tls::ImplicitCtxt::new(self.gcx); - ty::tls::enter_context(&icx, |_| f(icx.tcx)) - } -} - pub fn create_global_ctxt<'tcx>( compiler: &'tcx Compiler, lint_store: Lrc<LintStore>, dep_graph: DepGraph, untracked: Untracked, queries: &'tcx OnceCell<TcxQueries<'tcx>>, - global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>, + gcx_cell: &'tcx OnceCell<GlobalCtxt<'tcx>>, arena: &'tcx WorkerLocal<Arena<'tcx>>, hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, -) -> QueryContext<'tcx> { +) -> &'tcx GlobalCtxt<'tcx> { // We're constructing the HIR here; we don't care what we will // read, since we haven't even constructed the *input* to // incr. comp. yet. @@ -785,8 +771,8 @@ pub fn create_global_ctxt<'tcx>( TcxQueries::new(local_providers, extern_providers, query_result_on_disk_cache) }); - let gcx = sess.time("setup_global_ctxt", || { - global_ctxt.get_or_init(move || { + sess.time("setup_global_ctxt", || { + gcx_cell.get_or_init(move || { TyCtxt::create_global_ctxt( sess, lint_store, @@ -799,9 +785,7 @@ pub fn create_global_ctxt<'tcx>( rustc_query_impl::query_callbacks(arena), ) }) - }); - - QueryContext { gcx } + }) } /// Runs the resolution, type-checking, region checking and other diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 4b0180741c1..6512695873e 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -1,6 +1,6 @@ use crate::errors::{FailedWritingFile, RustcErrorFatal, RustcErrorUnexpectedAnnotation}; use crate::interface::{Compiler, Result}; -use crate::passes::{self, BoxedResolver, QueryContext}; +use crate::passes::{self, BoxedResolver}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; @@ -64,7 +64,7 @@ impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> { } } -impl<'a, 'tcx> QueryResult<'a, QueryContext<'tcx>> { +impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> { pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T { (*self.0).get_mut().enter(f) } @@ -78,7 +78,7 @@ impl<T> Default for Query<T> { pub struct Queries<'tcx> { compiler: &'tcx Compiler, - gcx: OnceCell<GlobalCtxt<'tcx>>, + gcx_cell: OnceCell<GlobalCtxt<'tcx>>, queries: OnceCell<TcxQueries<'tcx>>, arena: WorkerLocal<Arena<'tcx>>, @@ -90,7 +90,8 @@ pub struct Queries<'tcx> { register_plugins: Query<(ast::Crate, Lrc<LintStore>)>, expansion: Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>, dep_graph: Query<DepGraph>, - global_ctxt: Query<QueryContext<'tcx>>, + // This just points to what's in `gcx_cell`. + gcx: Query<&'tcx GlobalCtxt<'tcx>>, ongoing_codegen: Query<Box<dyn Any>>, } @@ -98,7 +99,7 @@ impl<'tcx> Queries<'tcx> { pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> { Queries { compiler, - gcx: OnceCell::new(), + gcx_cell: OnceCell::new(), queries: OnceCell::new(), arena: WorkerLocal::new(|_| Arena::default()), hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()), @@ -108,7 +109,7 @@ impl<'tcx> Queries<'tcx> { register_plugins: Default::default(), expansion: Default::default(), dep_graph: Default::default(), - global_ctxt: Default::default(), + gcx: Default::default(), ongoing_codegen: Default::default(), } } @@ -207,8 +208,8 @@ impl<'tcx> Queries<'tcx> { }) } - pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, QueryContext<'tcx>>> { - self.global_ctxt.compute(|| { + pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, &'tcx GlobalCtxt<'tcx>>> { + self.gcx.compute(|| { let crate_name = *self.crate_name()?.borrow(); let (krate, resolver, lint_store) = self.expansion()?.steal(); @@ -218,18 +219,18 @@ impl<'tcx> Queries<'tcx> { ast_lowering: untracked_resolver_for_lowering, } = BoxedResolver::to_resolver_outputs(resolver); - let mut qcx = passes::create_global_ctxt( + let gcx = passes::create_global_ctxt( self.compiler, lint_store, self.dep_graph()?.steal(), untracked, &self.queries, - &self.gcx, + &self.gcx_cell, &self.arena, &self.hir_arena, ); - qcx.enter(|tcx| { + gcx.enter(|tcx| { let feed = tcx.feed_unit_query(); feed.resolver_for_lowering( tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, krate))), @@ -239,7 +240,7 @@ impl<'tcx> Queries<'tcx> { let feed = tcx.feed_local_crate(); feed.crate_name(crate_name); }); - Ok(qcx) + Ok(gcx) }) } @@ -387,7 +388,7 @@ impl Compiler { // NOTE: intentionally does not compute the global context if it hasn't been built yet, // since that likely means there was a parse error. - if let Some(Ok(gcx)) = &mut *queries.global_ctxt.result.borrow_mut() { + if let Some(Ok(gcx)) = &mut *queries.gcx.result.borrow_mut() { let gcx = gcx.get_mut(); // We assume that no queries are run past here. If there are new queries // after this point, they'll show up as "<unknown>" in self-profiling data. diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5165ee424e3..0d3499ca9a0 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -5,6 +5,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; use rustc_session::config::rustc_optgroups; use rustc_session::config::Input; +use rustc_session::config::InstrumentXRay; use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ @@ -755,6 +756,7 @@ fn test_unstable_options_tracking_hash() { tracked!(inline_mir_threshold, Some(123)); tracked!(instrument_coverage, Some(InstrumentCoverage::All)); tracked!(instrument_mcount, true); + tracked!(instrument_xray, Some(InstrumentXRay::default())); tracked!(link_only, true); tracked!(llvm_plugins, vec![String::from("plugin_name")]); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 5d85cfe330a..7a50b6aec87 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1594,12 +1594,14 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { // Ignore projections, as they can only be global // if the trait bound is global Clause(Clause::Projection(..)) | + AliasEq(..) | // Ignore bounds that a user can't type WellFormed(..) | ObjectSafe(..) | ClosureKind(..) | Subtype(..) | Coerce(..) | + // FIXME(generic_const_exprs): `ConstEvaluatable` can be written ConstEvaluatable(..) | ConstEquate(..) | Ambiguous | diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 019fdc30dce..22924efa948 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -14,7 +14,7 @@ //! //! ``` //! fn main() { -//! rustc_log::init_rustc_env_logger().unwrap(); +//! rustc_log::init_env_logger("LOG").unwrap(); //! //! let edition = rustc_span::edition::Edition::Edition2021; //! rustc_span::create_session_globals_then(edition, || { @@ -23,9 +23,9 @@ //! } //! ``` //! -//! Now `RUSTC_LOG=debug cargo run` will run your minimal main.rs and show +//! Now `LOG=debug cargo run` will run your minimal main.rs and show //! rustc's debug logging. In a workflow like this, one might also add -//! `std::env::set_var("RUSTC_LOG", "debug")` to the top of main so that `cargo +//! `std::env::set_var("LOG", "debug")` to the top of main so that `cargo //! run` by itself is sufficient to get logs. //! //! The reason rustc_log is a tiny separate crate, as opposed to exposing the @@ -53,12 +53,6 @@ use tracing_subscriber::fmt::{ }; use tracing_subscriber::layer::SubscriberExt; -pub fn init_rustc_env_logger() -> Result<(), Error> { - init_env_logger("RUSTC_LOG") -} - -/// In contrast to `init_rustc_env_logger` this allows you to choose an env var -/// other than `RUSTC_LOG`. pub fn init_env_logger(env: &str) -> Result<(), Error> { let filter = match env::var(env) { Ok(env) => EnvFilter::new(env), @@ -98,7 +92,7 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { let fmt_layer = tracing_subscriber::fmt::layer() .with_writer(io::stderr) .without_time() - .event_format(BacktraceFormatter { backtrace_target: str.to_string() }); + .event_format(BacktraceFormatter { backtrace_target: str }); let subscriber = subscriber.with(fmt_layer); tracing::subscriber::set_global_default(subscriber).unwrap(); } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index e263fc74835..a8514c69d1c 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -49,20 +49,21 @@ fn find_bundled_library( name: Option<Symbol>, verbatim: Option<bool>, kind: NativeLibKind, + has_cfg: bool, sess: &Session, ) -> Option<Symbol> { - if sess.opts.unstable_opts.packed_bundled_libs && - sess.crate_types().iter().any(|ct| ct == &CrateType::Rlib || ct == &CrateType::Staticlib) && - let NativeLibKind::Static { bundle: Some(true) | None, .. } = kind { - find_native_static_library( - name.unwrap().as_str(), - verbatim.unwrap_or(false), - &sess.target_filesearch(PathKind::Native).search_path_dirs(), - sess, - ).file_name().and_then(|s| s.to_str()).map(Symbol::intern) - } else { - None + if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind + && sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib)) + && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true)) + { + let verbatim = verbatim.unwrap_or(false); + let search_paths = &sess.target_filesearch(PathKind::Native).search_path_dirs(); + return find_native_static_library(name.unwrap().as_str(), verbatim, search_paths, sess) + .file_name() + .and_then(|s| s.to_str()) + .map(Symbol::intern); } + None } pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { @@ -385,7 +386,7 @@ impl<'tcx> Collector<'tcx> { let name = name.map(|(name, _)| name); let kind = kind.unwrap_or(NativeLibKind::Unspecified); - let filename = find_bundled_library(name, verbatim, kind, sess); + let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess); self.libs.push(NativeLib { name, filename, @@ -475,7 +476,7 @@ impl<'tcx> Collector<'tcx> { let name = Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))); let sess = self.tcx.sess; let filename = - find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, sess); + find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess); self.libs.push(NativeLib { name, filename, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e2b07fad6e7..800f85063c4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -11,7 +11,7 @@ use rustc_data_structures::sync::{Lock, LockGuard, Lrc, OnceCell}; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; -use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathData, DefPathHash}; use rustc_hir::diagnostic_items::DiagnosticItems; @@ -1163,20 +1163,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } - /// Decodes all inherent impls in the crate (for rustdoc). - fn get_inherent_impls(self) -> impl Iterator<Item = (DefId, DefId)> + 'a { - (0..self.root.tables.inherent_impls.size()).flat_map(move |i| { - let ty_index = DefIndex::from_usize(i); - let ty_def_id = self.local_def_id(ty_index); - self.root - .tables - .inherent_impls - .get(self, ty_index) - .decode(self) - .map(move |impl_index| (ty_def_id, self.local_def_id(impl_index))) - }) - } - /// Decodes all traits in the crate (for rustdoc and rustc diagnostics). fn get_traits(self) -> impl Iterator<Item = DefId> + 'a { self.root.traits.decode(self).map(move |index| self.local_def_id(index)) @@ -1195,13 +1181,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { }) } - fn get_all_incoherent_impls(self) -> impl Iterator<Item = DefId> + 'a { - self.cdata - .incoherent_impls - .values() - .flat_map(move |impls| impls.decode(self).map(move |idx| self.local_def_id(idx))) - } - fn get_incoherent_impls(self, tcx: TyCtxt<'tcx>, simp: SimplifiedType) -> &'tcx [DefId] { if let Some(impls) = self.cdata.incoherent_impls.get(&simp) { tcx.arena.alloc_from_iter(impls.decode(self).map(|idx| self.local_def_id(idx))) @@ -1598,6 +1577,24 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { fn get_is_intrinsic(self, index: DefIndex) -> bool { self.root.tables.is_intrinsic.get(self, index) } + + fn get_doc_link_resolutions(self, index: DefIndex) -> DocLinkResMap { + self.root + .tables + .doc_link_resolutions + .get(self, index) + .expect("no resolutions for a doc link") + .decode(self) + } + + fn get_doc_link_traits_in_scope(self, index: DefIndex) -> impl Iterator<Item = DefId> + 'a { + self.root + .tables + .doc_link_traits_in_scope + .get(self, index) + .expect("no traits in scope for a doc link") + .decode(self) + } } impl CrateMetadata { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 07cc84ab953..b12f9b5c917 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -345,6 +345,10 @@ provide! { tcx, def_id, other, cdata, expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) } generator_diagnostic_data => { cdata.get_generator_diagnostic_data(tcx, def_id.index) } is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) } + doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(def_id.index)) } + doc_link_traits_in_scope => { + tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(def_id.index)) + } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { @@ -613,36 +617,6 @@ impl CStore { self.get_crate_data(cnum).get_trait_impls() } - /// Decodes all inherent impls in the crate (for rustdoc). - pub fn inherent_impls_in_crate_untracked( - &self, - cnum: CrateNum, - ) -> impl Iterator<Item = (DefId, DefId)> + '_ { - self.get_crate_data(cnum).get_inherent_impls() - } - - /// Decodes all incoherent inherent impls in the crate (for rustdoc). - pub fn incoherent_impls_in_crate_untracked( - &self, - cnum: CrateNum, - ) -> impl Iterator<Item = DefId> + '_ { - self.get_crate_data(cnum).get_all_incoherent_impls() - } - - pub fn associated_item_def_ids_untracked<'a>( - &'a self, - def_id: DefId, - sess: &'a Session, - ) -> impl Iterator<Item = DefId> + 'a { - self.get_crate_data(def_id.krate).get_associated_item_def_ids(def_id.index, sess) - } - - pub fn may_have_doc_links_untracked(&self, def_id: DefId) -> bool { - self.get_crate_data(def_id.krate) - .get_attr_flags(def_id.index) - .contains(AttrFlags::MAY_HAVE_DOC_LINKS) - } - pub fn is_doc_hidden_untracked(&self, def_id: DefId) -> bool { self.get_crate_data(def_id.krate) .get_attr_flags(def_id.index) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 85e9ae9a983..263c71ae702 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -3,7 +3,6 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef; use crate::rmeta::table::TableBuilder; use crate::rmeta::*; -use rustc_ast::util::comments; use rustc_ast::Attribute; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; @@ -772,7 +771,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { struct AnalyzeAttrState { is_exported: bool, - may_have_doc_links: bool, is_doc_hidden: bool, } @@ -790,15 +788,12 @@ fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool { let mut should_encode = false; if rustc_feature::is_builtin_only_local(attr.name_or_empty()) { // Attributes marked local-only don't need to be encoded for downstream crates. - } else if let Some(s) = attr.doc_str() { + } else if attr.doc_str().is_some() { // We keep all doc comments reachable to rustdoc because they might be "imported" into // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into // their own. if state.is_exported { should_encode = true; - if comments::may_have_doc_links(s.as_str()) { - state.may_have_doc_links = true; - } } } else if attr.has_name(sym::doc) { // If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in @@ -1139,7 +1134,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let mut state = AnalyzeAttrState { is_exported: tcx.effective_visibilities(()).is_exported(def_id), - may_have_doc_links: false, is_doc_hidden: false, }; let attr_iter = tcx @@ -1151,9 +1145,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter); let mut attr_flags = AttrFlags::empty(); - if state.may_have_doc_links { - attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS; - } if state.is_doc_hidden { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } @@ -1231,6 +1222,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_id.index })); } + + for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions { + record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); + } + + for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope { + record_array!(self.tables.doc_link_traits_in_scope[def_id.to_def_id()] <- traits); + } } #[instrument(level = "trace", skip(self))] @@ -1715,6 +1714,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); } self.encode_deprecation(LOCAL_CRATE.as_def_id()); + if let Some(res_map) = tcx.resolutions(()).doc_link_resolutions.get(&CRATE_DEF_ID) { + record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); + } + if let Some(traits) = tcx.resolutions(()).doc_link_traits_in_scope.get(&CRATE_DEF_ID) { + record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits); + } // Normally, this information is encoded when we walk the items // defined in this crate. However, we skip doing that for proc-macro crates, @@ -2225,6 +2230,18 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) { pub fn provide(providers: &mut Providers) { *providers = Providers { + doc_link_resolutions: |tcx, def_id| { + tcx.resolutions(()) + .doc_link_resolutions + .get(&def_id.expect_local()) + .expect("no resolutions for a doc link") + }, + doc_link_traits_in_scope: |tcx, def_id| { + tcx.resolutions(()) + .doc_link_traits_in_scope + .get(&def_id.expect_local()) + .expect("no traits in scope for a doc link") + }, traits_in_crate: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a74aa381d9e..9227609cc8b 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -9,7 +9,7 @@ use rustc_attr as attr; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::MetadataRef; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind}; +use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; @@ -413,6 +413,8 @@ define_tables! { module_reexports: Table<DefIndex, LazyArray<ModChild>>, deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>, trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, Ty<'static>>>>, + doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>, + doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>, } #[derive(TyEncodable, TyDecodable)] @@ -426,8 +428,7 @@ struct VariantData { bitflags::bitflags! { #[derive(Default)] pub struct AttrFlags: u8 { - const MAY_HAVE_DOC_LINKS = 1 << 0; - const IS_DOC_HIDDEN = 1 << 1; + const IS_DOC_HIDDEN = 1 << 0; } } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 2ba7ec5b151..9d2144c443b 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -113,6 +113,7 @@ macro_rules! arena_types { [decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap<rustc_hir::def_id::DefId, rustc_middle::ty::Ty<'tcx>>, [] 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, ]); ) } diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 2e62bebc852..2e82efba192 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -55,7 +55,7 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { ty::tls::with_context(|icx| { let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; - ty::tls::enter_context(&icx, |_| op()) + ty::tls::enter_context(&icx, op) }) } diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index d6f20a8fc06..e6e4545d9c3 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -345,9 +345,9 @@ impl<'tcx> CanonicalVarValues<'tcx> { var_values: tcx.mk_substs(infos.iter().enumerate().map( |(i, info)| -> ty::GenericArg<'tcx> { match info.kind { - CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => tcx - .mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into())) - .into(), + CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => { + tcx.mk_bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into()).into() + } CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => { let br = ty::BoundRegion { var: ty::BoundVar::from_usize(i), diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 95148de2518..56df1a66f9d 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -34,6 +34,7 @@ #![feature(get_mut_unchecked)] #![feature(if_let_guard)] #![feature(iter_from_generator)] +#![feature(local_key_cell_methods)] #![feature(negative_impls)] #![feature(never_type)] #![feature(extern_types)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index b5e0b88bbe5..9f544de9f87 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -162,7 +162,7 @@ impl<'tcx> Rvalue<'tcx> { match *self { Rvalue::Use(ref operand) => operand.ty(local_decls, tcx), Rvalue::Repeat(ref operand, count) => { - tcx.mk_ty(ty::Array(operand.ty(local_decls, tcx), count)) + tcx.mk_array_with_const_len(operand.ty(local_decls, tcx), count) } Rvalue::ThreadLocalRef(did) => { let static_ty = tcx.type_of(did); diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index e4bb3ce3d5a..dc02fd53ed0 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -8,7 +8,7 @@ use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::{self, layout::TyAndLayout, 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, VecCacheSelector}; +use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -45,7 +45,7 @@ pub trait Key: Sized { } impl Key for () { - type CacheSelector = DefaultCacheSelector<Self>; + type CacheSelector = SingleCacheSelector; #[inline(always)] fn query_crate_is_local(&self) -> bool { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0a16ede6499..d37d6b37a37 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2156,4 +2156,16 @@ rustc_queries! { desc { |tcx| "deducing parameter attributes for {}", tcx.def_path_str(def_id) } separate_provide_extern } + + query doc_link_resolutions(def_id: DefId) -> &'tcx DocLinkResMap { + eval_always + desc { "resolutions for documentation links for a module" } + separate_provide_extern + } + + query doc_link_traits_in_scope(def_id: DefId) -> &'tcx [DefId] { + eval_always + desc { "traits in scope for documentation links for a module" } + separate_provide_extern + } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9205a8a0ffe..5962384241e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -241,6 +241,11 @@ impl<'tcx> CtxtInterners<'tcx> { } } +const NUM_PREINTERNED_TY_VARS: u32 = 100; +const NUM_PREINTERNED_FRESH_TYS: u32 = 20; +const NUM_PREINTERNED_FRESH_INT_TYS: u32 = 3; +const NUM_PREINTERNED_FRESH_FLOAT_TYS: u32 = 3; + pub struct CommonTypes<'tcx> { pub unit: Ty<'tcx>, pub bool: Ty<'tcx>, @@ -266,7 +271,20 @@ pub struct CommonTypes<'tcx> { /// Dummy type used for the `Self` of a `TraitRef` created for converting /// a trait object, and which gets removed in `ExistentialTraitRef`. /// This type must not appear anywhere in other converted types. + /// `Infer(ty::FreshTy(0))` does the job. pub trait_object_dummy_self: Ty<'tcx>, + + /// Pre-interned `Infer(ty::TyVar(n))` for small values of `n`. + pub ty_vars: Vec<Ty<'tcx>>, + + /// Pre-interned `Infer(ty::FreshTy(n))` for small values of `n`. + pub fresh_tys: Vec<Ty<'tcx>>, + + /// Pre-interned `Infer(ty::FreshIntTy(n))` for small values of `n`. + pub fresh_int_tys: Vec<Ty<'tcx>>, + + /// Pre-interned `Infer(ty::FreshFloatTy(n))` for small values of `n`. + pub fresh_float_tys: Vec<Ty<'tcx>>, } pub struct CommonLifetimes<'tcx> { @@ -289,6 +307,15 @@ impl<'tcx> CommonTypes<'tcx> { ) -> CommonTypes<'tcx> { let mk = |ty| interners.intern_ty(ty, sess, untracked); + let ty_vars = + (0..NUM_PREINTERNED_TY_VARS).map(|n| mk(Infer(ty::TyVar(TyVid::from(n))))).collect(); + let fresh_tys: Vec<_> = + (0..NUM_PREINTERNED_FRESH_TYS).map(|n| mk(Infer(ty::FreshTy(n)))).collect(); + let fresh_int_tys: Vec<_> = + (0..NUM_PREINTERNED_FRESH_INT_TYS).map(|n| mk(Infer(ty::FreshIntTy(n)))).collect(); + let fresh_float_tys: Vec<_> = + (0..NUM_PREINTERNED_FRESH_FLOAT_TYS).map(|n| mk(Infer(ty::FreshFloatTy(n)))).collect(); + CommonTypes { unit: mk(Tuple(List::empty())), bool: mk(Bool), @@ -311,7 +338,12 @@ impl<'tcx> CommonTypes<'tcx> { str_: mk(Str), self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), - trait_object_dummy_self: mk(Infer(ty::FreshTy(0))), + trait_object_dummy_self: fresh_tys[0], + + ty_vars, + fresh_tys, + fresh_int_tys, + fresh_float_tys, } } } @@ -468,6 +500,18 @@ pub struct GlobalCtxt<'tcx> { pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>, } +impl<'tcx> GlobalCtxt<'tcx> { + /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of + /// `f`. + pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R + where + F: FnOnce(TyCtxt<'tcx>) -> R, + { + let icx = tls::ImplicitCtxt::new(self); + tls::enter_context(&icx, || f(icx.tcx)) + } +} + impl<'tcx> TyCtxt<'tcx> { /// Expects a body and returns its codegen attributes. /// @@ -649,6 +693,30 @@ impl<'tcx> TyCtxt<'tcx> { self.mk_ty(Error(reported)) } + /// Constructs a `RegionKind::ReError` lifetime. + #[track_caller] + pub fn re_error(self, reported: ErrorGuaranteed) -> Region<'tcx> { + self.mk_region(ty::ReError(reported)) + } + + /// Constructs a `RegionKind::ReError` lifetime and registers a `delay_span_bug` to ensure it + /// gets used. + #[track_caller] + pub fn re_error_misc(self) -> Region<'tcx> { + self.re_error_with_message( + DUMMY_SP, + "RegionKind::ReError constructed but no error reported", + ) + } + + /// Constructs a `RegionKind::ReError` lifetime and registers a `delay_span_bug` with the given + /// `msg` to ensure it gets used. + #[track_caller] + pub fn re_error_with_message<S: Into<MultiSpan>>(self, span: S, msg: &str) -> Region<'tcx> { + let reported = self.sess.delay_span_bug(span, msg); + self.re_error(reported) + } + /// Like [TyCtxt::ty_error] but for constants, with current `ErrorGuaranteed` #[track_caller] pub fn const_error_with_guaranteed( @@ -1568,6 +1636,7 @@ impl<'tcx> TyCtxt<'tcx> { if *r == kind { r } else { self.mk_region(kind) } } + // Avoid this in favour of more specific `mk_*` methods, where possible. #[allow(rustc::usage_of_ty_tykind)] #[inline] pub fn mk_ty(self, st: TyKind<'tcx>) -> Ty<'tcx> { @@ -1720,17 +1789,22 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] + pub fn mk_array_with_const_len(self, ty: Ty<'tcx>, ct: Const<'tcx>) -> Ty<'tcx> { + self.mk_ty(Array(ty, ct)) + } + + #[inline] pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> { self.mk_ty(Slice(ty)) } #[inline] pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> { - self.mk_ty(Tuple(self.intern_type_list(&ts))) + if ts.is_empty() { self.types.unit } else { self.mk_ty(Tuple(self.intern_type_list(&ts))) } } pub fn mk_tup<I: InternAs<Ty<'tcx>, Ty<'tcx>>>(self, iter: I) -> I::Output { - iter.intern_with(|ts| self.mk_ty(Tuple(self.intern_type_list(&ts)))) + iter.intern_with(|ts| self.intern_tup(ts)) } #[inline] @@ -1794,7 +1868,7 @@ impl<'tcx> TyCtxt<'tcx> { item_def_id: DefId, substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>, ) -> Ty<'tcx> { - self.mk_ty(Alias(ty::Projection, self.mk_alias_ty(item_def_id, substs))) + self.mk_alias(ty::Projection, self.mk_alias_ty(item_def_id, substs)) } #[inline] @@ -1832,28 +1906,54 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> { - self.mk_ty_infer(TyVar(v)) + pub fn mk_const(self, kind: impl Into<ty::ConstKind<'tcx>>, ty: Ty<'tcx>) -> Const<'tcx> { + self.mk_const_internal(ty::ConstData { kind: kind.into(), ty }) } #[inline] - pub fn mk_const(self, kind: impl Into<ty::ConstKind<'tcx>>, ty: Ty<'tcx>) -> Const<'tcx> { - self.mk_const_internal(ty::ConstData { kind: kind.into(), ty }) + pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> { + // Use a pre-interned one when possible. + self.types.ty_vars.get(v.as_usize()).copied().unwrap_or_else(|| self.mk_ty(Infer(TyVar(v)))) } #[inline] pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> { - self.mk_ty_infer(IntVar(v)) + self.mk_ty(Infer(IntVar(v))) } #[inline] pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> { - self.mk_ty_infer(FloatVar(v)) + self.mk_ty(Infer(FloatVar(v))) + } + + #[inline] + pub fn mk_fresh_ty(self, n: u32) -> Ty<'tcx> { + // Use a pre-interned one when possible. + self.types + .fresh_tys + .get(n as usize) + .copied() + .unwrap_or_else(|| self.mk_ty(Infer(ty::FreshTy(n)))) } #[inline] - pub fn mk_ty_infer(self, it: InferTy) -> Ty<'tcx> { - self.mk_ty(Infer(it)) + pub fn mk_fresh_int_ty(self, n: u32) -> Ty<'tcx> { + // Use a pre-interned one when possible. + self.types + .fresh_int_tys + .get(n as usize) + .copied() + .unwrap_or_else(|| self.mk_ty(Infer(ty::FreshIntTy(n)))) + } + + #[inline] + pub fn mk_fresh_float_ty(self, n: u32) -> Ty<'tcx> { + // Use a pre-interned one when possible. + self.types + .fresh_float_tys + .get(n as usize) + .copied() + .unwrap_or_else(|| self.mk_ty(Infer(ty::FreshFloatTy(n)))) } #[inline] @@ -1877,8 +1977,23 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] + pub fn mk_bound(self, index: ty::DebruijnIndex, bound_ty: ty::BoundTy) -> Ty<'tcx> { + self.mk_ty(Bound(index, bound_ty)) + } + + #[inline] + pub fn mk_placeholder(self, placeholder: ty::PlaceholderType) -> Ty<'tcx> { + self.mk_ty(Placeholder(placeholder)) + } + + #[inline] + pub fn mk_alias(self, kind: ty::AliasKind, alias_ty: ty::AliasTy<'tcx>) -> Ty<'tcx> { + self.mk_ty(Alias(kind, alias_ty)) + } + + #[inline] pub fn mk_opaque(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty(Alias(ty::Opaque, self.mk_alias_ty(def_id, substs))) + self.mk_alias(ty::Opaque, self.mk_alias_ty(def_id, substs)) } pub fn mk_place_field(self, place: Place<'tcx>, f: Field, ty: Ty<'tcx>) -> Place<'tcx> { @@ -2218,6 +2333,10 @@ impl<'tcx> TyCtxt<'tcx> { }) ) } + + pub fn trait_solver_next(self) -> bool { + self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next + } } impl<'tcx> TyCtxtAt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 71b025dc1be..5426ac8d739 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -89,9 +89,8 @@ mod tlv { /// This is used to set the pointer to the new `ImplicitCtxt`. #[inline] pub(super) fn with_tlv<F: FnOnce() -> R, R>(value: *const (), f: F) -> R { - let old = get_tlv(); - let _reset = rustc_data_structures::OnDrop(move || TLV.with(|tlv| tlv.set(old))); - TLV.with(|tlv| tlv.set(value)); + let old = TLV.replace(value); + let _reset = rustc_data_structures::OnDrop(move || TLV.set(old)); f() } } @@ -110,9 +109,9 @@ unsafe fn downcast<'a, 'tcx>(context: *const ()) -> &'a ImplicitCtxt<'a, 'tcx> { #[inline] pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R where - F: FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R, + F: FnOnce() -> R, { - tlv::with_tlv(erase(context), || f(&context)) + tlv::with_tlv(erase(context), f) } /// Allows access to the current `ImplicitCtxt` in a closure if one is available. diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index dc6f5851b7d..258bc9c3e41 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -264,10 +264,7 @@ impl FlagComputation { term, })) => { self.add_projection_ty(projection_ty); - match term.unpack() { - ty::TermKind::Ty(ty) => self.add_ty(ty), - ty::TermKind::Const(c) => self.add_const(c), - } + self.add_term(term); } ty::PredicateKind::WellFormed(arg) => { self.add_substs(slice::from_ref(&arg)); @@ -287,6 +284,10 @@ impl FlagComputation { self.add_ty(ty); } ty::PredicateKind::Ambiguous => {} + ty::PredicateKind::AliasEq(t1, t2) => { + self.add_term(t1); + self.add_term(t2); + } } } @@ -380,4 +381,11 @@ impl FlagComputation { } } } + + fn add_term(&mut self, term: ty::Term<'_>) { + match term.unpack() { + ty::TermKind::Ty(ty) => self.add_ty(ty), + ty::TermKind::Const(ct) => self.add_const(ct), + } + } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 8a0019bc012..c0d319edf76 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -562,10 +562,7 @@ impl<'tcx> TyCtxt<'tcx> { )) }, types: &mut |t: ty::BoundTy| { - self.mk_ty(ty::Bound( - ty::INNERMOST, - ty::BoundTy { var: shift_bv(t.var), kind: t.kind }, - )) + self.mk_bound(ty::INNERMOST, ty::BoundTy { var: shift_bv(t.var), kind: t.kind }) }, consts: &mut |c, ty: Ty<'tcx>| { self.mk_const(ty::ConstKind::Bound(ty::INNERMOST, shift_bv(c)), ty) @@ -614,7 +611,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon(index as u32)) }) .expect_ty(); - self.tcx.mk_ty(ty::Bound(ty::INNERMOST, BoundTy { var, kind })) + self.tcx.mk_bound(ty::INNERMOST, BoundTy { var, kind }) } fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { let entry = self.map.entry(bv); @@ -684,7 +681,7 @@ impl<'tcx> TypeFolder<'tcx> for Shifter<'tcx> { match *ty.kind() { ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { let debruijn = debruijn.shifted_in(self.amount); - self.tcx.mk_ty(ty::Bound(debruijn, bound_ty)) + self.tcx.mk_bound(debruijn, bound_ty) } _ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self), diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 801ca600445..ea95a38f272 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -100,7 +100,7 @@ impl GenericParamDef { preceding_substs: &[ty::GenericArg<'tcx>], ) -> ty::GenericArg<'tcx> { match &self.kind { - ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(), + ty::GenericParamDefKind::Lifetime => tcx.re_error_misc().into(), ty::GenericParamDefKind::Type { .. } => tcx.ty_error().into(), ty::GenericParamDefKind::Const { .. } => { tcx.const_error(tcx.bound_type_of(self.def_id).subst(tcx, preceding_substs)).into() diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 09c3d5b736c..fa2d3b89cf4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -36,7 +36,7 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, LifetimeRes, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_hir::Node; use rustc_index::vec::IndexVec; @@ -181,6 +181,8 @@ pub struct ResolverGlobalCtxt { /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: FxHashMap<Span, Span>, pub registered_tools: RegisteredTools, + pub doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>, + pub doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>, } /// Resolutions that should only be used for lowering. @@ -545,6 +547,7 @@ impl<'tcx> Predicate<'tcx> { | PredicateKind::Clause(Clause::RegionOutlives(_)) | PredicateKind::Clause(Clause::TypeOutlives(_)) | PredicateKind::Clause(Clause::Projection(_)) + | PredicateKind::AliasEq(..) | PredicateKind::ObjectSafe(_) | PredicateKind::ClosureKind(_, _, _) | PredicateKind::Subtype(_) @@ -632,6 +635,12 @@ pub enum PredicateKind<'tcx> { /// A marker predicate that is always ambiguous. /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. Ambiguous, + + /// Separate from `Clause::Projection` which is used for normalization in new solver. + /// This predicate requires two terms to be equal to eachother. + /// + /// Only used for new solver + AliasEq(Term<'tcx>, Term<'tcx>), } /// The crate outlives map is computed during typeck and contains the @@ -963,6 +972,33 @@ impl<'tcx> Term<'tcx> { TermKind::Const(c) => c.into(), } } + + /// This function returns `None` for `AliasKind::Opaque`. + /// + /// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly + /// deal with constants. + pub fn to_alias_term_no_opaque(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> { + match self.unpack() { + TermKind::Ty(ty) => match ty.kind() { + ty::Alias(kind, alias_ty) => match kind { + AliasKind::Projection => Some(*alias_ty), + AliasKind::Opaque => None, + }, + _ => None, + }, + TermKind::Const(ct) => match ct.kind() { + ConstKind::Unevaluated(uv) => Some(tcx.mk_alias_ty(uv.def.did, uv.substs)), + _ => None, + }, + } + } + + pub fn is_infer(&self) -> bool { + match self.unpack() { + TermKind::Ty(ty) => ty.is_ty_or_numeric_infer(), + TermKind::Const(ct) => ct.is_ct_infer(), + } + } } const TAG_MASK: usize = 0b11; @@ -1152,6 +1188,7 @@ impl<'tcx> Predicate<'tcx> { match predicate.skip_binder() { PredicateKind::Clause(Clause::Trait(t)) => Some(predicate.rebind(t)), PredicateKind::Clause(Clause::Projection(..)) + | PredicateKind::AliasEq(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) | PredicateKind::Clause(Clause::RegionOutlives(..)) @@ -1171,6 +1208,7 @@ impl<'tcx> Predicate<'tcx> { match predicate.skip_binder() { PredicateKind::Clause(Clause::Projection(t)) => Some(predicate.rebind(t)), PredicateKind::Clause(Clause::Trait(..)) + | PredicateKind::AliasEq(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) | PredicateKind::Clause(Clause::RegionOutlives(..)) @@ -1191,6 +1229,7 @@ impl<'tcx> Predicate<'tcx> { PredicateKind::Clause(Clause::TypeOutlives(data)) => Some(predicate.rebind(data)), PredicateKind::Clause(Clause::Trait(..)) | PredicateKind::Clause(Clause::Projection(..)) + | PredicateKind::AliasEq(..) | PredicateKind::Subtype(..) | PredicateKind::Coerce(..) | PredicateKind::Clause(Clause::RegionOutlives(..)) diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index 7ff58f02623..a5ebdbc8792 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -109,6 +109,8 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { // them. ty::ReErased => return r, + ty::ReError(_) => return r, + // The regions that we expect from borrow checking. ty::ReEarlyBound(_) | ty::ReFree(_) => {} @@ -125,20 +127,21 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { Some(u) => panic!("region mapped to unexpected kind: {:?}", u), None if self.do_not_error => self.tcx.lifetimes.re_static, None => { - self.tcx + let e = self + .tcx .sess .struct_span_err(self.span, "non-defining opaque type use in defining scope") .span_label( self.span, format!( "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", + parameter list of the `impl Trait` type alias", r ), ) .emit(); - self.tcx().lifetimes.re_static + self.tcx().re_error(e) } } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 84edb5f2a42..303675d3ca5 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -81,6 +81,8 @@ trivially_parameterized_over_tcx! { rustc_hir::IsAsync, rustc_hir::LangItem, rustc_hir::def::DefKind, + rustc_hir::def::DocLinkResMap, + rustc_hir::def_id::DefId, rustc_hir::def_id::DefIndex, rustc_hir::definitions::DefKey, rustc_index::bit_set::BitSet<u32>, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index bbb4fd999bc..c4a95ddacbf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -22,7 +22,6 @@ use rustc_target::spec::abi::Abi; use smallvec::SmallVec; use std::cell::Cell; -use std::char; use std::collections::BTreeMap; use std::fmt::{self, Write as _}; use std::iter; @@ -1210,7 +1209,7 @@ pub trait PrettyPrinter<'tcx>: // in order to place the projections inside the `<...>`. if !resugared { // Use a type that can't appear in defaults of type parameters. - let dummy_cx = cx.tcx().mk_ty_infer(ty::FreshTy(0)); + let dummy_cx = cx.tcx().mk_fresh_ty(0); let principal = principal.with_self_ty(cx.tcx(), dummy_cx); let args = cx @@ -2114,7 +2113,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { ty::ReVar(_) if identify_regions => true, - ty::ReVar(_) | ty::ReErased => false, + ty::ReVar(_) | ty::ReErased | ty::ReError(_) => false, ty::ReStatic => true, } @@ -2194,6 +2193,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { } ty::ReVar(_) => {} ty::ReErased => {} + ty::ReError(_) => {} ty::ReStatic => { p!("'static"); return Ok(self); @@ -2696,7 +2696,7 @@ define_print_and_forward_display! { ty::ExistentialTraitRef<'tcx> { // Use a type that can't appear in defaults of type parameters. - let dummy_self = cx.tcx().mk_ty_infer(ty::FreshTy(0)); + let dummy_self = cx.tcx().mk_fresh_ty(0); let trait_ref = self.with_self_ty(cx.tcx(), dummy_self); p!(print(trait_ref.print_only_trait_path())) } @@ -2841,6 +2841,7 @@ define_print_and_forward_display! { p!("the type `", print(ty), "` is found in the environment") } ty::PredicateKind::Ambiguous => p!("ambiguous"), + ty::PredicateKind::AliasEq(t1, t2) => p!(print(t1), " == ", print(t2)), } } diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 933aaadd62e..bec70974dde 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -45,7 +45,7 @@ use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordSet; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::hir_id::OwnerId; use rustc_hir::lang_items::{LangItem, LanguageItems}; diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 890dabde1f7..3b22da41a57 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -502,7 +502,7 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>( (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { let t = relation.relate(a_t, b_t)?; match relation.relate(sz_a, sz_b) { - Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))), + Ok(sz) => Ok(tcx.mk_array_with_const_len(t, sz)), Err(err) => { // Check whether the lengths are both concrete/known values, // but are unequal, for better diagnostics. diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8df639750c7..1ef66b01ea0 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -177,6 +177,7 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { write!(f, "TypeWellFormedFromEnv({:?})", ty) } ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"), + ty::PredicateKind::AliasEq(t1, t2) => write!(f, "AliasEq({t1:?}, {t2:?})"), } } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 98d6b683563..89cde91e755 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1171,7 +1171,7 @@ impl<'tcx> FallibleTypeFolder<'tcx> for SkipBindersAt<'tcx> { if index == self.index { Err(()) } else { - Ok(self.tcx().mk_ty(ty::Bound(index.shifted_out(1), bv))) + Ok(self.tcx().mk_bound(index.shifted_out(1), bv)) } } else { ty.try_super_fold_with(self) @@ -1260,7 +1260,7 @@ impl<'tcx> AliasTy<'tcx> { } pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - tcx.mk_ty(ty::Alias(self.kind(tcx), self)) + tcx.mk_alias(self.kind(tcx), self) } } @@ -1623,10 +1623,16 @@ impl<'tcx> Region<'tcx> { ty::ReVar(..) => false, ty::RePlaceholder(placeholder) => placeholder.name.is_named(), ty::ReErased => false, + ty::ReError(_) => false, } } #[inline] + pub fn is_error(self) -> bool { + matches!(*self, ty::ReError(_)) + } + + #[inline] pub fn is_static(self) -> bool { matches!(*self, ty::ReStatic) } @@ -1686,6 +1692,7 @@ impl<'tcx> Region<'tcx> { ty::ReErased => { flags = flags | TypeFlags::HAS_RE_ERASED; } + ty::ReError(_) => {} } debug!("type_flags({:?}) = {:?}", self, flags); diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs new file mode 100644 index 00000000000..89f8de23583 --- /dev/null +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -0,0 +1,298 @@ +use crate::rustc_middle::ty::util::IntTypeExt; +use crate::MirPass; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::interpret::AllocId; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, AdtDef, Const, ParamEnv, Ty, TyCtxt}; +use rustc_session::Session; +use rustc_target::abi::{HasDataLayout, Size, TagEncoding, Variants}; + +/// A pass that seeks to optimize unnecessary moves of large enum types, if there is a large +/// enough discrepancy between them. +/// +/// i.e. If there is are two variants: +/// ``` +/// enum Example { +/// Small, +/// Large([u32; 1024]), +/// } +/// ``` +/// Instead of emitting moves of the large variant, +/// Perform a memcpy instead. +/// Based off of [this HackMD](https://hackmd.io/@ft4bxUsFT5CEUBmRKYHr7w/rJM8BBPzD). +/// +/// In summary, what this does is at runtime determine which enum variant is active, +/// and instead of copying all the bytes of the largest possible variant, +/// copy only the bytes for the currently active variant. +pub struct EnumSizeOpt { + pub(crate) discrepancy: u64, +} + +impl<'tcx> MirPass<'tcx> for EnumSizeOpt { + fn is_enabled(&self, sess: &Session) -> bool { + sess.opts.unstable_opts.unsound_mir_opts || sess.mir_opt_level() >= 3 + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + // NOTE: This pass may produce different MIR based on the alignment of the target + // platform, but it will still be valid. + self.optim(tcx, body); + } +} + +impl EnumSizeOpt { + fn candidate<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + alloc_cache: &mut FxHashMap<Ty<'tcx>, AllocId>, + ) -> Option<(AdtDef<'tcx>, usize, AllocId)> { + let adt_def = match ty.kind() { + ty::Adt(adt_def, _substs) if adt_def.is_enum() => adt_def, + _ => return None, + }; + let layout = tcx.layout_of(param_env.and(ty)).ok()?; + let variants = match &layout.variants { + Variants::Single { .. } => return None, + Variants::Multiple { tag_encoding, .. } + if matches!(tag_encoding, TagEncoding::Niche { .. }) => + { + return None; + } + Variants::Multiple { variants, .. } if variants.len() <= 1 => return None, + Variants::Multiple { variants, .. } => variants, + }; + let min = variants.iter().map(|v| v.size).min().unwrap(); + let max = variants.iter().map(|v| v.size).max().unwrap(); + if max.bytes() - min.bytes() < self.discrepancy { + return None; + } + + let num_discrs = adt_def.discriminants(tcx).count(); + if variants.iter_enumerated().any(|(var_idx, _)| { + let discr_for_var = adt_def.discriminant_for_variant(tcx, var_idx).val; + (discr_for_var > usize::MAX as u128) || (discr_for_var as usize >= num_discrs) + }) { + return None; + } + if let Some(alloc_id) = alloc_cache.get(&ty) { + return Some((*adt_def, num_discrs, *alloc_id)); + } + + let data_layout = tcx.data_layout(); + let ptr_sized_int = data_layout.ptr_sized_integer(); + let target_bytes = ptr_sized_int.size().bytes() as usize; + let mut data = vec![0; target_bytes * num_discrs]; + macro_rules! encode_store { + ($curr_idx: expr, $endian: expr, $bytes: expr) => { + let bytes = match $endian { + rustc_target::abi::Endian::Little => $bytes.to_le_bytes(), + rustc_target::abi::Endian::Big => $bytes.to_be_bytes(), + }; + for (i, b) in bytes.into_iter().enumerate() { + data[$curr_idx + i] = b; + } + }; + } + + for (var_idx, layout) in variants.iter_enumerated() { + let curr_idx = + target_bytes * adt_def.discriminant_for_variant(tcx, var_idx).val as usize; + let sz = layout.size; + match ptr_sized_int { + rustc_target::abi::Integer::I32 => { + encode_store!(curr_idx, data_layout.endian, sz.bytes() as u32); + } + rustc_target::abi::Integer::I64 => { + encode_store!(curr_idx, data_layout.endian, sz.bytes()); + } + _ => unreachable!(), + }; + } + let alloc = interpret::Allocation::from_bytes( + data, + tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi, + Mutability::Not, + ); + let alloc = tcx.create_memory_alloc(tcx.intern_const_alloc(alloc)); + Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc))) + } + fn optim<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut alloc_cache = FxHashMap::default(); + let body_did = body.source.def_id(); + let param_env = tcx.param_env(body_did); + + let blocks = body.basic_blocks.as_mut(); + let local_decls = &mut body.local_decls; + + for bb in blocks { + bb.expand_statements(|st| { + if let StatementKind::Assign(box ( + lhs, + Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + )) = &st.kind + { + let ty = lhs.ty(local_decls, tcx).ty; + + let source_info = st.source_info; + let span = source_info.span; + + let (adt_def, num_variants, alloc_id) = + self.candidate(tcx, param_env, ty, &mut alloc_cache)?; + let alloc = tcx.global_alloc(alloc_id).unwrap_memory(); + + let tmp_ty = tcx.mk_ty(ty::Array( + tcx.types.usize, + Const::from_usize(tcx, num_variants as u64), + )); + + let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span)); + let store_live = Statement { + source_info, + kind: StatementKind::StorageLive(size_array_local), + }; + + let place = Place::from(size_array_local); + let constant_vals = Constant { + span, + user_ty: None, + literal: ConstantKind::Val( + interpret::ConstValue::ByRef { alloc, offset: Size::ZERO }, + tmp_ty, + ), + }; + let rval = Rvalue::Use(Operand::Constant(box (constant_vals))); + + let const_assign = + Statement { source_info, kind: StatementKind::Assign(box (place, rval)) }; + + let discr_place = Place::from( + local_decls + .push(LocalDecl::new(adt_def.repr().discr_type().to_ty(tcx), span)), + ); + + let store_discr = Statement { + source_info, + kind: StatementKind::Assign(box (discr_place, Rvalue::Discriminant(*rhs))), + }; + + let discr_cast_place = + Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); + + let cast_discr = Statement { + source_info, + kind: StatementKind::Assign(box ( + discr_cast_place, + Rvalue::Cast( + CastKind::IntToInt, + Operand::Copy(discr_place), + tcx.types.usize, + ), + )), + }; + + let size_place = + Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span))); + + let store_size = Statement { + source_info, + kind: StatementKind::Assign(box ( + size_place, + Rvalue::Use(Operand::Copy(Place { + local: size_array_local, + projection: tcx.intern_place_elems(&[PlaceElem::Index( + discr_cast_place.local, + )]), + })), + )), + }; + + let dst = + Place::from(local_decls.push(LocalDecl::new(tcx.mk_mut_ptr(ty), span))); + + let dst_ptr = Statement { + source_info, + kind: StatementKind::Assign(box ( + dst, + Rvalue::AddressOf(Mutability::Mut, *lhs), + )), + }; + + let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8); + let dst_cast_place = + Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span))); + + let dst_cast = Statement { + source_info, + kind: StatementKind::Assign(box ( + dst_cast_place, + Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(dst), dst_cast_ty), + )), + }; + + let src = + Place::from(local_decls.push(LocalDecl::new(tcx.mk_imm_ptr(ty), span))); + + let src_ptr = Statement { + source_info, + kind: StatementKind::Assign(box ( + src, + Rvalue::AddressOf(Mutability::Not, *rhs), + )), + }; + + let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8); + let src_cast_place = + Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span))); + + let src_cast = Statement { + source_info, + kind: StatementKind::Assign(box ( + src_cast_place, + Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(src), src_cast_ty), + )), + }; + + let deinit_old = + Statement { source_info, kind: StatementKind::Deinit(box dst) }; + + let copy_bytes = Statement { + source_info, + kind: StatementKind::Intrinsic( + box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src: Operand::Copy(src_cast_place), + dst: Operand::Copy(dst_cast_place), + count: Operand::Copy(size_place), + }), + ), + }; + + let store_dead = Statement { + source_info, + kind: StatementKind::StorageDead(size_array_local), + }; + let iter = [ + store_live, + const_assign, + store_discr, + cast_discr, + store_size, + dst_ptr, + dst_cast, + src_ptr, + src_cast, + deinit_old, + copy_bytes, + store_dead, + ] + .into_iter(); + + st.make_nop(); + Some(iter) + } else { + None + } + }); + } + } +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9070a7368b1..45cd4024c9f 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,6 +1,7 @@ #![allow(rustc::potential_query_instability)] #![feature(box_patterns)] #![feature(drain_filter)] +#![feature(box_syntax)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(min_specialization)] @@ -73,6 +74,7 @@ mod function_item_references; mod generator; mod inline; mod instcombine; +mod large_enums; mod lower_intrinsics; mod lower_slice_len; mod match_branches; @@ -583,6 +585,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &simplify::SimplifyLocals::new("final"), &multiple_return_terminators::MultipleReturnTerminators, &deduplicate_blocks::DeduplicateBlocks, + &large_enums::EnumSizeOpt { discrepancy: 128 }, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, // Dump the end result for testing and debugging purposes. diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 26acd406ed8..30d8511153a 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -318,6 +318,8 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> { // ConstProp will pick up the pieces and replace them by actual constants. StatementKind::Assign(box (place, Rvalue::Use(Operand::Constant(_)))) => { if let Some(final_locals) = self.replacements.place_fragments(place) { + // Put the deaggregated statements *after* the original one. + let location = location.successor_within_block(); for (field, ty, new_local) in final_locals { let rplace = self.tcx.mk_place_field(place, field, ty); let rvalue = Rvalue::Use(Operand::Move(rplace)); diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 088a87ca571..34a4fd02ea6 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -847,9 +847,7 @@ impl<'a> Parser<'a> { 0, ParseError { description: "expected format parameter to occur after `:`".to_owned(), - note: Some( - format!("`?` comes after `:`, try `{}:{}` instead", word, "?").to_owned(), - ), + note: Some(format!("`?` comes after `:`, try `{}:{}` instead", word, "?")), label: "expected `?` to occur after `:`".to_owned(), span: pos.to(pos), secondary_label: None, diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4dea03c1ef6..49309db564e 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -124,7 +124,7 @@ impl QueryContext for QueryCtxt<'_> { }; // Use the `ImplicitCtxt` while we execute the query. - tls::enter_context(&new_icx, |_| { + tls::enter_context(&new_icx, || { rustc_data_structures::stack::ensure_sufficient_stack(compute) }) }) diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 9f875b43731..c9dd75e4d55 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sharded; #[cfg(parallel_compiler)] use rustc_data_structures::sharded::Sharded; -#[cfg(not(parallel_compiler))] use rustc_data_structures::sync::Lock; use rustc_data_structures::sync::WorkerLocal; use rustc_index::vec::{Idx, IndexVec}; @@ -117,6 +116,52 @@ where } } +pub struct SingleCacheSelector; + +impl<'tcx, V: 'tcx> CacheSelector<'tcx, V> for SingleCacheSelector { + type Cache = SingleCache<V> + where + V: Copy; + type ArenaCache = ArenaCache<'tcx, (), V>; +} + +pub struct SingleCache<V> { + cache: Lock<Option<(V, DepNodeIndex)>>, +} + +impl<V> Default for SingleCache<V> { + fn default() -> Self { + SingleCache { cache: Lock::new(None) } + } +} + +impl<V: Copy + Debug> QueryStorage for SingleCache<V> { + type Value = V; + type Stored = V; +} + +impl<V> QueryCache for SingleCache<V> +where + V: Copy + Debug, +{ + type Key = (); + + #[inline(always)] + fn lookup(&self, _key: &()) -> Option<(V, DepNodeIndex)> { + *self.cache.lock() + } + + #[inline] + fn complete(&self, _key: (), value: V, index: DepNodeIndex) -> Self::Stored { + *self.cache.lock() = Some((value.clone(), index)); + value + } + + fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { + self.cache.lock().as_ref().map(|value| f(&(), &value.0, value.1)); + } +} + pub struct ArenaCache<'tcx, K, V> { arena: WorkerLocal<TypedArena<(V, DepNodeIndex)>>, #[cfg(parallel_compiler)] diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index d308af19207..6c0ee2bc2f6 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -8,7 +8,8 @@ pub use self::job::{print_query_stack, QueryInfo, QueryJob, QueryJobId, QueryJob mod caches; pub use self::caches::{ - CacheSelector, DefaultCacheSelector, QueryCache, QueryStorage, VecCacheSelector, + CacheSelector, DefaultCacheSelector, QueryCache, QueryStorage, SingleCacheSelector, + VecCacheSelector, }; mod config; diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index 7c3a0f8f277..d4935b52b10 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] bitflags = "1.2.1" +pulldown-cmark = { version = "0.9.2", default-features = false } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2fb62ce53ba..e74bb0a9a4f 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -95,7 +95,7 @@ impl<'a> Resolver<'a> { /// Reachable macros with block module parents exist due to `#[macro_export] macro_rules!`, /// but they cannot use def-site hygiene, so the assumption holds /// (<https://github.com/rust-lang/rust/pull/77984#issuecomment-712445508>). - pub fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> { + pub(crate) fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> { loop { match self.get_module(def_id) { Some(module) => return module, @@ -104,7 +104,7 @@ impl<'a> Resolver<'a> { } } - pub fn expect_module(&mut self, def_id: DefId) -> Module<'a> { + pub(crate) fn expect_module(&mut self, def_id: DefId) -> Module<'a> { self.get_module(def_id).expect("argument `DefId` is not a module") } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3ca10ac50ba..bd74a010fa3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -8,7 +8,7 @@ use RibKind::*; -use crate::{path_names_to_string, BindingError, Finalize, LexicalScopeBinding}; +use crate::{path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding}; use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult}; use crate::{ResolutionError, Resolver, Segment, UseError}; @@ -24,12 +24,13 @@ use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_lifetime::Set1; use rustc_middle::ty::DefIdTree; use rustc_middle::{bug, span_bug}; +use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint; +use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, SyntaxContext}; use smallvec::{smallvec, SmallVec}; -use rustc_span::source_map::{respan, Spanned}; use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::collections::{hash_map::Entry, BTreeSet}; @@ -493,6 +494,30 @@ impl<'a> PathSource<'a> { } } +/// At this point for most items we can answer whether that item is exported or not, +/// but some items like impls require type information to determine exported-ness, so we make a +/// conservative estimate for them (e.g. based on nominal visibility). +#[derive(Clone, Copy)] +enum MaybeExported<'a> { + Ok(NodeId), + Impl(Option<DefId>), + ImplItem(Result<DefId, &'a Visibility>), +} + +impl MaybeExported<'_> { + fn eval(self, r: &Resolver<'_>) -> bool { + let def_id = match self { + MaybeExported::Ok(node_id) => Some(r.local_def_id(node_id)), + MaybeExported::Impl(Some(trait_def_id)) | MaybeExported::ImplItem(Ok(trait_def_id)) => { + trait_def_id.as_local() + } + MaybeExported::Impl(None) => return true, + MaybeExported::ImplItem(Err(vis)) => return vis.kind.is_pub(), + }; + def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id)) + } +} + #[derive(Default)] struct DiagnosticMetadata<'ast> { /// The current trait's associated items' ident, used for diagnostic suggestions. @@ -620,7 +645,9 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.resolve_arm(arm); } fn visit_block(&mut self, block: &'ast Block) { + let old_macro_rules = self.parent_scope.macro_rules; self.resolve_block(block); + self.parent_scope.macro_rules = old_macro_rules; } fn visit_anon_const(&mut self, constant: &'ast AnonConst) { // We deal with repeat expressions explicitly in `resolve_expr`. @@ -771,6 +798,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { ); } fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) { + self.resolve_doc_links(&foreign_item.attrs, MaybeExported::Ok(foreign_item.id)); match foreign_item.kind { ForeignItemKind::TyAlias(box TyAlias { ref generics, .. }) => { self.with_generic_param_rib( @@ -1159,6 +1187,16 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { }) }); } + + fn visit_variant(&mut self, v: &'ast Variant) { + self.resolve_doc_links(&v.attrs, MaybeExported::Ok(v.id)); + visit::walk_variant(self, v) + } + + fn visit_field_def(&mut self, f: &'ast FieldDef) { + self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id)); + visit::walk_field_def(self, f) + } } impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { @@ -1725,7 +1763,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { !segment.has_generic_args, elided_lifetime_span, ); - err.note("assuming a `'static` lifetime..."); err.emit(); should_lint = false; @@ -2185,6 +2222,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } fn resolve_item(&mut self, item: &'ast Item) { + let mod_inner_docs = + matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs); + if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..)) { + self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + } + let name = item.ident.name; debug!("(resolving item) resolving {} ({:?})", name, item.kind); @@ -2229,7 +2272,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { .. }) => { self.diagnostic_metadata.current_impl_items = Some(impl_items); - self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items); + self.resolve_implementation( + &item.attrs, + generics, + of_trait, + &self_ty, + item.id, + impl_items, + ); self.diagnostic_metadata.current_impl_items = None; } @@ -2274,9 +2324,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ); } - ItemKind::Mod(..) | ItemKind::ForeignMod(_) => { + ItemKind::Mod(..) => { self.with_scope(item.id, |this| { + if mod_inner_docs { + this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + } + let old_macro_rules = this.parent_scope.macro_rules; visit::walk_item(this, item); + // Maintain macro_rules scopes in the same way as during early resolution + // for diagnostics and doc links. + if item.attrs.iter().all(|attr| { + !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape) + }) { + this.parent_scope.macro_rules = old_macro_rules; + } }); } @@ -2309,14 +2370,22 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.future_proof_import(use_tree); } - ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) => { - // do nothing, these are just around to be encoded + ItemKind::MacroDef(ref macro_def) => { + // Maintain macro_rules scopes in the same way as during early resolution + // for diagnostics and doc links. + if macro_def.macro_rules { + let (macro_rules_scope, _) = + self.r.macro_rules_scope(self.r.local_def_id(item.id)); + self.parent_scope.macro_rules = macro_rules_scope; + } } - ItemKind::GlobalAsm(_) => { + ItemKind::ForeignMod(_) | ItemKind::GlobalAsm(_) => { visit::walk_item(self, item); } + ItemKind::ExternCrate(..) => {} + ItemKind::MacCall(_) => panic!("unexpanded macro in resolve!"), } } @@ -2544,6 +2613,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }; for item in trait_items { + self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); match &item.kind { AssocItemKind::Const(_, ty, default) => { self.visit_ty(ty); @@ -2631,6 +2701,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { fn resolve_implementation( &mut self, + attrs: &[ast::Attribute], generics: &'ast Generics, opt_trait_reference: &'ast Option<TraitRef>, self_type: &'ast Ty, @@ -2661,6 +2732,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { opt_trait_reference.as_ref(), self_type, |this, trait_id| { + this.resolve_doc_links(attrs, MaybeExported::Impl(trait_id)); + let item_def_id = this.r.local_def_id(item_id); // Register the trait definitions from here. @@ -2694,7 +2767,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)"); let mut seen_trait_items = Default::default(); for item in impl_items { - this.resolve_impl_item(&**item, &mut seen_trait_items); + this.resolve_impl_item(&**item, &mut seen_trait_items, trait_id); } }); }); @@ -2712,8 +2785,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { &mut self, item: &'ast AssocItem, seen_trait_items: &mut FxHashMap<DefId, Span>, + trait_id: Option<DefId>, ) { use crate::ResolutionError::*; + self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis))); match &item.kind { AssocItemKind::Const(_, ty, default) => { debug!("resolve_implementation AssocItemKind::Const"); @@ -4116,6 +4191,102 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params); } } + + fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> bool { + // FIXME: This caching may be incorrect in case of multiple `macro_rules` + // items with the same name in the same module. + // Also hygiene is not considered. + let mut doc_link_resolutions = std::mem::take(&mut self.r.doc_link_resolutions); + let res = doc_link_resolutions + .entry(self.parent_scope.module.nearest_parent_mod().expect_local()) + .or_default() + .entry((Symbol::intern(path_str), ns)) + .or_insert_with_key(|(path, ns)| { + let res = self.r.resolve_rustdoc_path(path.as_str(), *ns, self.parent_scope); + if let Some(res) = res + && let Some(def_id) = res.opt_def_id() + && !def_id.is_local() + && self.r.session.crate_types().contains(&CrateType::ProcMacro) { + // Encoding foreign def ids in proc macro crate metadata will ICE. + return None; + } + res + }) + .is_some(); + self.r.doc_link_resolutions = doc_link_resolutions; + res + } + + fn resolve_doc_links(&mut self, attrs: &[Attribute], maybe_exported: MaybeExported<'_>) { + match self.r.session.opts.resolve_doc_links { + ResolveDocLinks::None => return, + ResolveDocLinks::ExportedMetadata + if !self.r.session.crate_types().iter().copied().any(CrateType::has_metadata) + || !maybe_exported.eval(self.r) => + { + return; + } + ResolveDocLinks::Exported if !maybe_exported.eval(self.r) => { + return; + } + ResolveDocLinks::ExportedMetadata + | ResolveDocLinks::Exported + | ResolveDocLinks::All => {} + } + + if !attrs.iter().any(|attr| attr.may_have_doc_links()) { + return; + } + + let mut need_traits_in_scope = false; + for path_str in rustdoc::attrs_to_preprocessed_links(attrs) { + // Resolve all namespaces due to no disambiguator or for diagnostics. + let mut any_resolved = false; + let mut need_assoc = false; + for ns in [TypeNS, ValueNS, MacroNS] { + if self.resolve_and_cache_rustdoc_path(&path_str, ns) { + any_resolved = true; + } else if ns != MacroNS { + need_assoc = true; + } + } + + // Resolve all prefixes for type-relative resolution or for diagnostics. + if need_assoc || !any_resolved { + let mut path = &path_str[..]; + while let Some(idx) = path.rfind("::") { + path = &path[..idx]; + need_traits_in_scope = true; + for ns in [TypeNS, ValueNS, MacroNS] { + self.resolve_and_cache_rustdoc_path(path, ns); + } + } + } + } + + if need_traits_in_scope { + // FIXME: hygiene is not considered. + let mut doc_link_traits_in_scope = std::mem::take(&mut self.r.doc_link_traits_in_scope); + doc_link_traits_in_scope + .entry(self.parent_scope.module.nearest_parent_mod().expect_local()) + .or_insert_with(|| { + self.r + .traits_in_scope(None, &self.parent_scope, SyntaxContext::root(), None) + .into_iter() + .filter_map(|tr| { + if !tr.def_id.is_local() + && self.r.session.crate_types().contains(&CrateType::ProcMacro) + { + // Encoding foreign def ids in proc macro crate metadata will ICE. + return None; + } + Some(tr.def_id) + }) + .collect() + }); + self.r.doc_link_traits_in_scope = doc_link_traits_in_scope; + } + } } struct LifetimeCountVisitor<'a, 'b> { @@ -4162,6 +4333,7 @@ impl<'a> Resolver<'a> { pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { visit::walk_crate(&mut LifetimeCountVisitor { r: self }, krate); let mut late_resolution_visitor = LateResolutionVisitor::new(self); + late_resolution_visitor.resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); visit::walk_crate(&mut late_resolution_visitor, krate); for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() { self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label"); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cee0a7f3c20..a9dbb3ca131 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2244,19 +2244,23 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } None => { debug!(?param.ident, ?param.ident.span); - let deletion_span = deletion_span(); - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_LIFETIMES, - param.id, - param.ident.span, - &format!("lifetime parameter `{}` never used", param.ident), - lint::BuiltinLintDiagnostics::SingleUseLifetime { - param_span: param.ident.span, - use_span: None, - deletion_span, - }, - ); + // the give lifetime originates from expanded code so we won't be able to remove it #104432 + let lifetime_only_in_expanded_code = + deletion_span.map(|sp| sp.in_derive_expansion()).unwrap_or(true); + if !lifetime_only_in_expanded_code { + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` never used", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: None, + deletion_span, + }, + ); + } } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1b181b71400..e61e83189c3 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -33,7 +33,7 @@ use rustc_data_structures::sync::{Lrc, RwLock}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed}; use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind}; use rustc_hir::def::Namespace::*; -use rustc_hir::def::{self, CtorOf, DefKind, LifetimeRes, PartialRes}; +use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::definitions::{DefPathData, Definitions}; @@ -78,6 +78,7 @@ mod ident; mod imports; mod late; mod macros; +pub mod rustdoc; enum Weak { Yes, @@ -138,17 +139,17 @@ enum ScopeSet<'a> { /// This struct is currently used only for early resolution (imports and macros), /// but not for late resolution yet. #[derive(Clone, Copy, Debug)] -pub struct ParentScope<'a> { - pub module: Module<'a>, +struct ParentScope<'a> { + module: Module<'a>, expansion: LocalExpnId, - pub macro_rules: MacroRulesScopeRef<'a>, + macro_rules: MacroRulesScopeRef<'a>, derives: &'a [ast::Path], } impl<'a> ParentScope<'a> { /// Creates a parent scope with the passed argument used as the module scope component, /// and other scope components set to default empty values. - pub fn module(module: Module<'a>, resolver: &Resolver<'a>) -> ParentScope<'a> { + fn module(module: Module<'a>, resolver: &Resolver<'a>) -> ParentScope<'a> { ParentScope { module, expansion: LocalExpnId::ROOT, @@ -1046,6 +1047,8 @@ pub struct Resolver<'a> { lifetime_elision_allowed: FxHashSet<NodeId>, effective_visibilities: EffectiveVisibilities, + doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>, + doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1374,6 +1377,8 @@ impl<'a> Resolver<'a> { confused_type_with_std_module: Default::default(), lifetime_elision_allowed: Default::default(), effective_visibilities: Default::default(), + doc_link_resolutions: Default::default(), + doc_link_traits_in_scope: Default::default(), }; let root_parent_scope = ParentScope::module(graph_root, &resolver); @@ -1450,6 +1455,8 @@ impl<'a> Resolver<'a> { proc_macros, confused_type_with_std_module, registered_tools: self.registered_tools, + doc_link_resolutions: self.doc_link_resolutions, + doc_link_traits_in_scope: self.doc_link_traits_in_scope, }; let ast_lowering = ty::ResolverAstLowering { legacy_const_generic_args: self.legacy_const_generic_args, @@ -1494,6 +1501,8 @@ impl<'a> Resolver<'a> { confused_type_with_std_module: self.confused_type_with_std_module.clone(), registered_tools: self.registered_tools.clone(), effective_visibilities: self.effective_visibilities.clone(), + doc_link_resolutions: self.doc_link_resolutions.clone(), + doc_link_traits_in_scope: self.doc_link_traits_in_scope.clone(), }; let ast_lowering = ty::ResolverAstLowering { legacy_const_generic_args: self.legacy_const_generic_args.clone(), @@ -1575,7 +1584,7 @@ impl<'a> Resolver<'a> { }); } - pub fn traits_in_scope( + fn traits_in_scope( &mut self, current_trait: Option<Module<'a>>, parent_scope: &ParentScope<'a>, @@ -1927,7 +1936,7 @@ impl<'a> Resolver<'a> { /// isn't something that can be returned because it can't be made to live that long, /// and also it's a private type. Fortunately rustdoc doesn't need to know the error, /// just that an error occurred. - pub fn resolve_rustdoc_path( + fn resolve_rustdoc_path( &mut self, path_str: &str, ns: Namespace, @@ -1960,16 +1969,6 @@ impl<'a> Resolver<'a> { } /// For rustdoc. - /// For local modules returns only reexports, for external modules returns all children. - pub fn module_children_or_reexports(&self, def_id: DefId) -> Vec<ModChild> { - if let Some(def_id) = def_id.as_local() { - self.reexport_map.get(&def_id).cloned().unwrap_or_default() - } else { - self.cstore().module_children_untracked(def_id, self.session).collect() - } - } - - /// For rustdoc. pub fn macro_rules_scope(&self, def_id: LocalDefId) -> (MacroRulesScopeRef<'a>, Res) { let scope = *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item"); match scope.get() { @@ -1978,11 +1977,6 @@ impl<'a> Resolver<'a> { } } - /// For rustdoc. - pub fn get_partial_res(&self, node_id: NodeId) -> Option<PartialRes> { - self.partial_res_map.get(&node_id).copied() - } - /// Retrieves the span of the given `DefId` if `DefId` is in the local crate. #[inline] pub fn opt_span(&self, def_id: DefId) -> Option<Span> { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b5b1602c5e0..0c2e8be0498 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -568,7 +568,7 @@ impl<'a> Resolver<'a> { Ok((ext, res)) } - pub fn resolve_macro_path( + pub(crate) fn resolve_macro_path( &mut self, path: &ast::Path, kind: Option<MacroKind>, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs new file mode 100644 index 00000000000..a967f4b940c --- /dev/null +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -0,0 +1,369 @@ +use pulldown_cmark::{BrokenLink, Event, Options, Parser, Tag}; +use rustc_ast as ast; +use rustc_ast::util::comments::beautify_doc_string; +use rustc_data_structures::fx::FxHashMap; +use rustc_span::def_id::DefId; +use rustc_span::symbol::{kw, Symbol}; +use rustc_span::Span; +use std::cell::RefCell; +use std::{cmp, mem}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum DocFragmentKind { + /// A doc fragment created from a `///` or `//!` doc comment. + SugaredDoc, + /// A doc fragment created from a "raw" `#[doc=""]` attribute. + RawDoc, +} + +/// A portion of documentation, extracted from a `#[doc]` attribute. +/// +/// Each variant contains the line number within the complete doc-comment where the fragment +/// starts, as well as the Span where the corresponding doc comment or attribute is located. +/// +/// Included files are kept separate from inline doc comments so that proper line-number +/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are +/// kept separate because of issue #42760. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct DocFragment { + pub span: Span, + /// The module this doc-comment came from. + /// + /// This allows distinguishing between the original documentation and a pub re-export. + /// If it is `None`, the item was not re-exported. + pub parent_module: Option<DefId>, + pub doc: Symbol, + pub kind: DocFragmentKind, + pub indent: usize, +} + +#[derive(Clone, Copy, Debug)] +pub enum MalformedGenerics { + /// This link has unbalanced angle brackets. + /// + /// For example, `Vec<T` should trigger this, as should `Vec<T>>`. + UnbalancedAngleBrackets, + /// The generics are not attached to a type. + /// + /// For example, `<T>` should trigger this. + /// + /// This is detected by checking if the path is empty after the generics are stripped. + MissingType, + /// The link uses fully-qualified syntax, which is currently unsupported. + /// + /// For example, `<Vec as IntoIterator>::into_iter` should trigger this. + /// + /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside + /// angle brackets. + HasFullyQualifiedSyntax, + /// The link has an invalid path separator. + /// + /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not** + /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be + /// called. + /// + /// Note that this will also **not** be triggered if the invalid path separator is inside angle + /// brackets because rustdoc mostly ignores what's inside angle brackets (except for + /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)). + /// + /// This is detected by checking if there is a colon followed by a non-colon in the link. + InvalidPathSeparator, + /// The link has too many angle brackets. + /// + /// For example, `Vec<<T>>` should trigger this. + TooManyAngleBrackets, + /// The link has empty angle brackets. + /// + /// For example, `Vec<>` should trigger this. + EmptyAngleBrackets, +} + +/// Removes excess indentation on comments in order for the Markdown +/// to be parsed correctly. This is necessary because the convention for +/// writing documentation is to provide a space between the /// or //! marker +/// and the doc text, but Markdown is whitespace-sensitive. For example, +/// a block of text with four-space indentation is parsed as a code block, +/// so if we didn't unindent comments, these list items +/// +/// /// A list: +/// /// +/// /// - Foo +/// /// - Bar +/// +/// would be parsed as if they were in a code block, which is likely not what the user intended. +pub fn unindent_doc_fragments(docs: &mut [DocFragment]) { + // `add` is used in case the most common sugared doc syntax is used ("/// "). The other + // fragments kind's lines are never starting with a whitespace unless they are using some + // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, + // we need to take into account the fact that the minimum indent minus one (to take this + // whitespace into account). + // + // For example: + // + // /// hello! + // #[doc = "another"] + // + // In this case, you want "hello! another" and not "hello! another". + let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind) + && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc) + { + // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to + // "decide" how much the minimum indent will be. + 1 + } else { + 0 + }; + + // `min_indent` is used to know how much whitespaces from the start of each lines must be + // removed. Example: + // + // /// hello! + // #[doc = "another"] + // + // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum + // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 + // (5 - 1) whitespaces. + let Some(min_indent) = docs + .iter() + .map(|fragment| { + fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { + if line.chars().all(|c| c.is_whitespace()) { + min_indent + } else { + // Compare against either space or tab, ignoring whether they are + // mixed or not. + let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); + cmp::min(min_indent, whitespace) + + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } + } + }) + }) + .min() + else { + return; + }; + + for fragment in docs { + if fragment.doc == kw::Empty { + continue; + } + + let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { + min_indent - add + } else { + min_indent + }; + + fragment.indent = min_indent; + } +} + +/// The goal of this function is to apply the `DocFragment` transformation that is required when +/// transforming into the final Markdown, which is applying the computed indent to each line in +/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`). +/// +/// Note: remove the trailing newline where appropriate +pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) { + let s = frag.doc.as_str(); + let mut iter = s.lines(); + if s.is_empty() { + out.push('\n'); + return; + } + while let Some(line) = iter.next() { + if line.chars().any(|c| !c.is_whitespace()) { + assert!(line.len() >= frag.indent); + out.push_str(&line[frag.indent..]); + } else { + out.push_str(line); + } + out.push('\n'); + } +} + +pub fn attrs_to_doc_fragments<'a>( + attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>, + doc_only: bool, +) -> (Vec<DocFragment>, ast::AttrVec) { + let mut doc_fragments = Vec::new(); + let mut other_attrs = ast::AttrVec::new(); + for (attr, parent_module) in attrs { + if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { + let doc = beautify_doc_string(doc_str, comment_kind); + let kind = if attr.is_doc_comment() { + DocFragmentKind::SugaredDoc + } else { + DocFragmentKind::RawDoc + }; + let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 }; + doc_fragments.push(fragment); + } else if !doc_only { + other_attrs.push(attr.clone()); + } + } + + unindent_doc_fragments(&mut doc_fragments); + + (doc_fragments, other_attrs) +} + +/// Return the doc-comments on this item, grouped by the module they came from. +/// The module can be different if this is a re-export with added documentation. +/// +/// The last newline is not trimmed so the produced strings are reusable between +/// early and late doc link resolution regardless of their position. +pub fn prepare_to_doc_link_resolution( + doc_fragments: &[DocFragment], +) -> FxHashMap<Option<DefId>, String> { + let mut res = FxHashMap::default(); + for fragment in doc_fragments { + let out_str = res.entry(fragment.parent_module).or_default(); + add_doc_fragment(out_str, fragment); + } + res +} + +/// Options for rendering Markdown in the main body of documentation. +pub fn main_body_opts() -> Options { + Options::ENABLE_TABLES + | Options::ENABLE_FOOTNOTES + | Options::ENABLE_STRIKETHROUGH + | Options::ENABLE_TASKLISTS + | Options::ENABLE_SMART_PUNCTUATION +} + +fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, MalformedGenerics> { + let mut stripped_segment = String::new(); + let mut param_depth = 0; + + let mut latest_generics_chunk = String::new(); + + for c in segment { + if c == '<' { + param_depth += 1; + latest_generics_chunk.clear(); + } else if c == '>' { + param_depth -= 1; + if latest_generics_chunk.contains(" as ") { + // The segment tries to use fully-qualified syntax, which is currently unsupported. + // Give a helpful error message instead of completely ignoring the angle brackets. + return Err(MalformedGenerics::HasFullyQualifiedSyntax); + } + } else { + if param_depth == 0 { + stripped_segment.push(c); + } else { + latest_generics_chunk.push(c); + } + } + } + + if param_depth == 0 { + Ok(stripped_segment) + } else { + // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>` + Err(MalformedGenerics::UnbalancedAngleBrackets) + } +} + +pub fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGenerics> { + if !path_str.contains(['<', '>']) { + return Ok(path_str.to_string()); + } + let mut stripped_segments = vec![]; + let mut path = path_str.chars().peekable(); + let mut segment = Vec::new(); + + while let Some(chr) = path.next() { + match chr { + ':' => { + if path.next_if_eq(&':').is_some() { + let stripped_segment = + strip_generics_from_path_segment(mem::take(&mut segment))?; + if !stripped_segment.is_empty() { + stripped_segments.push(stripped_segment); + } + } else { + return Err(MalformedGenerics::InvalidPathSeparator); + } + } + '<' => { + segment.push(chr); + + match path.next() { + Some('<') => { + return Err(MalformedGenerics::TooManyAngleBrackets); + } + Some('>') => { + return Err(MalformedGenerics::EmptyAngleBrackets); + } + Some(chr) => { + segment.push(chr); + + while let Some(chr) = path.next_if(|c| *c != '>') { + segment.push(chr); + } + } + None => break, + } + } + _ => segment.push(chr), + } + trace!("raw segment: {:?}", segment); + } + + if !segment.is_empty() { + let stripped_segment = strip_generics_from_path_segment(segment)?; + if !stripped_segment.is_empty() { + stripped_segments.push(stripped_segment); + } + } + + debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments); + + let stripped_path = stripped_segments.join("::"); + + if !stripped_path.is_empty() { Ok(stripped_path) } else { Err(MalformedGenerics::MissingType) } +} + +/// Returns whether the first doc-comment is an inner attribute. +/// +//// If there are no doc-comments, return true. +/// FIXME(#78591): Support both inner and outer attributes on the same item. +pub fn inner_docs(attrs: &[ast::Attribute]) -> bool { + attrs.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == ast::AttrStyle::Inner) +} + +/// 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 { + let link = link.replace('`', ""); + let link = link.split('#').next().unwrap(); + let link = link.rsplit('@').next().unwrap(); + let link = link.strip_suffix("()").unwrap_or(link); + 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()) +} + +/// 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> { + 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(); + + let links = RefCell::new(Vec::new()); + let mut callback = |link: BrokenLink<'_>| { + links.borrow_mut().push(preprocess_link(&link.reference)); + None + }; + for event in Parser::new_with_broken_link_callback(&doc, main_body_opts(), Some(&mut callback)) + { + if let Event::Start(Tag::Link(_, dest, _)) = event { + links.borrow_mut().push(preprocess_link(&dest)); + } + } + links.into_inner() +} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 973d860118e..e8bc19f88e3 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -174,6 +174,25 @@ pub enum InstrumentCoverage { Off, } +/// Settings for `-Z instrument-xray` flag. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub struct InstrumentXRay { + /// `-Z instrument-xray=always`, force instrumentation + pub always: bool, + /// `-Z instrument-xray=never`, disable instrumentation + pub never: bool, + /// `-Z instrument-xray=ignore-loops`, ignore presence of loops, + /// instrument functions based only on instruction count + pub ignore_loops: bool, + /// `-Z instrument-xray=instruction-threshold=N`, explicitly set instruction threshold + /// for instrumentation, or `None` to use compiler's default + pub instruction_threshold: Option<usize>, + /// `-Z instrument-xray=skip-entry`, do not instrument function entry + pub skip_entry: bool, + /// `-Z instrument-xray=skip-exit`, do not instrument function exit + pub skip_exit: bool, +} + #[derive(Clone, PartialEq, Hash, Debug)] pub enum LinkerPluginLto { LinkerPlugin(PathBuf), @@ -400,6 +419,18 @@ pub enum TrimmedDefPaths { GoodPath, } +#[derive(Clone, Hash)] +pub enum ResolveDocLinks { + /// Do not resolve doc links. + None, + /// Resolve doc links on exported items only for crate types that have metadata. + ExportedMetadata, + /// Resolve doc links on exported items. + Exported, + /// Resolve doc links on all items. + All, +} + /// Use tree-based collections to cheaply get a deterministic `Hash` implementation. /// *Do not* switch `BTreeMap` out for an unsorted container type! That would break /// dependency tracking for command-line arguments. Also only hash keys, since tracking @@ -769,6 +800,7 @@ impl Default for Options { unstable_features: UnstableFeatures::Disallow, debug_assertions: true, actually_rustdoc: false, + resolve_doc_links: ResolveDocLinks::None, trimmed_def_paths: TrimmedDefPaths::default(), cli_forced_codegen_units: None, cli_forced_local_thinlto_off: false, @@ -864,6 +896,15 @@ pub enum CrateType { ProcMacro, } +impl CrateType { + pub fn has_metadata(self) -> bool { + match self { + CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true, + CrateType::Executable | CrateType::Cdylib | CrateType::Staticlib => false, + } + } +} + #[derive(Clone, Hash, Debug, PartialEq, Eq)] pub enum Passes { Some(Vec<String>), @@ -2543,6 +2584,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { libs, debug_assertions, actually_rustdoc: false, + resolve_doc_links: ResolveDocLinks::ExportedMetadata, trimmed_def_paths: TrimmedDefPaths::default(), cli_forced_codegen_units: codegen_units, cli_forced_local_thinlto_off: disable_local_thinlto, @@ -2805,9 +2847,10 @@ impl PpMode { pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, - InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, - OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind, - SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, + InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, + OomStrategy, OptLevel, OutputType, OutputTypes, Passes, ResolveDocLinks, + SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, + TraitSolver, TrimmedDefPaths, }; use crate::lint; use crate::options::WasiExecModel; @@ -2876,6 +2919,7 @@ pub(crate) mod dep_tracking { CodeModel, TlsModel, InstrumentCoverage, + InstrumentXRay, CrateType, MergeFunctions, PanicStrategy, @@ -2893,6 +2937,7 @@ pub(crate) mod dep_tracking { TargetTriple, Edition, LinkerPluginLto, + ResolveDocLinks, SplitDebuginfo, SplitDwarfKind, StackProtector, diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 8e8fba5e236..c851145440b 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -72,6 +72,12 @@ pub struct ProfileSampleUseFileDoesNotExist<'a> { pub struct TargetRequiresUnwindTables; #[derive(Diagnostic)] +#[diag(session_instrumentation_not_supported)] +pub struct InstrumentationNotSupported { + pub us: String, +} + +#[derive(Diagnostic)] #[diag(session_sanitizer_not_supported)] pub struct SanitizerNotSupported { pub us: String, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 61cb81aec3d..81f7f6d72ae 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,7 +4,7 @@ use crate::early_error; use crate::lint; use crate::search_paths::SearchPath; use crate::utils::NativeLib; -use rustc_errors::LanguageIdentifier; +use rustc_errors::{LanguageIdentifier, TerminalUrl}; use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet}; use rustc_target::spec::{ RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel, @@ -169,6 +169,8 @@ top_level_options!( /// is currently just a hack and will be removed eventually, so please /// try to not rely on this too much. actually_rustdoc: bool [TRACKED], + /// Whether name resolver should resolve documentation links. + resolve_doc_links: ResolveDocLinks [TRACKED], /// Control path trimming. trimmed_def_paths: TrimmedDefPaths [TRACKED], @@ -349,7 +351,7 @@ fn build_options<O: Default>( #[allow(non_upper_case_globals)] mod desc { pub const parse_no_flag: &str = "no value"; - pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `n`, `no`, or `off`"; + pub const parse_bool: &str = "one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false`"; pub const parse_opt_bool: &str = parse_bool; pub const parse_string: &str = "a string"; pub const parse_opt_string: &str = parse_string; @@ -380,6 +382,7 @@ mod desc { pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`"; + pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; pub const parse_trait_solver: &str = @@ -399,6 +402,8 @@ mod desc { pub const parse_code_model: &str = "one of supported code models (`rustc --print code-models`)"; pub const parse_tls_model: &str = "one of supported TLS models (`rustc --print tls-models`)"; pub const parse_target_feature: &str = parse_string; + pub const parse_terminal_url: &str = + "either a boolean (`yes`, `no`, `on`, `off`, etc), or `auto`"; pub const parse_wasi_exec_model: &str = "either `command` or `reactor`"; pub const parse_split_debuginfo: &str = "one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)"; @@ -432,11 +437,11 @@ mod parse { /// Use this for any boolean option that has a static default. pub(crate) fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool { match v { - Some("y") | Some("yes") | Some("on") | None => { + Some("y") | Some("yes") | Some("on") | Some("true") | None => { *slot = true; true } - Some("n") | Some("no") | Some("off") => { + Some("n") | Some("no") | Some("off") | Some("false") => { *slot = false; true } @@ -449,11 +454,11 @@ mod parse { /// other factors, such as other options, or target options.) pub(crate) fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool { match v { - Some("y") | Some("yes") | Some("on") | None => { + Some("y") | Some("yes") | Some("on") | Some("true") | None => { *slot = Some(true); true } - Some("n") | Some("no") | Some("off") => { + Some("n") | Some("no") | Some("off") | Some("false") => { *slot = Some(false); true } @@ -869,6 +874,68 @@ mod parse { true } + pub(crate) fn parse_instrument_xray( + slot: &mut Option<InstrumentXRay>, + v: Option<&str>, + ) -> bool { + if v.is_some() { + let mut bool_arg = None; + if parse_opt_bool(&mut bool_arg, v) { + *slot = if bool_arg.unwrap() { Some(InstrumentXRay::default()) } else { None }; + return true; + } + } + + let mut options = slot.get_or_insert_default(); + let mut seen_always = false; + let mut seen_never = false; + let mut seen_ignore_loops = false; + let mut seen_instruction_threshold = false; + let mut seen_skip_entry = false; + let mut seen_skip_exit = false; + for option in v.into_iter().map(|v| v.split(',')).flatten() { + match option { + "always" if !seen_always && !seen_never => { + options.always = true; + options.never = false; + seen_always = true; + } + "never" if !seen_never && !seen_always => { + options.never = true; + options.always = false; + seen_never = true; + } + "ignore-loops" if !seen_ignore_loops => { + options.ignore_loops = true; + seen_ignore_loops = true; + } + option + if option.starts_with("instruction-threshold") + && !seen_instruction_threshold => + { + let Some(("instruction-threshold", n)) = option.split_once('=') else { + return false; + }; + match n.parse() { + Ok(n) => options.instruction_threshold = Some(n), + Err(_) => return false, + } + seen_instruction_threshold = true; + } + "skip-entry" if !seen_skip_entry => { + options.skip_entry = true; + seen_skip_entry = true; + } + "skip-exit" if !seen_skip_exit => { + options.skip_exit = true; + seen_skip_exit = true; + } + _ => return false, + } + } + true + } + pub(crate) fn parse_treat_err_as_bug(slot: &mut Option<NonZeroUsize>, v: Option<&str>) -> bool { match v { Some(s) => { @@ -979,6 +1046,16 @@ mod parse { true } + pub(crate) fn parse_terminal_url(slot: &mut TerminalUrl, v: Option<&str>) -> bool { + *slot = match v { + Some("on" | "" | "yes" | "y") | None => TerminalUrl::Yes, + Some("off" | "no" | "n") => TerminalUrl::No, + Some("auto") => TerminalUrl::Auto, + _ => return false, + }; + true + } + pub(crate) fn parse_symbol_mangling_version( slot: &mut Option<SymbolManglingVersion>, v: Option<&str>, @@ -1397,6 +1474,16 @@ options! { `=off` (default)"), instrument_mcount: bool = (false, parse_bool, [TRACKED], "insert function instrument code for mcount-based tracing (default: no)"), + instrument_xray: Option<InstrumentXRay> = (None, parse_instrument_xray, [TRACKED], + "insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas."), keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "keep hygiene data after analysis (default: no)"), layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED], @@ -1600,6 +1687,8 @@ options! { "show extended diagnostic help (default: no)"), temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED], "the directory the intermediate files are written to"), + terminal_urls: TerminalUrl = (TerminalUrl::No, parse_terminal_url, [UNTRACKED], + "use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output"), #[rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field")] thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 8a0176f6391..e608b9fe0b3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -24,6 +24,7 @@ use rustc_errors::registry::Registry; use rustc_errors::{ error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted, + TerminalUrl, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -156,7 +157,7 @@ pub struct Session { /// `-C metadata` arguments passed to the compiler. Its value forms a unique /// global identifier for the crate. It is used to allow multiple crates /// with the same name to coexist. See the - /// `rustc_codegen_llvm::back::symbol_names` module for more information. + /// `rustc_symbol_mangling` crate for more information. pub stable_crate_id: OnceCell<StableCrateId>, features: OnceCell<rustc_feature::Features>, @@ -1273,6 +1274,19 @@ fn default_emitter( ) -> Box<dyn Emitter + sync::Send> { let macro_backtrace = sopts.unstable_opts.macro_backtrace; let track_diagnostics = sopts.unstable_opts.track_diagnostics; + let terminal_url = match sopts.unstable_opts.terminal_urls { + TerminalUrl::Auto => { + match (std::env::var("COLORTERM").as_deref(), std::env::var("TERM").as_deref()) { + (Ok("truecolor"), Ok("xterm-256color")) + if sopts.unstable_features.is_nightly_build() => + { + TerminalUrl::Yes + } + _ => TerminalUrl::No, + } + } + t => t, + }; match sopts.error_format { config::ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); @@ -1297,6 +1311,7 @@ fn default_emitter( sopts.diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, ); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } @@ -1312,6 +1327,7 @@ fn default_emitter( sopts.diagnostic_width, macro_backtrace, track_diagnostics, + terminal_url, ) .ui_testing(sopts.unstable_opts.ui_testing), ), @@ -1589,6 +1605,10 @@ fn validate_commandline_args_with_session_available(sess: &Session) { { sess.emit_err(errors::SplitDebugInfoUnstablePlatform { debuginfo: sess.split_debuginfo() }); } + + if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray { + sess.emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); + } } /// Holds data on the current incremental compilation session, if there is one. @@ -1624,6 +1644,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler None, false, false, + TerminalUrl::No, )) } config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic( @@ -1634,6 +1655,7 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler None, false, false, + TerminalUrl::No, )), }; rustc_errors::Handler::with_emitter(true, None, emitter) diff --git a/compiler/rustc_span/src/lev_distance/tests.rs b/compiler/rustc_span/src/lev_distance/tests.rs index b17d6588c9f..ed03b22c61f 100644 --- a/compiler/rustc_span/src/lev_distance/tests.rs +++ b/compiler/rustc_span/src/lev_distance/tests.rs @@ -2,9 +2,8 @@ use super::*; #[test] fn test_lev_distance() { - use std::char::{from_u32, MAX}; // Test bytelength agnosticity - for c in (0..MAX as u32).filter_map(from_u32).map(|i| i.to_string()) { + for c in (0..char::MAX as u32).filter_map(char::from_u32).map(|i| i.to_string()) { assert_eq!(lev_distance(&c[..], &c[..], usize::MAX), Some(0)); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1ced75cccbb..ef417d3e0a7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1049,6 +1049,7 @@ symbols! { overlapping_marker_traits, owned_box, packed, + packed_bundled_libs, panic, panic_2015, panic_2021, diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index c9b4ab0a38d..710f3826403 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -299,6 +299,7 @@ fn encode_region<'tcx>( RegionKind::ReEarlyBound(..) | RegionKind::ReFree(..) | RegionKind::ReStatic + | RegionKind::ReError(_) | RegionKind::ReVar(..) | RegionKind::RePlaceholder(..) => { bug!("encode_region: unexpected `{:?}`", region.kind()); diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 00d1ff5ceed..c58b6a24ab5 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -540,7 +540,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { match predicate.as_ref().skip_binder() { ty::ExistentialPredicate::Trait(trait_ref) => { // Use a type that can't appear in defaults of type parameters. - let dummy_self = cx.tcx.mk_ty_infer(ty::FreshTy(0)); + let dummy_self = cx.tcx.mk_fresh_ty(0); let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self); cx = cx.print_def_path(trait_ref.def_id, trait_ref.substs)?; } diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index c85f7f62a42..daa946ccd51 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -19,6 +19,7 @@ pub fn target() -> Target { | SanitizerSet::MEMTAG | SanitizerSet::SHADOWCALLSTACK | SanitizerSet::ADDRESS, + supports_xray: true, ..super::android_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs index 3006044d54a..36d54f1d7cc 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -17,6 +17,7 @@ pub fn target() -> Target { | SanitizerSet::MEMTAG | SanitizerSet::THREAD | SanitizerSet::HWADDRESS, + supports_xray: true, ..super::linux_gnu_base::opts() }, } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs index 002d0dac2a6..9c299fed6be 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs @@ -3,6 +3,7 @@ use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.max_atomic_width = Some(128); + base.supports_xray = true; Target { llvm_target: "aarch64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a094c2c5452..bc1920e3424 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1718,6 +1718,9 @@ pub struct TargetOptions { /// The ABI of entry function. /// Default value is `Conv::C`, i.e. C call convention pub entry_abi: Conv, + + /// Whether the target supports XRay instrumentation. + pub supports_xray: bool, } /// Add arguments for the given flavor and also for its "twin" flavors @@ -1937,6 +1940,7 @@ impl Default for TargetOptions { supports_stack_protector: true, entry_name: "main".into(), entry_abi: Conv::C, + supports_xray: false, } } } @@ -2592,6 +2596,7 @@ impl Target { key!(supports_stack_protector, bool); key!(entry_name); key!(entry_abi, Conv)?; + key!(supports_xray, bool); if base.is_builtin { // This can cause unfortunate ICEs later down the line. @@ -2845,6 +2850,7 @@ impl ToJson for Target { target_option_val!(supports_stack_protector); target_option_val!(entry_name); target_option_val!(entry_abi); + target_option_val!(supports_xray); if let Some(abi) = self.default_adjusted_cabi { d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index 9c913784855..a3bdb5f5465 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -8,6 +8,7 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; + base.supports_xray = true; Target { llvm_target: "x86_64-linux-android".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index 98988ab3595..b41e5842aad 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -8,6 +8,7 @@ pub fn target() -> Target { base.stack_probes = StackProbeType::X86; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-freebsd".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs index a91ab365b66..9af1049b870 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-linux-gnu".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 9087dc3df60..bf4cf7d7bec 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -12,6 +12,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index 64ae425d8c0..74c434935ba 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -11,6 +11,7 @@ pub fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-netbsd".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index 66b8e20226f..8e4d42a0aca 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -6,6 +6,7 @@ pub fn target() -> Target { base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.stack_probes = StackProbeType::X86; + base.supports_xray = true; Target { llvm_target: "x86_64-unknown-openbsd".into(), diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3f863038efb..d3eba43b47e 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -24,3 +24,4 @@ rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +itertools = "0.10.1" diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index 8525b96c0c2..126ec60b3d6 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -3,7 +3,8 @@ use super::infcx_ext::InferCtxtExt; #[cfg(doc)] use super::trait_goals::structural_traits::*; -use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; +use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult}; +use itertools::Itertools; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate_predicates; @@ -399,10 +400,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Alias(_, alias_ty) => alias_ty, }; - for (assumption, _) in self - .tcx() - .bound_explicit_item_bounds(alias_ty.def_id) - .subst_iter_copied(self.tcx(), alias_ty.substs) + for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs) { match G::consider_assumption(self, goal, assumption) { Ok(result) => { @@ -462,4 +460,79 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } + + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn merge_candidates_and_discard_reservation_impls( + &mut self, + mut candidates: Vec<Candidate<'tcx>>, + ) -> QueryResult<'tcx> { + match candidates.len() { + 0 => return Err(NoSolution), + 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result), + _ => {} + } + + if candidates.len() > 1 { + let mut i = 0; + 'outer: while i < candidates.len() { + for j in (0..candidates.len()).filter(|&j| i != j) { + if self.trait_candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + ) { + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); + candidates.swap_remove(i); + continue 'outer; + } + } + + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); + i += 1; + } + + // If there are *STILL* multiple candidates that have *different* response + // results, give up and report ambiguity. + if candidates.len() > 1 && !candidates.iter().map(|cand| cand.result).all_equal() { + let certainty = if candidates.iter().all(|x| { + matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow)) + }) { + Certainty::Maybe(MaybeCause::Overflow) + } else { + Certainty::AMBIGUOUS + }; + return self.make_canonical_response(certainty); + } + } + + // FIXME: What if there are >1 candidates left with the same response, and one is a reservation impl? + Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result) + } + + fn trait_candidate_should_be_dropped_in_favor_of( + &self, + candidate: &Candidate<'tcx>, + other: &Candidate<'tcx>, + ) -> bool { + // FIXME: implement this + match (candidate.source, other.source) { + (CandidateSource::Impl(_), _) + | (CandidateSource::ParamEnv(_), _) + | (CandidateSource::AliasBound, _) + | (CandidateSource::BuiltinImpl, _) => false, + } + } + + fn discard_reservation_impl(&self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> { + if let CandidateSource::Impl(def_id) = candidate.source { + if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) { + debug!("Selected reservation impl"); + // We assemble all candidates inside of a probe so by + // making a new canonical response here our result will + // have no constraints. + candidate.result = self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(); + } + } + + candidate + } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index c1936b7dbe4..a55b984fd63 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -73,6 +73,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { MismatchedProjectionTypes { err: TypeError::Mismatch }, ) } + ty::PredicateKind::AliasEq(_, _) => { + FulfillmentErrorCode::CodeProjectionError( + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } ty::PredicateKind::Subtype(pred) => { let (a, b) = infcx.instantiate_binder_with_placeholders( goal.predicate.kind().rebind((pred.a, pred.b)), diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 9f092b6018f..e56588c58bd 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -42,6 +42,8 @@ mod trait_goals; pub use fulfill::FulfillmentCtxt; +use self::infcx_ext::InferCtxtExt; + /// A goal is a statement, i.e. `predicate`, we want to prove /// given some assumptions, i.e. `param_env`. /// @@ -81,6 +83,21 @@ pub struct Response<'tcx> { pub certainty: Certainty, } +trait CanonicalResponseExt { + fn has_no_inference_or_external_constraints(&self) -> bool; +} + +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.opaque_types.is_empty() + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)] pub enum Certainty { Yes, @@ -302,6 +319,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } + ty::PredicateKind::AliasEq(lhs, rhs) => { + self.compute_alias_eq_goal(Goal { param_env, predicate: (lhs, rhs) }) + } } } else { let kind = self.infcx.instantiate_binder_with_placeholders(kind); @@ -398,6 +418,63 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { None => self.make_canonical_response(Certainty::AMBIGUOUS), } } + + #[instrument(level = "debug", skip(self), ret)] + fn compute_alias_eq_goal( + &mut self, + goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>)>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + + let evaluate_normalizes_to = |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other| { + debug!("evaluate_normalizes_to(alias={:?}, other={:?})", alias, other); + let r = ecx.infcx.probe(|_| { + let (_, certainty) = ecx.evaluate_goal(goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: alias, + term: other, + }), + ))?; + ecx.make_canonical_response(certainty) + }); + debug!("evaluate_normalizes_to(..) -> {:?}", r); + r + }; + + if goal.predicate.0.is_infer() || goal.predicate.1.is_infer() { + bug!( + "`AliasEq` goal with an infer var on lhs or rhs which should have been instantiated" + ); + } + + match ( + goal.predicate.0.to_alias_term_no_opaque(tcx), + goal.predicate.1.to_alias_term_no_opaque(tcx), + ) { + (None, None) => bug!("`AliasEq` goal without an alias on either lhs or rhs"), + (Some(alias), None) => evaluate_normalizes_to(self, alias, goal.predicate.1), + (None, Some(alias)) => evaluate_normalizes_to(self, alias, goal.predicate.0), + (Some(alias_lhs), Some(alias_rhs)) => { + debug!("compute_alias_eq_goal: both sides are aliases"); + + let mut candidates = Vec::with_capacity(3); + + // Evaluate all 3 potential candidates for the alias' being equal + candidates.push(evaluate_normalizes_to(self, alias_lhs, goal.predicate.1)); + candidates.push(evaluate_normalizes_to(self, alias_rhs, goal.predicate.0)); + candidates.push(self.infcx.probe(|_| { + debug!("compute_alias_eq_goal: alias defids are equal, equating substs"); + let nested_goals = self.infcx.eq(goal.param_env, alias_lhs, alias_rhs)?; + self.evaluate_all_and_make_canonical_response(nested_goals) + })); + + debug!(?candidates); + + self.try_merge_responses(candidates.into_iter()) + } + } + } } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -449,6 +526,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) -> QueryResult<'tcx> { self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty)) } + + fn try_merge_responses( + &mut self, + responses: impl Iterator<Item = QueryResult<'tcx>>, + ) -> QueryResult<'tcx> { + let candidates = responses.into_iter().flatten().collect::<Box<[_]>>(); + + if candidates.is_empty() { + return Err(NoSolution); + } + + // FIXME(-Ztreat-solver=next): We should instead try to find a `Certainty::Yes` response with + // a subset of the constraints that all the other responses have. + let one = candidates[0]; + if candidates[1..].iter().all(|resp| resp == &one) { + return Ok(one); + } + + if let Some(response) = candidates.iter().find(|response| { + response.value.certainty == Certainty::Yes + && response.has_no_inference_or_external_constraints() + }) { + return Ok(response.clone()); + } + + let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { + certainty.unify_and(response.value.certainty) + }); + // FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the + // responses and use that for the constraints of this ambiguous response. + let response = self.make_canonical_response(certainty); + if let Ok(response) = &response { + assert!(response.has_no_inference_or_external_constraints()); + } + + response + } } #[instrument(level = "debug", skip(infcx), ret)] diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e3ec71d1b4f..4fea49893a6 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,6 +1,6 @@ use crate::traits::{specialization_graph, translate_substs}; -use super::assembly::{self, Candidate, CandidateSource}; +use super::assembly; use super::infcx_ext::InferCtxtExt; use super::trait_goals::structural_traits; use super::{Certainty, EvalCtxt, Goal, QueryResult}; @@ -34,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // projection cache in the solver. if self.term_is_fully_unconstrained(goal) { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_project_candidates(candidates) + self.merge_candidates_and_discard_reservation_impls(candidates) } else { let predicate = goal.predicate; let unconstrained_rhs = match predicate.term.unpack() { @@ -153,59 +153,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.make_canonical_response(normalization_certainty.unify_and(rhs_certainty)) } - - fn merge_project_candidates( - &mut self, - mut candidates: Vec<Candidate<'tcx>>, - ) -> QueryResult<'tcx> { - match candidates.len() { - 0 => return Err(NoSolution), - 1 => return Ok(candidates.pop().unwrap().result), - _ => {} - } - - if candidates.len() > 1 { - let mut i = 0; - 'outer: while i < candidates.len() { - for j in (0..candidates.len()).filter(|&j| i != j) { - if self.project_candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - ) { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - continue 'outer; - } - } - - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - i += 1; - if i > 1 { - debug!("multiple matches, ambig"); - // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. - unimplemented!(); - } - } - } - - Ok(candidates.pop().unwrap().result) - } - - fn project_candidate_should_be_dropped_in_favor_of( - &self, - candidate: &Candidate<'tcx>, - other: &Candidate<'tcx>, - ) -> bool { - // FIXME: implement this - match (candidate.source, other.source) { - (CandidateSource::Impl(_), _) - | (CandidateSource::ParamEnv(_), _) - | (CandidateSource::BuiltinImpl, _) - | (CandidateSource::AliasBound, _) => unimplemented!(), - } - } } impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { @@ -452,7 +399,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { [ty::GenericArg::from(goal.predicate.self_ty())], )); - let is_sized_certainty = ecx.evaluate_goal(goal.with(tcx, sized_predicate))?.1; + let (_, is_sized_certainty) = + ecx.evaluate_goal(goal.with(tcx, sized_predicate))?; return ecx.eq_term_and_make_canonical_response( goal, is_sized_certainty, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 06a72e95d49..6554c739b3f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,7 +2,7 @@ use std::iter; -use super::assembly::{self, Candidate, CandidateSource}; +use super::assembly; use super::infcx_ext::InferCtxtExt; use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; @@ -89,6 +89,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { + // This differs from the current stable behavior and + // fixes #84857. Due to breakage found via crater, we + // currently instead lint patterns which can be used to + // exploit this unsoundness on stable, see #93367 for + // more details. + if let Some(def_id) = ecx.tcx().find_map_relevant_impl( + goal.predicate.def_id(), + goal.predicate.self_ty(), + Some, + ) { + debug!(?def_id, ?goal, "disqualified auto-trait implementation"); + return Err(NoSolution); + } + ecx.probe_and_evaluate_goal_for_constituent_tys( goal, structural_traits::instantiate_constituent_tys_for_auto_trait, @@ -479,73 +493,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_trait_candidates_discard_reservation_impls(candidates) - } - - #[instrument(level = "debug", skip(self), ret)] - pub(super) fn merge_trait_candidates_discard_reservation_impls( - &mut self, - mut candidates: Vec<Candidate<'tcx>>, - ) -> QueryResult<'tcx> { - match candidates.len() { - 0 => return Err(NoSolution), - 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result), - _ => {} - } - - if candidates.len() > 1 { - let mut i = 0; - 'outer: while i < candidates.len() { - for j in (0..candidates.len()).filter(|&j| i != j) { - if self.trait_candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - ) { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - continue 'outer; - } - } - - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - i += 1; - if i > 1 { - debug!("multiple matches, ambig"); - // FIXME: return overflow if all candidates overflow, otherwise return ambiguity. - unimplemented!(); - } - } - } - - Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result) - } - - fn trait_candidate_should_be_dropped_in_favor_of( - &self, - candidate: &Candidate<'tcx>, - other: &Candidate<'tcx>, - ) -> bool { - // FIXME: implement this - match (candidate.source, other.source) { - (CandidateSource::Impl(_), _) - | (CandidateSource::ParamEnv(_), _) - | (CandidateSource::AliasBound, _) - | (CandidateSource::BuiltinImpl, _) => unimplemented!(), - } - } - - fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> { - if let CandidateSource::Impl(def_id) = candidate.source { - if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) { - debug!("Selected reservation impl"); - // FIXME: reduce candidate to ambiguous - // FIXME: replace `var_values` with identity, yeet external constraints. - unimplemented!() - } - } - - candidate + self.merge_candidates_and_discard_reservation_impls(candidates) } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 948632ccc6c..6a840704e86 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -823,14 +823,17 @@ impl<'tcx> AutoTraitFinder<'tcx> { _ => return false, } } + // There's not really much we can do with these predicates - // we start out with a `ParamEnv` with no inference variables, // and these don't correspond to adding any new bounds to // the `ParamEnv`. ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) + // FIXME(generic_const_exprs): you can absolutely add this as a where clauses | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs index ba9ee57d409..9474c70cb53 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs @@ -1,5 +1,7 @@ use crate::infer::InferCtxt; +use rustc_infer::infer::ObligationEmittingRelation; +use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -88,3 +90,16 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } } + +impl<'tcx> ObligationEmittingRelation<'tcx> for CollectAllMismatches<'_, 'tcx> { + fn register_obligations(&mut self, _obligations: PredicateObligations<'tcx>) { + // FIXME(deferred_projection_equality) + } + + fn register_predicates( + &mut self, + _obligations: impl IntoIterator<Item = impl ty::ToPredicate<'tcx>>, + ) { + // FIXME(deferred_projection_equality) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index cf1e05ada47..4867855c2ae 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1278,6 +1278,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span, "TypeWellFormedFromEnv predicate should only exist in the environment" ), + + ty::PredicateKind::AliasEq(..) => span_bug!( + span, + "AliasEq predicate should never be the predicate cause of a SelectionError" + ), } } 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 59aef52910e..cca178299df 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -19,6 +19,7 @@ use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; +use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_hir::{Expr, HirId}; @@ -98,6 +99,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { // obligation fn get_from_await_ty<F>( &self, + tcx: TyCtxt<'tcx>, visitor: AwaitsVisitor, hir: map::Map<'tcx>, ty_matches: F, @@ -134,9 +136,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { .unwrap_or_else(|| { bug!( "node_type: no type for node {}", - ty::tls::with(|tcx| tcx - .hir() - .node_to_string(await_expr.hir_id)) + tcx.hir().node_to_string(await_expr.hir_id) ) }) }, @@ -1350,14 +1350,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Applicability::MaybeIncorrect, ); } else { + // Issue #104961, we need to add parentheses properly for compond expressions + // for example, `x.starts_with("hi".to_string() + "you")` + // should be `x.starts_with(&("hi".to_string() + "you"))` + let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; }; + let body = self.tcx.hir().body(body_id); + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(body.value); + let Some(expr) = expr_finder.result else { return false; }; + let needs_parens = match expr.kind { + // parenthesize if needed (Issue #46756) + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + }; + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "consider{} borrowing here", - if is_mut { " mutably" } else { "" } - ), - format!("&{}", if is_mut { "mut " } else { "" }), + let span = if needs_parens { span } else { span.shrink_to_lo() }; + let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); + let sugg_msg = &format!( + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } + ); + + let suggestions = if !needs_parens { + vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))] + } else { + vec![ + (span.shrink_to_lo(), format!("{}(", sugg_prefix)), + (span.shrink_to_hi(), ")".to_string()), + ] + }; + err.multipart_suggestion_verbose( + sugg_msg, + suggestions, Applicability::MaybeIncorrect, ); } @@ -2351,7 +2378,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut interior_or_upvar_span = None; - let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches); + let from_awaited_ty = generator_data.get_from_await_ty(self.tcx, visitor, hir, ty_matches); debug!(?from_awaited_ty); // The generator interior types share the same binders @@ -3545,7 +3572,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { type_diffs = vec![ Sorts(ty::error::ExpectedFound { - expected: self.tcx.mk_ty(ty::Alias(ty::Projection, where_pred.skip_binder().projection_ty)), + expected: self.tcx.mk_alias(ty::Projection, where_pred.skip_binder().projection_ty), found, }), ]; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 3adc1e62e0d..19d47d33f67 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -328,6 +328,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } + ty::PredicateKind::AliasEq(..) => { + bug!("AliasEq is only used for new solver") + } }, Some(pred) => match pred { ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { @@ -594,6 +597,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } + ty::PredicateKind::AliasEq(..) => { + bug!("AliasEq is only used for new solver") + } }, } } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 8f548acfd2e..977446894e7 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -327,6 +327,8 @@ fn predicate_references_self<'tcx>( // possible alternatives. if data.projection_ty.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } } + ty::PredicateKind::AliasEq(..) => bug!("`AliasEq` not allowed as assumption"), + ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) @@ -334,6 +336,7 @@ fn predicate_references_self<'tcx>( | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) + // FIXME(generic_const_exprs): this can mention `Self` | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous @@ -368,6 +371,7 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, } @@ -646,11 +650,9 @@ fn object_ty_for_trait<'tcx>( debug!(?obligation); let pred = obligation.predicate.to_opt_poly_projection_pred()?; Some(pred.map_bound(|p| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - def_id: p.projection_ty.def_id, - substs: p.projection_ty.substs, - term: p.term, - }) + ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( + tcx, p, + )) })) }) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index aa81bc640aa..c2bce774bb5 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -785,7 +785,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { let universe = self.universe_for(debruijn); let p = ty::PlaceholderType { universe, name: bound_ty.kind }; self.mapped_types.insert(p, bound_ty); - self.infcx.tcx.mk_ty(ty::Placeholder(p)) + self.infcx.tcx.mk_placeholder(p) } _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), _ => t, @@ -915,7 +915,7 @@ impl<'tcx> TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - self.tcx().mk_ty(ty::Bound(db, *replace_var)) + self.tcx().mk_bound(db, *replace_var) } None => ty, } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index fcc4820c2a6..dc5bcb48cad 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -527,13 +527,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let kind = ty::BoundTyKind::Param(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Ty(kind); bound_vars.push(bound_var); - tcx.mk_ty(ty::Bound( + tcx.mk_bound( ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind, }, - )) + ) .into() } GenericParamDefKind::Lifetime => { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 984d6fde268..45c4811321a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -991,6 +991,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for chalk") } + ty::PredicateKind::AliasEq(..) => { + bug!("AliasEq is only used for new solver") + } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), } }) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 7c5e147a950..5cfb6cf332e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -187,6 +187,9 @@ pub fn predicate_obligations<'tcx>( ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } + ty::PredicateKind::AliasEq(..) => { + bug!("We should only wf check where clauses and `AliasEq` is not a `Clause`") + } } wf.normalize(infcx) @@ -876,7 +879,7 @@ pub fn object_region_bounds<'tcx>( // Since we don't actually *know* the self type for an object, // this "open(err)" serves as a kind of dummy standin -- basically // a placeholder type. - let open_ty = tcx.mk_ty_infer(ty::FreshTy(0)); + let open_ty = tcx.mk_fresh_ty(0); let predicates = existential_predicates.iter().filter_map(|predicate| { if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { @@ -928,6 +931,7 @@ pub(crate) fn required_region_bounds<'tcx>( | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( ref t, diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index dbd5f13fe4e..7101f404269 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -588,10 +588,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t _id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>, ) -> chalk_ir::Ty<RustInterner<'tcx>> { // FIXME(chalk): actually get hidden ty - self.interner - .tcx - .mk_ty(ty::Tuple(self.interner.tcx.intern_type_list(&[]))) - .lower_into(self.interner) + self.interner.tcx.types.unit.lower_into(self.interner) } fn closure_kind( @@ -721,13 +718,13 @@ impl<'tcx> chalk_ir::UnificationDatabase<RustInterner<'tcx>> for RustIrDatabase< fn bound_vars_for_item(tcx: TyCtxt<'_>, def_id: DefId) -> SubstsRef<'_> { InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind { ty::GenericParamDefKind::Type { .. } => tcx - .mk_ty(ty::Bound( + .mk_bound( ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from(param.index), kind: ty::BoundTyKind::Param(param.def_id, param.name), }, - )) + ) .into(), ty::GenericParamDefKind::Lifetime => { @@ -790,10 +787,9 @@ impl<'tcx> ty::TypeFolder<'tcx> for ReplaceOpaqueTyFolder<'tcx> { fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *ty.kind() { if def_id == self.opaque_ty_id.0 && substs == self.identity_substs { - return self.tcx.mk_ty(ty::Bound( - self.binder_index, - ty::BoundTy::from(ty::BoundVar::from_u32(0)), - )); + return self + .tcx + .mk_bound(self.binder_index, ty::BoundTy::from(ty::BoundVar::from_u32(0))); } } ty diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 9c5db3314c5..4f71dcde818 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -116,6 +116,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<' )), }, ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) @@ -210,6 +211,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi // We can defer this, but ultimately we'll want to express // some of these in terms of chalk operations. ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::Ambiguous @@ -493,6 +495,9 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t ty::ReEarlyBound(_) => { panic!("Should have already been substituted."); } + ty::ReError(_) => { + panic!("Error lifetime should not have already been lowered."); + } ty::ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), br.var.as_usize(), @@ -642,6 +647,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<' ty::PredicateKind::WellFormed(_ty) => None, ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) @@ -671,11 +677,11 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<Ru // shifted in by one so that they are still escaping. let predicates = ty::fold::shift_vars(interner.tcx, self, 1); - let self_ty = interner.tcx.mk_ty(ty::Bound( + let self_ty = interner.tcx.mk_bound( // This is going to be wrapped in a binder ty::DebruijnIndex::from_usize(1), ty::BoundTy { var: ty::BoundVar::from_usize(0), kind: ty::BoundTyKind::Anon(0) }, - )); + ); let where_clauses = predicates.into_iter().map(|predicate| { let (predicate, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, predicate); @@ -775,6 +781,7 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<Ru ty::PredicateKind::WellFormed(_ty) => None, ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) @@ -1070,18 +1077,18 @@ impl<'tcx> TypeFolder<'tcx> for ParamsSubstitutor<'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { - Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + Some(idx) => self.tcx.mk_placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), name: ty::BoundTyKind::Anon(idx as u32), - })), + }), None => { self.list.push(param); let idx = self.list.len() - 1 + self.next_ty_placeholder; self.params.insert(idx as u32, param); - self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + self.tcx.mk_placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), name: ty::BoundTyKind::Anon(idx as u32), - })) + }) } }, _ => t.super_fold_with(self), @@ -1140,7 +1147,7 @@ impl<'tcx> TypeFolder<'tcx> for ReverseParamsSubstitutor<'tcx> { match *t.kind() { ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, name }) => { match self.params.get(&name.expect_anon()) { - Some(param) => self.tcx.mk_ty(ty::Param(*param)), + Some(&ty::ParamTy { index, name }) => self.tcx.mk_ty_param(index, name), None => t, } } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index fe633d687d9..93f9b66e0f8 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -70,47 +70,63 @@ fn compute_implied_outlives_bounds<'tcx>( let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) .unwrap_or_default(); - // While these predicates should all be implied by other parts of - // the program, they are still relevant as they may constrain - // inference variables, which is necessary to add the correct - // implied bounds in some cases, mostly when dealing with projections. - ocx.register_obligations( - obligations.iter().filter(|o| o.predicate.has_non_region_infer()).cloned(), - ); - - // From the full set of obligations, just filter down to the - // region relationships. - outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| { + for obligation in obligations { + debug!(?obligation); assert!(!obligation.has_escaping_bound_vars()); - match obligation.predicate.kind().no_bound_vars() { - None => None, - Some(pred) => match pred { - ty::PredicateKind::Clause(ty::Clause::Trait(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::Clause::Projection(..)) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - ty::PredicateKind::WellFormed(arg) => { - wf_args.push(arg); - None + + // While these predicates should all be implied by other parts of + // the program, they are still relevant as they may constrain + // inference variables, which is necessary to add the correct + // implied bounds in some cases, mostly when dealing with projections. + // + // Another important point here: we only register `Projection` + // predicates, since otherwise we might register outlives + // predicates containing inference variables, and we don't + // learn anything new from those. + if obligation.predicate.has_non_region_infer() { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::AliasEq(..) => { + ocx.register_obligation(obligation.clone()); } + _ => {} + } + } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives( - ty::OutlivesPredicate(r_a, r_b), - )) => Some(ty::OutlivesPredicate(r_a.into(), r_b)), + let pred = match obligation.predicate.kind().no_bound_vars() { + None => continue, + Some(pred) => pred, + }; + match pred { + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} + + // We need to search through *all* WellFormed predicates + ty::PredicateKind::WellFormed(arg) => { + wf_args.push(arg); + } + + // We need to register region relationships + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate( + r_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( - ty_a, - r_b, - ))) => Some(ty::OutlivesPredicate(ty_a.into(), r_b)), - }, + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), } - })); + } } // This call to `select_all_or_error` is necessary to constrain inference variables, which we diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 9aa26667e7b..8bea5588ae7 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -4,6 +4,7 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] #![feature(let_chains)] +#![feature(drain_filter)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 5cad2c2ccb0..07e716cda42 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -60,6 +60,7 @@ fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => false, ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index 3ede95e8431..61557fdc0ed 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -960,6 +960,9 @@ pub enum RegionKind<I: Interner> { /// Erased region, used by trait selection, in MIR and during codegen. ReErased, + + /// A region that resulted from some other error. Used exclusively for diagnostics. + ReError(I::ErrorGuaranteed), } // This is manually implemented for `RegionKind` because `std::mem::discriminant` @@ -974,6 +977,7 @@ const fn regionkind_discriminant<I: Interner>(value: &RegionKind<I>) -> usize { ReVar(_) => 4, RePlaceholder(_) => 5, ReErased => 6, + ReError(_) => 7, } } @@ -985,6 +989,7 @@ where I::FreeRegion: Copy, I::RegionVid: Copy, I::PlaceholderRegion: Copy, + I::ErrorGuaranteed: Copy, { } @@ -999,6 +1004,7 @@ impl<I: Interner> Clone for RegionKind<I> { ReVar(r) => ReVar(r.clone()), RePlaceholder(r) => RePlaceholder(r.clone()), ReErased => ReErased, + ReError(r) => ReError(r.clone()), } } } @@ -1016,10 +1022,11 @@ impl<I: Interner> PartialEq for RegionKind<I> { (ReVar(a_r), ReVar(b_r)) => a_r == b_r, (RePlaceholder(a_r), RePlaceholder(b_r)) => a_r == b_r, (ReErased, ReErased) => true, + (ReError(_), ReError(_)) => true, _ => { debug_assert!( false, - "This branch must be unreachable, maybe the match is missing an arm? self = self = {self:?}, other = {other:?}" + "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}" ); true } @@ -1077,6 +1084,7 @@ impl<I: Interner> hash::Hash for RegionKind<I> { ReVar(r) => r.hash(state), RePlaceholder(r) => r.hash(state), ReErased => (), + ReError(_) => (), } } } @@ -1100,6 +1108,8 @@ impl<I: Interner> fmt::Debug for RegionKind<I> { RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), ReErased => f.write_str("ReErased"), + + ReError(_) => f.write_str("ReError"), } } } @@ -1134,6 +1144,7 @@ where a.encode(e); }), ReErased => e.emit_enum_variant(disc, |_| {}), + ReError(_) => e.emit_enum_variant(disc, |_| {}), } } } @@ -1146,6 +1157,7 @@ where I::FreeRegion: Decodable<D>, I::RegionVid: Decodable<D>, I::PlaceholderRegion: Decodable<D>, + I::ErrorGuaranteed: Decodable<D>, { fn decode(d: &mut D) -> Self { match Decoder::read_usize(d) { @@ -1156,6 +1168,7 @@ where 4 => ReVar(Decodable::decode(d)), 5 => RePlaceholder(Decodable::decode(d)), 6 => ReErased, + 7 => ReError(Decodable::decode(d)), _ => panic!( "{}", format!( @@ -1184,7 +1197,7 @@ where ) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - ReErased | ReStatic => { + ReErased | ReStatic | ReError(_) => { // No variant fields to hash for these ... } ReLateBound(d, r) => { diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index 11bd4c4dc1b..f99395c72aa 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -991,12 +991,6 @@ impl IntoStringError { pub fn utf8_error(&self) -> Utf8Error { self.error } - - #[doc(hidden)] - #[unstable(feature = "cstr_internals", issue = "none")] - pub fn __source(&self) -> &Utf8Error { - &self.error - } } impl IntoStringError { @@ -1141,6 +1135,6 @@ impl core::error::Error for IntoStringError { } fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - Some(self.__source()) + Some(&self.error) } } diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 5a10121bbbe..3751f2a2454 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -241,10 +241,15 @@ impl<T, A: Allocator> RawVec<T, A> { if T::IS_ZST || self.cap == 0 { None } else { - // We have an allocated chunk of memory, so we can bypass runtime - // checks to get our current layout. + // We could use Layout::array here which ensures the absence of isize and usize overflows + // and could hypothetically handle differences between stride and size, but this memory + // has already been allocated so we know it can't overflow and currently rust does not + // support such types. So we can do better by skipping some checks and avoid an unwrap. + let _: () = const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) }; unsafe { - let layout = Layout::array::<T>(self.cap).unwrap_unchecked(); + let align = mem::align_of::<T>(); + let size = mem::size_of::<T>().unchecked_mul(self.cap); + let layout = Layout::from_size_align_unchecked(size, align); Some((self.ptr.cast().into(), layout)) } } @@ -426,11 +431,13 @@ impl<T, A: Allocator> RawVec<T, A> { assert!(cap <= self.capacity(), "Tried to shrink to a larger capacity"); let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; - + // See current_memory() why this assert is here + let _: () = const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) }; let ptr = unsafe { // `Layout::array` cannot overflow here because it would have // overflowed earlier when capacity was larger. - let new_layout = Layout::array::<T>(cap).unwrap_unchecked(); + let new_size = mem::size_of::<T>().unchecked_mul(cap); + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); self.alloc .shrink(ptr, layout, new_layout) .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })? diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 75659188515..2b843647dd5 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -42,8 +42,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(not(no_global_oom_handling))] -use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; use core::error::Error; use core::fmt; use core::hash; @@ -683,7 +681,7 @@ impl String { // This isn't done via collect::<Result<_, _>>() for performance reasons. // FIXME: the function can be simplified again when #48994 is closed. let mut ret = String::with_capacity(v.len()); - for c in decode_utf16(v.iter().cloned()) { + for c in char::decode_utf16(v.iter().cloned()) { if let Ok(c) = c { ret.push(c); } else { @@ -722,7 +720,9 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn from_utf16_lossy(v: &[u16]) -> String { - decode_utf16(v.iter().cloned()).map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)).collect() + char::decode_utf16(v.iter().cloned()) + .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) + .collect() } /// Decomposes a `String` into its raw components. diff --git a/library/core/benches/array.rs b/library/core/benches/array.rs new file mode 100644 index 00000000000..845c6076294 --- /dev/null +++ b/library/core/benches/array.rs @@ -0,0 +1,19 @@ +use test::black_box; +use test::Bencher; + +macro_rules! map_array { + ($func_name:ident, $start_item: expr, $map_item: expr, $arr_size: expr) => { + #[bench] + fn $func_name(b: &mut Bencher) { + let arr = [$start_item; $arr_size]; + b.iter(|| black_box(arr).map(|_| black_box($map_item))); + } + }; +} + +map_array!(map_8byte_8byte_8, 0u64, 1u64, 800); +map_array!(map_8byte_8byte_64, 0u64, 1u64, 6400); +map_array!(map_8byte_8byte_256, 0u64, 1u64, 25600); + +map_array!(map_8byte_256byte_256, 0u64, [0u64; 4], 25600); +map_array!(map_256byte_8byte_256, [0u64; 4], 0u64, 25600); diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index f1244d93285..e4100120d82 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -9,6 +9,7 @@ extern crate test; mod any; +mod array; mod ascii; mod char; mod fmt; diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index 1d80b8bf9ec..18da70451f2 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -203,7 +203,7 @@ pub unsafe trait GlobalAlloc { ptr } - /// Shrink or grow a block of memory to the given `new_size`. + /// Shrink or grow a block of memory to the given `new_size` in bytes. /// The block is described by the given `ptr` pointer and `layout`. /// /// If this returns a non-null pointer, then ownership of the memory block @@ -211,10 +211,11 @@ pub unsafe trait GlobalAlloc { /// Any access to the old `ptr` is Undefined Behavior, even if the /// allocation remained in-place. The newly returned pointer is the only valid pointer /// for accessing this memory now. + /// /// The new memory block is allocated with `layout`, - /// but with the `size` updated to `new_size`. This new layout must be - /// used when deallocating the new memory block with `dealloc`. The range - /// `0..min(layout.size(), new_size)` of the new memory block is + /// but with the `size` updated to `new_size` in bytes. + /// This new layout must be used when deallocating the new memory block with `dealloc`. + /// The range `0..min(layout.size(), new_size)` of the new memory block is /// guaranteed to have the same values as the original block. /// /// If this method returns null, then ownership of the memory diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 2825e0bbb43..5decd7d5a65 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -131,7 +131,8 @@ pub struct TryFromSliceError(()); impl fmt::Display for TryFromSliceError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.__description(), f) + #[allow(deprecated)] + self.description().fmt(f) } } @@ -139,20 +140,6 @@ impl fmt::Display for TryFromSliceError { impl Error for TryFromSliceError { #[allow(deprecated)] fn description(&self) -> &str { - self.__description() - } -} - -impl TryFromSliceError { - #[unstable( - feature = "array_error_internals", - reason = "available through Error trait and this method should not \ - be exposed publicly", - issue = "none" - )] - #[inline] - #[doc(hidden)] - pub fn __description(&self) -> &str { "could not convert slice to array" } } diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index f1a51a550f5..136bbcb8b21 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -2,6 +2,7 @@ use crate::char::TryFromCharError; use crate::convert::TryFrom; +use crate::error::Error; use crate::fmt; use crate::mem::transmute; use crate::str::FromStr; @@ -150,14 +151,16 @@ pub struct ParseCharError { kind: CharErrorKind, } -impl ParseCharError { - #[unstable( - feature = "char_error_internals", - reason = "this method should not be available publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum CharErrorKind { + EmptyString, + TooManyChars, +} + +#[stable(feature = "char_from_str", since = "1.20.0")] +impl Error for ParseCharError { + #[allow(deprecated)] + fn description(&self) -> &str { match self.kind { CharErrorKind::EmptyString => "cannot parse char from empty string", CharErrorKind::TooManyChars => "too many characters in string", @@ -165,16 +168,11 @@ impl ParseCharError { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum CharErrorKind { - EmptyString, - TooManyChars, -} - #[stable(feature = "char_from_str", since = "1.20.0")] impl fmt::Display for ParseCharError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(f) + #[allow(deprecated)] + self.description().fmt(f) } } diff --git a/library/core/src/char/decode.rs b/library/core/src/char/decode.rs index eeb08803040..dbfe251f2bb 100644 --- a/library/core/src/char/decode.rs +++ b/library/core/src/char/decode.rs @@ -3,8 +3,6 @@ use crate::error::Error; use crate::fmt; -use super::from_u32_unchecked; - /// An iterator that decodes UTF-16 encoded code points from an iterator of `u16`s. /// /// This `struct` is created by the [`decode_utf16`] method on [`char`]. See its @@ -49,7 +47,7 @@ impl<I: Iterator<Item = u16>> Iterator for DecodeUtf16<I> { if !u.is_utf16_surrogate() { // SAFETY: not a surrogate - Some(Ok(unsafe { from_u32_unchecked(u as u32) })) + Some(Ok(unsafe { char::from_u32_unchecked(u as u32) })) } else if u >= 0xDC00 { // a trailing surrogate Some(Err(DecodeUtf16Error { code: u })) @@ -69,7 +67,7 @@ impl<I: Iterator<Item = u16>> Iterator for DecodeUtf16<I> { // all ok, so lets decode it. let c = (((u & 0x3ff) as u32) << 10 | (u2 & 0x3ff) as u32) + 0x1_0000; // SAFETY: we checked that it's a legal unicode value - Some(Ok(unsafe { from_u32_unchecked(c) })) + Some(Ok(unsafe { char::from_u32_unchecked(c) })) } } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3e7383b4cd1..9bc97ea0bff 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -53,15 +53,13 @@ impl char { /// Basic usage: /// /// ``` - /// use std::char::decode_utf16; - /// /// // 𝄞mus<invalid>ic<invalid> /// let v = [ /// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, /// ]; /// /// assert_eq!( - /// decode_utf16(v) + /// char::decode_utf16(v) /// .map(|r| r.map_err(|e| e.unpaired_surrogate())) /// .collect::<Vec<_>>(), /// vec![ @@ -77,16 +75,14 @@ impl char { /// A lossy decoder can be obtained by replacing `Err` results with the replacement character: /// /// ``` - /// use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; - /// /// // 𝄞mus<invalid>ic<invalid> /// let v = [ /// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, /// ]; /// /// assert_eq!( - /// decode_utf16(v) - /// .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) + /// char::decode_utf16(v) + /// .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) /// .collect::<String>(), /// "𝄞mus�ic�" /// ); @@ -123,8 +119,6 @@ impl char { /// Basic usage: /// /// ``` - /// use std::char; - /// /// let c = char::from_u32(0x2764); /// /// assert_eq!(Some('❤'), c); @@ -133,8 +127,6 @@ impl char { /// Returning `None` when the input is not a valid `char`: /// /// ``` - /// use std::char; - /// /// let c = char::from_u32(0x110000); /// /// assert_eq!(None, c); @@ -176,8 +168,6 @@ impl char { /// Basic usage: /// /// ``` - /// use std::char; - /// /// let c = unsafe { char::from_u32_unchecked(0x2764) }; /// /// assert_eq!('❤', c); @@ -210,8 +200,6 @@ impl char { /// Basic usage: /// /// ``` - /// use std::char; - /// /// let c = char::from_digit(4, 10); /// /// assert_eq!(Some('4'), c); @@ -225,8 +213,6 @@ impl char { /// Returning `None` when the input is not a digit: /// /// ``` - /// use std::char; - /// /// let c = char::from_digit(20, 10); /// /// assert_eq!(None, c); @@ -235,8 +221,6 @@ impl char { /// Passing a large radix, causing a panic: /// /// ```should_panic - /// use std::char; - /// /// // this panics /// let _c = char::from_digit(1, 37); /// ``` @@ -1786,7 +1770,7 @@ pub fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { } else { panic!( "encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", - from_u32_unchecked(code).len_utf16(), + char::from_u32_unchecked(code).len_utf16(), code, dst.len(), ) diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index af98059cf42..8ec78e88733 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -189,7 +189,7 @@ impl Iterator for EscapeUnicode { } EscapeUnicodeState::Value => { let hex_digit = ((self.c as u32) >> (self.hex_digit_idx * 4)) & 0xf; - let c = from_digit(hex_digit, 16).unwrap(); + let c = char::from_digit(hex_digit, 16).unwrap(); if self.hex_digit_idx == 0 { self.state = EscapeUnicodeState::RightBrace; } else { diff --git a/library/core/src/error.rs b/library/core/src/error.rs index e11a5e99184..571bc4bcfd1 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -486,25 +486,9 @@ impl Error for crate::char::CharTryFromError { } } -#[stable(feature = "char_from_str", since = "1.20.0")] -impl Error for crate::char::ParseCharError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - #[stable(feature = "duration_checked_float", since = "1.66.0")] impl Error for crate::time::TryFromFloatSecsError {} -#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] -impl Error for crate::ffi::FromBytesWithNulError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - #[stable(feature = "cstr_from_bytes_until_nul", since = "CURRENT_RUSTC_VERSION")] impl Error for crate::ffi::FromBytesUntilNulError {} diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 82e5fa75ded..cd00fd0daf9 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -1,4 +1,5 @@ use crate::cmp::Ordering; +use crate::error::Error; use crate::ffi::c_char; use crate::fmt; use crate::intrinsics; @@ -129,10 +130,12 @@ impl FromBytesWithNulError { const fn not_nul_terminated() -> FromBytesWithNulError { FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated } } +} - #[doc(hidden)] - #[unstable(feature = "cstr_internals", issue = "none")] - pub fn __description(&self) -> &str { +#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] +impl Error for FromBytesWithNulError { + #[allow(deprecated)] + fn description(&self) -> &str { match self.kind { FromBytesWithNulErrorKind::InteriorNul(..) => { "data provided contains an interior nul byte" @@ -180,7 +183,7 @@ impl Default for &CStr { impl fmt::Display for FromBytesWithNulError { #[allow(deprecated, deprecated_in_future)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.__description())?; + f.write_str(self.description())?; if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind { write!(f, " at byte pos {pos}")?; } diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index b5739f2f3c0..78e27d73065 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -1,4 +1,3 @@ -use crate::char; use crate::convert::TryFrom; use crate::mem; use crate::ops::{self, Try}; diff --git a/library/core/src/iter/traits/exact_size.rs b/library/core/src/iter/traits/exact_size.rs index 1757e37ec0e..908830d8a95 100644 --- a/library/core/src/iter/traits/exact_size.rs +++ b/library/core/src/iter/traits/exact_size.rs @@ -21,6 +21,16 @@ /// /// [`len`]: ExactSizeIterator::len /// +/// # When *shouldn't* an adapter be `ExactSizeIterator`? +/// +/// If an adapter makes an iterator *longer*, then it's usually incorrect for +/// that adapter to implement `ExactSizeIterator`. The inner exact-sized +/// iterator might already be `usize::MAX`-long, and thus the length of the +/// longer adapted iterator would no longer be exactly representable in `usize`. +/// +/// This is why [`Chain<A, B>`](crate::iter::Chain) isn't `ExactSizeIterator`, +/// even when `A` and `B` are both `ExactSizeIterator`. +/// /// # Examples /// /// Basic usage: diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index da753745740..af02848233d 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -31,6 +31,17 @@ impl<I: FusedIterator + ?Sized> FusedIterator for &mut I {} /// The iterator must produce exactly the number of elements it reported /// or diverge before reaching the end. /// +/// # When *shouldn't* an adapter be `TrustedLen`? +/// +/// If an adapter makes an iterator *shorter* by a given amount, then it's +/// usually incorrect for that adapter to implement `TrustedLen`. The inner +/// iterator might return more than `usize::MAX` items, but there's no way to +/// know what `k` elements less than that will be, since the `size_hint` from +/// the inner iterator has already saturated and lost that information. +/// +/// This is why [`Skip<I>`](crate::iter::Skip) isn't `TrustedLen`, even when +/// `I` implements `TrustedLen`. +/// /// # Safety /// /// This trait must only be implemented when the contract is upheld. Consumers diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index e11bca5962a..07a7d45c7eb 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -871,7 +871,10 @@ pub trait Destruct {} #[rustc_deny_explicit_impl] pub trait Tuple {} -/// A marker for things +/// A marker for pointer-like types. +/// +/// All types that have the same size and alignment as a `usize` or +/// `*const ()` automatically implement this trait. #[unstable(feature = "pointer_like_trait", issue = "none")] #[cfg_attr(bootstrap, lang = "pointer_sized")] #[cfg_attr(not(bootstrap), lang = "pointer_like")] diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index a888ced49b3..f8d493e8b62 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -75,6 +75,7 @@ issue = "none" )] +use crate::error::Error; use crate::fmt; use crate::str::FromStr; @@ -182,15 +183,10 @@ enum FloatErrorKind { Invalid, } -impl ParseFloatError { - #[unstable( - feature = "int_error_internals", - reason = "available through Error trait and this method should \ - not be exposed publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { +#[stable(feature = "rust1", since = "1.0.0")] +impl Error for ParseFloatError { + #[allow(deprecated)] + fn description(&self) -> &str { match self.kind { FloatErrorKind::Empty => "cannot parse float from empty string", FloatErrorKind::Invalid => "invalid float literal", @@ -201,7 +197,8 @@ impl ParseFloatError { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for ParseFloatError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(f) + #[allow(deprecated)] + self.description().fmt(f) } } diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index 768dd87816d..1bae4efe7d9 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -9,23 +9,19 @@ use crate::fmt; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TryFromIntError(pub(crate) ()); -impl TryFromIntError { - #[unstable( - feature = "int_error_internals", - reason = "available through Error trait and this method should \ - not be exposed publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { - "out of range integral type conversion attempted" +#[stable(feature = "try_from", since = "1.34.0")] +impl fmt::Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + #[allow(deprecated)] + self.description().fmt(fmt) } } #[stable(feature = "try_from", since = "1.34.0")] -impl fmt::Display for TryFromIntError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(fmt) +impl Error for TryFromIntError { + #[allow(deprecated)] + fn description(&self) -> &str { + "out of range integral type conversion attempted" } } @@ -121,28 +117,13 @@ impl ParseIntError { pub fn kind(&self) -> &IntErrorKind { &self.kind } - #[unstable( - feature = "int_error_internals", - reason = "available through Error trait and this method should \ - not be exposed publicly", - issue = "none" - )] - #[doc(hidden)] - pub fn __description(&self) -> &str { - match self.kind { - IntErrorKind::Empty => "cannot parse integer from empty string", - IntErrorKind::InvalidDigit => "invalid digit found in string", - IntErrorKind::PosOverflow => "number too large to fit in target type", - IntErrorKind::NegOverflow => "number too small to fit in target type", - IntErrorKind::Zero => "number would be zero for non-zero type", - } - } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for ParseIntError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.__description().fmt(f) + #[allow(deprecated)] + self.description().fmt(f) } } @@ -150,14 +131,12 @@ impl fmt::Display for ParseIntError { impl Error for ParseIntError { #[allow(deprecated)] fn description(&self) -> &str { - self.__description() - } -} - -#[stable(feature = "try_from", since = "1.34.0")] -impl Error for TryFromIntError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() + match self.kind { + IntErrorKind::Empty => "cannot parse integer from empty string", + IntErrorKind::InvalidDigit => "invalid digit found in string", + IntErrorKind::PosOverflow => "number too large to fit in target type", + IntErrorKind::NegOverflow => "number too small to fit in target type", + IntErrorKind::Zero => "number would be zero for non-zero type", + } } } diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs index 80472528f6c..0ce31b40a38 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/int_log10.rs @@ -138,3 +138,11 @@ pub const fn i64(val: i64) -> u32 { pub const fn i128(val: i128) -> u32 { u128(val as u128) } + +/// Instantiate this panic logic once, rather than for all the ilog methods +/// on every single primitive type. +#[cold] +#[track_caller] +pub const fn panic_for_nonpositive_argument() -> ! { + panic!("argument of integer logarithm must be positive") +} diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index b59f28193e2..479f8ffb78d 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2331,14 +2331,17 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog(self, base: Self) -> u32 { assert!(base >= 2, "base of integer logarithm must be at least 2"); - self.checked_ilog(base).expect("argument of integer logarithm must be positive") + if let Some(log) = self.checked_ilog(base) { + log + } else { + int_log10::panic_for_nonpositive_argument() + } } /// Returns the base 2 logarithm of the number, rounded down. @@ -2354,13 +2357,16 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog2(self) -> u32 { - self.checked_ilog2().expect("argument of integer logarithm must be positive") + if let Some(log) = self.checked_ilog2() { + log + } else { + int_log10::panic_for_nonpositive_argument() + } } /// Returns the base 10 logarithm of the number, rounded down. @@ -2376,13 +2382,16 @@ macro_rules! int_impl { /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog10(self) -> u32 { - self.checked_ilog10().expect("argument of integer logarithm must be positive") + if let Some(log) = self.checked_ilog10() { + log + } else { + int_log10::panic_for_nonpositive_argument() + } } /// Returns the logarithm of the number with respect to an arbitrary base, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index ac7f579ebb5..0497416745f 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -9,9 +9,6 @@ use crate::mem; use crate::ops::{Add, Mul, Sub}; use crate::str::FromStr; -#[cfg(not(no_fp_fmt_parse))] -use crate::error::Error; - // Used because the `?` operator is not allowed in a const context. macro_rules! try_opt { ($e:expr) => { @@ -61,15 +58,6 @@ pub use wrapping::Wrapping; #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; -#[cfg(not(no_fp_fmt_parse))] -#[stable(feature = "rust1", since = "1.0.0")] -impl Error for ParseFloatError { - #[allow(deprecated)] - fn description(&self) -> &str { - self.__description() - } -} - #[stable(feature = "rust1", since = "1.0.0")] pub use error::ParseIntError; diff --git a/library/core/src/num/shells/i128.rs b/library/core/src/num/shells/i128.rs index 7b048dc5206..b3b3d3b4875 100644 --- a/library/core/src/num/shells/i128.rs +++ b/library/core/src/num/shells/i128.rs @@ -1,6 +1,4 @@ -//! Constants for the 128-bit signed integer type. -//! -//! *[See also the `i128` primitive type][i128].* +//! Redundant constants module for the [`i128` primitive type][i128]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/i16.rs b/library/core/src/num/shells/i16.rs index 5c5812d5c5e..70a452e1939 100644 --- a/library/core/src/num/shells/i16.rs +++ b/library/core/src/num/shells/i16.rs @@ -1,6 +1,4 @@ -//! Constants for the 16-bit signed integer type. -//! -//! *[See also the `i16` primitive type][i16].* +//! Redundant constants module for the [`i16` primitive type][i16]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/i32.rs b/library/core/src/num/shells/i32.rs index b283ac64415..c30849e2591 100644 --- a/library/core/src/num/shells/i32.rs +++ b/library/core/src/num/shells/i32.rs @@ -1,6 +1,4 @@ -//! Constants for the 32-bit signed integer type. -//! -//! *[See also the `i32` primitive type][i32].* +//! Redundant constants module for the [`i32` primitive type][i32]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/i64.rs b/library/core/src/num/shells/i64.rs index a416fa7e936..77d95d71250 100644 --- a/library/core/src/num/shells/i64.rs +++ b/library/core/src/num/shells/i64.rs @@ -1,6 +1,4 @@ -//! Constants for the 64-bit signed integer type. -//! -//! *[See also the `i64` primitive type][i64].* +//! Redundant constants module for the [`i64` primitive type][i64]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/i8.rs b/library/core/src/num/shells/i8.rs index 02465013a4a..516ba8cdef3 100644 --- a/library/core/src/num/shells/i8.rs +++ b/library/core/src/num/shells/i8.rs @@ -1,6 +1,4 @@ -//! Constants for the 8-bit signed integer type. -//! -//! *[See also the `i8` primitive type][i8].* +//! Redundant constants module for the [`i8` primitive type][i8]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/isize.rs b/library/core/src/num/shells/isize.rs index 1579fbab6d4..828f7345baf 100644 --- a/library/core/src/num/shells/isize.rs +++ b/library/core/src/num/shells/isize.rs @@ -1,6 +1,4 @@ -//! Constants for the pointer-sized signed integer type. -//! -//! *[See also the `isize` primitive type][isize].* +//! Redundant constants module for the [`isize` primitive type][isize]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/u128.rs b/library/core/src/num/shells/u128.rs index fe08cee586c..b1e30e38435 100644 --- a/library/core/src/num/shells/u128.rs +++ b/library/core/src/num/shells/u128.rs @@ -1,6 +1,4 @@ -//! Constants for the 128-bit unsigned integer type. -//! -//! *[See also the `u128` primitive type][u128].* +//! Redundant constants module for the [`u128` primitive type][u128]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/u16.rs b/library/core/src/num/shells/u16.rs index 36f8c697878..b203806f460 100644 --- a/library/core/src/num/shells/u16.rs +++ b/library/core/src/num/shells/u16.rs @@ -1,6 +1,4 @@ -//! Constants for the 16-bit unsigned integer type. -//! -//! *[See also the `u16` primitive type][u16].* +//! Redundant constants module for the [`i16` primitive type][i16]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/u32.rs b/library/core/src/num/shells/u32.rs index 1c369097dcd..4c84274e752 100644 --- a/library/core/src/num/shells/u32.rs +++ b/library/core/src/num/shells/u32.rs @@ -1,6 +1,4 @@ -//! Constants for the 32-bit unsigned integer type. -//! -//! *[See also the `u32` primitive type][u32].* +//! Redundant constants module for the [`u32` primitive type][u32]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/u64.rs b/library/core/src/num/shells/u64.rs index e8b691d1555..47a95c6820f 100644 --- a/library/core/src/num/shells/u64.rs +++ b/library/core/src/num/shells/u64.rs @@ -1,6 +1,4 @@ -//! Constants for the 64-bit unsigned integer type. -//! -//! *[See also the `u64` primitive type][u64].* +//! Redundant constants module for the [`u64` primitive type][u64]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/u8.rs b/library/core/src/num/shells/u8.rs index 817c6a18aaa..360baef7228 100644 --- a/library/core/src/num/shells/u8.rs +++ b/library/core/src/num/shells/u8.rs @@ -1,6 +1,4 @@ -//! Constants for the 8-bit unsigned integer type. -//! -//! *[See also the `u8` primitive type][u8].* +//! Redundant constants module for the [`u8` primitive type][u8]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/shells/usize.rs b/library/core/src/num/shells/usize.rs index 3e1bec5ec48..44c24dfc2cf 100644 --- a/library/core/src/num/shells/usize.rs +++ b/library/core/src/num/shells/usize.rs @@ -1,6 +1,4 @@ -//! Constants for the pointer-sized unsigned integer type. -//! -//! *[See also the `usize` primitive type][usize].* +//! Redundant constants module for the [`usize` primitive type][usize]. //! //! New code should use the associated constants directly on the primitive type. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index dcc0835ecd6..495c44bd859 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -705,14 +705,17 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog(self, base: Self) -> u32 { assert!(base >= 2, "base of integer logarithm must be at least 2"); - self.checked_ilog(base).expect("argument of integer logarithm must be positive") + if let Some(log) = self.checked_ilog(base) { + log + } else { + int_log10::panic_for_nonpositive_argument() + } } /// Returns the base 2 logarithm of the number, rounded down. @@ -728,13 +731,16 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog2(self) -> u32 { - self.checked_ilog2().expect("argument of integer logarithm must be positive") + if let Some(log) = self.checked_ilog2() { + log + } else { + int_log10::panic_for_nonpositive_argument() + } } /// Returns the base 10 logarithm of the number, rounded down. @@ -750,13 +756,16 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "int_log", since = "1.67.0")] #[rustc_const_stable(feature = "int_log", since = "1.67.0")] - #[rustc_allow_const_fn_unstable(const_option)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] #[track_caller] pub const fn ilog10(self) -> u32 { - self.checked_ilog10().expect("argument of integer logarithm must be positive") + if let Some(log) = self.checked_ilog10() { + log + } else { + int_log10::panic_for_nonpositive_argument() + } } /// Returns the logarithm of the number with respect to an arbitrary base, diff --git a/library/core/src/result.rs b/library/core/src/result.rs index f00c40f35d5..7596e9cc005 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -458,7 +458,7 @@ //! [`Result`] of a collection of each contained value of the original //! [`Result`] values, or [`Err`] if any of the elements was [`Err`]. //! -//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA%2C%20E%3E%3E-for-Result%3CV%2C%20E%3E +//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA,+E%3E%3E-for-Result%3CV,+E%3E //! //! ``` //! let v = [Ok(2), Ok(4), Err("err!"), Ok(8)]; @@ -474,8 +474,8 @@ //! to provide the [`product`][Iterator::product] and //! [`sum`][Iterator::sum] methods. //! -//! [impl-Product]: Result#impl-Product%3CResult%3CU%2C%20E%3E%3E-for-Result%3CT%2C%20E%3E -//! [impl-Sum]: Result#impl-Sum%3CResult%3CU%2C%20E%3E%3E-for-Result%3CT%2C%20E%3E +//! [impl-Product]: Result#impl-Product%3CResult%3CU,+E%3E%3E-for-Result%3CT,+E%3E +//! [impl-Sum]: Result#impl-Sum%3CResult%3CU,+E%3E%3E-for-Result%3CT,+E%3E //! //! ``` //! let v = [Err("error!"), Ok(1), Ok(2), Ok(3), Err("foo")]; diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 2181f9a8118..4ca4eb86bde 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -13,115 +13,184 @@ use crate::cmp; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::ptr; -/// When dropped, copies from `src` into `dest`. -struct CopyOnDrop<T> { +// When dropped, copies from `src` into `dest`. +struct InsertionHole<T> { src: *const T, dest: *mut T, } -impl<T> Drop for CopyOnDrop<T> { +impl<T> Drop for InsertionHole<T> { fn drop(&mut self) { - // SAFETY: This is a helper class. - // Please refer to its usage for correctness. - // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`. + // SAFETY: This is a helper class. Please refer to its usage for correctness. Namely, one + // must be sure that `src` and `dst` does not overlap as required by + // `ptr::copy_nonoverlapping` and are both valid for writes. unsafe { ptr::copy_nonoverlapping(self.src, self.dest, 1); } } } -/// Shifts the first element to the right until it encounters a greater or equal element. -fn shift_head<T, F>(v: &mut [T], is_less: &mut F) +/// Inserts `v[v.len() - 1]` into pre-sorted sequence `v[..v.len() - 1]` so that whole `v[..]` +/// becomes sorted. +unsafe fn insert_tail<T, F>(v: &mut [T], is_less: &mut F) where F: FnMut(&T, &T) -> bool, { - let len = v.len(); - // SAFETY: The unsafe operations below involves indexing without a bounds check (by offsetting a - // pointer) and copying memory (`ptr::copy_nonoverlapping`). - // - // a. Indexing: - // 1. We checked the size of the array to >=2. - // 2. All the indexing that we will do is always between {0 <= index < len} at most. - // - // b. Memory copying - // 1. We are obtaining pointers to references which are guaranteed to be valid. - // 2. They cannot overlap because we obtain pointers to difference indices of the slice. - // Namely, `i` and `i-1`. - // 3. If the slice is properly aligned, the elements are properly aligned. - // It is the caller's responsibility to make sure the slice is properly aligned. - // - // See comments below for further detail. + debug_assert!(v.len() >= 2); + + let arr_ptr = v.as_mut_ptr(); + let i = v.len() - 1; + + // SAFETY: caller must ensure v is at least len 2. unsafe { - // If the first two elements are out-of-order... - if len >= 2 && is_less(v.get_unchecked(1), v.get_unchecked(0)) { - // Read the first element into a stack-allocated variable. If a following comparison - // operation panics, `hole` will get dropped and automatically write the element back - // into the slice. - let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(0))); - let v = v.as_mut_ptr(); - let mut hole = CopyOnDrop { src: &*tmp, dest: v.add(1) }; - ptr::copy_nonoverlapping(v.add(1), v.add(0), 1); - - for i in 2..len { - if !is_less(&*v.add(i), &*tmp) { + // See insert_head which talks about why this approach is beneficial. + let i_ptr = arr_ptr.add(i); + + // It's important that we use i_ptr here. If this check is positive and we continue, + // We want to make sure that no other copy of the value was seen by is_less. + // Otherwise we would have to copy it back. + if is_less(&*i_ptr, &*i_ptr.sub(1)) { + // It's important, that we use tmp for comparison from now on. As it is the value that + // will be copied back. And notionally we could have created a divergence if we copy + // back the wrong value. + let tmp = mem::ManuallyDrop::new(ptr::read(i_ptr)); + // Intermediate state of the insertion process is always tracked by `hole`, which + // serves two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining hole in `v` in the end. + // + // Panic safety: + // + // If `is_less` panics at any point during the process, `hole` will get dropped and + // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it + // initially held exactly once. + let mut hole = InsertionHole { src: &*tmp, dest: i_ptr.sub(1) }; + ptr::copy_nonoverlapping(hole.dest, i_ptr, 1); + + // SAFETY: We know i is at least 1. + for j in (0..(i - 1)).rev() { + let j_ptr = arr_ptr.add(j); + if !is_less(&*tmp, &*j_ptr) { break; } - // Move `i`-th element one place to the left, thus shifting the hole to the right. - ptr::copy_nonoverlapping(v.add(i), v.add(i - 1), 1); - hole.dest = v.add(i); + ptr::copy_nonoverlapping(j_ptr, hole.dest, 1); + hole.dest = j_ptr; } // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. } } } -/// Shifts the last element to the left until it encounters a smaller or equal element. -fn shift_tail<T, F>(v: &mut [T], is_less: &mut F) +/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. +/// +/// This is the integral subroutine of insertion sort. +unsafe fn insert_head<T, F>(v: &mut [T], is_less: &mut F) where F: FnMut(&T, &T) -> bool, { - let len = v.len(); - // SAFETY: The unsafe operations below involves indexing without a bound check (by offsetting a - // pointer) and copying memory (`ptr::copy_nonoverlapping`). - // - // a. Indexing: - // 1. We checked the size of the array to >= 2. - // 2. All the indexing that we will do is always between `0 <= index < len-1` at most. - // - // b. Memory copying - // 1. We are obtaining pointers to references which are guaranteed to be valid. - // 2. They cannot overlap because we obtain pointers to difference indices of the slice. - // Namely, `i` and `i+1`. - // 3. If the slice is properly aligned, the elements are properly aligned. - // It is the caller's responsibility to make sure the slice is properly aligned. - // - // See comments below for further detail. + debug_assert!(v.len() >= 2); + + // SAFETY: caller must ensure v is at least len 2. unsafe { - // If the last two elements are out-of-order... - if len >= 2 && is_less(v.get_unchecked(len - 1), v.get_unchecked(len - 2)) { - // Read the last element into a stack-allocated variable. If a following comparison - // operation panics, `hole` will get dropped and automatically write the element back - // into the slice. - let tmp = mem::ManuallyDrop::new(ptr::read(v.get_unchecked(len - 1))); - let v = v.as_mut_ptr(); - let mut hole = CopyOnDrop { src: &*tmp, dest: v.add(len - 2) }; - ptr::copy_nonoverlapping(v.add(len - 2), v.add(len - 1), 1); - - for i in (0..len - 2).rev() { - if !is_less(&*tmp, &*v.add(i)) { + if is_less(v.get_unchecked(1), v.get_unchecked(0)) { + let arr_ptr = v.as_mut_ptr(); + + // There are three ways to implement insertion here: + // + // 1. Swap adjacent elements until the first one gets to its final destination. + // However, this way we copy data around more than is necessary. If elements are big + // structures (costly to copy), this method will be slow. + // + // 2. Iterate until the right place for the first element is found. Then shift the + // elements succeeding it to make room for it and finally place it into the + // remaining hole. This is a good method. + // + // 3. Copy the first element into a temporary variable. Iterate until the right place + // for it is found. As we go along, copy every traversed element into the slot + // preceding it. Finally, copy data from the temporary variable into the remaining + // hole. This method is very good. Benchmarks demonstrated slightly better + // performance than with the 2nd method. + // + // All methods were benchmarked, and the 3rd showed best results. So we chose that one. + let tmp = mem::ManuallyDrop::new(ptr::read(arr_ptr)); + + // Intermediate state of the insertion process is always tracked by `hole`, which + // serves two purposes: + // 1. Protects integrity of `v` from panics in `is_less`. + // 2. Fills the remaining hole in `v` in the end. + // + // Panic safety: + // + // If `is_less` panics at any point during the process, `hole` will get dropped and + // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it + // initially held exactly once. + let mut hole = InsertionHole { src: &*tmp, dest: arr_ptr.add(1) }; + ptr::copy_nonoverlapping(arr_ptr.add(1), arr_ptr.add(0), 1); + + for i in 2..v.len() { + if !is_less(&v.get_unchecked(i), &*tmp) { break; } - - // Move `i`-th element one place to the right, thus shifting the hole to the left. - ptr::copy_nonoverlapping(v.add(i), v.add(i + 1), 1); - hole.dest = v.add(i); + ptr::copy_nonoverlapping(arr_ptr.add(i), arr_ptr.add(i - 1), 1); + hole.dest = arr_ptr.add(i); } // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. } } } +/// Sort `v` assuming `v[..offset]` is already sorted. +/// +/// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no +/// performance impact. Even improving performance in some cases. +#[inline(never)] +fn insertion_sort_shift_left<T, F>(v: &mut [T], offset: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + // Using assert here improves performance. + assert!(offset != 0 && offset <= len); + + // Shift each element of the unsorted region v[i..] as far left as is needed to make v sorted. + for i in offset..len { + // SAFETY: we tested that `offset` must be at least 1, so this loop is only entered if len + // >= 2. The range is exclusive and we know `i` must be at least 1 so this slice has at + // >least len 2. + unsafe { + insert_tail(&mut v[..=i], is_less); + } + } +} + +/// Sort `v` assuming `v[offset..]` is already sorted. +/// +/// Never inline this function to avoid code bloat. It still optimizes nicely and has practically no +/// performance impact. Even improving performance in some cases. +#[inline(never)] +fn insertion_sort_shift_right<T, F>(v: &mut [T], offset: usize, is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + // Using assert here improves performance. + assert!(offset != 0 && offset <= len && len >= 2); + + // Shift each element of the unsorted region v[..i] as far left as is needed to make v sorted. + for i in (0..offset).rev() { + // SAFETY: we tested that `offset` must be at least 1, so this loop is only entered if len + // >= 2.We ensured that the slice length is always at least 2 long. We know that start_found + // will be at least one less than end, and the range is exclusive. Which gives us i always + // <= (end - 2). + unsafe { + insert_head(&mut v[i..len], is_less); + } + } +} + /// Partially sorts a slice by shifting several out-of-order elements around. /// /// Returns `true` if the slice is sorted at the end. This function is *O*(*n*) worst-case. @@ -161,26 +230,19 @@ where // Swap the found pair of elements. This puts them in correct order. v.swap(i - 1, i); - // Shift the smaller element to the left. - shift_tail(&mut v[..i], is_less); - // Shift the greater element to the right. - shift_head(&mut v[i..], is_less); + if i >= 2 { + // Shift the smaller element to the left. + insertion_sort_shift_left(&mut v[..i], i - 1, is_less); + + // Shift the greater element to the right. + insertion_sort_shift_right(&mut v[..i], 1, is_less); + } } // Didn't manage to sort the slice in the limited number of steps. false } -/// Sorts a slice using insertion sort, which is *O*(*n*^2) worst-case. -fn insertion_sort<T, F>(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - for i in 1..v.len() { - shift_tail(&mut v[..i + 1], is_less); - } -} - /// Sorts `v` using heapsort, which guarantees *O*(*n* \* log(*n*)) worst-case. #[cold] #[unstable(feature = "sort_internals", reason = "internal to sort module", issue = "none")] @@ -198,8 +260,11 @@ where } // Choose the greater child. - if child + 1 < v.len() && is_less(&v[child], &v[child + 1]) { - child += 1; + if child + 1 < v.len() { + // We need a branch to be sure not to out-of-bounds index, + // but it's highly predictable. The comparison, however, + // is better done branchless, especially for primitives. + child += is_less(&v[child], &v[child + 1]) as usize; } // Stop if the invariant holds at `node`. @@ -507,7 +572,7 @@ where // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe. let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); - let _pivot_guard = CopyOnDrop { src: &*tmp, dest: pivot }; + let _pivot_guard = InsertionHole { src: &*tmp, dest: pivot }; let pivot = &*tmp; // Find the first pair of out-of-order elements. @@ -560,7 +625,7 @@ where // operation panics, the pivot will be automatically written back into the slice. // SAFETY: The pointer here is valid because it is obtained from a reference to a slice. let tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); - let _pivot_guard = CopyOnDrop { src: &*tmp, dest: pivot }; + let _pivot_guard = InsertionHole { src: &*tmp, dest: pivot }; let pivot = &*tmp; // Now partition the slice. @@ -742,7 +807,9 @@ where // Very short slices get sorted using insertion sort. if len <= MAX_INSERTION { - insertion_sort(v, is_less); + if len >= 2 { + insertion_sort_shift_left(v, 1, is_less); + } return; } @@ -844,10 +911,14 @@ fn partition_at_index_loop<'a, T, F>( let mut was_balanced = true; loop { + let len = v.len(); + // For slices of up to this length it's probably faster to simply sort them. const MAX_INSERTION: usize = 10; - if v.len() <= MAX_INSERTION { - insertion_sort(v, is_less); + if len <= MAX_INSERTION { + if len >= 2 { + insertion_sort_shift_left(v, 1, is_less); + } return; } @@ -887,7 +958,7 @@ fn partition_at_index_loop<'a, T, F>( } let (mid, _) = partition(v, pivot, is_less); - was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8; + was_balanced = cmp::min(mid, len - mid) >= len / 8; // Split the slice into `left`, `pivot`, and `right`. let (left, right) = v.split_at_mut(mid); @@ -954,75 +1025,6 @@ where (left, pivot, right) } -/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted. -/// -/// This is the integral subroutine of insertion sort. -fn insert_head<T, F>(v: &mut [T], is_less: &mut F) -where - F: FnMut(&T, &T) -> bool, -{ - if v.len() >= 2 && is_less(&v[1], &v[0]) { - // SAFETY: Copy tmp back even if panic, and ensure unique observation. - unsafe { - // There are three ways to implement insertion here: - // - // 1. Swap adjacent elements until the first one gets to its final destination. - // However, this way we copy data around more than is necessary. If elements are big - // structures (costly to copy), this method will be slow. - // - // 2. Iterate until the right place for the first element is found. Then shift the - // elements succeeding it to make room for it and finally place it into the - // remaining hole. This is a good method. - // - // 3. Copy the first element into a temporary variable. Iterate until the right place - // for it is found. As we go along, copy every traversed element into the slot - // preceding it. Finally, copy data from the temporary variable into the remaining - // hole. This method is very good. Benchmarks demonstrated slightly better - // performance than with the 2nd method. - // - // All methods were benchmarked, and the 3rd showed best results. So we chose that one. - let tmp = mem::ManuallyDrop::new(ptr::read(&v[0])); - - // Intermediate state of the insertion process is always tracked by `hole`, which - // serves two purposes: - // 1. Protects integrity of `v` from panics in `is_less`. - // 2. Fills the remaining hole in `v` in the end. - // - // Panic safety: - // - // If `is_less` panics at any point during the process, `hole` will get dropped and - // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it - // initially held exactly once. - let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] }; - ptr::copy_nonoverlapping(&v[1], &mut v[0], 1); - - for i in 2..v.len() { - if !is_less(&v[i], &*tmp) { - break; - } - ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1); - hole.dest = &mut v[i]; - } - // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`. - } - } - - // When dropped, copies from `src` into `dest`. - struct InsertionHole<T> { - src: *const T, - dest: *mut T, - } - - impl<T> Drop for InsertionHole<T> { - fn drop(&mut self) { - // SAFETY: The caller must ensure that src and dest are correctly set. - unsafe { - ptr::copy_nonoverlapping(self.src, self.dest, 1); - } - } - } -} - /// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and /// stores the result into `v[..]`. /// @@ -1180,8 +1182,6 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( { // Slices of up to this length get sorted using insertion sort. const MAX_INSERTION: usize = 20; - // Very short runs are extended using insertion sort to span at least this many elements. - const MIN_RUN: usize = 10; // The caller should have already checked that. debug_assert!(!T::IS_ZST); @@ -1191,9 +1191,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( // Short arrays get sorted in-place via insertion sort to avoid allocations. if len <= MAX_INSERTION { if len >= 2 { - for i in (0..len - 1).rev() { - insert_head(&mut v[i..], is_less); - } + insertion_sort_shift_left(v, 1, is_less); } return; } @@ -1203,59 +1201,43 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run, // which will always have length at most `len / 2`. let buf = BufGuard::new(len / 2, elem_alloc_fn, elem_dealloc_fn); - let buf_ptr = buf.buf_ptr; + let buf_ptr = buf.buf_ptr.as_ptr(); let mut runs = RunVec::new(run_alloc_fn, run_dealloc_fn); - // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a - // strange decision, but consider the fact that merges more often go in the opposite direction - // (forwards). According to benchmarks, merging forwards is slightly faster than merging - // backwards. To conclude, identifying runs by traversing backwards improves performance. - let mut end = len; - while end > 0 { - // Find the next natural run, and reverse it if it's strictly descending. - let mut start = end - 1; - if start > 0 { - start -= 1; - - // SAFETY: The v.get_unchecked must be fed with correct inbound indicies. - unsafe { - if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) { - while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) { - start -= 1; - } - v[start..end].reverse(); - } else { - while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) - { - start -= 1; - } - } - } + let mut end = 0; + let mut start = 0; + + // Scan forward. Memory pre-fetching prefers forward scanning vs backwards scanning, and the + // code-gen is usually better. For the most sensitive types such as integers, these are merged + // bidirectionally at once. So there is no benefit in scanning backwards. + while end < len { + let (streak_end, was_reversed) = find_streak(&v[start..], is_less); + end += streak_end; + if was_reversed { + v[start..end].reverse(); } // Insert some more elements into the run if it's too short. Insertion sort is faster than // merge sort on short sequences, so this significantly improves performance. - while start > 0 && end - start < MIN_RUN { - start -= 1; - insert_head(&mut v[start..end], is_less); - } + end = provide_sorted_batch(v, start, end, is_less); // Push this run onto the stack. runs.push(TimSortRun { start, len: end - start }); - end = start; + start = end; // Merge some pairs of adjacent runs to satisfy the invariants. - while let Some(r) = collapse(runs.as_slice()) { - let left = runs[r + 1]; - let right = runs[r]; + while let Some(r) = collapse(runs.as_slice(), len) { + let left = runs[r]; + let right = runs[r + 1]; + let merge_slice = &mut v[left.start..right.start + right.len]; // SAFETY: `buf_ptr` must hold enough capacity for the shorter of the two sides, and // neither side may be on length 0. unsafe { - merge(&mut v[left.start..right.start + right.len], left.len, buf_ptr, is_less); + merge(merge_slice, left.len, buf_ptr, is_less); } - runs[r] = TimSortRun { start: left.start, len: left.len + right.len }; - runs.remove(r + 1); + runs[r + 1] = TimSortRun { start: left.start, len: left.len + right.len }; + runs.remove(r); } } @@ -1277,10 +1259,10 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( // run starts at index 0, it will always demand a merge operation until the stack is fully // collapsed, in order to complete the sort. #[inline] - fn collapse(runs: &[TimSortRun]) -> Option<usize> { + fn collapse(runs: &[TimSortRun], stop: usize) -> Option<usize> { let n = runs.len(); if n >= 2 - && (runs[n - 1].start == 0 + && (runs[n - 1].start + runs[n - 1].len == stop || runs[n - 2].len <= runs[n - 1].len || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len) || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len)) @@ -1298,7 +1280,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( where ElemDeallocF: Fn(*mut T, usize), { - buf_ptr: *mut T, + buf_ptr: ptr::NonNull<T>, capacity: usize, elem_dealloc_fn: ElemDeallocF, } @@ -1315,7 +1297,11 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( where ElemAllocF: Fn(usize) -> *mut T, { - Self { buf_ptr: elem_alloc_fn(len), capacity: len, elem_dealloc_fn } + Self { + buf_ptr: ptr::NonNull::new(elem_alloc_fn(len)).unwrap(), + capacity: len, + elem_dealloc_fn, + } } } @@ -1324,7 +1310,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( ElemDeallocF: Fn(*mut T, usize), { fn drop(&mut self) { - (self.elem_dealloc_fn)(self.buf_ptr, self.capacity); + (self.elem_dealloc_fn)(self.buf_ptr.as_ptr(), self.capacity); } } @@ -1333,7 +1319,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( RunAllocF: Fn(usize) -> *mut TimSortRun, RunDeallocF: Fn(*mut TimSortRun, usize), { - buf_ptr: *mut TimSortRun, + buf_ptr: ptr::NonNull<TimSortRun>, capacity: usize, len: usize, run_alloc_fn: RunAllocF, @@ -1350,7 +1336,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( const START_RUN_CAPACITY: usize = 16; Self { - buf_ptr: run_alloc_fn(START_RUN_CAPACITY), + buf_ptr: ptr::NonNull::new(run_alloc_fn(START_RUN_CAPACITY)).unwrap(), capacity: START_RUN_CAPACITY, len: 0, run_alloc_fn, @@ -1361,15 +1347,15 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( fn push(&mut self, val: TimSortRun) { if self.len == self.capacity { let old_capacity = self.capacity; - let old_buf_ptr = self.buf_ptr; + let old_buf_ptr = self.buf_ptr.as_ptr(); self.capacity = self.capacity * 2; - self.buf_ptr = (self.run_alloc_fn)(self.capacity); + self.buf_ptr = ptr::NonNull::new((self.run_alloc_fn)(self.capacity)).unwrap(); // SAFETY: buf_ptr new and old were correctly allocated and old_buf_ptr has // old_capacity valid elements. unsafe { - ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr, old_capacity); + ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr.as_ptr(), old_capacity); } (self.run_dealloc_fn)(old_buf_ptr, old_capacity); @@ -1377,7 +1363,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( // SAFETY: The invariant was just checked. unsafe { - self.buf_ptr.add(self.len).write(val); + self.buf_ptr.as_ptr().add(self.len).write(val); } self.len += 1; } @@ -1390,7 +1376,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( // SAFETY: buf_ptr needs to be valid and len invariant upheld. unsafe { // the place we are taking from. - let ptr = self.buf_ptr.add(index); + let ptr = self.buf_ptr.as_ptr().add(index); // Shift everything down to fill in that spot. ptr::copy(ptr.add(1), ptr, self.len - index - 1); @@ -1400,7 +1386,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( fn as_slice(&self) -> &[TimSortRun] { // SAFETY: Safe as long as buf_ptr is valid and len invariant was upheld. - unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr, self.len) } + unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr.as_ptr(), self.len) } } fn len(&self) -> usize { @@ -1419,7 +1405,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( if index < self.len { // SAFETY: buf_ptr and len invariant must be upheld. unsafe { - return &*(self.buf_ptr.add(index)); + return &*(self.buf_ptr.as_ptr().add(index)); } } @@ -1436,7 +1422,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( if index < self.len { // SAFETY: buf_ptr and len invariant must be upheld. unsafe { - return &mut *(self.buf_ptr.add(index)); + return &mut *(self.buf_ptr.as_ptr().add(index)); } } @@ -1452,7 +1438,7 @@ pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>( fn drop(&mut self) { // As long as TimSortRun is Copy we don't need to drop them individually but just the // whole allocation. - (self.run_dealloc_fn)(self.buf_ptr, self.capacity); + (self.run_dealloc_fn)(self.buf_ptr.as_ptr(), self.capacity); } } } @@ -1463,3 +1449,71 @@ pub struct TimSortRun { len: usize, start: usize, } + +/// Takes a range as denoted by start and end, that is already sorted and extends it to the right if +/// necessary with sorts optimized for smaller ranges such as insertion sort. +#[cfg(not(no_global_oom_handling))] +fn provide_sorted_batch<T, F>(v: &mut [T], start: usize, mut end: usize, is_less: &mut F) -> usize +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + assert!(end >= start && end <= len); + + // This value is a balance between least comparisons and best performance, as + // influenced by for example cache locality. + const MIN_INSERTION_RUN: usize = 10; + + // Insert some more elements into the run if it's too short. Insertion sort is faster than + // merge sort on short sequences, so this significantly improves performance. + let start_end_diff = end - start; + + if start_end_diff < MIN_INSERTION_RUN && end < len { + // v[start_found..end] are elements that are already sorted in the input. We want to extend + // the sorted region to the left, so we push up MIN_INSERTION_RUN - 1 to the right. Which is + // more efficient that trying to push those already sorted elements to the left. + end = cmp::min(start + MIN_INSERTION_RUN, len); + let presorted_start = cmp::max(start_end_diff, 1); + + insertion_sort_shift_left(&mut v[start..end], presorted_start, is_less); + } + + end +} + +/// Finds a streak of presorted elements starting at the beginning of the slice. Returns the first +/// value that is not part of said streak, and a bool denoting wether the streak was reversed. +/// Streaks can be increasing or decreasing. +fn find_streak<T, F>(v: &[T], is_less: &mut F) -> (usize, bool) +where + F: FnMut(&T, &T) -> bool, +{ + let len = v.len(); + + if len < 2 { + return (len, false); + } + + let mut end = 2; + + // SAFETY: See below specific. + unsafe { + // SAFETY: We checked that len >= 2, so 0 and 1 are valid indices. + let assume_reverse = is_less(v.get_unchecked(1), v.get_unchecked(0)); + + // SAFETY: We know end >= 2 and check end < len. + // From that follows that accessing v at end and end - 1 is safe. + if assume_reverse { + while end < len && is_less(v.get_unchecked(end), v.get_unchecked(end - 1)) { + end += 1; + } + + (end, true) + } else { + while end < len && !is_less(v.get_unchecked(end), v.get_unchecked(end - 1)) { + end += 1; + } + (end, false) + } + } +} diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index d969475aa48..95c682f42d0 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -1,6 +1,6 @@ //! Iterators for `str` methods. -use crate::char; +use crate::char as char_mod; use crate::fmt::{self, Write}; use crate::iter::{Chain, FlatMap, Flatten}; use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen}; @@ -1455,8 +1455,8 @@ impl FusedIterator for EncodeUtf16<'_> {} #[derive(Clone, Debug)] pub struct EscapeDebug<'a> { pub(super) inner: Chain< - Flatten<option::IntoIter<char::EscapeDebug>>, - FlatMap<Chars<'a>, char::EscapeDebug, CharEscapeDebugContinue>, + Flatten<option::IntoIter<char_mod::EscapeDebug>>, + FlatMap<Chars<'a>, char_mod::EscapeDebug, CharEscapeDebugContinue>, >, } @@ -1464,14 +1464,14 @@ pub struct EscapeDebug<'a> { #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct EscapeDefault<'a> { - pub(super) inner: FlatMap<Chars<'a>, char::EscapeDefault, CharEscapeDefault>, + pub(super) inner: FlatMap<Chars<'a>, char_mod::EscapeDefault, CharEscapeDefault>, } /// The return type of [`str::escape_unicode`]. #[stable(feature = "str_escape", since = "1.34.0")] #[derive(Clone, Debug)] pub struct EscapeUnicode<'a> { - pub(super) inner: FlatMap<Chars<'a>, char::EscapeUnicode, CharEscapeUnicode>, + pub(super) inner: FlatMap<Chars<'a>, char_mod::EscapeUnicode, CharEscapeUnicode>, } macro_rules! escape_types_impls { diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index 84498a8eae5..0f91ffe2dfc 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -26,7 +26,6 @@ fn test_range() { #[test] fn test_char_range() { - use std::char; // Miri is too slow let from = if cfg!(miri) { char::from_u32(0xD800 - 10).unwrap() } else { '\0' }; let to = if cfg!(miri) { char::from_u32(0xDFFF + 10).unwrap() } else { char::MAX }; diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 80d30f14c66..c02cd99cc44 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -25,7 +25,7 @@ fn test() { snd: isize, } let mut p = Pair { fst: 10, snd: 20 }; - let pptr: *mut Pair = &mut p; + let pptr: *mut Pair = addr_of_mut!(p); let iptr: *mut isize = pptr as *mut isize; assert_eq!(*iptr, 10); *iptr = 30; @@ -1070,8 +1070,8 @@ fn swap_copy_untyped() { let mut x = 5u8; let mut y = 6u8; - let ptr1 = &mut x as *mut u8 as *mut bool; - let ptr2 = &mut y as *mut u8 as *mut bool; + let ptr1 = addr_of_mut!(x).cast::<bool>(); + let ptr2 = addr_of_mut!(y).cast::<bool>(); unsafe { ptr::swap(ptr1, ptr2); diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index e9d7a46c06f..5b1bfb30983 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -1,7 +1,6 @@ //! Serialization for client-server communication. use std::any::Any; -use std::char; use std::io::Write; use std::num::NonZeroU32; use std::str; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index cd9f74820ae..363a2667174 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -273,12 +273,9 @@ #![feature(utf8_chunks)] // // Library features (core): -#![feature(array_error_internals)] #![feature(atomic_mut_ptr)] -#![feature(char_error_internals)] #![feature(char_internals)] #![feature(core_intrinsics)] -#![feature(cstr_internals)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -290,7 +287,6 @@ #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(int_error_internals)] #![feature(is_some_and)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 6811fadb018..20fd3dd8f09 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -72,11 +72,6 @@ pub fn unsupported_err() -> crate::io::Error { ) } -#[no_mangle] -pub extern "C" fn floor(x: f64) -> f64 { - unsafe { intrinsics::floorf64(x) } -} - pub fn abort_internal() -> ! { unsafe { abi::abort(); diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index a342f0f5e85..a5ce6d5120d 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -141,12 +141,28 @@ mod imp { // list. let argv = ARGV.load(Ordering::Relaxed); let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) }; - (0..argc) - .map(|i| { - let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char); - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect() + let mut args = Vec::with_capacity(argc as usize); + for i in 0..argc { + let ptr = *argv.offset(i) as *const libc::c_char; + + // Some C commandline parsers (e.g. GLib and Qt) are replacing already + // handled arguments in `argv` with `NULL` and move them to the end. That + // means that `argc` might be bigger than the actual number of non-`NULL` + // pointers in `argv` at this point. + // + // To handle this we simply stop iterating at the first `NULL` argument. + // + // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments + // after the first `NULL` can safely be ignored. + if ptr.is_null() { + break; + } + + let cstr = CStr::from_ptr(ptr); + args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec())); + } + + args } } } diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index f5513e9996d..9919dc7087e 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -21,6 +21,7 @@ mod libc { extern "C" { pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; pub fn chdir(dir: *const c_char) -> c_int; + pub fn __wasilibc_get_environ() -> *mut *mut c_char; } } @@ -161,7 +162,12 @@ impl Iterator for Env { pub fn env() -> Env { unsafe { let _guard = env_read_lock(); - let mut environ = libc::environ; + + // Use `__wasilibc_get_environ` instead of `environ` here so that we + // don't require wasi-libc to eagerly initialize the environment + // variables. + let mut environ = libc::__wasilibc_get_environ(); + let mut result = Vec::new(); if !environ.is_null() { while !(*environ).is_null() { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 37809803828..f1a784b5fd2 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -1393,6 +1393,8 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let data_ptr = data.0.as_mut_ptr(); let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); + // Zero the header to ensure it's fully initialized, including reserved parameters. + *db = mem::zeroed(); let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); let mut i = 0; // FIXME: this conversion is very hacky diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 70c9b14a08f..c2cd48470bd 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -1,6 +1,5 @@ #![unstable(issue = "none", feature = "windows_stdio")] -use crate::char::decode_utf16; use crate::cmp; use crate::io; use crate::mem::MaybeUninit; @@ -369,7 +368,7 @@ fn read_u16s(handle: c::HANDLE, buf: &mut [MaybeUninit<u16>]) -> io::Result<usiz #[allow(unused)] fn utf16_to_utf8(utf16: &[u16], utf8: &mut [u8]) -> io::Result<usize> { let mut written = 0; - for chr in decode_utf16(utf16.iter().cloned()) { + for chr in char::decode_utf16(utf16.iter().cloned()) { match chr { Ok(chr) => { chr.encode_utf8(&mut utf8[written..]); diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index dd53767d452..e202d17e1c2 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -18,10 +18,10 @@ #[cfg(test)] mod tests; +use core::char::{encode_utf16_raw, encode_utf8_raw}; use core::str::next_code_point; use crate::borrow::Cow; -use crate::char; use crate::collections::TryReserveError; use crate::fmt; use crate::hash::{Hash, Hasher}; @@ -235,7 +235,7 @@ impl Wtf8Buf { /// This does **not** include the WTF-8 concatenation check or `is_known_utf8` check. fn push_code_point_unchecked(&mut self, code_point: CodePoint) { let mut bytes = [0; 4]; - let bytes = char::encode_utf8_raw(code_point.value, &mut bytes); + let bytes = encode_utf8_raw(code_point.value, &mut bytes); self.bytes.extend_from_slice(bytes) } @@ -939,7 +939,7 @@ impl<'a> Iterator for EncodeWide<'a> { let mut buf = [0; 2]; self.code_points.next().map(|code_point| { - let n = char::encode_utf16_raw(code_point.value, &mut buf).len(); + let n = encode_utf16_raw(code_point.value, &mut buf).len(); if n == 2 { self.extra = buf[1]; } diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 45f238ef4bf..013d1ab525b 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -784,6 +784,8 @@ class RustBuild(object): if self.get_toml("metrics", "build"): args.append("--features") args.append("build-metrics") + if self.json_output: + args.append("--message-format=json") if color == "always": args.append("--color=always") elif color == "never": @@ -841,6 +843,7 @@ def parse_args(): parser.add_argument('--build') parser.add_argument('--color', choices=['always', 'never', 'auto']) parser.add_argument('--clean', action='store_true') + parser.add_argument('--json-output', action='store_true') parser.add_argument('-v', '--verbose', action='count', default=0) return parser.parse_known_args(sys.argv)[0] @@ -852,6 +855,7 @@ def bootstrap(args): build.rust_root = os.path.abspath(os.path.join(__file__, '../../..')) build.verbose = args.verbose != 0 build.clean = args.clean + build.json_output = args.json_output # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, # then `config.toml` in the root directory. diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index d5fcd107502..3574f11189e 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -557,6 +557,7 @@ mod dist { rustfix_coverage: false, pass: None, run: None, + only_modified: false, }; let build = Build::new(config); @@ -627,6 +628,7 @@ mod dist { rustfix_coverage: false, pass: None, run: None, + only_modified: false, }; // Make sure rustfmt binary not being found isn't an error. config.channel = "beta".to_string(); diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 5c863015adb..d6592d2d771 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -181,8 +181,7 @@ impl Config { // appear to have this (even when `../lib` is redundant). // NOTE: there are only two paths here, delimited by a `:` let mut entries = OsString::from("$ORIGIN/../lib:"); - entries.push(t!(fs::canonicalize(nix_deps_dir))); - entries.push("/lib"); + entries.push(t!(fs::canonicalize(nix_deps_dir)).join("lib")); entries }; patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); @@ -229,10 +228,10 @@ impl Config { "--retry", "3", "-Sf", - "-o", ]); - curl.arg(tempfile); curl.arg(url); + let f = File::create(tempfile).unwrap(); + curl.stdout(Stdio::from(f)); if !self.check_run(&mut curl) { if self.build.contains("windows-msvc") { println!("Fallback to PowerShell"); @@ -340,9 +339,12 @@ impl Config { let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - let legacy_rustfmt = self.initial_rustc.with_file_name(exe("rustfmt", host)); - if !legacy_rustfmt.exists() { - t!(self.symlink_file(&rustfmt_path, &legacy_rustfmt)); + #[cfg(not(windows))] + { + let legacy_rustfmt = self.initial_rustc.with_file_name(exe("rustfmt", host)); + if !legacy_rustfmt.exists() { + t!(self.symlink_file(&rustfmt_path, &legacy_rustfmt)); + } } if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { @@ -367,6 +369,13 @@ impl Config { if self.should_fix_bins_and_dylibs() { self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if lib.path().extension() == Some(OsStr::new("so")) { + self.fix_bin_or_dylib(&lib.path()); + } + } } self.create(&rustfmt_stamp, &channel); diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 52c3dc0bf75..ff927ed561b 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -124,6 +124,7 @@ pub enum Subcommand { fail_fast: bool, doc_tests: DocTests, rustfix_coverage: bool, + only_modified: bool, }, Bench { paths: Vec<PathBuf>, @@ -301,6 +302,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`", opts.optflag("", "doc", "only run doc tests"); opts.optflag("", "bless", "update all stderr/stdout files of failing ui tests"); opts.optflag("", "force-rerun", "rerun tests even if the inputs are unchanged"); + opts.optflag("", "only-modified", "only run tests that result has been changed"); opts.optopt( "", "compare-mode", @@ -598,6 +600,7 @@ Arguments: rustc_args: matches.opt_strs("rustc-args"), fail_fast: !matches.opt_present("no-fail-fast"), rustfix_coverage: matches.opt_present("rustfix-coverage"), + only_modified: matches.opt_present("only-modified"), doc_tests: if matches.opt_present("doc") { DocTests::Only } else if matches.opt_present("no-doc") { @@ -777,6 +780,13 @@ impl Subcommand { } } + pub fn only_modified(&self) -> bool { + match *self { + Subcommand::Test { only_modified, .. } => only_modified, + _ => false, + } + } + pub fn force_rerun(&self) -> bool { match *self { Subcommand::Test { force_rerun, .. } => force_rerun, diff --git a/src/bootstrap/format.rs b/src/bootstrap/format.rs index bfc57a85cdb..6c9c26faef6 100644 --- a/src/bootstrap/format.rs +++ b/src/bootstrap/format.rs @@ -1,8 +1,8 @@ //! Runs rustfmt on the repository. use crate::builder::Builder; -use crate::util::{output, output_result, program_out_of_date, t}; -use build_helper::git::updated_master_branch; +use crate::util::{output, program_out_of_date, t}; +use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use std::collections::VecDeque; use std::path::{Path, PathBuf}; @@ -80,23 +80,11 @@ fn update_rustfmt_version(build: &Builder<'_>) { /// /// Returns `None` if all files should be formatted. fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, String> { - let Ok(updated_master) = updated_master_branch(Some(&build.config.src)) else { return Ok(None); }; - if !verify_rustfmt_version(build) { return Ok(None); } - let merge_base = - output_result(build.config.git().arg("merge-base").arg(&updated_master).arg("HEAD"))?; - Ok(Some( - output_result( - build.config.git().arg("diff-index").arg("--name-only").arg(merge_base.trim()), - )? - .lines() - .map(|s| s.trim().to_owned()) - .filter(|f| Path::new(f).extension().map_or(false, |ext| ext == "rs")) - .collect(), - )) + get_git_modified_files(Some(&build.config.src), &vec!["rs"]) } #[derive(serde::Deserialize)] diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 8a0c532cfb0..f8835fe11a8 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1510,6 +1510,10 @@ note: if you're sure you want to do this, please open an issue as to why. In the if builder.config.rust_optimize_tests { cmd.arg("--optimize-tests"); } + if builder.config.cmd.only_modified() { + cmd.arg("--only-modified"); + } + let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] }; flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests)); flags.extend(builder.config.cmd.rustc_args().iter().map(|s| s.to_string())); diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index c39e9c5fbc9..94ec2401292 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.14.1 \ No newline at end of file +0.14.3 \ No newline at end of file diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py index 662c9e36694..4e6bcba5e20 100644 --- a/src/ci/stage-build.py +++ b/src/ci/stage-build.py @@ -211,7 +211,8 @@ Duration = float TimerSection = Union[Duration, "Timer"] -def iterate_sections(section: TimerSection, name: str, level: int = 0) -> Iterator[Tuple[int, str, Duration]]: +def iterate_sections(section: TimerSection, name: str, level: int = 0) -> Iterator[ + Tuple[int, str, Duration]]: """ Hierarchically iterate the sections of a timer, in a depth-first order. """ @@ -239,7 +240,7 @@ class Timer: start = get_timestamp() exc = None - child_timer = Timer(parent_names=self.parent_names + (name, )) + child_timer = Timer(parent_names=self.parent_names + (name,)) full_name = " > ".join(child_timer.parent_names) try: LOGGER.info(f"Section `{full_name}` starts") @@ -648,6 +649,16 @@ def print_binary_sizes(pipeline: Pipeline): LOGGER.info(f"Rustc binary size\n{output.getvalue()}") +def print_free_disk_space(pipeline: Pipeline): + usage = shutil.disk_usage(pipeline.opt_artifacts()) + total = usage.total + used = usage.used + free = usage.free + + logging.info( + f"Free disk space: {format_bytes(free)} out of total {format_bytes(total)} ({(used / total) * 100:.2f}% used)") + + def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: List[str]): # Clear and prepare tmp directory shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True) @@ -666,6 +677,7 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L with stage1.section("Gather profiles"): gather_llvm_profiles(pipeline) + print_free_disk_space(pipeline) clear_llvm_files(pipeline) final_build_args += [ @@ -683,6 +695,7 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L with stage2.section("Gather profiles"): gather_rustc_profiles(pipeline) + print_free_disk_space(pipeline) clear_llvm_files(pipeline) final_build_args += [ @@ -702,6 +715,7 @@ def execute_build_pipeline(timer: Timer, pipeline: Pipeline, final_build_args: L with stage3.section("Gather profiles"): gather_llvm_bolt_profiles(pipeline) + print_free_disk_space(pipeline) clear_llvm_files(pipeline) final_build_args += [ "--llvm-bolt-profile-use", @@ -733,5 +747,6 @@ if __name__ == "__main__": raise e finally: timer.print_stats() + print_free_disk_space(pipeline) print_binary_sizes(pipeline) diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 1770c121a0e..c7f120dafea 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -49,10 +49,10 @@ Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) platform security feature. This flag is currently ignored for non-Windows targets. It takes one of the following values: -* `y`, `yes`, `on`, `checks`, or no value: enable Control Flow Guard. +* `y`, `yes`, `on`, `true`, `checks`, or no value: enable Control Flow Guard. * `nochecks`: emit Control Flow Guard metadata without runtime enforcement checks (this should only be used for testing purposes as it does not provide security enforcement). -* `n`, `no`, `off`: do not enable Control Flow Guard (the default). +* `n`, `no`, `off`, `false`: do not enable Control Flow Guard (the default). ## debug-assertions @@ -60,8 +60,8 @@ This flag lets you turn `cfg(debug_assertions)` [conditional compilation](../../reference/conditional-compilation.md#debug_assertions) on or off. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable debug-assertions. -* `n`, `no`, or `off`: disable debug-assertions. +* `y`, `yes`, `on`, `true`, or no value: enable debug-assertions. +* `n`, `no`, `off` or `false`: disable debug-assertions. If not specified, debug assertions are automatically enabled only if the [opt-level](#opt-level) is 0. @@ -82,8 +82,8 @@ Note: The [`-g` flag][option-g-debug] is an alias for `-C debuginfo=2`. This flag controls whether or not the linker includes its default libraries. It takes one of the following values: -* `y`, `yes`, `on`, or no value: include default libraries (the default). -* `n`, `no`, or `off`: exclude default libraries. +* `y`, `yes`, `on`, `true` or no value: include default libraries (the default). +* `n`, `no`, `off` or `false`: exclude default libraries. For example, for gcc flavor linkers, this issues the `-nodefaultlibs` flag to the linker. @@ -93,8 +93,8 @@ the linker. This flag controls whether or not the compiler embeds LLVM bitcode into object files. It takes one of the following values: -* `y`, `yes`, `on`, or no value: put bitcode in rlibs (the default). -* `n`, `no`, or `off`: omit bitcode from rlibs. +* `y`, `yes`, `on`, `true` or no value: put bitcode in rlibs (the default). +* `n`, `no`, `off` or `false`: omit bitcode from rlibs. LLVM bitcode is required when rustc is performing link-time optimization (LTO). It is also required on some targets like iOS ones where vendors look for LLVM @@ -135,8 +135,8 @@ flag][option-emit] for more information. This flag forces the use of frame pointers. It takes one of the following values: -* `y`, `yes`, `on`, or no value: force-enable frame pointers. -* `n`, `no`, or `off`: do not force-enable frame pointers. This does +* `y`, `yes`, `on`, `true` or no value: force-enable frame pointers. +* `n`, `no`, `off` or `false`: do not force-enable frame pointers. This does not necessarily mean frame pointers will be removed. The default behaviour, if frame pointers are not force-enabled, depends on the @@ -147,8 +147,8 @@ target. This flag forces the generation of unwind tables. It takes one of the following values: -* `y`, `yes`, `on`, or no value: Unwind tables are forced to be generated. -* `n`, `no`, or `off`: Unwind tables are not forced to be generated. If unwind +* `y`, `yes`, `on`, `true` or no value: Unwind tables are forced to be generated. +* `n`, `no`, `off` or `false`: Unwind tables are not forced to be generated. If unwind tables are required by the target an error will be emitted. The default if not specified depends on the target. @@ -202,8 +202,8 @@ options should be separated by spaces. This flag controls whether the linker will keep dead code. It takes one of the following values: -* `y`, `yes`, `on`, or no value: keep dead code. -* `n`, `no`, or `off`: remove dead code (the default). +* `y`, `yes`, `on`, `true` or no value: keep dead code. +* `n`, `no`, `off` or `false`: remove dead code (the default). An example of when this flag might be useful is when trying to construct code coverage metrics. @@ -215,8 +215,8 @@ linker will use libraries and objects shipped with Rust instead or those in the It takes one of the following values: * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools. -* `y`, `yes`, `on`: use only libraries/objects shipped with Rust. -* `n`, `no`, or `off`: rely on the user or the linker to provide non-Rust libraries/objects. +* `y`, `yes`, `on`, `true`: use only libraries/objects shipped with Rust. +* `n`, `no`, `off` or `false`: rely on the user or the linker to provide non-Rust libraries/objects. This allows overriding cases when detection fails or user wants to use shipped libraries. @@ -261,8 +261,8 @@ This flag defers LTO optimizations to the linker. See [linker-plugin-LTO](../linker-plugin-lto.md) for more details. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable linker plugin LTO. -* `n`, `no`, or `off`: disable linker plugin LTO (the default). +* `y`, `yes`, `on`, `true` or no value: enable linker plugin LTO. +* `n`, `no`, `off` or `false`: disable linker plugin LTO (the default). * A path to the linker plugin. More specifically this flag will cause the compiler to replace its typical @@ -292,9 +292,9 @@ optimizations](https://llvm.org/docs/LinkTimeOptimization.html) to produce better optimized code, using whole-program analysis, at the cost of longer linking time. It takes one of the following values: -* `y`, `yes`, `on`, `fat`, or no value: perform "fat" LTO which attempts to +* `y`, `yes`, `on`, `true`, `fat`, or no value: perform "fat" LTO which attempts to perform optimizations across all crates within the dependency graph. -* `n`, `no`, `off`: disables LTO. +* `n`, `no`, `off`, `false`: disables LTO. * `thin`: perform ["thin" LTO](http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html). This is similar to "fat", but takes substantially less time to run while @@ -333,8 +333,8 @@ This flag allows you to disable [the red zone](https://en.wikipedia.org/wiki/Red_zone_\(computing\)). It takes one of the following values: -* `y`, `yes`, `on`, or no value: disable the red zone. -* `n`, `no`, or `off`: enable the red zone. +* `y`, `yes`, `on`, `true` or no value: disable the red zone. +* `n`, `no`, `off` or `false`: enable the red zone. The default behaviour, if the flag is not specified, depends on the target. @@ -376,8 +376,8 @@ overflow](../../reference/expressions/operator-expr.md#overflow). When overflow-checks are enabled, a panic will occur on overflow. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: enable overflow checks. -* `n`, `no`, or `off`: disable overflow checks. +* `y`, `yes`, `on`, `true` or no value: enable overflow checks. +* `n`, `no`, `off` or `false`: disable overflow checks. If not specified, overflow checks are enabled if [debug-assertions](#debug-assertions) are enabled, disabled otherwise. @@ -409,8 +409,8 @@ for determining whether or not it is possible to statically or dynamically link with a dependency. For example, `cdylib` crate types may only use static linkage. This flag takes one of the following values: -* `y`, `yes`, `on`, or no value: use dynamic linking. -* `n`, `no`, or `off`: use static linking (the default). +* `y`, `yes`, `on`, `true` or no value: use dynamic linking. +* `n`, `no`, `off` or `false`: use static linking (the default). ## profile-generate @@ -487,24 +487,24 @@ The list of passes should be separated by spaces. This flag controls whether [`rpath`](https://en.wikipedia.org/wiki/Rpath) is enabled. It takes one of the following values: -* `y`, `yes`, `on`, or no value: enable rpath. -* `n`, `no`, or `off`: disable rpath (the default). +* `y`, `yes`, `on`, `true` or no value: enable rpath. +* `n`, `no`, `off` or `false`: disable rpath (the default). ## save-temps This flag controls whether temporary files generated during compilation are deleted once compilation finishes. It takes one of the following values: -* `y`, `yes`, `on`, or no value: save temporary files. -* `n`, `no`, or `off`: delete temporary files (the default). +* `y`, `yes`, `on`, `true` or no value: save temporary files. +* `n`, `no`, `off` or `false`: delete temporary files (the default). ## soft-float This option controls whether `rustc` generates code that emulates floating point instructions in software. It takes one of the following values: -* `y`, `yes`, `on`, or no value: use soft floats. -* `n`, `no`, or `off`: use hardware floats (the default). +* `y`, `yes`, `on`, `true` or no value: use soft floats. +* `n`, `no`, `off` or `false`: use hardware floats (the default). ## split-debuginfo diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md new file mode 100644 index 00000000000..7fb33cd68b4 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -0,0 +1,39 @@ +# `instrument-xray` + +The tracking issue for this feature is: [#102921](https://github.com/rust-lang/rust/issues/102921). + +------------------------ + +Enable generation of NOP sleds for XRay function tracing instrumentation. +For more information on XRay, +read [LLVM documentation](https://llvm.org/docs/XRay.html), +and/or the [XRay whitepaper](http://research.google.com/pubs/pub45287.html). + +Set the `-Z instrument-xray` compiler flag in order to enable XRay instrumentation. + + - `-Z instrument-xray` – use the default settings + - `-Z instrument-xray=skip-exit` – configure a custom setting + - `-Z instrument-xray=ignore-loops,instruction-threshold=300` – + multiple settings separated by commas + +Supported options: + + - `always` – force instrumentation of all functions + - `never` – do no instrument any functions + - `ignore-loops` – ignore presence of loops, + instrument functions based only on instruction count + - `instruction-threshold=10` – set a different instruction threshold for instrumentation + - `skip-entry` – do no instrument function entry + - `skip-exit` – do no instrument function exit + +The default settings are: + + - instrument both entry & exit from functions + - instrument functions with at least 200 instructions, + or containing a non-trivial loop + +Note that `-Z instrument-xray` only enables generation of NOP sleds +which on their own don't do anything useful. +In order to actually trace the functions, +you will need to link a separate runtime library of your choice, +such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). diff --git a/src/doc/unstable-book/src/library-features/char-error-internals.md b/src/doc/unstable-book/src/library-features/char-error-internals.md deleted file mode 100644 index 8013b4988e1..00000000000 --- a/src/doc/unstable-book/src/library-features/char-error-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `char_error_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/doc/unstable-book/src/library-features/int-error-internals.md b/src/doc/unstable-book/src/library-features/int-error-internals.md deleted file mode 100644 index 402e4fa5ef6..00000000000 --- a/src/doc/unstable-book/src/library-features/int-error-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `int_error_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 0271c27b4f5..5e592227d49 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -12,7 +12,6 @@ askama = { version = "0.11", default-features = false, features = ["config"] } itertools = "0.10.1" minifier = "0.2.2" once_cell = "1.10.0" -pulldown-cmark = { version = "0.9.2", default-features = false } regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } serde_json = "1.0" diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 80493b100bb..42bdbddbce6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -242,6 +242,7 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life ty::ReLateBound(..) | ty::ReFree(..) | ty::ReVar(..) + | ty::ReError(_) | ty::RePlaceholder(..) | ty::ReErased => { debug!("cannot clean region {:?}", region); @@ -310,10 +311,12 @@ pub(crate) fn clean_predicate<'tcx>( ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { Some(clean_projection_predicate(bound_predicate.rebind(pred), cx)) } + // FIXME(generic_const_exprs): should this do something? ty::PredicateKind::ConstEvaluatable(..) => None, ty::PredicateKind::WellFormed(..) => None, ty::PredicateKind::Subtype(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) @@ -2206,10 +2209,12 @@ fn clean_maybe_renamed_item<'tcx>( }; let mut extra_attrs = Vec::new(); - if let Some(hir::Node::Item(use_node)) = - import_id.and_then(|def_id| cx.tcx.hir().find_by_def_id(def_id)) + if let Some(import_id) = import_id && + let Some(hir::Node::Item(use_node)) = cx.tcx.hir().find_by_def_id(import_id) { - // We get all the various imports' attributes. + // First, we add the attributes from the current import. + extra_attrs.extend_from_slice(inline::load_attrs(cx, import_id.to_def_id())); + // Then we get all the various imports' attributes. get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs); } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 85dd3881593..ffe6fea7ea4 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -5,13 +5,12 @@ use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; use std::sync::OnceLock as OnceCell; -use std::{cmp, fmt, iter}; +use std::{fmt, iter}; use arrayvec::ArrayVec; use thin_vec::ThinVec; -use rustc_ast::util::comments::beautify_doc_string; -use rustc_ast::{self as ast, AttrStyle}; +use rustc_ast as ast; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -24,6 +23,7 @@ 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_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment}; use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -405,7 +405,7 @@ impl Item { pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { self.item_id .as_def_id() - .map(|did| tcx.get_attrs_unchecked(did).inner_docs()) + .map(|did| inner_docs(tcx.get_attrs_unchecked(did))) .unwrap_or(false) } @@ -874,8 +874,6 @@ pub(crate) trait AttributesExt { fn span(&self) -> Option<rustc_span::Span>; - fn inner_docs(&self) -> bool; - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>; } @@ -894,14 +892,6 @@ impl AttributesExt for [ast::Attribute] { self.iter().find(|attr| attr.doc_str().is_some()).map(|attr| attr.span) } - /// Returns whether the first doc-comment is an inner attribute. - /// - //// If there are no doc-comments, return true. - /// FIXME(#78591): Support both inner and outer attributes on the same item. - fn inner_docs(&self) -> bool { - self.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == AttrStyle::Inner) - } - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> { let sess = tcx.sess; let doc_cfg_active = tcx.features().doc_cfg; @@ -1010,58 +1000,6 @@ impl<I: Iterator<Item = ast::NestedMetaItem>> NestedAttributesExt for I { } } -/// A portion of documentation, extracted from a `#[doc]` attribute. -/// -/// Each variant contains the line number within the complete doc-comment where the fragment -/// starts, as well as the Span where the corresponding doc comment or attribute is located. -/// -/// Included files are kept separate from inline doc comments so that proper line-number -/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are -/// kept separate because of issue #42760. -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) struct DocFragment { - pub(crate) span: rustc_span::Span, - /// The module this doc-comment came from. - /// - /// This allows distinguishing between the original documentation and a pub re-export. - /// If it is `None`, the item was not re-exported. - pub(crate) parent_module: Option<DefId>, - pub(crate) doc: Symbol, - pub(crate) kind: DocFragmentKind, - pub(crate) indent: usize, -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub(crate) enum DocFragmentKind { - /// A doc fragment created from a `///` or `//!` doc comment. - SugaredDoc, - /// A doc fragment created from a "raw" `#[doc=""]` attribute. - RawDoc, -} - -/// The goal of this function is to apply the `DocFragment` transformation that is required when -/// transforming into the final Markdown, which is applying the computed indent to each line in -/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`). -/// -/// Note: remove the trailing newline where appropriate -fn add_doc_fragment(out: &mut String, frag: &DocFragment) { - let s = frag.doc.as_str(); - let mut iter = s.lines(); - if s.is_empty() { - out.push('\n'); - return; - } - while let Some(line) = iter.next() { - if line.chars().any(|c| !c.is_whitespace()) { - assert!(line.len() >= frag.indent); - out.push_str(&line[frag.indent..]); - } else { - out.push_str(line); - } - out.push('\n'); - } -} - /// Collapse a collection of [`DocFragment`]s into one string, /// handling indentation and newlines as needed. pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { @@ -1073,86 +1011,6 @@ pub(crate) fn collapse_doc_fragments(doc_strings: &[DocFragment]) -> String { acc } -/// Removes excess indentation on comments in order for the Markdown -/// to be parsed correctly. This is necessary because the convention for -/// writing documentation is to provide a space between the /// or //! marker -/// and the doc text, but Markdown is whitespace-sensitive. For example, -/// a block of text with four-space indentation is parsed as a code block, -/// so if we didn't unindent comments, these list items -/// -/// /// A list: -/// /// -/// /// - Foo -/// /// - Bar -/// -/// would be parsed as if they were in a code block, which is likely not what the user intended. -fn unindent_doc_fragments(docs: &mut Vec<DocFragment>) { - // `add` is used in case the most common sugared doc syntax is used ("/// "). The other - // fragments kind's lines are never starting with a whitespace unless they are using some - // markdown formatting requiring it. Therefore, if the doc block have a mix between the two, - // we need to take into account the fact that the minimum indent minus one (to take this - // whitespace into account). - // - // For example: - // - // /// hello! - // #[doc = "another"] - // - // In this case, you want "hello! another" and not "hello! another". - let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind) - && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc) - { - // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to - // "decide" how much the minimum indent will be. - 1 - } else { - 0 - }; - - // `min_indent` is used to know how much whitespaces from the start of each lines must be - // removed. Example: - // - // /// hello! - // #[doc = "another"] - // - // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum - // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 - // (5 - 1) whitespaces. - let Some(min_indent) = docs - .iter() - .map(|fragment| { - fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { - if line.chars().all(|c| c.is_whitespace()) { - min_indent - } else { - // Compare against either space or tab, ignoring whether they are - // mixed or not. - let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); - cmp::min(min_indent, whitespace) - + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add } - } - }) - }) - .min() - else { - return; - }; - - for fragment in docs { - if fragment.doc == kw::Empty { - continue; - } - - let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 { - min_indent - add - } else { - min_indent - }; - - fragment.indent = min_indent; - } -} - /// A link that has not yet been rendered. /// /// This link will be turned into a rendered link by [`Item::links`]. @@ -1231,26 +1089,7 @@ impl Attributes { attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>, doc_only: bool, ) -> Attributes { - let mut doc_strings = Vec::new(); - let mut other_attrs = ast::AttrVec::new(); - for (attr, parent_module) in attrs { - if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { - trace!("got doc_str={doc_str:?}"); - let doc = beautify_doc_string(doc_str, comment_kind); - let kind = if attr.is_doc_comment() { - DocFragmentKind::SugaredDoc - } else { - DocFragmentKind::RawDoc - }; - let fragment = DocFragment { span: attr.span, doc, kind, parent_module, indent: 0 }; - doc_strings.push(fragment); - } else if !doc_only { - other_attrs.push(attr.clone()); - } - } - - unindent_doc_fragments(&mut doc_strings); - + let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only); Attributes { doc_strings, other_attrs } } @@ -1269,20 +1108,6 @@ impl Attributes { if out.is_empty() { None } else { Some(out) } } - /// Return the doc-comments on this item, grouped by the module they came from. - /// The module can be different if this is a re-export with added documentation. - /// - /// The last newline is not trimmed so the produced strings are reusable between - /// early and late doc link resolution regardless of their position. - pub(crate) fn prepare_to_doc_link_resolution(&self) -> FxHashMap<Option<DefId>, String> { - let mut res = FxHashMap::default(); - for fragment in &self.doc_strings { - let out_str = res.entry(fragment.parent_module).or_default(); - add_doc_fragment(out_str, fragment); - } - res - } - /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined /// with newlines. pub(crate) fn collapsed_doc_value(&self) -> Option<String> { diff --git a/src/librustdoc/clean/types/tests.rs b/src/librustdoc/clean/types/tests.rs index 71eddf4348f..20627c2cfc1 100644 --- a/src/librustdoc/clean/types/tests.rs +++ b/src/librustdoc/clean/types/tests.rs @@ -2,6 +2,7 @@ use super::*; use crate::clean::collapse_doc_fragments; +use rustc_resolve::rustdoc::{unindent_doc_fragments, DocFragment, DocFragmentKind}; use rustc_span::create_default_session_globals_then; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Symbol; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 0ce43f7db8e..7e16c4701be 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -4,16 +4,16 @@ use rustc_data_structures::sync::{self, Lrc}; use rustc_data_structures::unord::UnordSet; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::json::JsonEmitter; +use rustc_errors::TerminalUrl; use rustc_feature::UnstableFeatures; -use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{HirId, Path, TraitCandidate}; +use rustc_hir::{HirId, Path}; use rustc_interface::interface; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; -use rustc_resolve as resolve; -use rustc_session::config::{self, CrateType, ErrorOutputType}; +use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks}; use rustc_session::lint; use rustc_session::Session; use rustc_span::symbol::sym; @@ -28,17 +28,11 @@ use crate::clean::inline::build_external_trait; use crate::clean::{self, ItemId}; use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; use crate::formats::cache::Cache; -use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink; use crate::passes::{self, Condition::*}; pub(crate) use rustc_session::config::{Input, Options, UnstableOptions}; pub(crate) struct ResolverCaches { - pub(crate) markdown_links: Option<FxHashMap<String, Vec<PreprocessedMarkdownLink>>>, - pub(crate) doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<NodeId>>>, - /// Traits in scope for a given module. - /// See `collect_intra_doc_links::traits_implemented_by` for more details. - pub(crate) traits_in_scope: DefIdMap<Vec<TraitCandidate>>, pub(crate) all_trait_impls: Option<Vec<DefId>>, pub(crate) all_macro_rules: FxHashMap<Symbol, Res<NodeId>>, pub(crate) extern_doc_reachable: DefIdSet, @@ -46,12 +40,6 @@ pub(crate) struct ResolverCaches { pub(crate) struct DocContext<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, - /// Name resolver. Used for intra-doc links. - /// - /// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by - /// [`rustc_interface::Queries::expansion()`]. - // FIXME: see if we can get rid of this RefCell somehow - pub(crate) resolver: Rc<RefCell<interface::BoxedResolver>>, pub(crate) resolver_caches: ResolverCaches, /// Used for normalization. /// @@ -100,13 +88,6 @@ impl<'tcx> DocContext<'tcx> { ret } - pub(crate) fn enter_resolver<F, R>(&self, f: F) -> R - where - F: FnOnce(&mut resolve::Resolver<'_>) -> R, - { - self.resolver.borrow_mut().access(f) - } - /// Call the closure with the given parameters set as /// the substitutions for a type alias' RHS. pub(crate) fn enter_alias<F, R>(&mut self, substs: DefIdMap<clean::SubstParam>, f: F) -> R @@ -164,6 +145,7 @@ pub(crate) fn new_handler( diagnostic_width, false, unstable_opts.track_diagnostics, + TerminalUrl::No, ) .ui_testing(unstable_opts.ui_testing), ) @@ -183,6 +165,7 @@ pub(crate) fn new_handler( diagnostic_width, false, unstable_opts.track_diagnostics, + TerminalUrl::No, ) .ui_testing(unstable_opts.ui_testing), ) @@ -218,6 +201,7 @@ pub(crate) fn create_config( scrape_examples_options, .. }: RustdocOptions, + RenderOptions { document_private, .. }: &RenderOptions, ) -> rustc_interface::Config { // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); @@ -245,6 +229,13 @@ pub(crate) fn create_config( let crate_types = if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; + let resolve_doc_links = if *document_private { + ResolveDocLinks::All + } else { + // Should be `ResolveDocLinks::Exported` in theory, but for some reason rustdoc + // still tries to request resolutions for links on private items. + ResolveDocLinks::All + }; let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false); // plays with error output here! let sessopts = config::Options { @@ -258,6 +249,7 @@ pub(crate) fn create_config( target_triple: target, unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()), actually_rustdoc: true, + resolve_doc_links, unstable_opts, error_format, diagnostic_width, @@ -313,7 +305,6 @@ pub(crate) fn create_config( pub(crate) fn run_global_ctxt( tcx: TyCtxt<'_>, - resolver: Rc<RefCell<interface::BoxedResolver>>, resolver_caches: ResolverCaches, show_coverage: bool, render_options: RenderOptions, @@ -348,7 +339,6 @@ pub(crate) fn run_global_ctxt( let mut ctxt = DocContext { tcx, - resolver, resolver_caches, param_env: ParamEnv::empty(), external_traits: Default::default(), diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 37a1005cba1..57c41b57311 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; +use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError, TerminalUrl}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; use rustc_interface::interface; @@ -557,6 +557,7 @@ pub(crate) fn make_test( Some(80), false, false, + TerminalUrl::No, ) .supports_color(); @@ -571,6 +572,7 @@ pub(crate) fn make_test( None, false, false, + TerminalUrl::No, ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser @@ -756,6 +758,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { None, false, false, + TerminalUrl::No, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1644d1c5a29..3c10eb524d7 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -96,13 +96,19 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>, to ); if tooltip != Tooltip::None { + let edition_code; write!( out, - "<div class='tooltip'{}>ⓘ</div>", - if let Tooltip::Edition(edition_info) = tooltip { - format!(" data-edition=\"{}\"", edition_info) - } else { - String::new() + "<a href=\"#\" class=\"tooltip\" title=\"{}\">ⓘ</a>", + match tooltip { + Tooltip::Ignore => "This example is not tested", + Tooltip::CompileFail => "This example deliberately fails to compile", + Tooltip::ShouldPanic => "This example panics", + Tooltip::Edition(edition) => { + edition_code = format!("This example runs with edition {edition}"); + &edition_code + } + Tooltip::None => unreachable!(), }, ); } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index fb7c34118a4..33180439393 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -28,6 +28,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; +pub(crate) use rustc_resolve::rustdoc::main_body_opts; use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; @@ -46,6 +47,7 @@ use crate::html::escape::Escape; use crate::html::format::Buffer; use crate::html::highlight; use crate::html::length_limit::HtmlWithLimit; +use crate::html::render::small_url_encode; use crate::html::toc::TocBuilder; use pulldown_cmark::{ @@ -57,15 +59,6 @@ mod tests; const MAX_HEADER_LEVEL: u32 = 6; -/// Options for rendering Markdown in the main body of documentation. -pub(crate) fn main_body_opts() -> Options { - Options::ENABLE_TABLES - | Options::ENABLE_FOOTNOTES - | Options::ENABLE_STRIKETHROUGH - | Options::ENABLE_TASKLISTS - | Options::ENABLE_SMART_PUNCTUATION -} - /// Options for rendering Markdown in summaries (e.g., in search results). pub(crate) fn summary_opts() -> Options { Options::ENABLE_TABLES @@ -294,47 +287,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> { doctest::make_test(&test, krate, false, &Default::default(), edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; - // These characters don't need to be escaped in a URI. - // See https://url.spec.whatwg.org/#query-percent-encode-set - // and https://url.spec.whatwg.org/#urlencoded-parsing - // and https://url.spec.whatwg.org/#url-code-points - fn dont_escape(c: u8) -> bool { - (b'a' <= c && c <= b'z') - || (b'A' <= c && c <= b'Z') - || (b'0' <= c && c <= b'9') - || c == b'-' - || c == b'_' - || c == b'.' - || c == b',' - || c == b'~' - || c == b'!' - || c == b'\'' - || c == b'(' - || c == b')' - || c == b'*' - || c == b'/' - || c == b';' - || c == b':' - || c == b'?' - // As described in urlencoded-parsing, the - // first `=` is the one that separates key from - // value. Following `=`s are part of the value. - || c == b'=' - } - let mut test_escaped = String::new(); - for b in test.bytes() { - if dont_escape(b) { - test_escaped.push(char::from(b)); - } else if b == b' ' { - // URL queries are decoded with + replaced with SP - test_escaped.push('+'); - } else if b == b'%' { - test_escaped.push('%'); - test_escaped.push('%'); - } else { - write!(test_escaped, "%{:02X}", b).unwrap(); - } - } + let test_escaped = small_url_encode(test); Some(format!( r#"<a class="test-arrow" target="_blank" href="{}?code={}{}&edition={}">Run</a>"#, url, test_escaped, channel, edition, @@ -787,11 +740,7 @@ pub(crate) fn find_testable_code<T: doctest::Tester>( } Event::Text(ref s) if register_header.is_some() => { let level = register_header.unwrap(); - if s.is_empty() { - tests.register_header("", level); - } else { - tests.register_header(s, level); - } + tests.register_header(s, level); register_header = None; } _ => {} @@ -1227,14 +1176,23 @@ pub(crate) fn short_markdown_summary(markdown: &str, link_names: &[RenderedLink] /// - Headings, links, and formatting are stripped. /// - Inline code is rendered as-is, surrounded by backticks. /// - HTML and code blocks are ignored. -pub(crate) fn plain_text_summary(md: &str) -> String { +pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> String { if md.is_empty() { return String::new(); } let mut s = String::with_capacity(md.len() * 3 / 2); - for event in Parser::new_ext(md, summary_opts()) { + let mut replacer = |broken_link: BrokenLink<'_>| { + link_names + .iter() + .find(|link| link.original_text.as_str() == &*broken_link.reference) + .map(|link| (link.href.as_str().into(), link.new_text.as_str().into())) + }; + + let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer)); + + for event in p { match &event { Event::Text(text) => s.push_str(text), Event::Code(code) => { diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 5878c58264e..e05635a0207 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -249,7 +249,7 @@ fn test_short_markdown_summary() { #[test] fn test_plain_text_summary() { fn t(input: &str, expect: &str) { - let output = plain_text_summary(input); + let output = plain_text_summary(input, &[]); assert_eq!(output, expect, "original: {}", input); } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index b59645ec2e2..6762fba9275 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -18,7 +18,7 @@ use super::search_index::build_index; use super::write_shared::write_shared; use super::{ collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes, - LinkFromSrc, NameDoc, StylePath, + LinkFromSrc, StylePath, }; use crate::clean::{self, types::ExternalLocation, ExternalCrate}; @@ -182,7 +182,10 @@ impl<'tcx> Context<'tcx> { }; title.push_str(" - Rust"); let tyname = it.type_(); - let desc = it.doc_value().as_ref().map(|doc| plain_text_summary(doc)); + let desc = it + .doc_value() + .as_ref() + .map(|doc| plain_text_summary(doc, &it.link_names(&self.cache()))); let desc = if let Some(desc) = desc { desc } else if it.is_crate() { @@ -256,7 +259,7 @@ impl<'tcx> Context<'tcx> { } /// Construct a map of items shown in the sidebar to a plain-text summary of their docs. - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> { + fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<String>> { // BTreeMap instead of HashMap to get a sorted output let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); let mut inserted: FxHashMap<ItemType, FxHashSet<Symbol>> = FxHashMap::default(); @@ -274,10 +277,7 @@ impl<'tcx> Context<'tcx> { if inserted.entry(short).or_default().insert(myname) { let short = short.to_string(); let myname = myname.to_string(); - map.entry(short).or_default().push(( - myname, - Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))), - )); + map.entry(short).or_default().push(myname); } } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 816a8f4e274..2086faf78ac 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -38,7 +38,7 @@ pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc}; use std::collections::VecDeque; use std::default::Default; -use std::fmt; +use std::fmt::{self, Write}; use std::fs; use std::iter::Peekable; use std::path::PathBuf; @@ -83,9 +83,6 @@ use crate::scrape_examples::{CallData, CallLocation}; use crate::try_none; use crate::DOC_RUST_LANG_ORG_CHANNEL; -/// A pair of name and its optional document. -pub(crate) type NameDoc = (String, Option<String>); - pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } @@ -1313,7 +1310,7 @@ pub(crate) fn notable_traits_button(ty: &clean::Type, cx: &mut Context<'_>) -> O if has_notable_trait { cx.types_with_notable_traits.insert(ty.clone()); Some(format!( - " <a href=\"#\" class=\"notable-traits\" data-ty=\"{ty}\">ⓘ</a>", + " <a href=\"#\" class=\"tooltip\" data-notable-ty=\"{ty}\">ⓘ</a>", ty = Escape(&format!("{:#}", ty.print(cx))), )) } else { @@ -2020,31 +2017,60 @@ fn get_associated_constants( .collect::<Vec<_>>() } -// The point is to url encode any potential character from a type with genericity. -fn small_url_encode(s: String) -> String { +pub(crate) fn small_url_encode(s: String) -> String { + // These characters don't need to be escaped in a URI. + // See https://url.spec.whatwg.org/#query-percent-encode-set + // and https://url.spec.whatwg.org/#urlencoded-parsing + // and https://url.spec.whatwg.org/#url-code-points + fn dont_escape(c: u8) -> bool { + (b'a' <= c && c <= b'z') + || (b'A' <= c && c <= b'Z') + || (b'0' <= c && c <= b'9') + || c == b'-' + || c == b'_' + || c == b'.' + || c == b',' + || c == b'~' + || c == b'!' + || c == b'\'' + || c == b'(' + || c == b')' + || c == b'*' + || c == b'/' + || c == b';' + || c == b':' + || c == b'?' + // As described in urlencoded-parsing, the + // first `=` is the one that separates key from + // value. Following `=`s are part of the value. + || c == b'=' + } let mut st = String::new(); let mut last_match = 0; - for (idx, c) in s.char_indices() { - let escaped = match c { - '<' => "%3C", - '>' => "%3E", - ' ' => "%20", - '?' => "%3F", - '\'' => "%27", - '&' => "%26", - ',' => "%2C", - ':' => "%3A", - ';' => "%3B", - '[' => "%5B", - ']' => "%5D", - '"' => "%22", - _ => continue, - }; + for (idx, b) in s.bytes().enumerate() { + if dont_escape(b) { + continue; + } - st += &s[last_match..idx]; - st += escaped; - // NOTE: we only expect single byte characters here - which is fine as long as we - // only match single byte characters + if last_match != idx { + // Invariant: `idx` must be the first byte in a character at this point. + st += &s[last_match..idx]; + } + if b == b' ' { + // URL queries are decoded with + replaced with SP. + // While the same is not true for hashes, rustdoc only needs to be + // consistent with itself when encoding them. + st += "+"; + } else if b == b'%' { + st += "%%"; + } else { + write!(st, "%{:02X}", b).unwrap(); + } + // Invariant: if the current byte is not at the start of a multi-byte character, + // we need to get down here so that when the next turn of the loop comes around, + // last_match winds up equalling idx. + // + // In other words, dont_escape must always return `false` in multi-byte character. last_match = idx + 1; } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index c2d24e51484..d0f497321ab 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1081,10 +1081,10 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); - write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx)); write!( w, - "type {}{}{where_clause} = {type_};", + "{}type {}{}{where_clause} = {type_};", + visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), it.name.unwrap(), t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), @@ -1109,7 +1109,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_item(w, |w| { render_attributes_in_pre(w, it, ""); - render_union(w, it, Some(&s.generics), &s.fields, "", cx); + render_union(w, it, Some(&s.generics), &s.fields, cx); }); document(w, cx, it, None, HeadingOffset::H2); @@ -1138,13 +1138,11 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: <a href=\"#{id}\" class=\"anchor field\">§</a>\ <code>{name}: {ty}</code>\ </span>", - id = id, - name = name, shortty = ItemType::StructField, ty = ty.print(cx), ); if let Some(stability_class) = field.stability_class(cx.tcx()) { - write!(w, "<span class=\"stab {stab}\"></span>", stab = stability_class); + write!(w, "<span class=\"stab {stability_class}\"></span>"); } document(w, cx, field, Some(it), HeadingOffset::H3); } @@ -1242,7 +1240,6 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: w, "<section id=\"{id}\" class=\"variant\">\ <a href=\"#{id}\" class=\"anchor\">§</a>", - id = id, ); render_stability_since_raw_with_extra( w, @@ -1280,8 +1277,11 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: if let Some((heading, fields)) = heading_and_fields { let variant_id = cx.derive_id(format!("{}.{}.fields", ItemType::Variant, variant.name.unwrap())); - write!(w, "<div class=\"sub-variant\" id=\"{id}\">", id = variant_id); - write!(w, "<h4>{heading}</h4>", heading = heading); + write!( + w, + "<div class=\"sub-variant\" id=\"{variant_id}\">\ + <h4>{heading}</h4>", + ); document_non_exhaustive(w, variant); for field in fields { match *field.kind { @@ -1299,7 +1299,6 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: <a href=\"#{id}\" class=\"anchor field\">§</a>\ <code>{f}: {t}</code>\ </span>", - id = id, f = field.name.unwrap(), t = ty.print(cx) ); @@ -1450,11 +1449,9 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean w, "<span id=\"{id}\" class=\"{item_type} small-section-header\">\ <a href=\"#{id}\" class=\"anchor field\">§</a>\ - <code>{name}: {ty}</code>\ + <code>{field_name}: {ty}</code>\ </span>", item_type = ItemType::StructField, - id = id, - name = field_name, ty = ty.print(cx) ); document(w, cx, field, Some(it), HeadingOffset::H3); @@ -1628,7 +1625,6 @@ fn render_union( it: &clean::Item, g: Option<&clean::Generics>, fields: &[clean::Item], - tab: &str, cx: &Context<'_>, ) { let tcx = cx.tcx(); @@ -1651,7 +1647,7 @@ fn render_union( w.write_str(" "); } - write!(w, "{{\n{}", tab); + write!(w, "{{\n"); let count_fields = fields.iter().filter(|f| matches!(*f.kind, clean::StructFieldItem(..))).count(); let toggle = should_hide_fields(count_fields); @@ -1663,17 +1659,16 @@ fn render_union( if let clean::StructFieldItem(ref ty) = *field.kind { write!( w, - " {}{}: {},\n{}", + " {}{}: {},\n", visibility_print_with_space(field.visibility(tcx), field.item_id, cx), field.name.unwrap(), - ty.print(cx), - tab + ty.print(cx) ); } } if it.has_stripped_entries().unwrap() { - write!(w, " /* private fields */\n{}", tab); + write!(w, " /* private fields */\n"); } if toggle { toggle_close(w); @@ -1842,8 +1837,8 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { if layout.abi.is_unsized() { write!(w, "(unsized)"); } else { - let bytes = layout.size.bytes() - tag_size; - write!(w, "{size} byte{pl}", size = bytes, pl = if bytes == 1 { "" } else { "s" },); + let size = layout.size.bytes() - tag_size; + write!(w, "{size} byte{pl}", pl = if size == 1 { "" } else { "s" },); } } @@ -1898,7 +1893,7 @@ fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) { for (index, layout) in variants.iter_enumerated() { let name = adt.variant(index).name; - write!(w, "<li><code>{name}</code>: ", name = name); + write!(w, "<li><code>{name}</code>: "); write_size_of_layout(w, layout, tag_size); writeln!(w, "</li>"); } diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 2a9548712f0..476dd606680 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -174,6 +174,14 @@ h1, h2, h3, h4 { .top-doc .docblock > h4 { border-bottom: 1px solid var(--headings-border-bottom-color); } +/* while line-height 1.5 is required for any "block of text", + which WCAG defines as more than one sentence, it looks weird for + very large main headers */ +h1, h2 { + line-height: 1.25; + padding-top: 3px; + padding-bottom: 9px; +} h3.code-header { font-size: 1.125rem; /* 18px */ } @@ -707,8 +715,8 @@ h2.small-section-header > .anchor { .main-heading a:hover, .example-wrap > pre.rust a:hover, .all-items a:hover, -.docblock a:not(.test-arrow):not(.scrape-help):hover, -.docblock-short a:not(.test-arrow):not(.scrape-help):hover, +.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover, +.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover, .item-info a { text-decoration: underline; } @@ -1101,44 +1109,8 @@ pre.rust .doccomment { display: block; left: -25px; top: 5px; -} - -.example-wrap .tooltip:hover::after { - padding: 5px 3px 3px 3px; - border-radius: 6px; - margin-left: 5px; - font-size: 1rem; - border: 1px solid var(--border-color); - position: absolute; - width: max-content; - top: -2px; - z-index: 1; - background-color: var(--tooltip-background-color); - color: var(--tooltip-color); -} - -.example-wrap .tooltip:hover::before { - content: " "; - position: absolute; - top: 50%; - left: 16px; - margin-top: -5px; - z-index: 1; - border: 5px solid transparent; - border-right-color: var(--tooltip-background-color); -} - -.example-wrap.ignore .tooltip:hover::after { - content: "This example is not tested"; -} -.example-wrap.compile_fail .tooltip:hover::after { - content: "This example deliberately fails to compile"; -} -.example-wrap.should_panic .tooltip:hover::after { - content: "This example panics"; -} -.example-wrap.edition .tooltip:hover::after { - content: "This code runs with edition " attr(data-edition); + margin: 0; + line-height: 1; } .example-wrap.compile_fail .tooltip, @@ -1205,7 +1177,7 @@ a.test-arrow:hover { border-right: 3px solid var(--target-border-color); } -.notable-traits { +.code-header a.tooltip { color: inherit; margin-right: 15px; position: relative; @@ -1214,7 +1186,7 @@ a.test-arrow:hover { /* placeholder thunk so that the mouse can easily travel from "(i)" to popover the resulting "hover tunnel" is a stepped triangle, approximating https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown */ -.notable-traits:hover::after { +a.tooltip:hover::after { position: absolute; top: calc(100% - 10px); left: -15px; @@ -1223,11 +1195,11 @@ a.test-arrow:hover { content: "\00a0"; } -.notable .content { +.popover.tooltip .content { margin: 0.25em 0.5em; } -.notable .content pre, .notable .content code { +.popover.tooltip .content pre, .popover.tooltip .content code { background: transparent; margin: 0; padding: 0; @@ -1235,7 +1207,7 @@ a.test-arrow:hover { white-space: pre-wrap; } -.notable .content > h3:first-child { +.popover.tooltip .content > h3:first-child { margin: 0 0 5px 0; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index b5a2cf7f28b..472a725f053 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -74,8 +74,6 @@ Original by Dempfi (https://github.com/dempfi/ayu) --test-arrow-hover-background-color: rgba(57, 175, 215, 0.368); --target-background-color: rgba(255, 236, 164, 0.06); --target-border-color: rgba(255, 180, 76, 0.85); - --tooltip-background-color: #314559; - --tooltip-color: #c5c5c5; --kbd-color: #c5c5c5; --kbd-background: #314559; --kbd-box-shadow-color: #5c6773; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index b84d87c4a54..5612bde96a8 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -69,8 +69,6 @@ --test-arrow-hover-background-color: #4e8bca; --target-background-color: #494a3d; --target-border-color: #bb7410; - --tooltip-background-color: #000; - --tooltip-color: #fff; --kbd-color: #000; --kbd-background: #fafbfc; --kbd-box-shadow-color: #c6cbd1; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 342274e6767..34b35c405a8 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -69,8 +69,6 @@ --test-arrow-hover-background-color: #4e8bca; --target-background-color: #fdffd3; --target-border-color: #ad7c37; - --tooltip-background-color: #000; - --tooltip-color: #fff; --kbd-color: #000; --kbd-background: #fafbfc; --kbd-box-shadow-color: #c6cbd1; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index c66f500f423..5e8c0e8d10c 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -378,7 +378,7 @@ function loadCss(cssUrl) { } ev.preventDefault(); searchState.defocus(); - window.hideAllModals(true); // true = reset focus for notable traits + window.hideAllModals(true); // true = reset focus for tooltips } function handleShortcut(ev) { @@ -455,10 +455,7 @@ function loadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block " + shortty; - for (const item of filtered) { - const name = item[0]; - const desc = item[1]; // can be null - + for (const name of filtered) { let path; if (shortty === "mod") { path = name + "/index.html"; @@ -468,7 +465,6 @@ function loadCss(cssUrl) { const current_page = document.location.href.split("/").pop(); const link = document.createElement("a"); link.href = path; - link.title = desc; if (path === current_page) { link.className = "current"; } @@ -788,17 +784,17 @@ function loadCss(cssUrl) { // we need to switch away from mobile mode and make the main content area scrollable. hideSidebar(); } - if (window.CURRENT_NOTABLE_ELEMENT) { - // As a workaround to the behavior of `contains: layout` used in doc togglers, the - // notable traits popup is positioned using javascript. + if (window.CURRENT_TOOLTIP_ELEMENT) { + // As a workaround to the behavior of `contains: layout` used in doc togglers, + // tooltip popovers are positioned using javascript. // // This means when the window is resized, we need to redo the layout. - const base = window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE; - const force_visible = base.NOTABLE_FORCE_VISIBLE; - hideNotable(false); + const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE; + const force_visible = base.TOOLTIP_FORCE_VISIBLE; + hideTooltip(false); if (force_visible) { - showNotable(base); - base.NOTABLE_FORCE_VISIBLE = true; + showTooltip(base); + base.TOOLTIP_FORCE_VISIBLE = true; } } }); @@ -826,27 +822,35 @@ function loadCss(cssUrl) { }); }); - function showNotable(e) { - if (!window.NOTABLE_TRAITS) { + function showTooltip(e) { + const notable_ty = e.getAttribute("data-notable-ty"); + if (!window.NOTABLE_TRAITS && notable_ty) { const data = document.getElementById("notable-traits-data"); if (data) { window.NOTABLE_TRAITS = JSON.parse(data.innerText); } else { - throw new Error("showNotable() called on page without any notable traits!"); + throw new Error("showTooltip() called with notable without any notable traits!"); } } - if (window.CURRENT_NOTABLE_ELEMENT && window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE === e) { + if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) { // Make this function idempotent. return; } window.hideAllModals(false); - const ty = e.getAttribute("data-ty"); const wrapper = document.createElement("div"); - wrapper.innerHTML = "<div class=\"content\">" + window.NOTABLE_TRAITS[ty] + "</div>"; - wrapper.className = "notable popover"; + if (notable_ty) { + wrapper.innerHTML = "<div class=\"content\">" + + window.NOTABLE_TRAITS[notable_ty] + "</div>"; + } else if (e.getAttribute("title") !== undefined) { + const titleContent = document.createElement("div"); + titleContent.className = "content"; + titleContent.appendChild(document.createTextNode(e.getAttribute("title"))); + wrapper.appendChild(titleContent); + } + wrapper.className = "tooltip popover"; const focusCatcher = document.createElement("div"); focusCatcher.setAttribute("tabindex", "0"); - focusCatcher.onfocus = hideNotable; + focusCatcher.onfocus = hideTooltip; wrapper.appendChild(focusCatcher); const pos = e.getBoundingClientRect(); // 5px overlap so that the mouse can easily travel from place to place @@ -868,62 +872,62 @@ function loadCss(cssUrl) { ); } wrapper.style.visibility = ""; - window.CURRENT_NOTABLE_ELEMENT = wrapper; - window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE = e; + window.CURRENT_TOOLTIP_ELEMENT = wrapper; + window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; wrapper.onpointerleave = function(ev) { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - if (!e.NOTABLE_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) { - hideNotable(true); + if (!e.TOOLTIP_FORCE_VISIBLE && !elemIsInParent(event.relatedTarget, e)) { + hideTooltip(true); } }; } - function notableBlurHandler(event) { - if (window.CURRENT_NOTABLE_ELEMENT && - !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT) && - !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT) && - !elemIsInParent(document.activeElement, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) && - !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE) + function tooltipBlurHandler(event) { + if (window.CURRENT_TOOLTIP_ELEMENT && + !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT) && + !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT) && + !elemIsInParent(document.activeElement, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) && + !elemIsInParent(event.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE) ) { // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari. - // When I click the button on an already-opened notable trait popover, Safari + // When I click the button on an already-opened tooltip popover, Safari // hides the popover and then immediately shows it again, while everyone else hides it // and it stays hidden. // // To work around this, make sure the click finishes being dispatched before - // hiding the popover. Since `hideNotable()` is idempotent, this makes Safari behave + // hiding the popover. Since `hideTooltip()` is idempotent, this makes Safari behave // consistently with the other two. - setTimeout(() => hideNotable(false), 0); + setTimeout(() => hideTooltip(false), 0); } } - function hideNotable(focus) { - if (window.CURRENT_NOTABLE_ELEMENT) { - if (window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE) { + function hideTooltip(focus) { + if (window.CURRENT_TOOLTIP_ELEMENT) { + if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) { if (focus) { - window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.focus(); + window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus(); } - window.CURRENT_NOTABLE_ELEMENT.NOTABLE_BASE.NOTABLE_FORCE_VISIBLE = false; + window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; } const body = document.getElementsByTagName("body")[0]; - body.removeChild(window.CURRENT_NOTABLE_ELEMENT); - window.CURRENT_NOTABLE_ELEMENT = null; + body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); + window.CURRENT_TOOLTIP_ELEMENT = null; } } - onEachLazy(document.getElementsByClassName("notable-traits"), e => { + onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = function() { - this.NOTABLE_FORCE_VISIBLE = this.NOTABLE_FORCE_VISIBLE ? false : true; - if (window.CURRENT_NOTABLE_ELEMENT && !this.NOTABLE_FORCE_VISIBLE) { - hideNotable(true); + this.TOOLTIP_FORCE_VISIBLE = this.TOOLTIP_FORCE_VISIBLE ? false : true; + if (window.CURRENT_TOOLTIP_ELEMENT && !this.TOOLTIP_FORCE_VISIBLE) { + hideTooltip(true); } else { - showNotable(this); - window.CURRENT_NOTABLE_ELEMENT.setAttribute("tabindex", "0"); - window.CURRENT_NOTABLE_ELEMENT.focus(); - window.CURRENT_NOTABLE_ELEMENT.onblur = notableBlurHandler; + showTooltip(this); + window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); + window.CURRENT_TOOLTIP_ELEMENT.focus(); + window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; } return false; }; @@ -932,16 +936,16 @@ function loadCss(cssUrl) { if (ev.pointerType !== "mouse") { return; } - showNotable(this); + showTooltip(this); }; e.onpointerleave = function(ev) { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } - if (!this.NOTABLE_FORCE_VISIBLE && - !elemIsInParent(ev.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) { - hideNotable(true); + if (!this.TOOLTIP_FORCE_VISIBLE && + !elemIsInParent(ev.relatedTarget, window.CURRENT_TOOLTIP_ELEMENT)) { + hideTooltip(true); } }; }); @@ -1043,14 +1047,14 @@ function loadCss(cssUrl) { } /** - * Hide popover menus, notable trait tooltips, and the sidebar (if applicable). + * Hide popover menus, clickable tooltips, and the sidebar (if applicable). * - * Pass "true" to reset focus for notable traits. + * Pass "true" to reset focus for tooltip popovers. */ window.hideAllModals = function(switchFocus) { hideSidebar(); window.hidePopoverMenus(); - hideNotable(switchFocus); + hideTooltip(switchFocus); }; /** diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 251e806c2d9..ea1875d8e27 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -142,13 +142,11 @@ function initSearch(rawSearchIndex) { } function itemTypeFromName(typename) { - for (let i = 0, len = itemTypes.length; i < len; ++i) { - if (itemTypes[i] === typename) { - return i; - } + const index = itemTypes.findIndex(i => i === typename); + if (index < 0) { + throw new Error("Unknown type filter `" + typename + "`"); } - - throw new Error("Unknown type filter `" + typename + "`"); + return index; } /** diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 64108c88285..feefb8b69d1 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -31,6 +31,7 @@ extern crate tracing; // // Dependencies listed in Cargo.toml do not need `extern crate`. +extern crate pulldown_cmark; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; @@ -741,7 +742,7 @@ fn main_args(at_args: &[String]) -> MainResult { (false, true) => { let input = options.input.clone(); let edition = options.edition; - let config = core::create_config(options); + let config = core::create_config(options, &render_options); // `markdown::render` can invoke `doctest::make_test`, which // requires session globals and a thread pool, so we use @@ -774,7 +775,7 @@ fn main_args(at_args: &[String]) -> MainResult { let scrape_examples_options = options.scrape_examples_options.clone(); let bin_crate = options.bin_crate; - let config = core::create_config(options); + let config = core::create_config(options, &render_options); interface::run_compiler(config, |compiler| { let sess = compiler.session(); @@ -792,35 +793,25 @@ fn main_args(at_args: &[String]) -> MainResult { } compiler.enter(|queries| { - // We need to hold on to the complete resolver, so we cause everything to be - // cloned for the analysis passes to use. Suboptimal, but necessary in the - // current architecture. - // FIXME(#83761): Resolver cloning can lead to inconsistencies between data in the - // two copies because one of the copies can be modified after `TyCtxt` construction. - let (resolver, resolver_caches) = { + let resolver_caches = { let expansion = abort_on_err(queries.expansion(), sess); let (krate, resolver, _) = &*expansion.borrow(); let resolver_caches = resolver.borrow_mut().access(|resolver| { - collect_intra_doc_links::early_resolve_intra_doc_links( - resolver, - krate, - render_options.document_private, - ) + collect_intra_doc_links::early_resolve_intra_doc_links(resolver, krate) }); - (resolver.clone(), resolver_caches) + resolver_caches }; if sess.diagnostic().has_errors_or_lint_errors().is_some() { sess.fatal("Compilation failed, aborting rustdoc"); } - let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess); + let mut gcx = abort_on_err(queries.global_ctxt(), sess); - global_ctxt.enter(|tcx| { + gcx.enter(|tcx| { let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || { core::run_global_ctxt( tcx, - resolver, resolver_caches, show_coverage, render_options, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 8435972bb11..0e2191185eb 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -15,7 +15,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::Mutability; use rustc_middle::ty::{DefIdTree, Ty, TyCtxt}; use rustc_middle::{bug, ty}; -use rustc_resolve::ParentScope; +use rustc_resolve::rustdoc::MalformedGenerics; +use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, strip_generics_from_path}; use rustc_session::lint::Lint; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -23,7 +24,6 @@ use rustc_span::BytePos; use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; -use std::mem; use std::ops::Range; use crate::clean::{self, utils::find_nearest_parent_module}; @@ -179,47 +179,6 @@ enum ResolutionFailure<'a> { NotResolved(UnresolvedPath<'a>), } -#[derive(Clone, Copy, Debug)] -enum MalformedGenerics { - /// This link has unbalanced angle brackets. - /// - /// For example, `Vec<T` should trigger this, as should `Vec<T>>`. - UnbalancedAngleBrackets, - /// The generics are not attached to a type. - /// - /// For example, `<T>` should trigger this. - /// - /// This is detected by checking if the path is empty after the generics are stripped. - MissingType, - /// The link uses fully-qualified syntax, which is currently unsupported. - /// - /// For example, `<Vec as IntoIterator>::into_iter` should trigger this. - /// - /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside - /// angle brackets. - HasFullyQualifiedSyntax, - /// The link has an invalid path separator. - /// - /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not** - /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be - /// called. - /// - /// Note that this will also **not** be triggered if the invalid path separator is inside angle - /// brackets because rustdoc mostly ignores what's inside angle brackets (except for - /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)). - /// - /// This is detected by checking if there is a colon followed by a non-colon in the link. - InvalidPathSeparator, - /// The link has too many angle brackets. - /// - /// For example, `Vec<<T>>` should trigger this. - TooManyAngleBrackets, - /// The link has empty angle brackets. - /// - /// For example, `Vec<>` should trigger this. - EmptyAngleBrackets, -} - #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub(crate) enum UrlFragment { Item(DefId), @@ -338,7 +297,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { match ty_res { Res::Def(DefKind::Enum, did) => match tcx.type_of(did).kind() { ty::Adt(def, _) if def.is_enum() => { - if let Some(field) = def.all_fields().find(|f| f.name == variant_field_name) { + if let Some(variant) = def.variants().iter().find(|v| v.name == variant_name) + && let Some(field) = variant.fields.iter().find(|f| f.name == variant_field_name) { Ok((ty_res, field.did)) } else { Err(UnresolvedPath { @@ -407,10 +367,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) } - /// Convenience wrapper around `resolve_rustdoc_path`. + /// Convenience wrapper around `doc_link_resolutions`. /// /// This also handles resolving `true` and `false` as booleans. - /// NOTE: `resolve_rustdoc_path` knows only about paths, not about types. + /// NOTE: `doc_link_resolutions` knows only about paths, not about types. /// Associated items will never be resolved by this function. fn resolve_path( &self, @@ -426,17 +386,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // Resolver doesn't know about true, false, and types that aren't paths (e.g. `()`). let result = self .cx - .resolver_caches - .doc_link_resolutions - .get(&(Symbol::intern(path_str), ns, module_id)) + .tcx + .doc_link_resolutions(module_id) + .get(&(Symbol::intern(path_str), ns)) .copied() - .unwrap_or_else(|| { - self.cx.enter_resolver(|resolver| { - let parent_scope = - ParentScope::module(resolver.expect_module(module_id), resolver); - resolver.resolve_rustdoc_path(path_str, ns, parent_scope) - }) - }) + .unwrap_or_else(|| panic!("no resolution for {:?} {:?} {:?}", path_str, ns, module_id)) .and_then(|res| res.try_into().ok()) .or_else(|| resolve_primitive(path_str, ns)); debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); @@ -562,27 +516,27 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // FIXME: Only simple types are supported here, see if we can support // other types such as Tuple, Array, Slice, etc. // See https://github.com/rust-lang/rust/issues/90703#issuecomment-1004263455 - Some(tcx.mk_ty(match prim { - Bool => ty::Bool, - Str => ty::Str, - Char => ty::Char, - Never => ty::Never, - I8 => ty::Int(ty::IntTy::I8), - I16 => ty::Int(ty::IntTy::I16), - I32 => ty::Int(ty::IntTy::I32), - I64 => ty::Int(ty::IntTy::I64), - I128 => ty::Int(ty::IntTy::I128), - Isize => ty::Int(ty::IntTy::Isize), - F32 => ty::Float(ty::FloatTy::F32), - F64 => ty::Float(ty::FloatTy::F64), - U8 => ty::Uint(ty::UintTy::U8), - U16 => ty::Uint(ty::UintTy::U16), - U32 => ty::Uint(ty::UintTy::U32), - U64 => ty::Uint(ty::UintTy::U64), - U128 => ty::Uint(ty::UintTy::U128), - Usize => ty::Uint(ty::UintTy::Usize), + Some(match prim { + Bool => tcx.types.bool, + Str => tcx.types.str_, + Char => tcx.types.char, + Never => tcx.types.never, + I8 => tcx.types.i8, + I16 => tcx.types.i16, + I32 => tcx.types.i32, + I64 => tcx.types.i64, + I128 => tcx.types.i128, + Isize => tcx.types.isize, + F32 => tcx.types.f32, + F64 => tcx.types.f64, + U8 => tcx.types.u8, + U16 => tcx.types.u16, + U32 => tcx.types.u32, + U64 => tcx.types.u64, + U128 => tcx.types.u128, + Usize => tcx.types.usize, _ => return None, - })) + }) } /// Resolve an associated item, returning its containing page's `Res` @@ -779,8 +733,7 @@ fn trait_impls_for<'a>( module: DefId, ) -> FxHashSet<(DefId, DefId)> { let tcx = cx.tcx; - let iter = cx.resolver_caches.traits_in_scope[&module].iter().flat_map(|trait_candidate| { - let trait_ = trait_candidate.def_id; + let iter = tcx.doc_link_traits_in_scope(module).iter().flat_map(|&trait_| { trace!("considering explicit impl for trait {:?}", trait_); // Look at each trait implementation to see if it's an impl for `did` @@ -846,7 +799,7 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> { // In the presence of re-exports, this is not the same as the module of the item. // Rather than merging all documentation into one, resolve it one attribute at a time // so we know which module it came from. - for (parent_module, doc) in item.attrs.prepare_to_doc_link_resolution() { + for (parent_module, doc) in prepare_to_doc_link_resolution(&item.attrs.doc_strings) { if !may_have_doc_links(&doc) { continue; } @@ -854,22 +807,12 @@ impl<'a, 'tcx> DocVisitor for LinkCollector<'a, 'tcx> { // NOTE: if there are links that start in one crate and end in another, this will not resolve them. // This is a degenerate case and it's not supported by rustdoc. let parent_node = parent_module.or(parent_node); - let mut tmp_links = self - .cx - .resolver_caches - .markdown_links - .take() - .expect("`markdown_links` are already borrowed"); - if !tmp_links.contains_key(&doc) { - tmp_links.insert(doc.clone(), preprocessed_markdown_links(&doc)); - } - for md_link in &tmp_links[&doc] { - let link = self.resolve_link(item, &doc, parent_node, md_link); + for md_link in preprocessed_markdown_links(&doc) { + let link = self.resolve_link(item, &doc, parent_node, &md_link); if let Some(link) = link { self.cx.cache.intra_doc_links.entry(item.item_id).or_default().push(link); } } - self.cx.resolver_caches.markdown_links = Some(tmp_links); } if item.is_mod() { @@ -975,16 +918,12 @@ fn preprocess_link( } // Strip generics from the path. - let path_str = if path_str.contains(['<', '>'].as_slice()) { - match strip_generics_from_path(path_str) { - Ok(path) => path, - Err(err) => { - debug!("link has malformed generics: {}", path_str); - return Some(Err(PreprocessingError::MalformedGenerics(err, path_str.to_owned()))); - } + let path_str = match strip_generics_from_path(path_str) { + Ok(path) => path, + Err(err) => { + debug!("link has malformed generics: {}", path_str); + return Some(Err(PreprocessingError::MalformedGenerics(err, path_str.to_owned()))); } - } else { - path_str.to_owned() }; // Sanity check to make sure we don't have any angle brackets after stripping generics. @@ -1768,15 +1707,35 @@ fn resolution_failure( // Otherwise, it must be an associated item or variant let res = partial_res.expect("None case was handled by `last_found_module`"); - let kind = match res { - Res::Def(kind, _) => Some(kind), + let kind_did = match res { + Res::Def(kind, did) => Some((kind, did)), Res::Primitive(_) => None, }; - let path_description = if let Some(kind) = kind { + let is_struct_variant = |did| { + if let ty::Adt(def, _) = tcx.type_of(did).kind() + && def.is_enum() + && let Some(variant) = def.variants().iter().find(|v| v.name == res.name(tcx)) { + // ctor is `None` if variant is a struct + variant.ctor.is_none() + } else { + false + } + }; + let path_description = if let Some((kind, did)) = kind_did { match kind { Mod | ForeignMod => "inner item", Struct => "field or associated item", Enum | Union => "variant or associated item", + Variant if is_struct_variant(did) => { + let variant = res.name(tcx); + let note = format!("variant `{variant}` has no such field"); + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); + } + return; + } Variant | Field | Closure @@ -2064,94 +2023,3 @@ fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<Res> { debug!("resolved primitives {:?}", prim); Some(Res::Primitive(prim)) } - -fn strip_generics_from_path(path_str: &str) -> Result<String, MalformedGenerics> { - let mut stripped_segments = vec![]; - let mut path = path_str.chars().peekable(); - let mut segment = Vec::new(); - - while let Some(chr) = path.next() { - match chr { - ':' => { - if path.next_if_eq(&':').is_some() { - let stripped_segment = - strip_generics_from_path_segment(mem::take(&mut segment))?; - if !stripped_segment.is_empty() { - stripped_segments.push(stripped_segment); - } - } else { - return Err(MalformedGenerics::InvalidPathSeparator); - } - } - '<' => { - segment.push(chr); - - match path.next() { - Some('<') => { - return Err(MalformedGenerics::TooManyAngleBrackets); - } - Some('>') => { - return Err(MalformedGenerics::EmptyAngleBrackets); - } - Some(chr) => { - segment.push(chr); - - while let Some(chr) = path.next_if(|c| *c != '>') { - segment.push(chr); - } - } - None => break, - } - } - _ => segment.push(chr), - } - trace!("raw segment: {:?}", segment); - } - - if !segment.is_empty() { - let stripped_segment = strip_generics_from_path_segment(segment)?; - if !stripped_segment.is_empty() { - stripped_segments.push(stripped_segment); - } - } - - debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments); - - let stripped_path = stripped_segments.join("::"); - - if !stripped_path.is_empty() { Ok(stripped_path) } else { Err(MalformedGenerics::MissingType) } -} - -fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, MalformedGenerics> { - let mut stripped_segment = String::new(); - let mut param_depth = 0; - - let mut latest_generics_chunk = String::new(); - - for c in segment { - if c == '<' { - param_depth += 1; - latest_generics_chunk.clear(); - } else if c == '>' { - param_depth -= 1; - if latest_generics_chunk.contains(" as ") { - // The segment tries to use fully-qualified syntax, which is currently unsupported. - // Give a helpful error message instead of completely ignoring the angle brackets. - return Err(MalformedGenerics::HasFullyQualifiedSyntax); - } - } else { - if param_depth == 0 { - stripped_segment.push(c); - } else { - latest_generics_chunk.push(c); - } - } - } - - if param_depth == 0 { - Ok(stripped_segment) - } else { - // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>` - Err(MalformedGenerics::UnbalancedAngleBrackets) - } -} diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs index f690c49005d..ec449e94ce5 100644 --- a/src/librustdoc/passes/collect_intra_doc_links/early.rs +++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs @@ -1,357 +1,55 @@ -use crate::clean::Attributes; use crate::core::ResolverCaches; -use crate::passes::collect_intra_doc_links::preprocessed_markdown_links; -use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink}; use crate::visit_lib::early_lib_embargo_visit_item; -use rustc_ast::visit::{self, AssocCtxt, Visitor}; +use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, ItemKind}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::Namespace::*; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID}; -use rustc_hir::TraitCandidate; -use rustc_middle::ty::{DefIdTree, Visibility}; -use rustc_resolve::{ParentScope, Resolver}; -use rustc_span::symbol::sym; -use rustc_span::{Symbol, SyntaxContext}; - -use std::collections::hash_map::Entry; -use std::mem; +use rustc_hir::def::Res; +use rustc_hir::def_id::{DefId, DefIdSet}; +use rustc_resolve::Resolver; +use rustc_span::Symbol; pub(crate) fn early_resolve_intra_doc_links( resolver: &mut Resolver<'_>, krate: &ast::Crate, - document_private_items: bool, ) -> ResolverCaches { - let parent_scope = - ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver); let mut link_resolver = EarlyDocLinkResolver { resolver, - parent_scope, - visited_mods: Default::default(), - markdown_links: Default::default(), - doc_link_resolutions: Default::default(), - traits_in_scope: Default::default(), all_trait_impls: Default::default(), all_macro_rules: Default::default(), extern_doc_reachable: Default::default(), - local_doc_reachable: Default::default(), - document_private_items, }; - // Overridden `visit_item` below doesn't apply to the crate root, - // so we have to visit its attributes and reexports separately. - link_resolver.resolve_doc_links_local(&krate.attrs); - link_resolver.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id()); visit::walk_crate(&mut link_resolver, krate); - - // FIXME: somehow rustdoc is still missing crates even though we loaded all - // the known necessary crates. Load them all unconditionally until we find a way to fix this. - // DO NOT REMOVE THIS without first testing on the reproducer in - // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb - for (extern_name, _) in - link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude) - { - link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope); - } - link_resolver.process_extern_impls(); ResolverCaches { - markdown_links: Some(link_resolver.markdown_links), - doc_link_resolutions: link_resolver.doc_link_resolutions, - traits_in_scope: link_resolver.traits_in_scope, all_trait_impls: Some(link_resolver.all_trait_impls), all_macro_rules: link_resolver.all_macro_rules, extern_doc_reachable: link_resolver.extern_doc_reachable, } } -fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes { - Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true) -} - struct EarlyDocLinkResolver<'r, 'ra> { resolver: &'r mut Resolver<'ra>, - parent_scope: ParentScope<'ra>, - visited_mods: DefIdSet, - markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>, - doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>, - traits_in_scope: DefIdMap<Vec<TraitCandidate>>, all_trait_impls: Vec<DefId>, all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>, /// This set is used as a seed for `effective_visibilities`, which are then extended by some /// more items using `lib_embargo_visit_item` during doc inlining. extern_doc_reachable: DefIdSet, - /// This is an easily identifiable superset of items added to `effective_visibilities` - /// using `lib_embargo_visit_item` during doc inlining. - /// The union of `(extern,local)_doc_reachable` is therefore a superset of - /// `effective_visibilities` and can be used for pruning extern impls here - /// in early doc link resolution. - local_doc_reachable: DefIdSet, - document_private_items: bool, } impl<'ra> EarlyDocLinkResolver<'_, 'ra> { - fn add_traits_in_scope(&mut self, def_id: DefId) { - // Calls to `traits_in_scope` are expensive, so try to avoid them if only possible. - // Keys in the `traits_in_scope` cache are always module IDs. - if let Entry::Vacant(entry) = self.traits_in_scope.entry(def_id) { - let module = self.resolver.get_nearest_non_block_module(def_id); - let module_id = module.def_id(); - let entry = if module_id == def_id { - Some(entry) - } else if let Entry::Vacant(entry) = self.traits_in_scope.entry(module_id) { - Some(entry) - } else { - None - }; - if let Some(entry) = entry { - entry.insert(self.resolver.traits_in_scope( - None, - &ParentScope::module(module, self.resolver), - SyntaxContext::root(), - None, - )); - } - } - } - - fn is_doc_reachable(&self, def_id: DefId) -> bool { - self.extern_doc_reachable.contains(&def_id) || self.local_doc_reachable.contains(&def_id) - } - - /// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass. - /// That pass filters impls using type-based information, but we don't yet have such - /// information here, so we just conservatively calculate traits in scope for *all* modules - /// having impls in them. fn process_extern_impls(&mut self) { - // Resolving links in already existing crates may trigger loading of new crates. - let mut start_cnum = 0; - loop { - let crates = Vec::from_iter(self.resolver.cstore().crates_untracked()); - for cnum in &crates[start_cnum..] { - early_lib_embargo_visit_item( - self.resolver, - &mut self.extern_doc_reachable, - cnum.as_def_id(), - true, - ); - } - for &cnum in &crates[start_cnum..] { - let all_trait_impls = - Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum)); - let all_inherent_impls = - Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum)); - let all_incoherent_impls = Vec::from_iter( - self.resolver.cstore().incoherent_impls_in_crate_untracked(cnum), - ); - - // Querying traits in scope is expensive so we try to prune the impl lists using - // privacy, private traits and impls from other crates are never documented in - // the current crate, and links in their doc comments are not resolved. - for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls { - if self.is_doc_reachable(trait_def_id) - && simplified_self_ty - .and_then(|ty| ty.def()) - .map_or(true, |ty_def_id| self.is_doc_reachable(ty_def_id)) - { - if self.visited_mods.insert(trait_def_id) { - self.resolve_doc_links_extern_impl(trait_def_id, false); - } - self.resolve_doc_links_extern_impl(impl_def_id, false); - } - self.all_trait_impls.push(impl_def_id); - } - for (ty_def_id, impl_def_id) in all_inherent_impls { - if self.is_doc_reachable(ty_def_id) { - self.resolve_doc_links_extern_impl(impl_def_id, true); - } - } - for impl_def_id in all_incoherent_impls { - self.resolve_doc_links_extern_impl(impl_def_id, true); - } - } - - if crates.len() > start_cnum { - start_cnum = crates.len(); - } else { - break; - } - } - } - - fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) { - self.resolve_doc_links_extern_outer_fixme(def_id, def_id); - let assoc_item_def_ids = Vec::from_iter( - self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()), - ); - for assoc_def_id in assoc_item_def_ids { - if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public() - { - self.resolve_doc_links_extern_outer_fixme(assoc_def_id, def_id); - } - } - } - - // FIXME: replace all uses with `resolve_doc_links_extern_outer` to actually resolve links, not - // just add traits in scope. This may be expensive and require benchmarking and optimization. - fn resolve_doc_links_extern_outer_fixme(&mut self, def_id: DefId, scope_id: DefId) { - if !self.resolver.cstore().may_have_doc_links_untracked(def_id) { - return; - } - if let Some(parent_id) = self.resolver.opt_parent(scope_id) { - self.add_traits_in_scope(parent_id); - } - } - - fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) { - if !self.resolver.cstore().may_have_doc_links_untracked(def_id) { - return; - } - let attrs = Vec::from_iter( - self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()), - ); - let parent_scope = ParentScope::module( - self.resolver.get_nearest_non_block_module( - self.resolver.opt_parent(scope_id).unwrap_or(scope_id), - ), - self.resolver, - ); - self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope); - } - - fn resolve_doc_links_extern_inner(&mut self, def_id: DefId) { - if !self.resolver.cstore().may_have_doc_links_untracked(def_id) { - return; - } - let attrs = Vec::from_iter( - self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()), - ); - let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver); - self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope); - } - - fn resolve_doc_links_local(&mut self, attrs: &[ast::Attribute]) { - if !attrs.iter().any(|attr| attr.may_have_doc_links()) { - return; - } - self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope); - } - - fn resolve_and_cache( - &mut self, - path_str: &str, - ns: Namespace, - parent_scope: &ParentScope<'ra>, - ) -> bool { - // FIXME: This caching may be incorrect in case of multiple `macro_rules` - // items with the same name in the same module. - self.doc_link_resolutions - .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id())) - .or_insert_with_key(|(path, ns, _)| { - self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope) - }) - .is_some() - } - - fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) { - let mut need_traits_in_scope = false; - for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() { - assert_eq!(doc_module, None); - let mut tmp_links = mem::take(&mut self.markdown_links); - let links = - tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc)); - for PreprocessedMarkdownLink(pp_link, _) in links { - if let Ok(pinfo) = pp_link { - // The logic here is a conservative approximation for path resolution in - // `resolve_with_disambiguator`. - if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) { - if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) { - continue; - } - } - - // Resolve all namespaces due to no disambiguator or for diagnostics. - let mut any_resolved = false; - let mut need_assoc = false; - for ns in [TypeNS, ValueNS, MacroNS] { - if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) { - any_resolved = true; - } else if ns != MacroNS { - need_assoc = true; - } - } - - // Resolve all prefixes for type-relative resolution or for diagnostics. - if need_assoc || !any_resolved { - let mut path = &pinfo.path_str[..]; - while let Some(idx) = path.rfind("::") { - path = &path[..idx]; - need_traits_in_scope = true; - for ns in [TypeNS, ValueNS, MacroNS] { - self.resolve_and_cache(path, ns, &parent_scope); - } - } - } - } - } - self.markdown_links = tmp_links; - } - - if need_traits_in_scope { - self.add_traits_in_scope(parent_scope.module.def_id()); - } - } - - /// When reexports are inlined, they are replaced with item which they refer to, those items - /// may have links in their doc comments, those links are resolved at the item definition site, - /// so we need to know traits in scope at that definition site. - fn process_module_children_or_reexports(&mut self, module_id: DefId) { - if !self.visited_mods.insert(module_id) { - return; // avoid infinite recursion - } - - for child in self.resolver.module_children_or_reexports(module_id) { - // This condition should give a superset of `denied` from `fn clean_use_statement`. - if child.vis.is_public() - || self.document_private_items - && child.vis != Visibility::Restricted(module_id) - && module_id.is_local() - { - if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() { - self.local_doc_reachable.insert(def_id); - let scope_id = match child.res { - Res::Def( - DefKind::Variant - | DefKind::AssocTy - | DefKind::AssocFn - | DefKind::AssocConst, - .., - ) => self.resolver.parent(def_id), - _ => def_id, - }; - self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope - if let Res::Def(DefKind::Mod, ..) = child.res { - self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope - } - if let Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, ..) = child.res { - self.process_module_children_or_reexports(def_id); - } - if let Res::Def(DefKind::Struct | DefKind::Union | DefKind::Variant, _) = - child.res - { - let field_def_ids = Vec::from_iter( - self.resolver - .cstore() - .associated_item_def_ids_untracked(def_id, self.resolver.sess()), - ); - for field_def_id in field_def_ids { - self.resolve_doc_links_extern_outer(field_def_id, scope_id); - } - } - } + for cnum in self.resolver.cstore().crates_untracked() { + early_lib_embargo_visit_item( + self.resolver, + &mut self.extern_doc_reachable, + cnum.as_def_id(), + true, + ); + for (_, impl_def_id, _) in self.resolver.cstore().trait_impls_in_crate_untracked(cnum) { + self.all_trait_impls.push(impl_def_id); } } } @@ -359,73 +57,16 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> { impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> { fn visit_item(&mut self, item: &ast::Item) { - self.resolve_doc_links_local(&item.attrs); // Outer attribute scope - if let ItemKind::Mod(..) = item.kind { - let module_def_id = self.resolver.local_def_id(item.id).to_def_id(); - let module = self.resolver.expect_module(module_def_id); - let old_module = mem::replace(&mut self.parent_scope.module, module); - let old_macro_rules = self.parent_scope.macro_rules; - self.resolve_doc_links_local(&item.attrs); // Inner attribute scope - self.process_module_children_or_reexports(module_def_id); - visit::walk_item(self, item); - if item - .attrs - .iter() - .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape)) - { - self.parent_scope.macro_rules = old_macro_rules; + match &item.kind { + ItemKind::Impl(impl_) if impl_.of_trait.is_some() => { + self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id()); } - self.parent_scope.module = old_module; - } else { - match &item.kind { - ItemKind::Impl(box ast::Impl { of_trait: Some(trait_ref), .. }) => { - if let Some(partial_res) = self.resolver.get_partial_res(trait_ref.ref_id) - && let Some(res) = partial_res.full_res() - && let Some(trait_def_id) = res.opt_def_id() - && !trait_def_id.is_local() - && self.visited_mods.insert(trait_def_id) { - self.resolve_doc_links_extern_impl(trait_def_id, false); - } - self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id()); - } - ItemKind::MacroDef(macro_def) if macro_def.macro_rules => { - let (macro_rules_scope, res) = - self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id)); - self.parent_scope.macro_rules = macro_rules_scope; - self.all_macro_rules.insert(item.ident.name, res); - } - _ => {} + ItemKind::MacroDef(macro_def) if macro_def.macro_rules => { + let (_, res) = self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id)); + self.all_macro_rules.insert(item.ident.name, res); } - visit::walk_item(self, item); + _ => {} } + visit::walk_item(self, item); } - - fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) { - self.resolve_doc_links_local(&item.attrs); - visit::walk_assoc_item(self, item, ctxt) - } - - fn visit_foreign_item(&mut self, item: &ast::ForeignItem) { - self.resolve_doc_links_local(&item.attrs); - visit::walk_foreign_item(self, item) - } - - fn visit_variant(&mut self, v: &ast::Variant) { - self.resolve_doc_links_local(&v.attrs); - visit::walk_variant(self, v) - } - - fn visit_field_def(&mut self, field: &ast::FieldDef) { - self.resolve_doc_links_local(&field.attrs); - visit::walk_field_def(self, field) - } - - fn visit_block(&mut self, block: &ast::Block) { - let old_macro_rules = self.parent_scope.macro_rules; - visit::walk_block(self, block); - self.parent_scope.macro_rules = old_macro_rules; - } - - // NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters), - // then this will have to implement other visitor methods too. } diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 634e70ec97a..4b1ff68df50 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -2,11 +2,12 @@ //! process. use rustc_middle::ty::TyCtxt; +use rustc_resolve::rustdoc::DocFragmentKind; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use std::ops::Range; use self::Condition::*; -use crate::clean::{self, DocFragmentKind}; +use crate::clean; use crate::core::DocContext; mod stripper; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 088cb3f3394..9c1e5f4a3cd 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -378,7 +378,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let nonexported = !tcx.has_attr(def_id, sym::macro_export); if is_macro_2_0 || nonexported || self.inlining { - self.add_to_current_mod(item, renamed, None); + self.add_to_current_mod(item, renamed, import_id); } } hir::ItemKind::Mod(ref m) => { diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs index dc62051cb85..168633c8f63 100644 --- a/src/tools/build_helper/src/git.rs +++ b/src/tools/build_helper/src/git.rs @@ -1,5 +1,24 @@ +use std::process::Stdio; use std::{path::Path, process::Command}; +/// Runs a command and returns the output +fn output_result(cmd: &mut Command) -> Result<String, String> { + let output = match cmd.stderr(Stdio::inherit()).output() { + Ok(status) => status, + Err(e) => return Err(format!("failed to run command: {:?}: {}", cmd, e)), + }; + if !output.status.success() { + return Err(format!( + "command did not execute successfully: {:?}\n\ + expected success, got: {}\n{}", + cmd, + output.status, + String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))? + )); + } + Ok(String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?) +} + /// Finds the remote for rust-lang/rust. /// For example for these remotes it will return `upstream`. /// ```text @@ -14,13 +33,7 @@ pub fn get_rust_lang_rust_remote(git_dir: Option<&Path>) -> Result<String, Strin git.current_dir(git_dir); } git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]); - - let output = git.output().map_err(|err| format!("{err:?}"))?; - if !output.status.success() { - return Err("failed to execute git config command".to_owned()); - } - - let stdout = String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))?; + let stdout = output_result(&mut git)?; let rust_lang_remote = stdout .lines() @@ -73,3 +86,48 @@ pub fn updated_master_branch(git_dir: Option<&Path>) -> Result<String, String> { // We could implement smarter logic here in the future. Ok("origin/master".into()) } + +/// Returns the files that have been modified in the current branch compared to the master branch. +/// The `extensions` parameter can be used to filter the files by their extension. +/// If `extensions` is empty, all files will be returned. +pub fn get_git_modified_files( + git_dir: Option<&Path>, + extensions: &Vec<&str>, +) -> Result<Option<Vec<String>>, String> { + let Ok(updated_master) = updated_master_branch(git_dir) else { return Ok(None); }; + + let git = || { + let mut git = Command::new("git"); + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); + } + git + }; + + let merge_base = output_result(git().arg("merge-base").arg(&updated_master).arg("HEAD"))?; + let files = output_result(git().arg("diff-index").arg("--name-only").arg(merge_base.trim()))? + .lines() + .map(|s| s.trim().to_owned()) + .filter(|f| { + Path::new(f).extension().map_or(false, |ext| { + extensions.is_empty() || extensions.contains(&ext.to_str().unwrap()) + }) + }) + .collect(); + Ok(Some(files)) +} + +/// Returns the files that haven't been added to git yet. +pub fn get_git_untracked_files(git_dir: Option<&Path>) -> Result<Option<Vec<String>>, String> { + let Ok(_updated_master) = updated_master_branch(git_dir) else { return Ok(None); }; + let mut git = Command::new("git"); + if let Some(git_dir) = git_dir { + git.current_dir(git_dir); + } + + let files = output_result(git.arg("ls-files").arg("--others").arg("--exclude-standard"))? + .lines() + .map(|s| s.trim().to_owned()) + .collect(); + Ok(Some(files)) +} diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index e2cde09776f..659e8aebcd5 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -4383,6 +4383,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes +[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 2cfb47dd758..70d1268090f 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -42,6 +42,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies +clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index ab44db69483..95f6d2cc45c 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md index 23867df8efe..df4a1f2702e 100644 --- a/src/tools/clippy/book/src/README.md +++ b/src/tools/clippy/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index f79dbb50ff4..32e8e218c40 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -43,6 +43,7 @@ Please use that command to update the file and do not edit it by hand. | [allowed-scripts](#allowed-scripts) | `["Latin"]` | | [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` | | [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` | +| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` | | [max-include-file-size](#max-include-file-size) | `1000000` | | [allow-expect-in-tests](#allow-expect-in-tests) | `false` | | [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` | @@ -167,6 +168,17 @@ The minimum rust version that the project supports * [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp) * [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else) * [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction) +* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace) +* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) +* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) +* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) +* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) +* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) +* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls) +* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check) +* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) ### cognitive-complexity-threshold @@ -279,7 +291,7 @@ The minimum size (in bytes) to consider a type for passing by reference instead **Default Value:** `256` (`u64`) -* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move) +* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) ### too-many-lines-threshold @@ -442,6 +454,14 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. * [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) +### await-holding-invalid-types + + +**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`) + +* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type) + + ### max-include-file-size The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes @@ -497,7 +517,7 @@ for the generic parameters for determining interior mutability **Default Value:** `["bytes::Bytes"]` (`Vec<String>`) -* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key) +* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type) ### allow-mixed-uninlined-format-args @@ -509,7 +529,7 @@ Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar) ### suppress-restriction-lint-in-const -In same +Whether to suppress a restriction lint in constant code. In same cases the restructured operation might not be unavoidable, as the suggested counterparts are unavailable in constant code. This configuration will cause restriction lints to trigger even diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 510c7e852af..c3f8a782d27 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "3.2" +clap = "4.1.4" indoc = "1.0" itertools = "0.10.1" opener = "0.5" diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index d3e03669204..e2457e5a8a5 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -2,7 +2,7 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; @@ -11,22 +11,22 @@ fn main() { match matches.subcommand() { Some(("bless", matches)) => { - bless::bless(matches.contains_id("ignore-timestamp")); + bless::bless(matches.get_flag("ignore-timestamp")); }, Some(("dogfood", matches)) => { dogfood::dogfood( - matches.contains_id("fix"), - matches.contains_id("allow-dirty"), - matches.contains_id("allow-staged"), + matches.get_flag("fix"), + matches.get_flag("allow-dirty"), + matches.get_flag("allow-staged"), ); }, Some(("fmt", matches)) => { - fmt::run(matches.contains_id("check"), matches.contains_id("verbose")); + fmt::run(matches.get_flag("check"), matches.get_flag("verbose")); }, Some(("update_lints", matches)) => { - if matches.contains_id("print-only") { + if matches.get_flag("print-only") { update_lints::print_lints(); - } else if matches.contains_id("check") { + } else if matches.get_flag("check") { update_lints::update(update_lints::UpdateMode::Check); } else { update_lints::update(update_lints::UpdateMode::Change); @@ -38,7 +38,7 @@ fn main() { matches.get_one::<String>("name"), matches.get_one::<String>("category").map(String::as_str), matches.get_one::<String>("type").map(String::as_str), - matches.contains_id("msrv"), + matches.get_flag("msrv"), ) { Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), @@ -46,7 +46,7 @@ fn main() { }, Some(("setup", sub_command)) => match sub_command.subcommand() { Some(("intellij", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::intellij::remove_rustc_src(); } else { setup::intellij::setup_rustc_src( @@ -57,17 +57,17 @@ fn main() { } }, Some(("git-hook", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::git_hook::remove_hook(); } else { - setup::git_hook::install_hook(matches.contains_id("force-override")); + setup::git_hook::install_hook(matches.get_flag("force-override")); } }, Some(("vscode-tasks", matches)) => { - if matches.contains_id("remove") { + if matches.get_flag("remove") { setup::vscode::remove_tasks(); } else { - setup::vscode::install_tasks(matches.contains_id("force-override")); + setup::vscode::install_tasks(matches.get_flag("force-override")); } }, _ => {}, @@ -91,7 +91,7 @@ fn main() { Some(("rename_lint", matches)) => { let old_name = matches.get_one::<String>("old_name").unwrap(); let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name); - let uplift = matches.contains_id("uplift"); + let uplift = matches.get_flag("uplift"); update_lints::rename(old_name, new_name, uplift); }, Some(("deprecate", matches)) => { @@ -110,24 +110,37 @@ fn get_clap_config() -> ArgMatches { Command::new("bless").about("bless the test output changes").arg( Arg::new("ignore-timestamp") .long("ignore-timestamp") + .action(ArgAction::SetTrue) .help("Include files updated before clippy was built"), ), Command::new("dogfood").about("Runs the dogfood test").args([ - Arg::new("fix").long("fix").help("Apply the suggestions when possible"), + Arg::new("fix") + .long("fix") + .action(ArgAction::SetTrue) + .help("Apply the suggestions when possible"), Arg::new("allow-dirty") .long("allow-dirty") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has changes") .requires("fix"), Arg::new("allow-staged") .long("allow-staged") + .action(ArgAction::SetTrue) .help("Fix code even if the working directory has staged changes") .requires("fix"), ]), Command::new("fmt") .about("Run rustfmt on all projects and tests") .args([ - Arg::new("check").long("check").help("Use the rustfmt --check option"), - Arg::new("verbose").short('v').long("verbose").help("Echo commands run"), + Arg::new("check") + .long("check") + .action(ArgAction::SetTrue) + .help("Use the rustfmt --check option"), + Arg::new("verbose") + .short('v') + .long("verbose") + .action(ArgAction::SetTrue) + .help("Echo commands run"), ]), Command::new("update_lints") .about("Updates lint registration and information from the source code") @@ -140,13 +153,17 @@ fn get_clap_config() -> ArgMatches { * all lints are registered in the lint store", ) .args([ - Arg::new("print-only").long("print-only").help( - "Print a table of lints to STDOUT. \ - This does not include deprecated and internal lints. \ - (Does not modify any files)", - ), + Arg::new("print-only") + .long("print-only") + .action(ArgAction::SetTrue) + .help( + "Print a table of lints to STDOUT. \ + This does not include deprecated and internal lints. \ + (Does not modify any files)", + ), Arg::new("check") .long("check") + .action(ArgAction::SetTrue) .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ]), Command::new("new_lint") @@ -156,15 +173,13 @@ fn get_clap_config() -> ArgMatches { .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") - .takes_value(true) - .value_parser([PossibleValue::new("early"), PossibleValue::new("late")]) + .value_parser(["early", "late"]) .conflicts_with("type") .required_unless_present("type"), Arg::new("name") .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .takes_value(true) .required(true), Arg::new("category") .short('c') @@ -172,25 +187,23 @@ fn get_clap_config() -> ArgMatches { .help("What category the lint belongs to") .default_value("nursery") .value_parser([ - PossibleValue::new("style"), - PossibleValue::new("correctness"), - PossibleValue::new("suspicious"), - PossibleValue::new("complexity"), - PossibleValue::new("perf"), - PossibleValue::new("pedantic"), - PossibleValue::new("restriction"), - PossibleValue::new("cargo"), - PossibleValue::new("nursery"), - PossibleValue::new("internal"), - PossibleValue::new("internal_warn"), - ]) - .takes_value(true), - Arg::new("type") - .long("type") - .help("What directory the lint belongs in") - .takes_value(true) - .required(false), - Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"), + "style", + "correctness", + "suspicious", + "complexity", + "perf", + "pedantic", + "restriction", + "cargo", + "nursery", + "internal", + "internal_warn", + ]), + Arg::new("type").long("type").help("What directory the lint belongs in"), + Arg::new("msrv") + .long("msrv") + .action(ArgAction::SetTrue) + .help("Add MSRV config code to the lint"), ]), Command::new("setup") .about("Support for setting up your personal development environment") @@ -201,13 +214,12 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the dependencies added with 'cargo dev setup intellij'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the dependencies added with 'cargo dev setup intellij'"), Arg::new("rustc-repo-path") .long("repo-path") .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") - .takes_value(true) .value_name("path") .conflicts_with("remove") .required(true), @@ -217,26 +229,26 @@ fn get_clap_config() -> ArgMatches { .args([ Arg::new("remove") .long("remove") - .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of an existing git pre-commit hook") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of an existing git pre-commit hook"), ]), Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .args([ Arg::new("remove") .long("remove") - .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") - .required(false), + .action(ArgAction::SetTrue) + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"), Arg::new("force-override") .long("force-override") .short('f') - .help("Forces the override of existing vscode tasks") - .required(false), + .action(ArgAction::SetTrue) + .help("Forces the override of existing vscode tasks"), ]), ]), Command::new("remove") @@ -295,6 +307,7 @@ fn get_clap_config() -> ArgMatches { .help("The new name of the lint"), Arg::new("uplift") .long("uplift") + .action(ArgAction::SetTrue) .help("This lint will be uplifted into rustc"), ]), Command::new("deprecate").about("Deprecates the given lint").args([ @@ -305,8 +318,6 @@ fn get_clap_config() -> ArgMatches { Arg::new("reason") .long("reason") .short('r') - .required(false) - .takes_value(true) .help("The reason for deprecation"), ]), ]) diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 4c40483e3ec..796f1ff1695 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2021" [dependencies] -cargo_metadata = "0.14" +cargo_metadata = "0.15.3" clippy_utils = { path = "../clippy_utils" } declare_clippy_lint = { path = "../declare_clippy_lint" } if_chain = "1.0" diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index 556fa579000..1d9096ea64d 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -34,14 +35,16 @@ declare_clippy_lint! { declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); -fn is_bool_lit(e: &Expr<'_>) -> bool { - matches!( - e.kind, - ExprKind::Lit(Lit { - node: LitKind::Bool(_), - .. - }) - ) && !e.span.from_expansion() +fn extract_bool_lit(e: &Expr<'_>) -> Option<bool> { + if let ExprKind::Lit(Lit { + node: LitKind::Bool(b), .. + }) = e.kind + && !e.span.from_expansion() + { + Some(b) + } else { + None + } } fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -69,24 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; let macro_name = cx.tcx.item_name(macro_call.def_id); - if !matches!( - macro_name.as_str(), - "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" - ) { - return; - } + let eq_macro = match macro_name.as_str() { + "assert_eq" | "debug_assert_eq" => true, + "assert_ne" | "debug_assert_ne" => false, + _ => return, + }; let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; let a_span = a.span.source_callsite(); let b_span = b.span.source_callsite(); - let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) { - // assert_eq!(true, b) - // ^^^^^^ - (true, false) => (a_span.until(b_span), b), - // assert_eq!(a, true) - // ^^^^^^ - (false, true) => (b_span.with_lo(a_span.hi()), a), + let (lit_span, bool_value, non_lit_expr) = match (extract_bool_lit(a), extract_bool_lit(b)) { + // assert_eq!(true/false, b) + // ^^^^^^^^^^^^ + (Some(bool_value), None) => (a_span.until(b_span), bool_value, b), + // assert_eq!(a, true/false) + // ^^^^^^^^^^^^ + (None, Some(bool_value)) => (b_span.with_lo(a_span.hi()), bool_value, a), // If there are two boolean arguments, we definitely don't understand // what's going on, so better leave things as is... // @@ -121,9 +123,16 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { // ^^^^^^^^^ let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())]; + + if bool_value ^ eq_macro { + let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return }; + suggestions.push((non_lit_expr.span, (!sugg).to_string())); + } + diag.multipart_suggestion( format!("replace it with `{non_eq_mac}!(..)`"), - vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())], + suggestions, Applicability::MachineApplicable, ); }, diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 36a366fc974..457a25826e7 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -156,6 +156,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, crate::exit::EXIT_INFO, crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO, crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, crate::float_literal::EXCESSIVE_PRECISION_INFO, crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 127201b72e2..0b31e20fc87 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, SuggestionStyle}; +use rustc_errors::{Applicability, Handler, SuggestionStyle, TerminalUrl}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -717,6 +717,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { None, false, false, + TerminalUrl::No, ); let handler = Handler::with_emitter(false, None, Box::new(emitter)); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 9d089fcad70..aef2db38583 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; use rustc_span::def_id::LocalDefId; +use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { 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 new file mode 100644 index 00000000000..2fdd8a71466 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -0,0 +1,178 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::trait_ref_of_method; +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, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{def_id::DefId, Span}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters in generics that are never used anywhere else. + /// + /// ### Why is this bad? + /// Functions cannot infer the value of unused type parameters; therefore, calling them + /// requires using a turbofish, which serves no purpose but to satisfy the compiler. + /// + /// ### Example + /// ```rust + /// // unused type parameters + /// fn unused_ty<T>(x: u8) { + /// // .. + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn no_unused_ty(x: u8) { + /// // .. + /// } + /// ``` + #[clippy::version = "1.69.0"] + pub EXTRA_UNUSED_TYPE_PARAMETERS, + complexity, + "unused type parameters in function definitions" +} +declare_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. + ty_params: FxHashMap<DefId, Span>, + /// Collection of any (inline) trait bounds corresponding to each type parameter. + bounds: FxHashMap<DefId, Span>, + /// The entire `Generics` object of the function, useful for querying purposes. + generics: &'tcx Generics<'tcx>, + /// The value of this will remain `true` if *every* parameter: + /// 1. Is a type parameter, and + /// 2. Goes unused in the function. + /// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic + /// parameters are present, this will be set to `false`. + all_params_unused: bool, +} + +impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { + fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self { + let mut all_params_unused = true; + let ty_params = generics + .params + .iter() + .filter_map(|param| { + if let GenericParamKind::Type { .. } = param.kind { + Some((param.def_id.into(), param.span)) + } else { + if !param.is_elided_lifetime() { + all_params_unused = false; + } + None + } + }) + .collect(); + Self { + cx, + ty_params, + bounds: FxHashMap::default(), + generics, + all_params_unused, + } + } + + fn emit_lint(&self) { + let (msg, help) = match self.ty_params.len() { + 0 => return, + 1 => ( + "type parameter goes unused in function definition", + "consider removing the parameter", + ), + _ => ( + "type parameters go unused in function definition", + "consider removing the parameters", + ), + }; + + let source_map = self.cx.tcx.sess.source_map(); + let span = if self.all_params_unused { + self.generics.span.into() // Remove the entire list of generics + } else { + MultiSpan::from_spans( + self.ty_params + .iter() + .map(|(def_id, &span)| { + // Extend the span past any trait bounds, and include the comma at the end. + let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi); + let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false); + let comma_span = source_map.span_through_char(comma_range, ','); + span.with_hi(comma_span.hi()) + }) + .collect(), + ) + }; + + span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help); + } +} + +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; + } + } 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 + // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked. + let item = self.nested_visit_map().item(id); + walk_item(self, item); + } else { + walk_ty(self, t); + } + } + + 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); + } + // Only walk the right-hand side of where-bounds + for bound in predicate.bounds { + walk_param_bound(self, bound); + } + } + } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } +} + +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 { + let mut walker = TypeWalker::new(cx, generics); + walk_item(&mut walker, item); + walker.emit_lint(); + } + } + + 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() { + 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 d376ad3bfe3..ea26b96ee07 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -125,7 +125,7 @@ declare_clippy_lint! { /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.66.0"] pub UNINLINED_FORMAT_ARGS, - style, + pedantic, "using non-inlined variables in `format!` calls" } diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs index d6b50537c2e..8b53ee68ebd 100644 --- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs +++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs @@ -10,13 +10,7 @@ use std::iter; use super::MISNAMED_GETTERS; -pub fn check_fn( - cx: &LateContext<'_>, - kind: FnKind<'_>, - decl: &FnDecl<'_>, - body: &Body<'_>, - span: Span, -) { +pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, span: Span) { let FnKind::Method(ref ident, sig) = kind else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index a13909a2cdb..f2aa7b597a7 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -25,7 +25,7 @@ pub(super) fn check_fn<'tcx>( intravisit::FnKind::Closure => return, }; - check_raw_ptr(cx, unsafety, decl, body, def_id) + check_raw_ptr(cx, unsafety, decl, body, def_id); } pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index 7557a9ce13f..c924d7361ce 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -66,7 +66,9 @@ impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { if sig.decl.implicit_self.has_implicit_self() { - let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); + let ret_ty = cx + .tcx + .erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output()); let ret_ty = cx .tcx .try_normalize_erasing_regions(cx.param_env, ret_ty) diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 80ed2862a41..e13bc47973b 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -135,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if item.ident.name == sym::len; if let ImplItemKind::Fn(sig, _) = &item.kind; if sig.decl.implicit_self.has_implicit_self(); + if sig.decl.inputs.len() == 1; if cx.effective_visibilities.is_exported(item.owner_id.def_id); if matches!(sig.decl.output, FnRetTy::Return(_)); if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); @@ -196,7 +197,15 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool { item.ident.name == name && if let AssocItemKind::Fn { has_self } = item.kind { - has_self && { cx.tcx.fn_sig(item.id.owner_id).skip_binder().inputs().skip_binder().len() == 1 } + has_self && { + cx.tcx + .fn_sig(item.id.owner_id) + .skip_binder() + .inputs() + .skip_binder() + .len() + == 1 + } } else { false } @@ -342,7 +351,11 @@ fn check_for_is_empty<'tcx>( ), Some(is_empty) if !(is_empty.fn_has_self_parameter - && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output)) => + && check_is_empty_sig( + cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), + self_kind, + output, + )) => { ( format!( diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index d0683039776..565c5b7af00 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -121,6 +121,7 @@ mod excessive_bools; mod exhaustive_items; mod exit; mod explicit_write; +mod extra_unused_type_parameters; mod fallible_impl_from; mod float_literal; mod floating_point_arithmetic; @@ -909,6 +910,7 @@ 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)); // 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 747a94ba5a6..43a1a65a43a 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,20 +1,21 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; -use rustc_hir::lang_items; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn, - TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, + Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter as middle_nested_filter; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Span; @@ -33,8 +34,6 @@ declare_clippy_lint! { /// ### Known problems /// - We bail out if the function has a `where` clause where lifetimes /// are mentioned due to potential false positives. - /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the - /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// ### Example /// ```rust @@ -92,7 +91,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, generics, id) = item.kind { - check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -105,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none(); check_fn_inner( cx, - sig.decl, + sig, Some(id), None, item.generics, @@ -121,29 +120,21 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); } } } -/// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug, Clone)] -enum RefLt { - Unnamed, - Static, - Named(LocalDefId), -} - fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, - decl: &'tcx FnDecl<'_>, + sig: &'tcx FnSig<'_>, body: Option<BodyId>, trait_sig: Option<&[Ident]>, generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, generics) { + if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; } @@ -162,7 +153,7 @@ fn check_fn_inner<'tcx>( for bound in pred.bounds { let mut visitor = RefVisitor::new(cx); walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) { return; } if let GenericBound::Trait(ref trait_ref, _) = *bound { @@ -189,12 +180,12 @@ fn check_fn_inner<'tcx>( } } - if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { 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 // `Node::GenericParam`. - .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident()) + .filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident()) .map(|ident| ident.to_string()) .collect::<Vec<_>>() .join(", "); @@ -202,21 +193,99 @@ fn check_fn_inner<'tcx>( span_lint_and_then( cx, NEEDLESS_LIFETIMES, - span.with_hi(decl.output.span().hi()), + span.with_hi(sig.decl.output.span().hi()), &format!("the following explicit lifetimes could be elided: {lts}"), |diag| { - if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) { - diag.span_help(span, "replace with `'_` in generic arguments such as here"); + if sig.header.is_async() { + // async functions have usages whose spans point at the lifetime declaration which messes up + // suggestions + return; + }; + + if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) { + diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); } }, ); } if report_extra_lifetimes { - self::report_extra_lifetimes(cx, decl, generics); + self::report_extra_lifetimes(cx, sig.decl, generics); } } +fn elision_suggestions( + cx: &LateContext<'_>, + generics: &Generics<'_>, + elidable_lts: &[LocalDefId], + usages: &[Lifetime], +) -> Option<Vec<(Span, String)>> { + let explicit_params = generics + .params + .iter() + .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) + .collect::<Vec<_>>(); + + let mut suggestions = if elidable_lts.len() == explicit_params.len() { + // if all the params are elided remove the whole generic block + // + // fn x<'a>() {} + // ^^^^ + vec![(generics.span, String::new())] + } else { + elidable_lts + .iter() + .map(|&id| { + let pos = explicit_params.iter().position(|param| param.def_id == id)?; + let param = explicit_params.get(pos)?; + + let span = if let Some(next) = explicit_params.get(pos + 1) { + // fn x<'prev, 'a, 'next>() {} + // ^^^^ + param.span.until(next.span) + } else { + // `pos` should be at least 1 here, because the param in position 0 would either have a `next` + // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. + let prev = explicit_params.get(pos - 1)?; + + // fn x<'prev, 'a>() {} + // ^^^^ + param.span.with_lo(prev.span.hi()) + }; + + Some((span, String::new())) + }) + .collect::<Option<Vec<_>>>()? + }; + + suggestions.extend( + usages + .iter() + .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id))) + .map(|usage| { + match cx.tcx.hir().get_parent(usage.hir_id) { + Node::Ty(Ty { + kind: TyKind::Ref(..), .. + }) => { + // expand `&'a T` to `&'a T` + // ^^ ^^^ + let span = cx + .sess() + .source_map() + .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace()) + .unwrap_or(usage.ident.span); + + (span, String::new()) + }, + // `T<'a>` and `impl Foo + 'a` should be replaced by `'_` + _ => (usage.ident.span, String::from("'_")), + } + }), + ); + + Some(suggestions) +} + // elision doesn't work for explicit self types, see rust-lang/rust#69064 fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool { if_chain! { @@ -236,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: } } +fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> { + match lt.res { + LifetimeName::Param(id) if !lt.is_anonymous() => Some(id), + _ => None, + } +} + fn could_use_elision<'tcx>( cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, body: Option<BodyId>, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> Option<Vec<(LocalDefId, Option<Span>)>> { +) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> { // There are two scenarios where elision works: // * no output references, all input references have different LT // * output references, exactly one input reference with same LT @@ -300,32 +376,24 @@ fn could_use_elision<'tcx>( // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { - if !allowed_lts.contains(lt) { + if let Some(id) = named_lifetime(lt) + && !allowed_lts.contains(&id) + { return None; } } // check for higher-ranked trait bounds if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() { - let allowed_lts: FxHashSet<_> = allowed_lts - .iter() - .filter_map(|lt| match lt { - RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())), - _ => None, - }) - .collect(); + let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect(); for lt in input_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } for lt in output_visitor.nested_elision_site_lts { - if let RefLt::Named(def_id) = lt { - if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) { - return None; - } + if allowed_lts.contains(<.ident.name) { + return None; } } } @@ -337,15 +405,10 @@ fn could_use_elision<'tcx>( let elidable_lts = named_lifetime_occurrences(&input_lts) .into_iter() .filter_map(|(def_id, occurrences)| { - if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) { - Some(( - def_id, - input_visitor - .lifetime_generic_arg_spans - .get(&def_id) - .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id)) - .copied(), - )) + if occurrences == 1 + && (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id))) + { + Some(def_id) } else { None } @@ -353,31 +416,34 @@ fn could_use_elision<'tcx>( .collect::<Vec<_>>(); if elidable_lts.is_empty() { - None - } else { - Some(elidable_lts) + return None; } + + let usages = itertools::chain(input_lts, output_lts).collect(); + + Some((elidable_lts, usages)) } -fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> { - let mut allowed_lts = FxHashSet::default(); - for par in named_generics.iter() { - if let GenericParamKind::Lifetime { .. } = par.kind { - allowed_lts.insert(RefLt::Named(par.def_id)); - } - } - allowed_lts.insert(RefLt::Unnamed); - allowed_lts.insert(RefLt::Static); - allowed_lts +fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> { + named_generics + .iter() + .filter_map(|par| { + if let GenericParamKind::Lifetime { .. } = par.kind { + Some(par.def_id) + } else { + None + } + }) + .collect() } /// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve /// relative order. #[must_use] -fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { +fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> { let mut occurrences = Vec::new(); for lt in lts { - if let &RefLt::Named(curr_def_id) = lt { + if let Some(curr_def_id) = named_lifetime(lt) { if let Some(pair) = occurrences .iter_mut() .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) @@ -391,12 +457,10 @@ fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { occurrences } -/// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, - lts: Vec<RefLt>, - lifetime_generic_arg_spans: FxHashMap<LocalDefId, Span>, - nested_elision_site_lts: Vec<RefLt>, + lts: Vec<Lifetime>, + nested_elision_site_lts: Vec<Lifetime>, unelided_trait_object_lifetime: bool, } @@ -405,32 +469,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - lifetime_generic_arg_spans: FxHashMap::default(), nested_elision_site_lts: Vec::new(), unelided_trait_object_lifetime: false, } } - fn record(&mut self, lifetime: &Option<Lifetime>) { - if let Some(ref lt) = *lifetime { - if lt.is_static() { - self.lts.push(RefLt::Static); - } else if lt.is_anonymous() { - // Fresh lifetimes generated should be ignored. - self.lts.push(RefLt::Unnamed); - } else if let LifetimeName::Param(def_id) = lt.res { - self.lts.push(RefLt::Named(def_id)); - } - } else { - self.lts.push(RefLt::Unnamed); - } - } - - fn all_lts(&self) -> Vec<RefLt> { + fn all_lts(&self) -> Vec<Lifetime> { self.lts .iter() .chain(self.nested_elision_site_lts.iter()) - .cloned() + .copied() .collect::<Vec<_>>() } @@ -442,7 +490,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { - self.record(&Some(*lifetime)); + self.lts.push(*lifetime); } fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) { @@ -467,11 +515,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { walk_item(self, item); self.lts.truncate(len); self.lts.extend(bounds.iter().filter_map(|bound| match bound { - GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res { - RefLt::Named(def_id) - } else { - RefLt::Unnamed - }), + GenericArg::Lifetime(&l) => Some(l), _ => None, })); }, @@ -491,13 +535,6 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { _ => walk_ty(self, ty), } } - - fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { - if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { - self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); - } - walk_generic_arg(self, generic_arg); - } } /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to @@ -521,8 +558,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { - return true; + for lt in visitor.all_lts() { + if let Some(id) = named_lifetime(<) + && !allowed_lts.contains(&id) + { + return true; + } } }, WherePredicate::EqPredicate(ref pred) => { diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 8e52cac4323..610a0233eee 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -61,7 +61,8 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Just iterating the collection itself makes the intent - /// more clear and is probably faster. + /// more clear and is probably faster because it eliminates + /// the bounds check that is done when indexing. /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index e6ed4ea7a5d..25a1a5842f7 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -149,7 +149,7 @@ pub(super) fn check<'tcx>( |diag| { multispan_sugg( diag, - "consider using an iterator", + "consider using an iterator and enumerate()", vec![ (pat.span, format!("({}, <item>)", ident.name)), ( diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index 4277455a3a2..ce5d657bcf0 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -1,7 +1,6 @@ use crate::rustc_lint::LintContext; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::macros::root_macro_call; use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, UnOp}; @@ -38,57 +37,57 @@ declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); impl<'tcx> LateLintPass<'tcx> for ManualAssert { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if_chain! { - if let ExprKind::If(cond, then, None) = expr.kind; - if !matches!(cond.kind, ExprKind::Let(_)); - if !expr.span.from_expansion(); - let then = peel_blocks_with_stmt(then); - if let Some(macro_call) = root_macro_call(then.span); - if cx.tcx.item_name(macro_call.def_id) == sym::panic; - if !cx.tcx.sess.source_map().is_multiline(cond.span); - if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); + if let ExprKind::If(cond, then, None) = expr.kind + && !matches!(cond.kind, ExprKind::Let(_)) + && !expr.span.from_expansion() + && let then = peel_blocks_with_stmt(then) + && let Some(macro_call) = root_macro_call(then.span) + && cx.tcx.item_name(macro_call.def_id) == sym::panic + && !cx.tcx.sess.source_map().is_multiline(cond.span) + && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span) + && let Some(panic_snippet) = panic_snippet.strip_suffix(')') + && let Some((_, format_args_snip)) = panic_snippet.split_once('(') // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just // shuffles the condition around. // Should this have a config value? - if !is_else_clause(cx.tcx, expr); - then { - let mut applicability = Applicability::MachineApplicable; - let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); - let cond = cond.peel_drop_temps(); - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); - if !comments.is_empty() { - comments += "\n"; - } - let (cond, not) = match cond.kind { - ExprKind::Unary(UnOp::Not, e) => (e, ""), - _ => (cond, "!"), - }; - let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); - let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); - // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block - span_lint_and_then( - cx, - MANUAL_ASSERT, - expr.span, - "only a `panic!` in `if`-then statement", - |diag| { - // comments can be noisy, do not show them to the user - if !comments.is_empty() { - diag.tool_only_span_suggestion( - expr.span.shrink_to_lo(), - "add comments back", - comments, - applicability); - } - diag.span_suggestion( - expr.span, - "try instead", - sugg, - applicability); - } - - ); + && !is_else_clause(cx.tcx, expr) + { + let mut applicability = Applicability::MachineApplicable; + let cond = cond.peel_drop_temps(); + let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + if !comments.is_empty() { + comments += "\n"; } + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), + }; + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); + // we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block + span_lint_and_then( + cx, + MANUAL_ASSERT, + expr.span, + "only a `panic!` in `if`-then statement", + |diag| { + // comments can be noisy, do not show them to the user + if !comments.is_empty() { + diag.tool_only_span_suggestion( + expr.span.shrink_to_lo(), + "add comments back", + comments, + applicability + ); + } + diag.span_suggestion( + expr.span, + "try instead", + sugg, + applicability + ); + } + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 59de8c0384b..3126b590180 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -45,8 +45,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x)); - let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect(); + let is_external = adt_def.did().as_local().is_none(); + let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x)); + let mut missing_variants: Vec<_> = adt_def + .variants() + .iter() + .filter(|x| !(is_external && is_hidden(cx, x))) + .collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -133,7 +138,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -144,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden { + let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index fb94dfa5980..f1e8be7f2b8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1818,6 +1818,7 @@ declare_clippy_lint! { /// - `or_else` to `or` /// - `get_or_insert_with` to `get_or_insert` /// - `ok_or_else` to `ok_or` + /// - `then` to `then_some` (for msrv >= 1.62.0) /// /// ### Why is this bad? /// Using eager evaluation is shorter and simpler in some cases. diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs index fe88fa41fd9..e818f1892e5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; use if_chain::if_chain; @@ -17,19 +17,31 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - let input_type = cx.typeck_results().expr_ty(expr); if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind(); if cx.tcx.is_diagnostic_item(sym::Cow, adt.did()); + then { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( + span_lint_and_then( cx, SUSPICIOUS_TO_OWNED, expr.span, &with_forced_trimmed_paths!(format!( "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned" )), - "consider using, depending on intent", - format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"), - app, + |diag| { + diag.span_suggestion( + expr.span, + "depending on intent, either make the Cow an Owned variant", + format!("{recv_snip}.into_owned()"), + app + ); + diag.span_suggestion( + expr.span, + "or clone the Cow itself", + format!("{recv_snip}.clone()"), + app + ); + } ); return true; } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 2814c92e67a..63c575fca30 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -10,6 +10,7 @@ use hir::{ use rustc_ast::Mutability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -66,7 +67,7 @@ declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]) impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) { + if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) { return; } let mut unsafe_ops = vec![]; diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 472f52380bb..c5ea09590d3 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -25,11 +25,11 @@ declare_clippy_lint! { /// Using the dedicated functions of the `Option` type is clearer and /// more concise than an `if let` expression. /// - /// ### Known problems - /// This lint uses a deliberately conservative metric for checking - /// if the inside of either body contains breaks or continues which will - /// cause it to not suggest a fix if either block contains a loop with - /// continues or breaks contained within the loop. + /// ### Notes + /// This lint uses a deliberately conservative metric for checking if the + /// inside of either body contains loop control expressions `break` or + /// `continue` (which cannot be used within closures). If these are found, + /// this lint will not be raised. /// /// ### Example /// ```rust diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 8afe286fbd5..d88409c356e 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -624,7 +624,10 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: return; }; - match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i].peel_refs().kind() { + match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i] + .peel_refs() + .kind() + { ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { set_skip_flag(); }, diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 1fda58fa54d..9e6c6c73d4f 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -1,5 +1,8 @@ +use std::fmt::Display; + use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::source::snippet_opt; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; @@ -77,13 +80,45 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[must_use] -fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { - let offset = u32::from(offset); - let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset); - let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset); - assert!(start <= end); - Span::new(start, end, base.ctxt(), base.parent()) +fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescaped: &str, base: Span, offset: u8) { + let parts: Option<(_, _, &dyn Display)> = match &error { + regex_syntax::Error::Parse(e) => Some((e.span(), e.auxiliary_span(), e.kind())), + regex_syntax::Error::Translate(e) => Some((e.span(), None, e.kind())), + _ => None, + }; + + let convert_span = |regex_span: ®ex_syntax::ast::Span| { + let offset = u32::from(offset); + let start = base.lo() + BytePos(u32::try_from(regex_span.start.offset).expect("offset too large") + offset); + let end = base.lo() + BytePos(u32::try_from(regex_span.end.offset).expect("offset too large") + offset); + + Span::new(start, end, base.ctxt(), base.parent()) + }; + + if let Some((primary, auxiliary, kind)) = parts + && let Some(literal_snippet) = snippet_opt(cx, base) + && let Some(inner) = literal_snippet.get(offset as usize..) + // Only convert to native rustc spans if the parsed regex matches the + // source snippet exactly, to ensure the span offsets are correct + && inner.get(..unescaped.len()) == Some(unescaped) + { + let spans = if let Some(auxiliary) = auxiliary { + vec![convert_span(primary), convert_span(auxiliary)] + } else { + vec![convert_span(primary)] + }; + + span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}")); + } else { + span_lint_and_help( + cx, + INVALID_REGEX, + base, + &error.to_string(), + None, + "consider using a raw string literal: `r\"..\"`", + ); + } } fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> { @@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - str_span(expr.span, *e.span(), offset), - &format!("regex syntax error: {}", e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => lint_syntax_error(cx, &e, r, expr.span, offset), } } } else if let Some(r) = const_str(cx, expr) { @@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl); } }, - Err(regex_syntax::Error::Parse(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(regex_syntax::Error::Translate(e)) => { - span_lint( - cx, - INVALID_REGEX, - expr.span, - &format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()), - ); - }, - Err(e) => { - span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}")); - }, + Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()), } } } diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 8c39b4fc569..bccf421e8f3 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -78,8 +78,8 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa // We don't want to emit this lint if the `#[must_use]` attribute is already there. if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)); if cx.tcx.visibility(fn_def.to_def_id()).is_public(); - let ret_ty = return_ty(cx, owner_id.into()); - let self_arg = nth_arg(cx, owner_id.into(), 0); + let ret_ty = return_ty(cx, owner_id); + let self_arg = nth_arg(cx, owner_id, 0); // If `Self` has the same type as the returned type, then we want to warn. // // For this check, we don't want to remove the reference on the returned type because if diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 8f1d1490e1f..34a3e5ddf4f 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x); } /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_INSIDE_BLOCK, restriction, "add a semicolon inside the block" @@ -59,7 +59,7 @@ declare_clippy_lint! { /// # let x = 0; /// unsafe { f(x) }; /// ``` - #[clippy::version = "1.66.0"] + #[clippy::version = "1.68.0"] pub SEMICOLON_OUTSIDE_BLOCK, restriction, "add a semicolon outside the block" diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 585e2075fa9..c1f228d5f90 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -392,9 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let is_exported = cx - .effective_visibilities - .is_exported(field.def_id); + let is_exported = cx.effective_visibilities.is_exported(field.def_id); self.check_ty( cx, diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 92053cec59f..0e526c216be 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() { - "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { + "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => { check_map_error(cx, arg_0, expr); }, _ => (), diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index f48be27592b..1d78c7cfae0 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -253,7 +253,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN. /// /// The minimum rust version that the project supports (msrv: Option<String> = None), @@ -323,7 +323,7 @@ define_Conf! { /// /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit: Option<u64> = None), - /// Lint: LARGE_TYPE_PASS_BY_MOVE. + /// Lint: LARGE_TYPES_PASSED_BY_VALUE. /// /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. (pass_by_value_size_limit: u64 = 256), @@ -411,7 +411,7 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE + /// Lint: AWAIT_HOLDING_INVALID_TYPE. (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()), /// Lint: LARGE_INCLUDE_FILE. /// @@ -437,7 +437,7 @@ define_Conf! { /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY. + /// Lint: MUTABLE_KEY_TYPE. /// /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability @@ -446,7 +446,7 @@ define_Conf! { /// /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING + /// Lint: INDEXING_SLICING. /// /// Whether to suppress a restriction lint in constant code. In same /// cases the restructured operation might not be unavoidable, as the diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs index 01efc527a8c..092041aecf2 100644 --- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,5 @@ use clippy_utils::get_attr; +use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -47,6 +48,18 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir { println!("{stmt:#?}"); } } + + fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } } fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 4c3b1b131fd..f718207654f 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -215,14 +215,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { cx, }; let body_id = cx.tcx.hir().body_owned_by( - cx.tcx.hir().local_def_id( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id(), - ), + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .owner_id + .def_id, ); collector.visit_expr(cx.tcx.hir().body(body_id).value); } diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index d7f466c1976..63dccbf697c 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -770,10 +770,7 @@ impl<'tcx> FormatSpec<'tcx> { /// Has no other formatting specifiers than setting the format trait. returns true for `{}`, /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}` pub fn is_default_for_trait(&self) -> bool { - self.width.is_implied() - && self.precision.is_implied() - && self.align == Alignment::AlignUnknown - && self.no_flags + self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.no_flags } } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 72705878075..26b1d019749 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -36,6 +36,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue, + ty::PredicateKind::AliasEq(..) => panic!("alias eq predicate on function: {predicate:#?}"), ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"), diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index b8c87aa5e1e..78fb2e0eb7e 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -809,7 +809,10 @@ pub struct DerefClosure { /// /// note: this only works on single line immutable closures with exactly one input parameter. pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option<DerefClosure> { - if let hir::ExprKind::Closure(&Closure { fn_decl, def_id, body, .. }) = closure.kind { + if let hir::ExprKind::Closure(&Closure { + fn_decl, def_id, body, .. + }) = closure.kind + { let closure_body = cx.tcx.hir().body(body); // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`) // a type annotation is present if param `kind` is different from `TyKind::Infer` diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index d18b62d1bf1..00073bcd82a 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -392,12 +392,16 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { .cx .typeck_results() .type_dependent_def_id(e.hir_id) - .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe) => + .map_or(false, |id| { + self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe + }) => { self.is_unsafe = true; }, ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() { - ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => self.is_unsafe = true, + ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => { + self.is_unsafe = true; + }, ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true, _ => walk_expr(self, e), }, diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index de31c16b819..653121af54d 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -10,8 +10,8 @@ edition = "2021" publish = false [dependencies] -cargo_metadata = "0.14" -clap = "3.2" +cargo_metadata = "0.15.3" +clap = "4.1.4" crossbeam-channel = "0.5.6" flate2 = "1.0" rayon = "1.5.1" diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 4e7fc565a32..adea8c53df2 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-01-27" +channel = "nightly-2023-02-10" 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 d521e8d8839..e45835efe74 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -220,6 +220,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { None, false, false, + rustc_errors::TerminalUrl::No, )); let handler = rustc_errors::Handler::with_emitter(true, None, emitter); 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 2a240cc249b..c1a10ba55ef 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,19 @@ error: hardcoded path to a diagnostic item - --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 + --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43 | -LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `sym::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:12:43 + --> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36 | -LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: convert all references to use `sym::deref_method` + = help: convert all references to use `sym::Deref` error: hardcoded path to a language item --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40 diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs index 1e5f20e8c39..a13af565203 100644 --- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs +++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs @@ -149,3 +149,22 @@ macro_rules! almost_complete_range { let _ = '0'..'9'; }; } + +#[macro_export] +macro_rules! unsafe_macro { + () => { + unsafe { + *core::ptr::null::<()>(); + *core::ptr::null::<()>(); + } + }; +} + +#[macro_export] +macro_rules! needless_lifetime { + () => { + fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; +} diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed index 95f35a61bb2..b8dd92906c8 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed @@ -86,7 +86,7 @@ fn main() { let b = ImplNotTraitWithBool; assert_eq!("a".len(), 1); - assert!("a".is_empty()); + assert!(!"a".is_empty()); assert!("".is_empty()); assert!("".is_empty()); assert_eq!(a!(), b!()); @@ -97,16 +97,16 @@ fn main() { assert_ne!("a".len(), 1); assert!("a".is_empty()); - assert!("".is_empty()); - assert!("".is_empty()); + assert!(!"".is_empty()); + assert!(!"".is_empty()); assert_ne!(a!(), b!()); assert_ne!(a!(), "".is_empty()); assert_ne!("".is_empty(), b!()); assert_ne!(a, true); - assert!(b); + assert!(!b); debug_assert_eq!("a".len(), 1); - debug_assert!("a".is_empty()); + debug_assert!(!"a".is_empty()); debug_assert!("".is_empty()); debug_assert!("".is_empty()); debug_assert_eq!(a!(), b!()); @@ -117,27 +117,27 @@ fn main() { debug_assert_ne!("a".len(), 1); debug_assert!("a".is_empty()); - debug_assert!("".is_empty()); - debug_assert!("".is_empty()); + debug_assert!(!"".is_empty()); + debug_assert!(!"".is_empty()); debug_assert_ne!(a!(), b!()); debug_assert_ne!(a!(), "".is_empty()); debug_assert_ne!("".is_empty(), b!()); debug_assert_ne!(a, true); - debug_assert!(b); + debug_assert!(!b); // assert with error messages assert_eq!("a".len(), 1, "tadam {}", 1); assert_eq!("a".len(), 1, "tadam {}", true); - assert!("a".is_empty(), "tadam {}", 1); - assert!("a".is_empty(), "tadam {}", true); - assert!("a".is_empty(), "tadam {}", true); + assert!(!"a".is_empty(), "tadam {}", 1); + assert!(!"a".is_empty(), "tadam {}", true); + assert!(!"a".is_empty(), "tadam {}", true); assert_eq!(a, true, "tadam {}", false); debug_assert_eq!("a".len(), 1, "tadam {}", 1); debug_assert_eq!("a".len(), 1, "tadam {}", true); - debug_assert!("a".is_empty(), "tadam {}", 1); - debug_assert!("a".is_empty(), "tadam {}", true); - debug_assert!("a".is_empty(), "tadam {}", true); + debug_assert!(!"a".is_empty(), "tadam {}", 1); + debug_assert!(!"a".is_empty(), "tadam {}", true); + debug_assert!(!"a".is_empty(), "tadam {}", true); debug_assert_eq!(a, true, "tadam {}", false); assert!(a!()); @@ -158,4 +158,14 @@ fn main() { }}; } in_macro!(a); + + assert!("".is_empty()); + assert!("".is_empty()); + assert!(!"requires negation".is_empty()); + assert!(!"requires negation".is_empty()); + + debug_assert!("".is_empty()); + debug_assert!("".is_empty()); + debug_assert!(!"requires negation".is_empty()); + debug_assert!(!"requires negation".is_empty()); } diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs index 88e7560b4f9..0a8ad34fda5 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs @@ -158,4 +158,14 @@ fn main() { }}; } in_macro!(a); + + assert_eq!("".is_empty(), true); + assert_ne!("".is_empty(), false); + assert_ne!("requires negation".is_empty(), true); + assert_eq!("requires negation".is_empty(), false); + + debug_assert_eq!("".is_empty(), true); + debug_assert_ne!("".is_empty(), false); + debug_assert_ne!("requires negation".is_empty(), true); + debug_assert_eq!("requires negation".is_empty(), false); } diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr index 3d9f8573e61..89cefc95a9f 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr @@ -8,7 +8,7 @@ LL | assert_eq!("a".is_empty(), false); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false); -LL + assert!("a".is_empty()); +LL + assert!(!"a".is_empty()); | error: used `assert_eq!` with a literal bool @@ -68,7 +68,7 @@ LL | assert_ne!("".is_empty(), true); help: replace it with `assert!(..)` | LL - assert_ne!("".is_empty(), true); -LL + assert!("".is_empty()); +LL + assert!(!"".is_empty()); | error: used `assert_ne!` with a literal bool @@ -80,7 +80,7 @@ LL | assert_ne!(true, "".is_empty()); help: replace it with `assert!(..)` | LL - assert_ne!(true, "".is_empty()); -LL + assert!("".is_empty()); +LL + assert!(!"".is_empty()); | error: used `assert_ne!` with a literal bool @@ -92,7 +92,7 @@ LL | assert_ne!(b, true); help: replace it with `assert!(..)` | LL - assert_ne!(b, true); -LL + assert!(b); +LL + assert!(!b); | error: used `debug_assert_eq!` with a literal bool @@ -104,7 +104,7 @@ LL | debug_assert_eq!("a".is_empty(), false); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false); -LL + debug_assert!("a".is_empty()); +LL + debug_assert!(!"a".is_empty()); | error: used `debug_assert_eq!` with a literal bool @@ -164,7 +164,7 @@ LL | debug_assert_ne!("".is_empty(), true); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!("".is_empty(), true); -LL + debug_assert!("".is_empty()); +LL + debug_assert!(!"".is_empty()); | error: used `debug_assert_ne!` with a literal bool @@ -176,7 +176,7 @@ LL | debug_assert_ne!(true, "".is_empty()); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!(true, "".is_empty()); -LL + debug_assert!("".is_empty()); +LL + debug_assert!(!"".is_empty()); | error: used `debug_assert_ne!` with a literal bool @@ -188,7 +188,7 @@ LL | debug_assert_ne!(b, true); help: replace it with `debug_assert!(..)` | LL - debug_assert_ne!(b, true); -LL + debug_assert!(b); +LL + debug_assert!(!b); | error: used `assert_eq!` with a literal bool @@ -200,7 +200,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false, "tadam {}", 1); -LL + assert!("a".is_empty(), "tadam {}", 1); +LL + assert!(!"a".is_empty(), "tadam {}", 1); | error: used `assert_eq!` with a literal bool @@ -212,7 +212,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", true); help: replace it with `assert!(..)` | LL - assert_eq!("a".is_empty(), false, "tadam {}", true); -LL + assert!("a".is_empty(), "tadam {}", true); +LL + assert!(!"a".is_empty(), "tadam {}", true); | error: used `assert_eq!` with a literal bool @@ -224,7 +224,7 @@ LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); help: replace it with `assert!(..)` | LL - assert_eq!(false, "a".is_empty(), "tadam {}", true); -LL + assert!("a".is_empty(), "tadam {}", true); +LL + assert!(!"a".is_empty(), "tadam {}", true); | error: used `debug_assert_eq!` with a literal bool @@ -236,7 +236,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); -LL + debug_assert!("a".is_empty(), "tadam {}", 1); +LL + debug_assert!(!"a".is_empty(), "tadam {}", 1); | error: used `debug_assert_eq!` with a literal bool @@ -248,7 +248,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true); -LL + debug_assert!("a".is_empty(), "tadam {}", true); +LL + debug_assert!(!"a".is_empty(), "tadam {}", true); | error: used `debug_assert_eq!` with a literal bool @@ -260,7 +260,7 @@ LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); help: replace it with `debug_assert!(..)` | LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); -LL + debug_assert!("a".is_empty(), "tadam {}", true); +LL + debug_assert!(!"a".is_empty(), "tadam {}", true); | error: used `assert_eq!` with a literal bool @@ -299,5 +299,101 @@ LL - renamed!(b, true); LL + debug_assert!(b); | -error: aborting due to 25 previous errors +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:162:5 + | +LL | assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("".is_empty(), true); +LL + assert!("".is_empty()); + | + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:163:5 + | +LL | assert_ne!("".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("".is_empty(), false); +LL + assert!("".is_empty()); + | + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:164:5 + | +LL | assert_ne!("requires negation".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_ne!("requires negation".is_empty(), true); +LL + assert!(!"requires negation".is_empty()); + | + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:165:5 + | +LL | assert_eq!("requires negation".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!("requires negation".is_empty(), false); +LL + assert!(!"requires negation".is_empty()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:167:5 + | +LL | debug_assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("".is_empty(), true); +LL + debug_assert!("".is_empty()); + | + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:168:5 + | +LL | debug_assert_ne!("".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("".is_empty(), false); +LL + debug_assert!("".is_empty()); + | + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:169:5 + | +LL | debug_assert_ne!("requires negation".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_ne!("requires negation".is_empty(), true); +LL + debug_assert!(!"requires negation".is_empty()); + | + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:170:5 + | +LL | debug_assert_eq!("requires negation".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `debug_assert!(..)` + | +LL - debug_assert_eq!("requires negation".is_empty(), false); +LL + debug_assert!(!"requires negation".is_empty()); + | + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr index 1f26c7f4db6..c5ea0b16d1b 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2774.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-2774.stderr @@ -5,6 +5,11 @@ LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { +LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) { + | error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 875d5ab4f21..0b0e0ad2684 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -9,6 +9,11 @@ note: the lint level is defined here | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs new file mode 100644 index 00000000000..5cb80cb6233 --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.rs @@ -0,0 +1,69 @@ +#![allow(unused, clippy::needless_lifetimes)] +#![warn(clippy::extra_unused_type_parameters)] + +fn unused_ty<T>(x: u8) {} + +fn unused_multi<T, U>(x: u8) {} + +fn unused_with_lt<'a, T>(x: &'a u8) {} + +fn used_ty<T>(x: T, y: u8) {} + +fn used_ref<'a, T>(x: &'a T) {} + +fn used_ret<T: Default>(x: u8) -> T { + T::default() +} + +fn unused_bounded<T: Default, U>(x: U) {} + +fn unused_where_clause<T, U>(x: U) +where + T: Default, +{ +} + +fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {} + +fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize { + iter.count() +} + +fn used_ret_opaque<A>() -> impl Iterator<Item = A> { + std::iter::empty() +} + +fn used_vec_box<T>(x: Vec<Box<T>>) {} + +fn used_body<T: Default + ToString>() -> String { + T::default().to_string() +} + +fn used_closure<T: Default + ToString>() -> impl Fn() { + || println!("{}", T::default().to_string()) +} + +struct S; + +impl S { + fn unused_ty_impl<T>(&self) {} +} + +// Don't lint on trait methods +trait Foo { + fn bar<T>(&self); +} + +impl Foo for S { + fn bar<T>(&self) {} +} + +fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A> +where + Iter: Iterator<Item = A>, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} + +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 new file mode 100644 index 00000000000..1c8dd53e638 --- /dev/null +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters.stderr @@ -0,0 +1,59 @@ +error: type parameter goes unused in function definition + --> $DIR/extra_unused_type_parameters.rs:4:13 + | +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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +LL | fn unused_ty_impl<T>(&self) {} + | ^^^ + | + = help: consider removing the parameter + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/len_without_is_empty.rs b/src/tools/clippy/tests/ui/len_without_is_empty.rs index 78397c2af34..b5dec6c46bd 100644 --- a/src/tools/clippy/tests/ui/len_without_is_empty.rs +++ b/src/tools/clippy/tests/ui/len_without_is_empty.rs @@ -282,4 +282,50 @@ impl AsyncLen { } } +// issue #9520 +pub struct NonStandardLenAndIsEmptySignature; +impl NonStandardLenAndIsEmptySignature { + // don't lint + pub fn len(&self, something: usize) -> usize { + something + } + + pub fn is_empty(&self, something: usize) -> bool { + something == 0 + } +} + +// test case for #9520 with generics in the function signature +pub trait TestResource { + type NonStandardSignatureWithGenerics: Copy; + fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result<Option<&[u8]>, String>; +} +pub struct NonStandardSignatureWithGenerics(u32); +impl NonStandardSignatureWithGenerics { + pub fn is_empty<T, U>(self, resource: &T) -> bool + where + T: TestResource<NonStandardSignatureWithGenerics = U>, + U: Copy + From<NonStandardSignatureWithGenerics>, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.is_empty() + } else { + true + } + } + + // test case for #9520 with generics in the function signature + pub fn len<T, U>(self, resource: &T) -> usize + where + T: TestResource<NonStandardSignatureWithGenerics = U>, + U: Copy + From<NonStandardSignatureWithGenerics>, + { + if let Ok(Some(content)) = resource.lookup_content(self.into()) { + content.len() + } else { + 0_usize + } + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed index 638320dd6ee..8c7e919bf62 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.fixed @@ -29,9 +29,7 @@ fn main() { panic!("qaqaq{:?}", a); } assert!(a.is_empty(), "qaqaq{:?}", a); - if !a.is_empty() { - panic!("qwqwq"); - } + assert!(a.is_empty(), "qwqwq"); if a.len() == 3 { println!("qwq"); println!("qwq"); @@ -46,21 +44,11 @@ fn main() { println!("qwq"); } let b = vec![1, 2, 3]; - if b.is_empty() { - panic!("panic1"); - } - if b.is_empty() && a.is_empty() { - panic!("panic2"); - } - if a.is_empty() && !b.is_empty() { - panic!("panic3"); - } - if b.is_empty() || a.is_empty() { - panic!("panic4"); - } - if a.is_empty() || !b.is_empty() { - panic!("panic5"); - } + assert!(!b.is_empty(), "panic1"); + assert!(!(b.is_empty() && a.is_empty()), "panic2"); + assert!(!(a.is_empty() && !b.is_empty()), "panic3"); + assert!(!(b.is_empty() || a.is_empty()), "panic4"); + assert!(!(a.is_empty() || !b.is_empty()), "panic5"); assert!(!a.is_empty(), "with expansion {}", one!()); if a.is_empty() { let _ = 0; @@ -71,12 +59,11 @@ fn main() { fn issue7730(a: u8) { // Suggestion should preserve comment - if a > 2 { - // comment - /* this is a + // comment +/* this is a multiline comment */ - /// Doc comment - panic!("panic with comment") // comment after `panic!` - } +/// Doc comment +// comment after `panic!` +assert!(!(a > 2), "panic with comment"); } diff --git a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr index 1f2e1e3087b..3555ac29243 100644 --- a/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr +++ b/src/tools/clippy/tests/ui/manual_assert.edition2018.stderr @@ -9,6 +9,54 @@ LL | | } = note: `-D clippy::manual-assert` implied by `-D warnings` error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:34:5 + | +LL | / if !a.is_empty() { +LL | | panic!("qwqwq"); +LL | | } + | |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:51:5 + | +LL | / if b.is_empty() { +LL | | panic!("panic1"); +LL | | } + | |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:54:5 + | +LL | / if b.is_empty() && a.is_empty() { +LL | | panic!("panic2"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:57:5 + | +LL | / if a.is_empty() && !b.is_empty() { +LL | | panic!("panic3"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:60:5 + | +LL | / if b.is_empty() || a.is_empty() { +LL | | panic!("panic4"); +LL | | } + | |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");` + +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:63:5 + | +LL | / if a.is_empty() || !b.is_empty() { +LL | | panic!("panic5"); +LL | | } + | |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");` + +error: only a `panic!` in `if`-then statement --> $DIR/manual_assert.rs:66:5 | LL | / if a.is_empty() { @@ -16,5 +64,22 @@ LL | | panic!("with expansion {}", one!()) LL | | } | |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());` -error: aborting due to 2 previous errors +error: only a `panic!` in `if`-then statement + --> $DIR/manual_assert.rs:78:5 + | +LL | / if a > 2 { +LL | | // comment +LL | | /* this is a +LL | | multiline +... | +LL | | panic!("panic with comment") // comment after `panic!` +LL | | } + | |_____^ + | +help: try instead + | +LL | assert!(!(a > 2), "panic with comment"); + | + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed index fc252cdd352..9fd3739b69c 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.fixed @@ -123,7 +123,7 @@ fn main() { Enum::A => (), Enum::B => (), Enum::C => (), - _ => (), + Enum::__Private => (), } match Enum::A { Enum::A => (), diff --git a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr index 6fa313dc911..105b4c4b41d 100644 --- a/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr +++ b/src/tools/clippy/tests/ui/match_wildcard_for_single_variants.stderr @@ -49,10 +49,16 @@ LL | _ => (), | ^ help: try this: `Color::Blue` error: wildcard matches only a single variant and will also match any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:126:13 + | +LL | _ => (), + | ^ help: try this: `Enum::__Private` + +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:153:13 | LL | _ => 2, | ^ help: try this: `Foo::B` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 41263535df6..4511bc99c3c 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,9 +1,13 @@ +// aux-build:macro_rules.rs #![allow(unused)] #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] #![allow(clippy::drop_copy)] #![warn(clippy::multiple_unsafe_ops_per_block)] +#[macro_use] +extern crate macro_rules; + use core::arch::asm; fn raw_ptr() -> *const () { @@ -107,4 +111,9 @@ unsafe fn read_char_good(ptr: *const u8) -> char { unsafe { core::char::from_u32_unchecked(int_value) } } +// no lint +fn issue10259() { + unsafe_macro!(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr index f6b8341795d..303aeb7aee0 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:32:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:36:5 | LL | / unsafe { LL | | STATIC += 1; @@ -8,19 +8,19 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:33:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:37:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:34:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:38:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:41:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:45:5 | LL | / unsafe { LL | | drop(u.u); @@ -29,18 +29,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:42:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:46:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:43:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:47:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:48:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:52:5 | LL | / unsafe { LL | | asm!("nop"); @@ -50,23 +50,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:49:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:53:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:50:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:54:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:51:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:55:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:57:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:61:5 | LL | / unsafe { LL | | drop(u.u); @@ -78,49 +78,49 @@ LL | | } | |_____^ | note: union field access occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:58:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:62:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:59:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:63:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:60:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:64:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:61:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:65:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:62:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:66:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> $DIR/multiple_unsafe_ops_per_block.rs:63:9 + --> $DIR/multiple_unsafe_ops_per_block.rs:67:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> $DIR/multiple_unsafe_ops_per_block.rs:101:5 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:5 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:14 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:14 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> $DIR/multiple_unsafe_ops_per_block.rs:101:39 + --> $DIR/multiple_unsafe_ops_per_block.rs:105:39 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed new file mode 100644 index 00000000000..d286ef4ba37 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -0,0 +1,538 @@ +// run-rustfix +// aux-build:macro_rules.rs + +#![warn(clippy::needless_lifetimes)] +#![allow( + unused, + clippy::boxed_local, + clippy::extra_unused_type_parameters, + clippy::needless_pass_by_value, + clippy::unnecessary_wraps, + dyn_drop, + clippy::get_first +)] + +#[macro_use] +extern crate macro_rules; + +fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + +fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + +// No error; same lifetime on two params. +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {} + +// No error; static involved. +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {} + +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {} + +fn in_and_out(x: &u8, _y: u8) -> &u8 { + x +} + +// No error; multiple input refs. +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 +// ^^^ +fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 +// ^^^ +fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + y +} + +// No error; multiple input refs +async fn func<'a>(args: &[&'a str]) -> Option<&'a str> { + args.get(0).cloned() +} + +// No error; static involved. +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 { + x +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> +// ^^^ +fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + Ok(x) +} + +// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: +// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> +// ^^^ +fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + Ok(y) +} + +// No error; two input refs. +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 { + x.unwrap() +} + +fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + Ok(x) +} + +// Where-clause, but without lifetimes. +fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()> +where + T: Copy, +{ + Ok(x) +} + +type Ref<'r> = &'r u8; + +// No error; same lifetime on two params. +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {} + +fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} + +// No error; bounded lifetime. +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {} + +// No error; bounded lifetime. +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) +where + 'b: 'a, +{ +} + +struct Lt<'a, I: 'static> { + x: &'a I, +} + +// No error; fn bound references `'a`. +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +where + F: Fn(Lt<'a, I>) -> Lt<'a, I>, +{ + unreachable!() +} + +fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> +where + for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>, +{ + unreachable!() +} + +// No error; see below. +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) { + f(x); +} + +fn fn_bound_3_cannot_elide() { + let x = 42; + let p = &x; + let mut q = &x; + // This will fail if we elide lifetimes of `fn_bound_3`. + fn_bound_3(p, |y| q = y); +} + +// No error; multiple input refs. +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { + if cond { x } else { f() } +} + +struct X { + x: u8, +} + +impl X { + fn self_and_out(&self) -> &u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 + // ^^^ + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + &self.x + } + + // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid: + // fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 + // ^^^^^ + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + x + } + + fn distinct_self_and_in(&self, _x: &u8) {} + + // No error; same lifetimes on two params. + fn self_and_same_in<'s>(&'s self, _x: &'s u8) {} +} + +struct Foo<'a>(&'a u8); + +impl<'a> Foo<'a> { + // No error; lifetime `'a` not defined in method. + fn self_shared_lifetime(&self, _: &'a u8) {} + // No error; bounds exist. + fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {} +} + +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 { + unimplemented!() +} + +fn struct_with_lt(_foo: Foo<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`). +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`). +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str +// ^^ +fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str +// ^^^^ +fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + unimplemented!() +} + +trait WithLifetime<'a> {} + +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>; + +// Should not warn because it won't build without the lifetime. +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str { + unimplemented!() +} + +// Should warn because there is no lifetime on `Drop`, so this would be +// unambiguous if we elided the lifetime. +fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + unimplemented!() +} + +type FooAlias<'a> = Foo<'a>; + +fn alias_with_lt(_foo: FooAlias<'_>) -> &str { + unimplemented!() +} + +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`). +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str { + unimplemented!() +} + +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`). +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str +// ^^ +fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { + unimplemented!() +} + +// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is +// valid: +// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str +// ^^^^^^^^^ +fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + unimplemented!() +} + +fn named_input_elided_output(_arg: &str) -> &str { + unimplemented!() +} + +fn elided_input_named_output<'a>(_arg: &str) -> &'a str { + unimplemented!() +} + +fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) { + unimplemented!() +} +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) { + unimplemented!() +} + +// Don't warn on these; see issue #292. +fn trait_bound_bug<'a, T: WithLifetime<'a>>() { + unimplemented!() +} + +// See issue #740. +struct Test { + vec: Vec<usize>, +} + +impl Test { + fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> { + unimplemented!() + } +} + +trait LintContext<'a> {} + +fn f<'a, T: LintContext<'a>>(_: &T) {} + +fn test<'a>(x: &'a [u8]) -> u8 { + let y: &'a u8 = &x[5]; + *y +} + +// Issue #3284: give hint regarding lifetime in return type. +struct Cow<'a> { + x: &'a str, +} +fn out_return_type_lts(e: &str) -> Cow<'_> { + unimplemented!() +} + +// Make sure we still warn on implementations +mod issue4291 { + trait BadTrait { + fn needless_lt(x: &u8) {} + } + + impl BadTrait for () { + fn needless_lt(_x: &u8) {} + } +} + +mod issue2944 { + trait Foo {} + struct Bar; + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz(&self) -> impl Foo + '_ { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + +mod issue7296 { + use std::rc::Rc; + use std::sync::Arc; + + struct Foo; + impl Foo { + fn implicit(&self) -> &() { + &() + } + fn implicit_mut(&mut self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc<Self>) -> &'a () { + &() + } + fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() { + &() + } + } + + trait Bar { + fn implicit(&self) -> &(); + fn implicit_provided(&self) -> &() { + &() + } + + fn explicit<'a>(self: &'a Arc<Self>) -> &'a (); + fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () { + &() + } + + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &(); + fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() { + &() + } + } +} + +mod pr_9743_false_negative_fix { + #![allow(unused)] + + fn foo(x: &u8, y: &'_ u8) {} + + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} +} + +mod pr_9743_output_lifetime_checks { + #![allow(unused)] + + // lint: only one input + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + + // lint: multiple inputs, output would not be elided + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + unimplemented!() + } + + // don't lint: multiple inputs, output would be elided (which would create an ambiguity) + fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 { + unimplemented!() + } +} + +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input(x: &u8) -> &u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index 2efc936752e..409528b291d 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -1,13 +1,20 @@ +// run-rustfix +// aux-build:macro_rules.rs + #![warn(clippy::needless_lifetimes)] #![allow( - dead_code, + unused, clippy::boxed_local, + clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, clippy::unnecessary_wraps, dyn_drop, clippy::get_first )] +#[macro_use] +extern crate macro_rules; + fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} @@ -495,4 +502,37 @@ mod pr_9743_output_lifetime_checks { } } +mod in_macro { + macro_rules! local_one_input_macro { + () => { + fn one_input<'a>(x: &'a u8) -> &'a u8 { + unimplemented!() + } + }; + } + + // lint local macro expands to function with needless lifetimes + local_one_input_macro!(); + + // no lint on external macro + macro_rules::needless_lifetime!(); +} + +mod issue5787 { + use std::sync::MutexGuard; + + struct Foo; + + impl Foo { + // doesn't get linted without async + pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { + guard + } + } + + async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str { + y + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index 5a7cf13c86d..4e3c8f20d8c 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -1,316 +1,559 @@ error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:11:1 + --> $DIR/needless_lifetimes.rs:18:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-lifetimes` implied by `-D warnings` +help: elide the lifetimes + | +LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} +LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:13:1 + --> $DIR/needless_lifetimes.rs:20:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} +LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:23:1 + --> $DIR/needless_lifetimes.rs:30:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { +LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:35:1 + --> $DIR/needless_lifetimes.rs:42:1 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { +LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:42:1 + --> $DIR/needless_lifetimes.rs:49:1 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { +LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { + | error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:59:1 + --> $DIR/needless_lifetimes.rs:66:1 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:66:1 + --> $DIR/needless_lifetimes.rs:73:1 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { +LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:75:1 + --> $DIR/needless_lifetimes.rs:82:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { +LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:80:1 + --> $DIR/needless_lifetimes.rs:87:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> +LL + fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()> + | error: the following explicit lifetimes could be elided: 'a, 'b - --> $DIR/needless_lifetimes.rs:92:1 + --> $DIR/needless_lifetimes.rs:99:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:92:37 +help: elide the lifetimes + | +LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} +LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | -LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:116:1 + --> $DIR/needless_lifetimes.rs:123:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:116:32 +help: elide the lifetimes + | +LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> +LL + fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^ error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:146:5 + --> $DIR/needless_lifetimes.rs:153:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_out<'s>(&'s self) -> &'s u8 { +LL + fn self_and_out(&self) -> &u8 { + | error: the following explicit lifetimes could be elided: 't - --> $DIR/needless_lifetimes.rs:153:5 + --> $DIR/needless_lifetimes.rs:160:5 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { +LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { + | error: the following explicit lifetimes could be elided: 's - --> $DIR/needless_lifetimes.rs:160:5 + --> $DIR/needless_lifetimes.rs:167:5 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { +LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { + | error: the following explicit lifetimes could be elided: 's, 't - --> $DIR/needless_lifetimes.rs:164:5 + --> $DIR/needless_lifetimes.rs:171:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} +LL + fn distinct_self_and_in(&self, _x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:183:1 + --> $DIR/needless_lifetimes.rs:190:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:183:33 +help: elide the lifetimes + | +LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { +LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | -LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:201:1 + --> $DIR/needless_lifetimes.rs:208:1 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:201:43 +help: elide the lifetimes + | +LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { +LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | -LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:209:1 + --> $DIR/needless_lifetimes.rs:216:1 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { +LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:224:1 + --> $DIR/needless_lifetimes.rs:231:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { +LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:230:1 + --> $DIR/needless_lifetimes.rs:237:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:230:37 +help: elide the lifetimes + | +LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { +LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | -LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'b - --> $DIR/needless_lifetimes.rs:248:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:248:47 +help: elide the lifetimes + | +LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { +LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | -LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:256:1 + --> $DIR/needless_lifetimes.rs:263:1 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { +LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:260:1 + --> $DIR/needless_lifetimes.rs:267:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str { +LL + fn named_input_elided_output(_arg: &str) -> &str { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:268:1 + --> $DIR/needless_lifetimes.rs:275:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { +LL + fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:304:1 + --> $DIR/needless_lifetimes.rs:311:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: replace with `'_` in generic arguments such as here - --> $DIR/needless_lifetimes.rs:304:47 +help: elide the lifetimes + | +LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { +LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | -LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { - | ^^ error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:311:9 + --> $DIR/needless_lifetimes.rs:318:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(x: &'a u8) {} +LL + fn needless_lt(x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:315:9 + --> $DIR/needless_lifetimes.rs:322:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn needless_lt<'a>(_x: &'a u8) {} +LL + fn needless_lt(_x: &u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:328:9 + --> $DIR/needless_lifetimes.rs:335:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn baz<'a>(&'a self) -> impl Foo + 'a { +LL + fn baz(&self) -> impl Foo + '_ { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:360:5 + --> $DIR/needless_lifetimes.rs:367:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { +LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:369:5 + --> $DIR/needless_lifetimes.rs:376:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { +LL + fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:381:5 + --> $DIR/needless_lifetimes.rs:388:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 +LL + fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32 + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:396:5 + --> $DIR/needless_lifetimes.rs:403:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { +LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:409:5 + --> $DIR/needless_lifetimes.rs:416:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { +LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:412:5 + --> $DIR/needless_lifetimes.rs:419:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { +LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:434:9 + --> $DIR/needless_lifetimes.rs:441:9 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a () { +LL + fn implicit(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:437:9 + --> $DIR/needless_lifetimes.rs:444:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_mut<'a>(&'a mut self) -> &'a () { +LL + fn implicit_mut(&mut self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:448:9 + --> $DIR/needless_lifetimes.rs:455:9 | LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:454:9 + --> $DIR/needless_lifetimes.rs:461:9 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit<'a>(&'a self) -> &'a (); +LL + fn implicit(&self) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:455:9 + --> $DIR/needless_lifetimes.rs:462:9 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn implicit_provided<'a>(&'a self) -> &'a () { +LL + fn implicit_provided(&self) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:464:9 + --> $DIR/needless_lifetimes.rs:471:9 | LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a (); +LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &(); + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:465:9 + --> $DIR/needless_lifetimes.rs:472:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () { +LL + fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:474:5 + --> $DIR/needless_lifetimes.rs:481:5 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn foo<'a>(x: &'a u8, y: &'_ u8) {} +LL + fn foo(x: &u8, y: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:476:5 + --> $DIR/needless_lifetimes.rs:483:5 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} +LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:483:5 + --> $DIR/needless_lifetimes.rs:490:5 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | error: the following explicit lifetimes could be elided: 'a - --> $DIR/needless_lifetimes.rs:488:5 + --> $DIR/needless_lifetimes.rs:495:5 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: elide the lifetimes + | +LL - fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { +LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 { + | + +error: the following explicit lifetimes could be elided: 'a + --> $DIR/needless_lifetimes.rs:508:13 + | +LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | local_one_input_macro!(); + | ------------------------ in this macro invocation + | + = note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info) +help: elide the lifetimes + | +LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { +LL + fn one_input(x: &u8) -> &u8 { + | -error: aborting due to 45 previous errors +error: aborting due to 46 previous errors diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr index b31544ec334..cffa19bec3a 100644 --- a/src/tools/clippy/tests/ui/needless_range_loop.stderr +++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr @@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ @@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter().enumerate().skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 5..10 { | ^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec` LL | for i in 0..vec.len() { | ^^^^^^^^^^^^ | -help: consider using an iterator +help: consider using an iterator and enumerate() | LL | for (i, <item>) in vec.iter_mut().enumerate() { | ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs index 65809023f8d..7803418cb04 100644 --- a/src/tools/clippy/tests/ui/new_without_default.rs +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] +#![allow( + dead_code, + clippy::missing_safety_doc, + clippy::extra_unused_lifetimes, + clippy::extra_unused_type_parameters +)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr index 212a69ab94e..583dd327d6a 100644 --- a/src/tools/clippy/tests/ui/new_without_default.stderr +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -1,5 +1,5 @@ error: you should consider adding a `Default` implementation for `Foo` - --> $DIR/new_without_default.rs:7:5 + --> $DIR/new_without_default.rs:12:5 | LL | / pub fn new() -> Foo { LL | | Foo @@ -17,7 +17,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Bar` - --> $DIR/new_without_default.rs:15:5 + --> $DIR/new_without_default.rs:20:5 | LL | / pub fn new() -> Self { LL | | Bar @@ -34,7 +34,7 @@ LL + } | error: you should consider adding a `Default` implementation for `LtKo<'c>` - --> $DIR/new_without_default.rs:79:5 + --> $DIR/new_without_default.rs:84:5 | LL | / pub fn new() -> LtKo<'c> { LL | | unimplemented!() @@ -51,7 +51,7 @@ LL + } | error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` - --> $DIR/new_without_default.rs:172:5 + --> $DIR/new_without_default.rs:177:5 | LL | / pub fn new() -> Self { LL | | NewNotEqualToDerive { foo: 1 } @@ -68,7 +68,7 @@ LL + } | error: you should consider adding a `Default` implementation for `FooGenerics<T>` - --> $DIR/new_without_default.rs:180:5 + --> $DIR/new_without_default.rs:185:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -85,7 +85,7 @@ LL + } | error: you should consider adding a `Default` implementation for `BarGenerics<T>` - --> $DIR/new_without_default.rs:187:5 + --> $DIR/new_without_default.rs:192:5 | LL | / pub fn new() -> Self { LL | | Self(Default::default()) @@ -102,7 +102,7 @@ LL + } | error: you should consider adding a `Default` implementation for `Foo<T>` - --> $DIR/new_without_default.rs:198:9 + --> $DIR/new_without_default.rs:203:9 | LL | / pub fn new() -> Self { LL | | todo!() diff --git a/src/tools/clippy/tests/ui/redundant_field_names.fixed b/src/tools/clippy/tests/ui/redundant_field_names.fixed index ec7f8ae923a..276266a2dd8 100644 --- a/src/tools/clippy/tests/ui/redundant_field_names.fixed +++ b/src/tools/clippy/tests/ui/redundant_field_names.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/src/tools/clippy/tests/ui/redundant_field_names.rs b/src/tools/clippy/tests/ui/redundant_field_names.rs index 73122016cf6..f674141c138 100644 --- a/src/tools/clippy/tests/ui/redundant_field_names.rs +++ b/src/tools/clippy/tests/ui/redundant_field_names.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::redundant_field_names)] -#![allow(clippy::no_effect, dead_code, unused_variables)] +#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)] #[macro_use] extern crate derive_new; diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index f0e1a8128d7..ab8ac97a0e7 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -36,6 +36,10 @@ fn syntax_error() { let raw_string_error = Regex::new(r"[...\/...]"); let raw_string_error = Regex::new(r#"[...\/...]"#); + + let escaped_string_span = Regex::new("\\b\\c"); + + let aux_span = Regex::new("(?ixi)"); } fn trivial_regex() { diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr index 2424644c6f6..c2440f39e0a 100644 --- a/src/tools/clippy/tests/ui/regex.stderr +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -29,7 +29,10 @@ error: regex syntax error: invalid character class range, the start must be <= t LL | let some_unicode = Regex::new("[é-è]"); | ^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:18:33 | LL | let some_regex = Regex::new(OPENING_PAREN); @@ -43,25 +46,37 @@ LL | let binary_pipe_in_wrong_position = BRegex::new("|"); | = help: the regex is unlikely to be useful as it is -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:21:41 | LL | let some_binary_regex = BRegex::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:22:56 | LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:34:37 | LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); | ^^^^^^^^^^^^^ -error: regex syntax error on position 0: unclosed group +error: regex parse error: + ( + ^ + error: unclosed group --> $DIR/regex.rs:35:39 | LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]); @@ -79,8 +94,25 @@ error: regex syntax error: unrecognized escape sequence LL | let raw_string_error = Regex::new(r#"[...//...]"#); | ^^ +error: regex parse error: + /b/c + ^^ + error: unrecognized escape sequence + --> $DIR/regex.rs:40:42 + | +LL | let escaped_string_span = Regex::new("/b/c"); + | ^^^^^^^^ + | + = help: consider using a raw string literal: `r".."` + +error: regex syntax error: duplicate flag + --> $DIR/regex.rs:42:34 + | +LL | let aux_span = Regex::new("(?ixi)"); + | ^ ^ + error: trivial regex - --> $DIR/regex.rs:42:33 + --> $DIR/regex.rs:46:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:44:48 + --> $DIR/regex.rs:48:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:46:42 + --> $DIR/regex.rs:50:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -104,7 +136,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:48:40 + --> $DIR/regex.rs:52:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -112,7 +144,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:50:39 + --> $DIR/regex.rs:54:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -120,7 +152,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:52:39 + --> $DIR/regex.rs:56:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -128,7 +160,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:58:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -136,7 +168,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:57:36 + --> $DIR/regex.rs:61:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -144,7 +176,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:59:36 + --> $DIR/regex.rs:63:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -152,7 +184,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:61:36 + --> $DIR/regex.rs:65:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -160,12 +192,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:63:44 + --> $DIR/regex.rs:67:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed index 713cff604a1..dc24d447c60 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs index 467003a1a66..4adde2c4018 100644 --- a/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs @@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) { +fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) { t.seek(SeekFrom::Start(0)); } @@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) { // This should NOT trigger clippy warning because // StructWithSeekMethod does not implement std::io::Seek; -fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) { +fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) { t.seek(SeekFrom::Start(0)); } diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr index dec3f50d6f1..c4ec7aa88a2 100644 --- a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr +++ b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr @@ -2,27 +2,62 @@ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause th --> $DIR/suspicious_to_owned.rs:16:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ | = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned --> $DIR/suspicious_to_owned.rs:26:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, Vec<char>> itself and does not cause the Cow<'_, Vec<char>> contents to become owned --> $DIR/suspicious_to_owned.rs:36:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned --> $DIR/suspicious_to_owned.rs:46:13 | LL | let _ = cow.to_owned(); - | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | ^^^^^^^^^^^^^^ + | +help: depending on intent, either make the Cow an Owned variant + | +LL | let _ = cow.into_owned(); + | ~~~~~~~~~~~~~~~~ +help: or clone the Cow itself + | +LL | let _ = cow.clone(); + | ~~~~~~~~~~~ error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type --> $DIR/suspicious_to_owned.rs:60:13 diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs index 2eca1f4701c..8b4613b3f6e 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.rs @@ -1,4 +1,5 @@ #![deny(clippy::type_repetition_in_bounds)] +#![allow(clippy::extra_unused_type_parameters)] use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; diff --git a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr index 70d700c1cc4..a90df03c04f 100644 --- a/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr @@ -1,5 +1,5 @@ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:8:5 + --> $DIR/type_repetition_in_bounds.rs:9:5 | LL | T: Clone, | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:25:5 + --> $DIR/type_repetition_in_bounds.rs:26:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord, = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:85:5 + --> $DIR/type_repetition_in_bounds.rs:86:5 | LL | T: Clone, | ^^^^^^^^ @@ -28,7 +28,7 @@ LL | T: Clone, = help: consider combining the bounds: `T: ?Sized + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:90:5 + --> $DIR/type_repetition_in_bounds.rs:91:5 | LL | T: ?Sized, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index 4b059558173..8d3e094b759 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -63,6 +63,14 @@ fn combine_or(file: &str) -> Result<(), Error> { Ok(()) } +fn is_ok_err<T: io::Read + io::Write>(s: &mut T) { + s.write(b"ok").is_ok(); + s.write(b"err").is_err(); + let mut buf = [0u8; 0]; + s.read(&mut buf).is_ok(); + s.read(&mut buf).is_err(); +} + async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) { w.write(b"hello world").await.unwrap(); } diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr index 7ba7e09c0f0..0865c5213f6 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.stderr +++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr @@ -82,13 +82,45 @@ LL | | .expect("error"); error: written amount is not handled --> $DIR/unused_io_amount.rs:67:5 | +LL | s.write(b"ok").is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:68:5 + | +LL | s.write(b"err").is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Write::write_all` instead, or handle partial writes + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:70:5 + | +LL | s.read(&mut buf).is_ok(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: read amount is not handled + --> $DIR/unused_io_amount.rs:71:5 + | +LL | s.read(&mut buf).is_err(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `Read::read_exact` instead, or handle partial reads + +error: written amount is not handled + --> $DIR/unused_io_amount.rs:75:5 + | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:72:5 + --> $DIR/unused_io_amount.rs:80:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | r.read(&mut buf[..]).await.unwrap(); = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:85:9 + --> $DIR/unused_io_amount.rs:93:9 | LL | w.write(b"hello world").await?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | w.write(b"hello world").await?; = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:93:9 + --> $DIR/unused_io_amount.rs:101:9 | LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?; = help: use `AsyncReadExt::read_exact` instead, or handle partial reads error: written amount is not handled - --> $DIR/unused_io_amount.rs:101:5 + --> $DIR/unused_io_amount.rs:109:5 | LL | w.write(b"hello world").await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | w.write(b"hello world").await.unwrap(); = help: use `AsyncWriteExt::write_all` instead, or handle partial writes error: read amount is not handled - --> $DIR/unused_io_amount.rs:106:5 + --> $DIR/unused_io_amount.rs:114:5 | LL | r.read(&mut buf[..]).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use `AsyncReadExt::read_exact` instead, or handle partial reads -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed index 23607497841..293bf75a717 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed @@ -96,7 +96,7 @@ fn main() { } match Enum::A { Enum::A => (), - Enum::B | _ => (), + Enum::B | Enum::__Private => (), } } } diff --git a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr index efecc9576cc..30d29aa4e77 100644 --- a/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr +++ b/src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr @@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` -error: wildcard matches known variants and will also match future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | _` + | ^ help: try this: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index 1911f0f9c94..deed6fbd439 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -9,6 +9,7 @@ diff = "0.1.10" unified-diff = "0.2.1" getopts = "0.2" miropt-test-tools = { path = "../miropt-test-tools" } +build_helper = { path = "../build_helper" } tracing = "0.1" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } regex = "1.0" diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 3676f69b100..7fe2e6257d9 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -380,6 +380,9 @@ pub struct Config { /// Whether to rerun tests even if the inputs are unchanged. pub force_rerun: bool, + /// Only rerun the tests that result has been modified accoring to Git status + pub only_modified: bool, + pub target_cfg: LazyCell<TargetCfg>, } diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 45fd87bea9b..e11ebca6ea9 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -941,6 +941,7 @@ pub fn make_test_description<R: Read>( let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target); let has_shadow_call_stack = util::SHADOWCALLSTACK_SUPPORTED_TARGETS.contains(&&*config.target); + let has_xray = util::XRAY_SUPPORTED_TARGETS.contains(&&*config.target); // For tests using the `needs-rust-lld` directive (e.g. for `-Zgcc-ld=lld`), we need to find // whether `rust-lld` is present in the compiler under test. @@ -1019,6 +1020,7 @@ pub fn make_test_description<R: Read>( && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack") ); reason!(!config.can_unwind() && config.parse_name_directive(ln, "needs-unwind")); + reason!(!has_xray && config.parse_name_directive(ln, "needs-xray")); reason!( config.target == "wasm32-unknown-unknown" && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS) diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 3092c656cd7..c648b2f12f1 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -8,15 +8,17 @@ extern crate test; use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS}; use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, TestPaths}; use crate::util::logv; +use build_helper::git::{get_git_modified_files, get_git_untracked_files}; +use core::panic; use getopts::Options; use lazycell::LazyCell; -use std::env; use std::ffi::OsString; use std::fs; use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::time::SystemTime; +use std::{env, vec}; use test::ColorConfig; use tracing::*; use walkdir::WalkDir; @@ -145,9 +147,10 @@ pub fn parse_config(args: Vec<String>) -> Config { "", "rustfix-coverage", "enable this to generate a Rustfix coverage file, which is saved in \ - `./<build_base>/rustfix_missing_coverage.txt`", + `./<build_base>/rustfix_missing_coverage.txt`", ) .optflag("", "force-rerun", "rerun tests even if the inputs are unchanged") + .optflag("", "only-modified", "only run tests that result been modified") .optflag("h", "help", "show this message") .reqopt("", "channel", "current Rust channel", "CHANNEL") .optopt("", "edition", "default Rust edition", "EDITION"); @@ -279,6 +282,7 @@ pub fn parse_config(args: Vec<String>) -> Config { lldb_python_dir: matches.opt_str("lldb-python-dir"), verbose: matches.opt_present("verbose"), quiet: matches.opt_present("quiet"), + only_modified: matches.opt_present("only-modified"), color, remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from), compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse), @@ -521,8 +525,18 @@ pub fn test_opts(config: &Config) -> test::TestOpts { pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) { debug!("making tests from {:?}", config.src_base.display()); let inputs = common_inputs_stamp(config); - collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests) - .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display())); + let modified_tests = modified_tests(config, &config.src_base).unwrap_or_else(|err| { + panic!("modified_tests got error from dir: {}, error: {}", config.src_base.display(), err) + }); + collect_tests_from_dir( + config, + &config.src_base, + &PathBuf::new(), + &inputs, + tests, + &modified_tests, + ) + .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display())); } /// Returns a stamp constructed from input files common to all test cases. @@ -561,12 +575,35 @@ fn common_inputs_stamp(config: &Config) -> Stamp { stamp } +fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> { + if !config.only_modified { + return Ok(vec![]); + } + let files = + get_git_modified_files(Some(dir), &vec!["rs", "stderr", "fixed"])?.unwrap_or(vec![]); + // Add new test cases to the list, it will be convenient in daily development. + let untracked_files = get_git_untracked_files(None)?.unwrap_or(vec![]); + + let all_paths = [&files[..], &untracked_files[..]].concat(); + let full_paths = { + let mut full_paths: Vec<PathBuf> = all_paths + .into_iter() + .map(|f| fs::canonicalize(&f).unwrap().with_extension("").with_extension("rs")) + .collect(); + full_paths.dedup(); + full_paths.sort_unstable(); + full_paths + }; + Ok(full_paths) +} + fn collect_tests_from_dir( config: &Config, dir: &Path, relative_dir_path: &Path, inputs: &Stamp, tests: &mut Vec<test::TestDescAndFn>, + modified_tests: &Vec<PathBuf>, ) -> io::Result<()> { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { @@ -597,7 +634,7 @@ fn collect_tests_from_dir( let file = file?; let file_path = file.path(); let file_name = file.file_name(); - if is_test(&file_name) { + if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) { debug!("found test file: {:?}", file_path.display()); let paths = TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; @@ -607,7 +644,14 @@ fn collect_tests_from_dir( let relative_file_path = relative_dir_path.join(file.file_name()); if &file_name != "auxiliary" { debug!("found directory: {:?}", file_path.display()); - collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?; + collect_tests_from_dir( + config, + &file_path, + &relative_file_path, + inputs, + tests, + modified_tests, + )?; } } else { debug!("found other file/directory: {:?}", file_path.display()); diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index ff7e8df9878..67f49bb6397 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -78,6 +78,19 @@ pub const MEMTAG_SUPPORTED_TARGETS: &[&str] = pub const SHADOWCALLSTACK_SUPPORTED_TARGETS: &[&str] = &["aarch64-linux-android"]; +pub const XRAY_SUPPORTED_TARGETS: &[&str] = &[ + "aarch64-linux-android", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "x86_64-linux-android", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64-unknown-netbsd", + "x86_64-unknown-none-linuxkernel", + "x86_64-unknown-openbsd", +]; + pub fn make_new_path(path: &str) -> String { assert!(cfg!(windows)); // Windows just uses PATH as the library search path, so we have to diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 6bfec79cd70..9014026b0aa 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{Lrc, Send}; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::translation::Translate; -use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel}; +use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ source_map::{FilePathMapping, SourceMap}, @@ -135,6 +135,7 @@ fn default_handler( None, false, false, + TerminalUrl::No, )) }; Handler::with_emitter( diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 8ce19c8b514..c4b994af13b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -178,6 +178,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "ppv-lite86", "proc-macro-hack", "proc-macro2", + "pulldown-cmark", "psm", "punycode", "quote", @@ -246,6 +247,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "unic-langid-macros", "unic-langid-macros-impl", "unic-ucd-version", + "unicase", "unicode-ident", "unicode-normalization", "unicode-script", diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 83551a1d820..ef3abb9514f 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -9,7 +9,7 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. -const ROOT_ENTRY_LIMIT: usize = 939; +const ROOT_ENTRY_LIMIT: usize = 940; const ISSUES_ENTRY_LIMIT: usize = 2001; fn check_entries(path: &Path, bad: &mut bool) { diff --git a/tests/codegen/instrument-xray/basic.rs b/tests/codegen/instrument-xray/basic.rs new file mode 100644 index 00000000000..d3e49d53174 --- /dev/null +++ b/tests/codegen/instrument-xray/basic.rs @@ -0,0 +1,9 @@ +// Checks that `-Z instrument-xray` produces expected instrumentation. +// +// needs-xray +// compile-flags: -Z instrument-xray=always + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "function-instrument"="xray-always" +pub fn function() {} diff --git a/tests/codegen/instrument-xray/options-combine.rs b/tests/codegen/instrument-xray/options-combine.rs new file mode 100644 index 00000000000..f7e500b65f6 --- /dev/null +++ b/tests/codegen/instrument-xray/options-combine.rs @@ -0,0 +1,12 @@ +// Checks that `-Z instrument-xray` options can be specified multiple times. +// +// needs-xray +// compile-flags: -Z instrument-xray=skip-exit +// compile-flags: -Z instrument-xray=instruction-threshold=123 +// compile-flags: -Z instrument-xray=instruction-threshold=456 + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "xray-instruction-threshold"="456" "xray-skip-exit" +// CHECK-NOT: attributes #{{.*}} "xray-instruction-threshold"="123" +pub fn function() {} diff --git a/tests/codegen/instrument-xray/options-override.rs b/tests/codegen/instrument-xray/options-override.rs new file mode 100644 index 00000000000..00f81837902 --- /dev/null +++ b/tests/codegen/instrument-xray/options-override.rs @@ -0,0 +1,11 @@ +// Checks that the last `-Z instrument-xray` option wins. +// +// needs-xray +// compile-flags: -Z instrument-xray=always +// compile-flags: -Z instrument-xray=never + +#![crate_type = "lib"] + +// CHECK: attributes #{{.*}} "function-instrument"="xray-never" +// CHECK-NOT: attributes #{{.*}} "function-instrument"="xray-always" +pub fn function() {} diff --git a/tests/codegen/issue-75659.rs b/tests/codegen/issue-75659.rs index 6bcb59affe3..9394868c08d 100644 --- a/tests/codegen/issue-75659.rs +++ b/tests/codegen/issue-75659.rs @@ -1,7 +1,7 @@ // This test checks that the call to memchr/slice_contains is optimized away // when searching in small slices. -// compile-flags: -O -Zinline-mir=no +// compile-flags: -O -Zinline-mir=false // only-x86_64 #![crate_type = "lib"] diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..b139deeee1f --- /dev/null +++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.32bit.diff @@ -0,0 +1,68 @@ +- // MIR for `cand` before EnumSizeOpt ++ // MIR for `cand` after EnumSizeOpt + + fn cand() -> Candidate { + let mut _0: Candidate; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:27 + let mut _1: Candidate; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _8: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _10: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = Candidate::Small(const 1_u8); // scope 0 at $DIR/enum_opt.rs:+1:15: +1:34 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = Candidate::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _4 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..b139deeee1f --- /dev/null +++ b/tests/mir-opt/enum_opt.cand.EnumSizeOpt.64bit.diff @@ -0,0 +1,68 @@ +- // MIR for `cand` before EnumSizeOpt ++ // MIR for `cand` after EnumSizeOpt + + fn cand() -> Candidate { + let mut _0: Candidate; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:27 + let mut _1: Candidate; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _8: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _10: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:34 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = Candidate::Small(const 1_u8); // scope 0 at $DIR/enum_opt.rs:+1:15: +1:34 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = Candidate::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _4 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [2_usize, 8197_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..a80001149eb --- /dev/null +++ b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.32bit.diff @@ -0,0 +1,28 @@ +- // MIR for `invalid` before EnumSizeOpt ++ // MIR for `invalid` after EnumSizeOpt + + fn invalid() -> InvalidIdxs { + let mut _0: InvalidIdxs; // return place in scope 0 at $DIR/enum_opt.rs:+0:21: +0:32 + let mut _1: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:36 + let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:26: +2:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = InvalidIdxs::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:29 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _3 = [const 0_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _2 = InvalidIdxs::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:36 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..a80001149eb --- /dev/null +++ b/tests/mir-opt/enum_opt.invalid.EnumSizeOpt.64bit.diff @@ -0,0 +1,28 @@ +- // MIR for `invalid` before EnumSizeOpt ++ // MIR for `invalid` after EnumSizeOpt + + fn invalid() -> InvalidIdxs { + let mut _0: InvalidIdxs; // return place in scope 0 at $DIR/enum_opt.rs:+0:21: +0:32 + let mut _1: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:36 + let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:26: +2:35 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = InvalidIdxs::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:29 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _3 = [const 0_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:26: +2:35 + _2 = InvalidIdxs::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:36 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:36 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:35: +2:36 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs new file mode 100644 index 00000000000..2768d708049 --- /dev/null +++ b/tests/mir-opt/enum_opt.rs @@ -0,0 +1,86 @@ +// unit-test: EnumSizeOpt +// EMIT_MIR_FOR_EACH_BIT_WIDTH +// compile-flags: -Zunsound-mir-opts + +#![feature(arbitrary_enum_discriminant, repr128)] + +// Tests that an enum with a variant with no data gets correctly transformed. +pub enum NoData { + Large([u8; 8196]), + None, +} + +// Tests that an enum with a variant with data that is a valid candidate gets transformed. +pub enum Candidate { + Small(u8), + Large([u8; 8196]), +} + +// Tests that an enum which has a discriminant much higher than the variant does not get +// tformed. +#[repr(u32)] +pub enum InvalidIdxs { + A = 302, + Large([u64; 1024]), +} + +// Tests that an enum with too high of a discriminant index (not in bounds of usize) does not +// get tformed. +#[repr(u128)] +pub enum NotTrunctable { + A = 0, + B([u8; 1024]) = 1, + C([u8; 4096]) = 0x10000000000000001, +} + +// Tests that an enum with discriminants in random order still gets tformed correctly. +#[repr(u32)] +pub enum RandOrderDiscr { + A = 13, + B([u8; 1024]) = 5, + C = 7, +} + +// EMIT_MIR enum_opt.unin.EnumSizeOpt.diff +pub fn unin() -> NoData { + let mut a = NoData::None; + a = NoData::Large([1; 8196]); + a +} + +// EMIT_MIR enum_opt.cand.EnumSizeOpt.diff +pub fn cand() -> Candidate { + let mut a = Candidate::Small(1); + a = Candidate::Large([1; 8196]); + a +} + +// EMIT_MIR enum_opt.invalid.EnumSizeOpt.diff +pub fn invalid() -> InvalidIdxs { + let mut a = InvalidIdxs::A; + a = InvalidIdxs::Large([0; 1024]); + a +} + +// EMIT_MIR enum_opt.trunc.EnumSizeOpt.diff +pub fn trunc() -> NotTrunctable { + let mut a = NotTrunctable::A; + a = NotTrunctable::B([0; 1024]); + a = NotTrunctable::C([0; 4096]); + a +} + +pub fn rand_order() -> RandOrderDiscr { + let mut a = RandOrderDiscr::A; + a = RandOrderDiscr::B([0; 1024]); + a = RandOrderDiscr::C; + a +} + +pub fn main() { + unin(); + cand(); + invalid(); + trunc(); + rand_order(); +} diff --git a/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..1ef79044d4f --- /dev/null +++ b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.32bit.diff @@ -0,0 +1,37 @@ +- // MIR for `trunc` before EnumSizeOpt ++ // MIR for `trunc` after EnumSizeOpt + + fn trunc() -> NotTrunctable { + let mut _0: NotTrunctable; // return place in scope 0 at $DIR/enum_opt.rs:+0:19: +0:32 + let mut _1: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 + let mut _4: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+3:7: +3:34 + let mut _5: [u8; 4096]; // in scope 0 at $DIR/enum_opt.rs:+3:24: +3:33 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NotTrunctable::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:31 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 0_u8; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = NotTrunctable::B(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageLive(_5); // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _5 = [const 0_u8; 4096]; // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _4 = NotTrunctable::C(move _5); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageDead(_5); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _1 = move _4; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:34 + StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+4:3: +4:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+5:1: +5:2 + return; // scope 0 at $DIR/enum_opt.rs:+5:2: +5:2 + } + } + diff --git a/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..1ef79044d4f --- /dev/null +++ b/tests/mir-opt/enum_opt.trunc.EnumSizeOpt.64bit.diff @@ -0,0 +1,37 @@ +- // MIR for `trunc` before EnumSizeOpt ++ // MIR for `trunc` after EnumSizeOpt + + fn trunc() -> NotTrunctable { + let mut _0: NotTrunctable; // return place in scope 0 at $DIR/enum_opt.rs:+0:19: +0:32 + let mut _1: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:34 + let mut _3: [u8; 1024]; // in scope 0 at $DIR/enum_opt.rs:+2:24: +2:33 + let mut _4: NotTrunctable; // in scope 0 at $DIR/enum_opt.rs:+3:7: +3:34 + let mut _5: [u8; 4096]; // in scope 0 at $DIR/enum_opt.rs:+3:24: +3:33 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NotTrunctable::A; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:31 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _3 = [const 0_u8; 1024]; // scope 1 at $DIR/enum_opt.rs:+2:24: +2:33 + _2 = NotTrunctable::B(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:34 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:34 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:33: +2:34 + StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageLive(_5); // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _5 = [const 0_u8; 4096]; // scope 1 at $DIR/enum_opt.rs:+3:24: +3:33 + _4 = NotTrunctable::C(move _5); // scope 1 at $DIR/enum_opt.rs:+3:7: +3:34 + StorageDead(_5); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _1 = move _4; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:34 + StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+3:33: +3:34 + _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+4:3: +4:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+5:1: +5:2 + return; // scope 0 at $DIR/enum_opt.rs:+5:2: +5:2 + } + } + diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff new file mode 100644 index 00000000000..ad9f12cf959 --- /dev/null +++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.32bit.diff @@ -0,0 +1,68 @@ +- // MIR for `unin` before EnumSizeOpt ++ // MIR for `unin` after EnumSizeOpt + + fn unin() -> NoData { + let mut _0: NoData; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:24 + let mut _1: NoData; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NoData; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:31 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:21: +2:30 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _8: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _10: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NoData::None; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:27 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _2 = NoData::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _4 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff new file mode 100644 index 00000000000..ad9f12cf959 --- /dev/null +++ b/tests/mir-opt/enum_opt.unin.EnumSizeOpt.64bit.diff @@ -0,0 +1,68 @@ +- // MIR for `unin` before EnumSizeOpt ++ // MIR for `unin` after EnumSizeOpt + + fn unin() -> NoData { + let mut _0: NoData; // return place in scope 0 at $DIR/enum_opt.rs:+0:18: +0:24 + let mut _1: NoData; // in scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + let mut _2: NoData; // in scope 0 at $DIR/enum_opt.rs:+2:7: +2:31 + let mut _3: [u8; 8196]; // in scope 0 at $DIR/enum_opt.rs:+2:21: +2:30 ++ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _8: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _10: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:+2:3: +2:31 ++ let mut _12: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _13: isize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _14: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _15: usize; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _16: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _17: *mut u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _18: *const NoData; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 ++ let mut _19: *const u8; // in scope 0 at $DIR/enum_opt.rs:+3:3: +3:4 + scope 1 { + debug a => _1; // in scope 1 at $DIR/enum_opt.rs:+1:7: +1:12 + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:+1:7: +1:12 + _1 = NoData::None; // scope 0 at $DIR/enum_opt.rs:+1:15: +1:27 + StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _3 = [const 1_u8; 8196]; // scope 1 at $DIR/enum_opt.rs:+2:21: +2:30 + _2 = NoData::Large(move _3); // scope 1 at $DIR/enum_opt.rs:+2:7: +2:31 + StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _4 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _6 = _5 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _9 = _8 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ _11 = _10 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ Deinit(_8); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ copy_nonoverlapping(dst = _9, src = _11, count = _7); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 ++ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:+2:3: +2:31 + StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:+2:30: +2:31 +- _0 = move _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageLive(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _12 = const [8197_usize, 1_usize]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _13 = discriminant(_1); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _14 = _13 as usize (IntToInt); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _15 = _12[_14]; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _16 = &raw mut _0; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _17 = _16 as *mut u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _18 = &raw const _1; // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ _19 = _18 as *const u8 (PtrToPtr); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ Deinit(_16); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ copy_nonoverlapping(dst = _17, src = _19, count = _15); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 ++ StorageDead(_12); // scope 1 at $DIR/enum_opt.rs:+3:3: +3:4 + StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:+4:1: +4:2 + return; // scope 0 at $DIR/enum_opt.rs:+4:2: +4:2 + } + } + diff --git a/tests/mir-opt/sroa.constant.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa.constant.ScalarReplacementOfAggregates.diff new file mode 100644 index 00000000000..9e33215f2b5 --- /dev/null +++ b/tests/mir-opt/sroa.constant.ScalarReplacementOfAggregates.diff @@ -0,0 +1,46 @@ +- // MIR for `constant` before ScalarReplacementOfAggregates ++ // MIR for `constant` after ScalarReplacementOfAggregates + + fn constant() -> () { + let mut _0: (); // return place in scope 0 at $DIR/sroa.rs:+0:15: +0:15 + let _1: (usize, u8); // in scope 0 at $DIR/sroa.rs:+2:9: +2:10 ++ let _4: usize; // in scope 0 at $DIR/sroa.rs:+2:9: +2:10 ++ let _5: u8; // in scope 0 at $DIR/sroa.rs:+2:9: +2:10 + scope 1 { +- debug y => _1; // in scope 1 at $DIR/sroa.rs:+2:9: +2:10 ++ debug y => (usize, u8){ .0 => _4, .1 => _5, }; // in scope 1 at $DIR/sroa.rs:+2:9: +2:10 + let _2: usize; // in scope 1 at $DIR/sroa.rs:+3:9: +3:10 + scope 2 { + debug t => _2; // in scope 2 at $DIR/sroa.rs:+3:9: +3:10 + let _3: u8; // in scope 2 at $DIR/sroa.rs:+4:9: +4:10 + scope 3 { + debug u => _3; // in scope 3 at $DIR/sroa.rs:+4:9: +4:10 + } + } + } + + bb0: { +- StorageLive(_1); // scope 0 at $DIR/sroa.rs:+2:9: +2:10 ++ StorageLive(_4); // scope 0 at $DIR/sroa.rs:+2:9: +2:10 ++ StorageLive(_5); // scope 0 at $DIR/sroa.rs:+2:9: +2:10 ++ nop; // scope 0 at $DIR/sroa.rs:+2:9: +2:10 + _1 = const _; // scope 0 at $DIR/sroa.rs:+2:13: +2:14 ++ _4 = move (_1.0: usize); // scope 1 at $DIR/sroa.rs:+3:9: +3:10 ++ _5 = move (_1.1: u8); // scope 1 at $DIR/sroa.rs:+3:9: +3:10 + StorageLive(_2); // scope 1 at $DIR/sroa.rs:+3:9: +3:10 +- _2 = (_1.0: usize); // scope 1 at $DIR/sroa.rs:+3:13: +3:16 ++ _2 = _4; // scope 1 at $DIR/sroa.rs:+3:13: +3:16 + StorageLive(_3); // scope 2 at $DIR/sroa.rs:+4:9: +4:10 +- _3 = (_1.1: u8); // scope 2 at $DIR/sroa.rs:+4:13: +4:16 ++ _3 = _5; // scope 2 at $DIR/sroa.rs:+4:13: +4:16 + _0 = const (); // scope 0 at $DIR/sroa.rs:+0:15: +5:2 + StorageDead(_3); // scope 2 at $DIR/sroa.rs:+5:1: +5:2 + StorageDead(_2); // scope 1 at $DIR/sroa.rs:+5:1: +5:2 +- StorageDead(_1); // scope 0 at $DIR/sroa.rs:+5:1: +5:2 ++ StorageDead(_4); // scope 0 at $DIR/sroa.rs:+5:1: +5:2 ++ StorageDead(_5); // scope 0 at $DIR/sroa.rs:+5:1: +5:2 ++ nop; // scope 0 at $DIR/sroa.rs:+5:1: +5:2 + return; // scope 0 at $DIR/sroa.rs:+5:2: +5:2 + } + } + diff --git a/tests/mir-opt/sroa.rs b/tests/mir-opt/sroa.rs index 471aac9f9d8..b69de2e124e 100644 --- a/tests/mir-opt/sroa.rs +++ b/tests/mir-opt/sroa.rs @@ -87,6 +87,13 @@ fn ref_copies(x: &Foo) { let u = y.c; } +fn constant() { + const U: (usize, u8) = (5, 9); + let y = U; + let t = y.0; + let u = y.1; +} + fn main() { dropping(); enums(5); @@ -96,6 +103,7 @@ fn main() { escaping(); copies(Foo { a: 5, b: (), c: "a", d: Some(-4) }); ref_copies(&Foo { a: 5, b: (), c: "a", d: Some(-4) }); + constant(); } // EMIT_MIR sroa.dropping.ScalarReplacementOfAggregates.diff @@ -106,3 +114,4 @@ fn main() { // EMIT_MIR sroa.escaping.ScalarReplacementOfAggregates.diff // EMIT_MIR sroa.copies.ScalarReplacementOfAggregates.diff // EMIT_MIR sroa.ref_copies.ScalarReplacementOfAggregates.diff +// EMIT_MIR sroa.constant.ScalarReplacementOfAggregates.diff diff --git a/tests/run-make/no-input-file/Makefile b/tests/run-make/no-input-file/Makefile new file mode 100644 index 00000000000..2f02159229d --- /dev/null +++ b/tests/run-make/no-input-file/Makefile @@ -0,0 +1,4 @@ +include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --print crate-name 2>&1 | diff - no-input-file.stderr diff --git a/tests/run-make/no-input-file/no-input-file.stderr b/tests/run-make/no-input-file/no-input-file.stderr new file mode 100644 index 00000000000..b843eb524f3 --- /dev/null +++ b/tests/run-make/no-input-file/no-input-file.stderr @@ -0,0 +1,2 @@ +error: no input filename given + diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/Makefile b/tests/run-make/rlib-format-packed-bundled-libs-3/Makefile new file mode 100644 index 00000000000..62dc1b5f606 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/Makefile @@ -0,0 +1,35 @@ +-include ../../run-make-fulldeps/tools.mk + +# ignore-cross-compile +# only-linux + +# Make sure -Zpacked_bundled_libs-like behavior activates with whole-archive. + +# We're using the llvm-nm instead of the system nm to ensure it is compatible +# with the LLVM bitcode generated by rustc. +NM = "$(LLVM_BIN_DIR)"/llvm-nm + +all: $(call NATIVE_STATICLIB,native_dep_1) $(call NATIVE_STATICLIB,native_dep_2) $(call NATIVE_STATICLIB,native_dep_3) $(call NATIVE_STATICLIB,native_dep_4) + # test cfg with packed bundle + $(RUSTC) rust_dep_cfg.rs --crate-type=rlib -Zpacked_bundled_libs + $(RUSTC) main.rs --extern rust_dep=$(TMPDIR)/librust_dep_cfg.rlib --crate-type=staticlib --cfg should_add + $(AR) t $(TMPDIR)/librust_dep_cfg.rlib | $(CGREP) -e "libnative_dep_1.a" + $(AR) t $(TMPDIR)/librust_dep_cfg.rlib | $(CGREP) -e "libnative_dep_2.a" + $(AR) t $(TMPDIR)/libmain.a | $(CGREP) -e "libnative_dep_1.o" + $(AR) t $(TMPDIR)/libmain.a | $(CGREP) -ev "libnative_dep_2.o" + + + # test bundle with whole_archive + $(RUSTC) rust_dep.rs --crate-type=rlib + $(AR) t $(TMPDIR)/librust_dep.rlib | $(CGREP) -e "native_dep_1" + $(AR) t $(TMPDIR)/librust_dep.rlib | $(CGREP) -e "native_dep_3" + $(AR) t $(TMPDIR)/librust_dep.rlib | $(CGREP) -ev "native_dep_2" + $(AR) t $(TMPDIR)/librust_dep.rlib | $(CGREP) -ev "native_dep_4" + + # Make sure compiler doesn't use files, that it shouldn't know about. + rm $(TMPDIR)/libnative_dep_1.a + rm $(TMPDIR)/libnative_dep_3.a + + $(RUSTC) main.rs --extern rust_dep=$(TMPDIR)/librust_dep.rlib --print link-args > $(TMPDIR)/link_args + cat $(TMPDIR)/link_args | $(CGREP) -ev "native_dep_3" + cat $(TMPDIR)/link_args | $(CGREP) -e "--whole-archive.*native_dep_1.*--whole-archive.*lnative_dep_2.*no-whole-archive.*lnative_dep_4" diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/main.rs b/tests/run-make/rlib-format-packed-bundled-libs-3/main.rs new file mode 100644 index 00000000000..8d2b8a2859c --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/main.rs @@ -0,0 +1,5 @@ +extern crate rust_dep; + +pub fn main() { + rust_dep::rust_dep(); +} diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_1.c b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_1.c new file mode 100644 index 00000000000..07be8562c92 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_1.c @@ -0,0 +1 @@ +int native_f1() { return 1; } diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_2.c b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_2.c new file mode 100644 index 00000000000..a1b94e40dc0 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_2.c @@ -0,0 +1 @@ +int native_f2() { return 2; } diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_3.c b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_3.c new file mode 100644 index 00000000000..f81f397a4b1 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_3.c @@ -0,0 +1 @@ +int native_f3() { return 3; } diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_4.c b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_4.c new file mode 100644 index 00000000000..14d41d60b1f --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/native_dep_4.c @@ -0,0 +1 @@ +int native_f4() { return 4; } diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/rust_dep.rs b/tests/run-make/rlib-format-packed-bundled-libs-3/rust_dep.rs new file mode 100644 index 00000000000..abd846b6862 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/rust_dep.rs @@ -0,0 +1,16 @@ +#![feature(packed_bundled_libs)] + +#[link(name = "native_dep_1", kind = "static", modifiers = "+whole-archive,+bundle")] +extern "C" {} + +#[link(name = "native_dep_2", kind = "static", modifiers = "+whole-archive,-bundle")] +extern "C" {} + +#[link(name = "native_dep_3", kind = "static", modifiers = "+bundle")] +extern "C" {} + +#[link(name = "native_dep_4", kind = "static", modifiers = "-bundle")] +extern "C" {} + +#[no_mangle] +pub fn rust_dep() {} diff --git a/tests/run-make/rlib-format-packed-bundled-libs-3/rust_dep_cfg.rs b/tests/run-make/rlib-format-packed-bundled-libs-3/rust_dep_cfg.rs new file mode 100644 index 00000000000..506ca62a8a6 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-3/rust_dep_cfg.rs @@ -0,0 +1,10 @@ +#![feature(link_cfg)] + +#[link(name = "native_dep_1", kind = "static", cfg(should_add))] +extern "C" {} + +#[link(name = "native_dep_2", kind = "static", cfg(should_not_add))] +extern "C" {} + +#[no_mangle] +pub fn rust_dep() {} diff --git a/tests/rustdoc-gui/codeblock-tooltip.goml b/tests/rustdoc-gui/codeblock-tooltip.goml index a3ef4e77b54..36b67073a03 100644 --- a/tests/rustdoc-gui/codeblock-tooltip.goml +++ b/tests/rustdoc-gui/codeblock-tooltip.goml @@ -30,24 +30,16 @@ define-function: ( ".docblock .example-wrap.compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"}, ) - assert-css: ( - ".docblock .example-wrap.compile_fail .tooltip::after", - { - "content": '"This example deliberately fails to compile"', - "padding": "5px 3px 3px", - "background-color": |background|, - "color": |color|, - "border": "1px solid " + |border|, - }, - ) - assert-css: ( - ".docblock .example-wrap.compile_fail .tooltip::before", - { - "border-width": "5px", - "border-style": "solid", - "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)", - }, - ) + click: ".docblock .example-wrap.compile_fail .tooltip" + assert-text: ( + ".popover.tooltip", + "This example deliberately fails to compile" + ) + assert-css: (".popover.tooltip", { + "color": |color|, + "background-color": |background|, + "border-color": |border|, + }) // should_panic block assert-css: ( @@ -69,24 +61,16 @@ define-function: ( ".docblock .example-wrap.should_panic", {"border-left": "2px solid rgb(255, 0, 0)"}, ) - assert-css: ( - ".docblock .example-wrap.should_panic .tooltip::after", - { - "content": '"This example panics"', - "padding": "5px 3px 3px", - "background-color": |background|, - "color": |color|, - "border": "1px solid " + |border|, - }, - ) - assert-css: ( - ".docblock .example-wrap.should_panic .tooltip::before", - { - "border-width": "5px", - "border-style": "solid", - "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)", - }, + click: ".docblock .example-wrap.should_panic .tooltip" + assert-text: ( + ".popover.tooltip", + "This example panics" ) + assert-css: (".popover.tooltip", { + "color": |color|, + "background-color": |background|, + "border-color": |border|, + }) // ignore block assert-css: ( @@ -108,42 +92,36 @@ define-function: ( ".docblock .example-wrap.ignore", {"border-left": "2px solid rgb(255, 142, 0)"}, ) - assert-css: ( - ".docblock .example-wrap.ignore .tooltip::after", - { - "content": '"This example is not tested"', - "padding": "5px 3px 3px", - "background-color": |background|, - "color": |color|, - "border": "1px solid " + |border|, - }, - ) - assert-css: ( - ".docblock .example-wrap.ignore .tooltip::before", - { - "border-width": "5px", - "border-style": "solid", - "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)", - }, - ) + click: ".docblock .example-wrap.ignore .tooltip" + assert-text: ( + ".popover.tooltip", + "This example is not tested" + ) + assert-css: (".popover.tooltip", { + "color": |color|, + "background-color": |background|, + "border-color": |border|, + }) + click: ".docblock .example-wrap.ignore .tooltip" + assert-false: ".popover.tooltip" }, ) call-function: ("check-colors", { "theme": "ayu", - "background": "rgb(49, 69, 89)", + "background": "rgb(15, 20, 25)", "color": "rgb(197, 197, 197)", "border": "rgb(92, 103, 115)", }) call-function: ("check-colors", { "theme": "dark", - "background": "rgb(0, 0, 0)", - "color": "rgb(255, 255, 255)", + "background": "rgb(53, 53, 53)", + "color": "rgb(221, 221, 221)", "border": "rgb(224, 224, 224)", }) call-function: ("check-colors", { "theme": "light", - "background": "rgb(0, 0, 0)", - "color": "rgb(255, 255, 255)", + "background": "rgb(255, 255, 255)", + "color": "rgb(0, 0, 0)", "border": "rgb(224, 224, 224)", }) diff --git a/tests/rustdoc-gui/mobile.goml b/tests/rustdoc-gui/mobile.goml index 3e444cbd6dc..8c8516ebff8 100644 --- a/tests/rustdoc-gui/mobile.goml +++ b/tests/rustdoc-gui/mobile.goml @@ -12,7 +12,7 @@ assert-css: (".main-heading", { "flex-direction": "column" }) -assert-property: (".mobile-topbar h2", {"offsetHeight": 36}) +assert-property: (".mobile-topbar h2", {"offsetHeight": 33}) // Note: We can't use assert-text here because the 'Since' is set by CSS and // is therefore not part of the DOM. diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index b4fa7d0dbf0..20728915199 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -6,13 +6,13 @@ size: (1100, 600) // Checking they have the same y position. compare-elements-position: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("y"), ) // Checking they don't have the same x position. compare-elements-position-false: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("x"), ) // The `i` should be *after* the type. @@ -21,33 +21,33 @@ assert-position: ( {"x": 677}, ) assert-position: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", {"x": 955}, ) // The tooltip should be below the `i` // Also, clicking the tooltip should bring its text into the DOM -assert-count: ("//*[@class='notable popover']", 0) -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -assert-count: ("//*[@class='notable popover']", 1) +assert-count: ("//*[@class='tooltip popover']", 0) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +assert-count: ("//*[@class='tooltip popover']", 1) compare-elements-position-near: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", - "//*[@class='notable popover']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", + "//*[@class='tooltip popover']", {"y": 30} ) compare-elements-position-false: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", - "//*[@class='notable popover']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", + "//*[@class='tooltip popover']", ("x") ) -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" move-cursor-to: "//h1" -assert-count: ("//*[@class='notable popover']", 0) +assert-count: ("//*[@class='tooltip popover']", 0) // Now only the `i` should be on the next line. size: (1055, 600) compare-elements-position-false: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("y", "x"), ) @@ -56,13 +56,13 @@ size: (980, 600) // Checking they have the same y position. compare-elements-position: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("y"), ) // Checking they don't have the same x position. compare-elements-position-false: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("x"), ) // The `i` should be *after* the type. @@ -71,7 +71,7 @@ assert-position: ( {"x": 245}, ) assert-position: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", {"x": 523}, ) @@ -80,13 +80,13 @@ size: (650, 600) // Checking they have the same y position. compare-elements-position: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("y"), ) // Checking they don't have the same x position. compare-elements-position-false: ( "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']", - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", ("x"), ) // The `i` should be *after* the type. @@ -95,29 +95,29 @@ assert-position: ( {"x": 15}, ) assert-position: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", {"x": 293}, ) // The tooltip should STILL be below `i` -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -assert-count: ("//*[@class='notable popover']", 1) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +assert-count: ("//*[@class='tooltip popover']", 1) compare-elements-position-near: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", - "//*[@class='notable popover']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", + "//*[@class='tooltip popover']", {"y": 30} ) compare-elements-position-false: ( - "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']", - "//*[@class='notable popover']", + "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']", + "//*[@class='tooltip popover']", ("x") ) assert-position: ( - "//*[@class='notable popover']", + "//*[@class='tooltip popover']", {"x": 0} ) -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" move-cursor-to: "//h1" -assert-count: ("//*[@class='notable popover']", 0) +assert-count: ("//*[@class='tooltip popover']", 0) // Now check the colors. define-function: ( @@ -133,26 +133,26 @@ define-function: ( // We reload the page so the local storage settings are being used. reload: - move-cursor-to: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" - assert-count: (".notable.popover", 1) + move-cursor-to: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" + assert-count: (".tooltip.popover", 1) assert-css: ( - ".notable.popover h3", + ".tooltip.popover h3", {"color": |header_color|}, ALL, ) assert-css: ( - ".notable.popover pre", + ".tooltip.popover pre", {"color": |content_color|}, ALL, ) assert-css: ( - ".notable.popover pre a.struct", + ".tooltip.popover pre a.struct", {"color": |type_color|}, ALL, ) assert-css: ( - ".notable.popover pre a.trait", + ".tooltip.popover pre a.trait", {"color": |trait_color|}, ALL, ) @@ -195,24 +195,24 @@ call-function: ( reload: // Check that pressing escape works -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -move-cursor-to: "//*[@class='notable popover']" -assert-count: ("//*[@class='notable popover']", 1) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +move-cursor-to: "//*[@class='tooltip popover']" +assert-count: ("//*[@class='tooltip popover']", 1) press-key: "Escape" -assert-count: ("//*[@class='notable popover']", 0) -assert: "#method\.create_an_iterator_from_read .notable-traits:focus" +assert-count: ("//*[@class='tooltip popover']", 0) +assert: "#method\.create_an_iterator_from_read .tooltip:focus" // Check that clicking outside works. -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -assert-count: ("//*[@class='notable popover']", 1) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +assert-count: ("//*[@class='tooltip popover']", 1) click: ".search-input" -assert-count: ("//*[@class='notable popover']", 0) -assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus" +assert-count: ("//*[@class='tooltip popover']", 0) +assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" // Check that pressing tab over and over works. -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -move-cursor-to: "//*[@class='notable popover']" -assert-count: ("//*[@class='notable popover']", 1) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +move-cursor-to: "//*[@class='tooltip popover']" +assert-count: ("//*[@class='tooltip popover']", 1) press-key: "Tab" press-key: "Tab" press-key: "Tab" @@ -220,8 +220,8 @@ press-key: "Tab" press-key: "Tab" press-key: "Tab" press-key: "Tab" -assert-count: ("//*[@class='notable popover']", 0) -assert: "#method\.create_an_iterator_from_read .notable-traits:focus" +assert-count: ("//*[@class='tooltip popover']", 0) +assert: "#method\.create_an_iterator_from_read .tooltip:focus" // Now we check that the focus isn't given back to the wrong item when opening // another popover. @@ -231,8 +231,8 @@ click: "#method\.create_an_iterator_from_read .fn" assert-window-property-false: {"scrollY": |scroll|} // Store the new position. store-window-property: (scroll, "scrollY") -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -wait-for: "//*[@class='notable popover']" +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +wait-for: "//*[@class='tooltip popover']" click: "#settings-menu a" click: ".search-input" // We ensure we didn't come back to the previous focused item. @@ -245,8 +245,8 @@ click: "#method\.create_an_iterator_from_read .fn" assert-window-property-false: {"scrollY": |scroll|} // Store the new position. store-window-property: (scroll, "scrollY") -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -wait-for: "//*[@class='notable popover']" +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +wait-for: "//*[@class='tooltip popover']" click: "#settings-menu a" press-key: "Escape" // We ensure we didn't come back to the previous focused item. @@ -254,23 +254,23 @@ assert-window-property-false: {"scrollY": |scroll|} // Opening the mobile sidebar should close the popover. size: (650, 600) -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -assert-count: ("//*[@class='notable popover']", 1) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +assert-count: ("//*[@class='tooltip popover']", 1) click: ".sidebar-menu-toggle" assert: "//*[@class='sidebar shown']" -assert-count: ("//*[@class='notable popover']", 0) -assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus" -// Clicking a notable popover should close the sidebar. -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -assert-count: ("//*[@class='notable popover']", 1) +assert-count: ("//*[@class='tooltip popover']", 0) +assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" +// Clicking a notable trait tooltip popover should close the sidebar. +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +assert-count: ("//*[@class='tooltip popover']", 1) assert-false: "//*[@class='sidebar shown']" // Also check the focus handling for the help button. size: (1100, 600) reload: -assert-count: ("//*[@class='notable popover']", 0) -click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']" -assert-count: ("//*[@class='notable popover']", 1) +assert-count: ("//*[@class='tooltip popover']", 0) +click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" +assert-count: ("//*[@class='tooltip popover']", 1) click: "#help-button a" -assert-count: ("//*[@class='notable popover']", 0) -assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus" +assert-count: ("//*[@class='tooltip popover']", 0) +assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index 95102528ec1..dad727c7757 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -40,10 +40,10 @@ assert-property: ( store-value: (offset_y, 4) // First with desktop -assert-position: (".scraped-example .code-wrapper", {"y": 255}) -assert-position: (".scraped-example .code-wrapper .prev", {"y": 255 + |offset_y|}) +assert-position: (".scraped-example .code-wrapper", {"y": 253}) +assert-position: (".scraped-example .code-wrapper .prev", {"y": 253 + |offset_y|}) // Then with mobile size: (600, 600) -assert-position: (".scraped-example .code-wrapper", {"y": 314}) -assert-position: (".scraped-example .code-wrapper .prev", {"y": 314 + |offset_y|}) +assert-position: (".scraped-example .code-wrapper", {"y": 308}) +assert-position: (".scraped-example .code-wrapper .prev", {"y": 308 + |offset_y|}) diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml index 43e608228d8..20a88c36edb 100644 --- a/tests/rustdoc-gui/search-result-display.goml +++ b/tests/rustdoc-gui/search-result-display.goml @@ -22,7 +22,7 @@ size: (900, 900) // First we check the current width, height and position. assert-css: ("#crate-search", {"width": "223px"}) -assert-css: (".search-results-title", {"height": "44px", "width": "640px"}) +assert-css: (".search-results-title", {"height": "50px", "width": "640px"}) assert-css: ("#search", {"width": "640px"}) // Then we update the text of one of the `<option>`. @@ -33,7 +33,7 @@ text: ( // Then we compare again to confirm the height didn't change. assert-css: ("#crate-search", {"width": "527px"}) -assert-css: (".search-results-title", {"height": "44px", "width": "640px"}) +assert-css: (".search-results-title", {"height": "50px", "width": "640px"}) // And we check that the `<select>` isn't bigger than its container (".search-results-title"). assert-css: ("#search", {"width": "640px"}) diff --git a/tests/rustdoc-gui/sidebar-mobile-scroll.goml b/tests/rustdoc-gui/sidebar-mobile-scroll.goml index 2449269b192..4442b263e9a 100644 --- a/tests/rustdoc-gui/sidebar-mobile-scroll.goml +++ b/tests/rustdoc-gui/sidebar-mobile-scroll.goml @@ -6,7 +6,7 @@ assert-css: (".sidebar", {"display": "block", "left": "-1000px"}) // Scroll down. scroll-to: "//h2[@id='blanket-implementations']" -assert-window-property: {"pageYOffset": "627"} +assert-window-property: {"pageYOffset": "622"} // Open the sidebar menu. click: ".sidebar-menu-toggle" @@ -21,11 +21,11 @@ assert-window-property: {"pageYOffset": "0"} // Close the sidebar menu. Make sure the scroll position gets restored. click: ".sidebar-menu-toggle" wait-for-css: (".sidebar", {"left": "-1000px"}) -assert-window-property: {"pageYOffset": "627"} +assert-window-property: {"pageYOffset": "622"} // Now test that scrollability returns when the browser window is just resized. click: ".sidebar-menu-toggle" wait-for-css: (".sidebar", {"left": "0px"}) assert-window-property: {"pageYOffset": "0"} size: (900, 600) -assert-window-property: {"pageYOffset": "627"} +assert-window-property: {"pageYOffset": "622"} diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index d5f4b619629..cc6267c3dc9 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -45,7 +45,7 @@ assert-property: (".mobile-topbar", {"clientHeight": "45"}) // so the target is not obscured by the topbar. click: ".sidebar-menu-toggle" click: ".sidebar-elems section .block li > a" -assert-position: ("#method\.must_use", {"y": 45}) +assert-position: ("#method\.must_use", {"y": 46}) // Check that the bottom-most item on the sidebar menu can be scrolled fully into view. click: ".sidebar-menu-toggle" diff --git a/tests/rustdoc-ui/intra-doc/errors.rs b/tests/rustdoc-ui/intra-doc/errors.rs index b29f7c29b5d..95dd2b98e03 100644 --- a/tests/rustdoc-ui/intra-doc/errors.rs +++ b/tests/rustdoc-ui/intra-doc/errors.rs @@ -103,3 +103,19 @@ pub trait T { macro_rules! m { () => {}; } + +///[`TestEnum::Variant1::field_name`] +//~^ ERROR unresolved link +//~| NOTE variant `Variant1` has no such field +pub enum TestEnum { + Variant1 {}, + Variant2 { field_name: u64 }, +} + +///[`TestEnumNoFields::Variant1::field_name`] +//~^ ERROR unresolved link +//~| NOTE `Variant1` is a variant, not a module or type, and cannot have associated items +pub enum TestEnumNoFields { + Variant1 (), + Variant2 {}, +} diff --git a/tests/rustdoc-ui/intra-doc/errors.stderr b/tests/rustdoc-ui/intra-doc/errors.stderr index 9a1896fb0cd..1b2416d7da7 100644 --- a/tests/rustdoc-ui/intra-doc/errors.stderr +++ b/tests/rustdoc-ui/intra-doc/errors.stderr @@ -142,6 +142,18 @@ error: unresolved link to `T::h` LL | /// [T::h!] | ^^^^^ the trait `T` has no macro named `h` +error: unresolved link to `TestEnum::Variant1::field_name` + --> $DIR/errors.rs:107:6 + | +LL | ///[`TestEnum::Variant1::field_name`] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variant `Variant1` has no such field + +error: unresolved link to `TestEnumNoFields::Variant1::field_name` + --> $DIR/errors.rs:115:6 + | +LL | ///[`TestEnumNoFields::Variant1::field_name`] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Variant1` is a variant, not a module or type, and cannot have associated items + error: unresolved link to `m` --> $DIR/errors.rs:98:6 | @@ -153,5 +165,5 @@ help: to link to the macro, add an exclamation mark LL | /// [m!()] | + -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/tests/rustdoc-ui/z-help.stdout b/tests/rustdoc-ui/z-help.stdout index 706db892cb3..96329f31723 100644 --- a/tests/rustdoc-ui/z-help.stdout +++ b/tests/rustdoc-ui/z-help.stdout @@ -70,6 +70,15 @@ `=except-unused-functions` `=off` (default) -Z instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) + -Z instrument-xray=val -- insert function instrument code for XRay-based tracing (default: no) + Optional extra settings: + `=always` + `=never` + `=ignore-loops` + `=instruction-threshold=N` + `=skip-entry` + `=skip-exit` + Multiple options can be combined with commas. -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) -Z layout-seed=val -- seed layout randomization -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) @@ -167,6 +176,7 @@ -Z symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') -Z teach=val -- show extended diagnostic help (default: no) -Z temps-dir=val -- the directory the intermediate files are written to + -Z terminal-urls=val -- use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output -Z thinlto=val -- enable ThinLTO when possible -Z thir-unsafeck=val -- use the THIR unsafety checker (default: no) -Z threads=val -- use a thread pool with N threads diff --git a/tests/rustdoc/codeblock-title.rs b/tests/rustdoc/codeblock-title.rs index b9b0b0d1abf..761afb8bd08 100644 --- a/tests/rustdoc/codeblock-title.rs +++ b/tests/rustdoc/codeblock-title.rs @@ -3,7 +3,7 @@ // @has foo/fn.bar.html '//*[@class="example-wrap compile_fail"]/*[@class="tooltip"]' "ⓘ" // @has foo/fn.bar.html '//*[@class="example-wrap ignore"]/*[@class="tooltip"]' "ⓘ" // @has foo/fn.bar.html '//*[@class="example-wrap should_panic"]/*[@class="tooltip"]' "ⓘ" -// @has foo/fn.bar.html '//*[@data-edition="2018"]' "ⓘ" +// @has foo/fn.bar.html '//*[@title="This example runs with edition 2018"]' "ⓘ" /// foo /// diff --git a/tests/rustdoc/const-generics/const-generics-docs.rs b/tests/rustdoc/const-generics/const-generics-docs.rs index cbda095424b..828486a41d4 100644 --- a/tests/rustdoc/const-generics/const-generics-docs.rs +++ b/tests/rustdoc/const-generics/const-generics-docs.rs @@ -21,8 +21,8 @@ pub use extern_crate::WTrait; // 'pub trait Trait<const N: usize>' // @has - '//*[@id="impl-Trait%3C1%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<1> for u8' // @has - '//*[@id="impl-Trait%3C2%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<2> for u8' -// @has - '//*[@id="impl-Trait%3C{1%20+%202}%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<{1 + 2}> for u8' -// @has - '//*[@id="impl-Trait%3CN%3E-for-%5Bu8%3B%20N%5D"]//h3[@class="code-header"]' \ +// @has - '//*[@id="impl-Trait%3C%7B1+%2B+2%7D%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<{1 + 2}> for u8' +// @has - '//*[@id="impl-Trait%3CN%3E-for-%5Bu8;+N%5D"]//h3[@class="code-header"]' \ // 'impl<const N: usize> Trait<N> for [u8; N]' pub trait Trait<const N: usize> {} impl Trait<1> for u8 {} @@ -47,7 +47,7 @@ impl<const M: usize> Foo<M> where u8: Trait<M> { } } -// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8%2C%20M%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Bar<u8, M>' +// @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8,+M%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Bar<u8, M>' impl<const M: usize> Bar<u8, M> { // @has - '//*[@id="method.hey"]' \ // 'pub fn hey<const N: usize>(&self) -> Foo<N>where u8: Trait<N>' diff --git a/tests/rustdoc/const-generics/const-impl.rs b/tests/rustdoc/const-generics/const-impl.rs index 91866b7d890..152b643bf4b 100644 --- a/tests/rustdoc/const-generics/const-impl.rs +++ b/tests/rustdoc/const-generics/const-impl.rs @@ -9,20 +9,20 @@ pub enum Order { } // @has foo/struct.VSet.html '//pre[@class="rust item-decl"]' 'pub struct VSet<T, const ORDER: Order>' -// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>' -// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>' +// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT,+ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>' +// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT,+ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>' pub struct VSet<T, const ORDER: Order> { inner: Vec<T>, } -// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT%2C%20{%20Order%3A%3ASorted%20}%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Sorted }>' +// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT,+%7B+Order::Sorted+%7D%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Sorted }>' impl<T> VSet<T, { Order::Sorted }> { pub fn new() -> Self { Self { inner: Vec::new() } } } -// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT%2C%20{%20Order%3A%3AUnsorted%20}%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Unsorted }>' +// @has foo/struct.VSet.html '//*[@id="impl-VSet%3CT,+%7B+Order::Unsorted+%7D%3E"]/h3[@class="code-header"]' 'impl<T> VSet<T, { Order::Unsorted }>' impl<T> VSet<T, { Order::Unsorted }> { pub fn new() -> Self { Self { inner: Vec::new() } @@ -31,7 +31,7 @@ impl<T> VSet<T, { Order::Unsorted }> { pub struct Escape<const S: &'static str>; -// @has foo/struct.Escape.html '//*[@id="impl-Escape%3Cr#%22%3Cscript%3Ealert(%22Escape%22)%3B%3C/script%3E%22#%3E"]/h3[@class="code-header"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>' +// @has foo/struct.Escape.html '//*[@id="impl-Escape%3Cr%23%22%3Cscript%3Ealert(%22Escape%22);%3C/script%3E%22%23%3E"]/h3[@class="code-header"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>' impl Escape<r#"<script>alert("Escape");</script>"#> { pub fn f() {} } diff --git a/tests/rustdoc/description.rs b/tests/rustdoc/description.rs index 05ec4282208..43cd59ebd09 100644 --- a/tests/rustdoc/description.rs +++ b/tests/rustdoc/description.rs @@ -22,3 +22,9 @@ pub mod foo_mod { // 'Only paragraph.' /// Only paragraph. pub fn foo_fn() {} + +// @has 'foo/fn.bar_fn.html' '//meta[@name="description"]/@content' \ +// 'Description with intra-doc link to foo_fn and [nonexistent_item] and foo_fn.' +#[allow(rustdoc::broken_intra_doc_links)] +/// Description with intra-doc link to [foo_fn] and [nonexistent_item] and [foo_fn](self::foo_fn). +pub fn bar_fn() {} diff --git a/tests/rustdoc/doc-notable_trait.rs b/tests/rustdoc/doc-notable_trait.rs index 279faf55401..d8941769fa6 100644 --- a/tests/rustdoc/doc-notable_trait.rs +++ b/tests/rustdoc/doc-notable_trait.rs @@ -9,7 +9,7 @@ impl<T: SomeTrait> SomeTrait for Wrapper<T> {} #[doc(notable_trait)] pub trait SomeTrait { // @has doc_notable_trait/trait.SomeTrait.html - // @has - '//a[@class="notable-traits"]/@data-ty' 'Wrapper<Self>' + // @has - '//a[@class="tooltip"]/@data-notable-ty' 'Wrapper<Self>' // @snapshot wrap-me - '//script[@id="notable-traits-data"]' fn wrap_me(self) -> Wrapper<Self> where Self: Sized { Wrapper { @@ -23,7 +23,7 @@ impl SomeTrait for SomeStruct {} impl SomeStruct { // @has doc_notable_trait/struct.SomeStruct.html - // @has - '//a[@class="notable-traits"]/@data-ty' 'SomeStruct' + // @has - '//a[@class="tooltip"]/@data-notable-ty' 'SomeStruct' // @snapshot some-struct-new - '//script[@id="notable-traits-data"]' pub fn new() -> SomeStruct { SomeStruct @@ -31,7 +31,7 @@ impl SomeStruct { } // @has doc_notable_trait/fn.bare_fn.html -// @has - '//a[@class="notable-traits"]/@data-ty' 'SomeStruct' +// @has - '//a[@class="tooltip"]/@data-notable-ty' 'SomeStruct' // @snapshot bare-fn - '//script[@id="notable-traits-data"]' pub fn bare_fn() -> SomeStruct { SomeStruct diff --git a/tests/rustdoc/double-quote-escape.rs b/tests/rustdoc/double-quote-escape.rs index 350c897417d..4f4436377a0 100644 --- a/tests/rustdoc/double-quote-escape.rs +++ b/tests/rustdoc/double-quote-escape.rs @@ -7,5 +7,5 @@ pub trait Foo<T> { pub struct Bar; // @has foo/struct.Bar.html -// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe%20extern%20%22C%22%20fn()%3E-for-Bar"]' 'Foo<unsafe extern "C" fn()>' +// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe+extern+%22C%22+fn()%3E-for-Bar"]' 'Foo<unsafe extern "C" fn()>' impl Foo<unsafe extern "C" fn()> for Bar {} diff --git a/tests/rustdoc/markdown-summaries.rs b/tests/rustdoc/markdown-summaries.rs deleted file mode 100644 index 31e7072b5ce..00000000000 --- a/tests/rustdoc/markdown-summaries.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![crate_type = "lib"] -#![crate_name = "summaries"] - -//! This *summary* has a [link] and `code`. -//! -//! This is the second paragraph. -//! -//! [link]: https://example.com - -// @hasraw search-index.js 'This <em>summary</em> has a link and <code>code</code>.' -// @!hasraw - 'second paragraph' - -/// This `code` will be rendered in a code tag. -/// -/// This text should not be rendered. -pub struct Sidebar; - -// @hasraw search-index.js 'This <code>code</code> will be rendered in a code tag.' -// @hasraw summaries/sidebar-items.js 'This `code` will be rendered in a code tag.' -// @!hasraw - 'text should not be rendered' - -/// ```text -/// this block should not be rendered -/// ``` -pub struct Sidebar2; - -// @!hasraw summaries/sidebar-items.js 'block should not be rendered' diff --git a/tests/rustdoc/primitive-tuple-variadic.rs b/tests/rustdoc/primitive-tuple-variadic.rs index db7cfd60c71..846028bbb19 100644 --- a/tests/rustdoc/primitive-tuple-variadic.rs +++ b/tests/rustdoc/primitive-tuple-variadic.rs @@ -6,13 +6,13 @@ pub trait Foo {} // @has foo/trait.Foo.html -// @has - '//section[@id="impl-Foo-for-(T%2C)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)' +// @has - '//section[@id="impl-Foo-for-(T,)"]/h3' 'impl<T> Foo for (T₁, T₂, …, Tₙ)' #[doc(fake_variadic)] impl<T> Foo for (T,) {} pub trait Bar {} // @has foo/trait.Bar.html -// @has - '//section[@id="impl-Bar-for-(U%2C)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)' +// @has - '//section[@id="impl-Bar-for-(U,)"]/h3' 'impl<U: Foo> Bar for (U₁, U₂, …, Uₙ)' #[doc(fake_variadic)] impl<U: Foo> Bar for (U,) {} diff --git a/tests/rustdoc/reexport-macro.rs b/tests/rustdoc/reexport-macro.rs new file mode 100644 index 00000000000..c4dec703aed --- /dev/null +++ b/tests/rustdoc/reexport-macro.rs @@ -0,0 +1,23 @@ +// Ensure that macros are correctly reexported and that they get both the comment from the +// `pub use` and from the macro. + +#![crate_name = "foo"] + +// @has 'foo/macro.foo.html' +// @!has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'x y' +// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'y' +#[macro_use] +mod my_module { + /// y + #[macro_export] + macro_rules! foo { + () => (); + } +} + +// @has 'foo/another_mod/macro.bar.html' +// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'x y' +pub mod another_mod { + /// x + pub use crate::foo as bar; +} diff --git a/tests/rustdoc/sidebar-links-to-foreign-impl.rs b/tests/rustdoc/sidebar-links-to-foreign-impl.rs index 11e94694802..caa17dfbb1c 100644 --- a/tests/rustdoc/sidebar-links-to-foreign-impl.rs +++ b/tests/rustdoc/sidebar-links-to-foreign-impl.rs @@ -7,8 +7,8 @@ // @has - '//h2[@id="foreign-impls"]' 'Implementations on Foreign Types' // @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-u32"]' 'u32' // @has - '//*[@id="impl-Foo-for-u32"]//h3[@class="code-header"]' 'impl Foo for u32' -// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-%26%27a%20str"]' "&'a str" -// @has - '//*[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header"]' "impl<'a> Foo for &'a str" +// @has - "//*[@class=\"sidebar-elems\"]//section//a[@href=\"#impl-Foo-for-%26'a+str\"]" "&'a str" +// @has - "//*[@id=\"impl-Foo-for-%26'a+str\"]//h3[@class=\"code-header\"]" "impl<'a> Foo for &'a str" pub trait Foo {} impl Foo for u32 {} diff --git a/tests/rustdoc/spotlight-from-dependency.rs b/tests/rustdoc/spotlight-from-dependency.rs index 090ad187d9c..426759c7bf8 100644 --- a/tests/rustdoc/spotlight-from-dependency.rs +++ b/tests/rustdoc/spotlight-from-dependency.rs @@ -3,7 +3,7 @@ use std::iter::Iterator; // @has foo/struct.Odd.html -// @has - '//*[@id="method.new"]//a[@class="notable-traits"]/@data-ty' 'Odd' +// @has - '//*[@id="method.new"]//a[@class="tooltip"]/@data-notable-ty' 'Odd' // @snapshot odd - '//script[@id="notable-traits-data"]' pub struct Odd { current: usize, diff --git a/tests/rustdoc/where-clause-order.rs b/tests/rustdoc/where-clause-order.rs index b8502e10a48..b10f8f6856e 100644 --- a/tests/rustdoc/where-clause-order.rs +++ b/tests/rustdoc/where-clause-order.rs @@ -7,7 +7,7 @@ where } // @has 'foo/trait.SomeTrait.html' -// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, " +// @has - "//*[@id='impl-SomeTrait%3C(A,+B,+C,+D,+E)%3E-for-(A,+B,+C,+D,+E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, " impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, diff --git a/tests/ui/associated-type-bounds/elision.stderr b/tests/ui/associated-type-bounds/elision.stderr index b64a4dab206..cc10bbcc0b5 100644 --- a/tests/ui/associated-type-bounds/elision.stderr +++ b/tests/ui/associated-type-bounds/elision.stderr @@ -16,10 +16,10 @@ error[E0308]: mismatched types LL | fn f(x: &mut dyn Iterator<Item: Iterator<Item = &'_ ()>>) -> Option<&'_ ()> { x.next() } | ----------------------------- -------------- ^^^^^^^^ expected `Option<&()>`, found `Option<impl Iterator<Item = &'_ ()>>` | | | - | | expected `Option<&'static ()>` because of return type + | | expected `Option<&()>` because of return type | this type parameter | - = note: expected enum `Option<&'static ()>` + = note: expected enum `Option<&()>` found enum `Option<impl Iterator<Item = &'_ ()>>` error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/async-fn-path-elision.stderr b/tests/ui/async-await/async-fn-path-elision.stderr index 5e0c8c29989..224198653dc 100644 --- a/tests/ui/async-await/async-fn-path-elision.stderr +++ b/tests/ui/async-await/async-fn-path-elision.stderr @@ -4,7 +4,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | async fn error(lt: HasLifetime) { | ^^^^^^^^^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | async fn error(lt: HasLifetime<'_>) { diff --git a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr index 4bba42c7782..656bc29466f 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.min.stderr +++ b/tests/ui/const-generics/const-param-elided-lifetime.min.stderr @@ -28,7 +28,7 @@ error[E0637]: `&` without an explicit lifetime name cannot be used here LL | fn bar<const N: &u8>() {} | ^ explicit lifetime name needed here -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:9:19 | LL | struct A<const N: &u8>; @@ -37,7 +37,7 @@ LL | struct A<const N: &u8>; = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:14:15 | LL | impl<const N: &u8> A<N> { @@ -46,7 +46,7 @@ LL | impl<const N: &u8> A<N> { = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:22:15 | LL | impl<const N: &u8> B for A<N> {} @@ -55,7 +55,7 @@ LL | impl<const N: &u8> B for A<N> {} = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:26:17 | LL | fn bar<const N: &u8>() {} @@ -64,7 +64,7 @@ LL | fn bar<const N: &u8>() {} = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error: `&'static u8` is forbidden as the type of a const generic parameter +error: `&u8` is forbidden as the type of a const generic parameter --> $DIR/const-param-elided-lifetime.rs:17:21 | LL | fn foo<const M: &u8>(&self) {} diff --git a/tests/ui/const-generics/const-param-elided-lifetime.rs b/tests/ui/const-generics/const-param-elided-lifetime.rs index 487b82dbf4a..45611d6bf5f 100644 --- a/tests/ui/const-generics/const-param-elided-lifetime.rs +++ b/tests/ui/const-generics/const-param-elided-lifetime.rs @@ -8,23 +8,23 @@ struct A<const N: &u8>; //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden trait B {} impl<const N: &u8> A<N> { //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden fn foo<const M: &u8>(&self) {} //~^ ERROR `&` without an explicit lifetime name cannot be used here - //[min]~^^ ERROR `&'static u8` is forbidden + //[min]~^^ ERROR `&u8` is forbidden } impl<const N: &u8> B for A<N> {} //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden fn bar<const N: &u8>() {} //~^ ERROR `&` without an explicit lifetime name cannot be used here -//[min]~^^ ERROR `&'static u8` is forbidden +//[min]~^^ ERROR `&u8` is forbidden fn main() {} diff --git a/tests/ui/const-generics/issues/issue-56445-1.min.stderr b/tests/ui/const-generics/issues/issue-56445-1.min.stderr index 43a5df117fd..9f880134162 100644 --- a/tests/ui/const-generics/issues/issue-56445-1.min.stderr +++ b/tests/ui/const-generics/issues/issue-56445-1.min.stderr @@ -6,7 +6,7 @@ LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); | = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052> -error: `&'static str` is forbidden as the type of a const generic parameter +error: `&str` is forbidden as the type of a const generic parameter --> $DIR/issue-56445-1.rs:9:25 | LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); diff --git a/tests/ui/const-generics/issues/issue-56445-1.rs b/tests/ui/const-generics/issues/issue-56445-1.rs index 13eb2ea9f69..0741c3796ad 100644 --- a/tests/ui/const-generics/issues/issue-56445-1.rs +++ b/tests/ui/const-generics/issues/issue-56445-1.rs @@ -8,6 +8,6 @@ use std::marker::PhantomData; struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>); //~^ ERROR: use of non-static lifetime `'a` in const generic -//[min]~| ERROR: `&'static str` is forbidden as the type of a const generic parameter +//[min]~| ERROR: `&str` is forbidden as the type of a const generic parameter impl Bug<'_, ""> {} diff --git a/tests/ui/const-generics/wrong-normalization.rs b/tests/ui/const-generics/wrong-normalization.rs new file mode 100644 index 00000000000..f1ce317b3f7 --- /dev/null +++ b/tests/ui/const-generics/wrong-normalization.rs @@ -0,0 +1,19 @@ +// This test ensures that if implementation on projections is supported, +// it doesn't end in very weird cycle error. + +#![crate_type = "lib"] + +pub trait Identity { + type Identity: ?Sized; +} + +impl<T: ?Sized> Identity for T { + type Identity = Self; +} + +pub struct I8<const F: i8>; + +impl <I8<{i8::MIN}> as Identity>::Identity { +//~^ ERROR no nominal type found for inherent implementation + pub fn foo(&self) {} +} diff --git a/tests/ui/const-generics/wrong-normalization.stderr b/tests/ui/const-generics/wrong-normalization.stderr new file mode 100644 index 00000000000..fb806bdb1e7 --- /dev/null +++ b/tests/ui/const-generics/wrong-normalization.stderr @@ -0,0 +1,11 @@ +error[E0118]: no nominal type found for inherent implementation + --> $DIR/wrong-normalization.rs:16:6 + | +LL | impl <I8<{i8::MIN}> as Identity>::Identity { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl requires a nominal type + | + = note: either implement a trait on it or create a newtype to wrap it instead + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0118`. diff --git a/tests/ui/diagnostic-flags/terminal_urls.rs b/tests/ui/diagnostic-flags/terminal_urls.rs new file mode 100644 index 00000000000..1f04e2aade1 --- /dev/null +++ b/tests/ui/diagnostic-flags/terminal_urls.rs @@ -0,0 +1,4 @@ +// compile-flags: -Zterminal-urls=yes +fn main() { + let () = 4; //~ ERROR +} diff --git a/tests/ui/diagnostic-flags/terminal_urls.stderr b/tests/ui/diagnostic-flags/terminal_urls.stderr new file mode 100644 index 00000000000..7f7e69c5d5d --- /dev/null +++ b/tests/ui/diagnostic-flags/terminal_urls.stderr @@ -0,0 +1,11 @@ +error[]8;;https://doc.rust-lang.org/error_codes/E0308.htmlE0308]8;;]: mismatched types + --> $DIR/terminal_urls.rs:3:9 + | +LL | let () = 4; + | ^^ - this expression has type `{integer}` + | | + | expected integer, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs index 9ea9fc71b55..54b483f53d4 100644 --- a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs +++ b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.rs @@ -8,7 +8,6 @@ fn should_error<T>() where T : Into<&u32> {} trait X<'a, K: 'a> { fn foo<'b, L: X<&'b Nested<K>>>(); //~^ ERROR missing lifetime specifier [E0106] - //~| ERROR the type `&'b Nested<K>` does not fulfill the required lifetime } fn bar<'b, L: X<&'b Nested<i32>>>(){} diff --git a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr index 9d859fddf56..faf4c9eb872 100644 --- a/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr +++ b/tests/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr @@ -29,7 +29,7 @@ LL | fn foo<'b, L: X<'lifetime, &'b Nested<K>>>(); | ++++++++++ error[E0106]: missing lifetime specifier - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:14:16 + --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:16 | LL | fn bar<'b, L: X<&'b Nested<i32>>>(){} | ^ expected named lifetime parameter @@ -39,19 +39,7 @@ help: consider using the `'b` lifetime LL | fn bar<'b, L: X<'b, &'b Nested<i32>>>(){} | +++ -error[E0477]: the type `&'b Nested<K>` does not fulfill the required lifetime - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:19 - | -LL | fn foo<'b, L: X<&'b Nested<K>>>(); - | ^^^^^^^^^^^^^^^^ - | -note: type must satisfy the static lifetime as required by this binding - --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:8:16 - | -LL | trait X<'a, K: 'a> { - | ^^ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0106, E0477, E0637. +Some errors have detailed explanations: E0106, E0637. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index 0b23b1cc2f4..1c6a7b02f8e 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,2 +1,2 @@ -thread 'main' panicked at 'capacity overflow', library/alloc/src/raw_vec.rs:518:5 +thread 'main' panicked at 'capacity overflow', library/alloc/src/raw_vec.rs:525:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/ui/impl-header-lifetime-elision/path-elided.stderr b/tests/ui/impl-header-lifetime-elision/path-elided.stderr index 0b7d3f1e851..18e4c618dba 100644 --- a/tests/ui/impl-header-lifetime-elision/path-elided.stderr +++ b/tests/ui/impl-header-lifetime-elision/path-elided.stderr @@ -4,7 +4,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl MyTrait for Foo { | ^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl MyTrait for Foo<'_> { diff --git a/tests/ui/impl-header-lifetime-elision/trait-elided.stderr b/tests/ui/impl-header-lifetime-elision/trait-elided.stderr index 412bba6be71..74631a03786 100644 --- a/tests/ui/impl-header-lifetime-elision/trait-elided.stderr +++ b/tests/ui/impl-header-lifetime-elision/trait-elided.stderr @@ -4,7 +4,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl MyTrait for u32 {} | ^^^^^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl MyTrait<'_> for u32 {} diff --git a/tests/ui/impl-trait/in-trait/signature-mismatch.stderr b/tests/ui/impl-trait/in-trait/signature-mismatch.stderr index e105660173b..c4fcaabe446 100644 --- a/tests/ui/impl-trait/in-trait/signature-mismatch.stderr +++ b/tests/ui/impl-trait/in-trait/signature-mismatch.stderr @@ -2,12 +2,12 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/signature-mismatch.rs:15:5 | LL | fn async_fn(&self, buff: &[u8]) -> impl Future<Output = Vec<u8>>; - | ----------------------------------------------------------------- expected `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + 'static` + | ----------------------------------------------------------------- expected `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3` ... LL | fn async_fn<'a>(&self, buff: &'a [u8]) -> impl Future<Output = Vec<u8>> + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2` | - = note: expected signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + 'static` + = note: expected signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '3` found signature `fn(&'1 Struct, &'2 [u8]) -> impl Future<Output = Vec<u8>> + '2` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/tests/ui/impl-trait/nested-return-type2.rs b/tests/ui/impl-trait/nested-return-type2.rs index cc1f1f4ec44..fe883ce6fc8 100644 --- a/tests/ui/impl-trait/nested-return-type2.rs +++ b/tests/ui/impl-trait/nested-return-type2.rs @@ -1,4 +1,7 @@ // check-pass +// compile-flags: -Zvalidate-mir + +// Using -Zvalidate-mir as a regression test for #107346. trait Duh {} diff --git a/tests/ui/impl-trait/nested-return-type2.stderr b/tests/ui/impl-trait/nested-return-type2.stderr index 3aed05ca132..09ad3bd05c1 100644 --- a/tests/ui/impl-trait/nested-return-type2.stderr +++ b/tests/ui/impl-trait/nested-return-type2.stderr @@ -1,5 +1,5 @@ warning: opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds - --> $DIR/nested-return-type2.rs:25:24 + --> $DIR/nested-return-type2.rs:28:24 | LL | type Assoc: Duh; | --- this associated type bound is unsatisfied for `impl Send` diff --git a/tests/ui/inference/issue-107090.rs b/tests/ui/inference/issue-107090.rs index 9426445656f..a22e12c6d88 100644 --- a/tests/ui/inference/issue-107090.rs +++ b/tests/ui/inference/issue-107090.rs @@ -2,9 +2,7 @@ use std::marker::PhantomData; struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) where Foo<'short, 'out, T>: Convert<'a, 'b>; - //~^ ERROR mismatched types - //~^^ ERROR mismatched types - //~^^^ ERROR use of undeclared lifetime name + //~^ ERROR use of undeclared lifetime name //~| ERROR use of undeclared lifetime name `'out` trait Convert<'a, 'b>: Sized { @@ -13,19 +11,15 @@ trait Convert<'a, 'b>: Sized { impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { //~^ ERROR use of undeclared lifetime name //~^^ ERROR use of undeclared lifetime name `'out` - //~| ERROR cannot infer an appropriate lifetime for lifetime parameter fn cast(&'long self) -> &'short Foo<'short, 'out, T> { //~^ ERROR use of undeclared lifetime name - //~| ERROR cannot infer an appropriate lifetime for lifetime parameter self } } fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { //~^ ERROR use of undeclared lifetime name - //~^^ ERROR incompatible lifetime on type - //~| ERROR `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement - sadness.cast() + sadness.cast() //~ ERROR mismatched types } fn main() {} diff --git a/tests/ui/inference/issue-107090.stderr b/tests/ui/inference/issue-107090.stderr index 33cb39014ac..6233b629ad6 100644 --- a/tests/ui/inference/issue-107090.stderr +++ b/tests/ui/inference/issue-107090.stderr @@ -30,7 +30,7 @@ LL | struct Foo<'out, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) | +++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/issue-107090.rs:13:47 + --> $DIR/issue-107090.rs:11:47 | LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { | - ^^ undeclared lifetime @@ -38,13 +38,13 @@ LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> | help: consider introducing lifetime `'b` here: `'b,` error[E0261]: use of undeclared lifetime name `'out` - --> $DIR/issue-107090.rs:13:67 + --> $DIR/issue-107090.rs:11:67 | LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { | - help: consider introducing lifetime `'out` here: `'out,` ^^^^ undeclared lifetime error[E0261]: use of undeclared lifetime name `'out` - --> $DIR/issue-107090.rs:17:49 + --> $DIR/issue-107090.rs:14:49 | LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { | ^^^^ undeclared lifetime @@ -59,7 +59,7 @@ LL | impl<'out, 'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'ou | +++++ error[E0261]: use of undeclared lifetime name `'short` - --> $DIR/issue-107090.rs:24:68 + --> $DIR/issue-107090.rs:20:68 | LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { | - ^^^^^^ undeclared lifetime @@ -67,107 +67,18 @@ LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, | help: consider introducing lifetime `'short` here: `'short,` error[E0308]: mismatched types - --> $DIR/issue-107090.rs:4:27 - | -LL | Foo<'short, 'out, T>: Convert<'a, 'b>; - | ^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected trait `Convert<'static, 'static>` - found trait `Convert<'a, 'b>` -note: the lifetime `'a` as defined here... - --> $DIR/issue-107090.rs:2:12 - | -LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) - | ^^ - = note: ...does not necessarily outlive the static lifetime - -error[E0308]: mismatched types - --> $DIR/issue-107090.rs:4:27 - | -LL | Foo<'short, 'out, T>: Convert<'a, 'b>; - | ^^^^^^^^^^^^^^^ lifetime mismatch - | - = note: expected trait `Convert<'static, 'static>` - found trait `Convert<'a, 'b>` -note: the lifetime `'b` as defined here... - --> $DIR/issue-107090.rs:2:16 - | -LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>) - | ^^ - = note: ...does not necessarily outlive the static lifetime - -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements - --> $DIR/issue-107090.rs:13:55 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^^^^^^^^^^^^^^^ - | -note: first, the lifetime cannot outlive the lifetime `'short` as defined here... - --> $DIR/issue-107090.rs:13:21 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^ - = note: ...but the lifetime must also be valid for the static lifetime... -note: ...so that the types are compatible - --> $DIR/issue-107090.rs:13:55 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^^^^^^^^^^^^^^^ - = note: expected `Convert<'short, 'static>` - found `Convert<'_, 'static>` - -error: incompatible lifetime on type - --> $DIR/issue-107090.rs:24:29 - | -LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | ^^^^^^^^^^^^^^^^^^ - | -note: because this has an unmet lifetime requirement - --> $DIR/issue-107090.rs:4:27 - | -LL | Foo<'short, 'out, T>: Convert<'a, 'b>; - | ^^^^^^^^^^^^^^^ introduces a `'static` lifetime requirement -note: the lifetime `'out` as defined here... - --> $DIR/issue-107090.rs:24:17 - | -LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | ^^^^ -note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` - --> $DIR/issue-107090.rs:13:1 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0759]: `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement - --> $DIR/issue-107090.rs:24:29 + --> $DIR/issue-107090.rs:22:5 | LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | ^^^^^^^^^^^^^^^^^^ - | | - | this data with lifetime `'in_`... - | ...is used and required to live as long as `'static` here - -error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements - --> $DIR/issue-107090.rs:17:13 - | -LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { - | ^^^^^^^^^^^ + | - this type parameter ------- expected `&'out T` because of return type +LL | +LL | sadness.cast() + | ^^^^^^^^^^^^^^ expected `&T`, found `&Foo<'_, '_, T>` | -note: first, the lifetime cannot outlive the lifetime `'short` as defined here... - --> $DIR/issue-107090.rs:13:21 - | -LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { - | ^^^^^^ - = note: ...but the lifetime must also be valid for the static lifetime... -note: ...so that the types are compatible - --> $DIR/issue-107090.rs:17:13 - | -LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> { - | ^^^^^^^^^^^ - = note: expected `Convert<'short, 'static>` - found `Convert<'_, 'static>` + = note: expected reference `&'out T` + found reference `&Foo<'_, '_, T>` -error: aborting due to 12 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0261, E0308, E0495, E0759. +Some errors have detailed explanations: E0261, E0308. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs new file mode 100644 index 00000000000..7f6758f47f8 --- /dev/null +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.rs @@ -0,0 +1,19 @@ +// ignore-tidy-linelength + +// Regression test for #107745. +// Previously need_type_info::update_infer_source will consider expressions originating from +// macro expressions as candiate "previous sources". This unfortunately can mean that +// for macros expansions such as `format!()` internal implementation details can leak, such as: +// +// ``` +// error[E0282]: type annotations needed +// --> src/main.rs:2:22 +// | +//2 | println!("{:?}", []); +// | ^^ cannot infer type of the type parameter `T` declared on the associated function `new_debug` +// ``` + +fn main() { + println!("{:?}", []); + //~^ ERROR type annotations needed +} diff --git a/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr new file mode 100644 index 00000000000..464655bbcf4 --- /dev/null +++ b/tests/ui/inference/need_type_info/issue-107745-avoid-expr-from-macro-expansion.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/issue-107745-avoid-expr-from-macro-expansion.rs:17:22 + | +LL | println!("{:?}", []); + | ^^ cannot infer type + | + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/instrument-xray/flags-always-never-1.rs b/tests/ui/instrument-xray/flags-always-never-1.rs new file mode 100644 index 00000000000..4dd43439eb7 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-1.rs @@ -0,0 +1,7 @@ +// Checks that `-Z instrument-xray` does not allow `always` and `never` simultaneously. +// +// needs-xray +// compile-flags: -Z instrument-xray=always,never +// error-pattern: incorrect value `always,never` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-always-never-1.stderr b/tests/ui/instrument-xray/flags-always-never-1.stderr new file mode 100644 index 00000000000..e211c6f6025 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-1.stderr @@ -0,0 +1,2 @@ +error: incorrect value `always,never` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected + diff --git a/tests/ui/instrument-xray/flags-always-never-2.rs b/tests/ui/instrument-xray/flags-always-never-2.rs new file mode 100644 index 00000000000..7310aa0a0d2 --- /dev/null +++ b/tests/ui/instrument-xray/flags-always-never-2.rs @@ -0,0 +1,9 @@ +// Checks that `-Z instrument-xray` allows `always` and `never` sequentially. +// (The last specified setting wins, like `-Z instrument-xray=no` as well.) +// +// needs-xray +// compile-flags: -Z instrument-xray=always +// compile-flags: -Z instrument-xray=never +// check-pass + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-basic.rs b/tests/ui/instrument-xray/flags-basic.rs new file mode 100644 index 00000000000..b97f0dd8a07 --- /dev/null +++ b/tests/ui/instrument-xray/flags-basic.rs @@ -0,0 +1,9 @@ +// Verifies basic `-Z instrument-xray` flags. +// +// needs-xray +// compile-flags: -Z instrument-xray +// compile-flags: -Z instrument-xray=skip-exit +// compile-flags: -Z instrument-xray=ignore-loops,instruction-threshold=300 +// check-pass + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-always.rs b/tests/ui/instrument-xray/flags-dupe-always.rs new file mode 100644 index 00000000000..407f3e2aa5d --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-always.rs @@ -0,0 +1,7 @@ +// Checks that `-Z instrument-xray` does not allow duplicates. +// +// needs-xray +// compile-flags: -Z instrument-xray=always,always +// error-pattern: incorrect value `always,always` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-always.stderr b/tests/ui/instrument-xray/flags-dupe-always.stderr new file mode 100644 index 00000000000..d1ac113fa43 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-always.stderr @@ -0,0 +1,2 @@ +error: incorrect value `always,always` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected + diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs new file mode 100644 index 00000000000..75b210a6547 --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.rs @@ -0,0 +1,7 @@ +// Checks that `-Z instrument-xray` does not allow duplicates. +// +// needs-xray +// compile-flags: -Z instrument-xray=ignore-loops,ignore-loops +// error-pattern: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` + +fn main() {} diff --git a/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr new file mode 100644 index 00000000000..52f6b33075b --- /dev/null +++ b/tests/ui/instrument-xray/flags-dupe-ignore-loops.stderr @@ -0,0 +1,2 @@ +error: incorrect value `ignore-loops,ignore-loops` for unstable option `instrument-xray` - either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit` was expected + diff --git a/tests/ui/instrument-xray/target-not-supported.rs b/tests/ui/instrument-xray/target-not-supported.rs new file mode 100644 index 00000000000..e6bdd23e8fc --- /dev/null +++ b/tests/ui/instrument-xray/target-not-supported.rs @@ -0,0 +1,9 @@ +// Verifies that `-Z instrument-xray` cannot be used with unsupported targets, +// +// needs-llvm-components: x86 +// compile-flags: -Z instrument-xray --target x86_64-apple-darwin +// error-pattern: error: XRay instrumentation is not supported for this target + +#![feature(no_core)] +#![no_core] +#![no_main] diff --git a/tests/ui/instrument-xray/target-not-supported.stderr b/tests/ui/instrument-xray/target-not-supported.stderr new file mode 100644 index 00000000000..6e3b0c8a380 --- /dev/null +++ b/tests/ui/instrument-xray/target-not-supported.stderr @@ -0,0 +1,4 @@ +error: XRay instrumentation is not supported for this target + +error: aborting due to previous error + diff --git a/tests/ui/issues/issue-10412.stderr b/tests/ui/issues/issue-10412.stderr index 46b9fd541ad..26666782d2a 100644 --- a/tests/ui/issues/issue-10412.stderr +++ b/tests/ui/issues/issue-10412.stderr @@ -46,7 +46,6 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl<'self> Serializable<str> for &'self str { | ^^^^^^^^^^^^^^^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl<'self> Serializable<'_, str> for &'self str { diff --git a/tests/ui/issues/issue-16966.stderr b/tests/ui/issues/issue-16966.stderr index 60f5190dbd0..8c92505b5eb 100644 --- a/tests/ui/issues/issue-16966.stderr +++ b/tests/ui/issues/issue-16966.stderr @@ -1,10 +1,8 @@ error[E0282]: type annotations needed - --> $DIR/issue-16966.rs:2:5 + --> $DIR/issue-16966.rs:2:12 | LL | panic!(std::default::Default::default()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `M` declared on the function `begin_panic` - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type error: aborting due to previous error diff --git a/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs b/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs new file mode 100644 index 00000000000..5d5429ec895 --- /dev/null +++ b/tests/ui/lifetimes/issue-104432-unused-lifetimes-in-expansion.rs @@ -0,0 +1,12 @@ +// check-pass + +#![deny(unused_lifetimes)] +trait Trait2 { + type As; +} + +// we should not warn about an unused lifetime about code generated from this proc macro here +#[derive(Clone)] +struct ShimMethod4<T: Trait2 + 'static>(pub &'static dyn for<'s> Fn(&'s mut T::As)); + +pub fn main() {} diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index 4dfacb93801..30afcecf827 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -40,9 +40,9 @@ error[E0308]: mismatched types LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() } | ---- ^^^^^^^^^^^ expected `&str`, found `Option<&str>` | | - | expected `&'static str` because of return type + | expected `&str` because of return type | - = note: expected reference `&'static str` + = note: expected reference `&str` found enum `Option<&str>` error[E0061]: this function takes 1 argument but 0 arguments were supplied diff --git a/tests/ui/lifetimes/issue-69314.fixed b/tests/ui/lifetimes/issue-69314.fixed new file mode 100644 index 00000000000..41116d4ea61 --- /dev/null +++ b/tests/ui/lifetimes/issue-69314.fixed @@ -0,0 +1,22 @@ +// run-rustfix +// edition:2021 +#![allow(dead_code, unused_mut, unused_variables)] +struct A {} +struct Msg<'a> { + s: &'a [i32], +} +impl A { + async fn g(buf: &[i32]) -> Msg<'_> { + Msg { s: &buf[0..1] } + } + async fn f() { + let mut buf = [0; 512]; + let m2 = &buf[..]; //~ ERROR `buf` does not live long enough + let m = Self::g(m2).await; + Self::f2(m).await; + } + async fn f2(m: Msg<'_>) {} + //~^ ERROR implicit elided lifetime not allowed here +} + +fn main() {} diff --git a/tests/ui/lifetimes/issue-69314.rs b/tests/ui/lifetimes/issue-69314.rs new file mode 100644 index 00000000000..17445341eb6 --- /dev/null +++ b/tests/ui/lifetimes/issue-69314.rs @@ -0,0 +1,22 @@ +// run-rustfix +// edition:2021 +#![allow(dead_code, unused_mut, unused_variables)] +struct A {} +struct Msg<'a> { + s: &'a [i32], +} +impl A { + async fn g(buf: &[i32]) -> Msg<'_> { + Msg { s: &buf[0..1] } + } + async fn f() { + let mut buf = [0; 512]; + let m2 = &buf[..]; //~ ERROR `buf` does not live long enough + let m = Self::g(m2).await; + Self::f2(m).await; + } + async fn f2(m: Msg) {} + //~^ ERROR implicit elided lifetime not allowed here +} + +fn main() {} diff --git a/tests/ui/lifetimes/issue-69314.stderr b/tests/ui/lifetimes/issue-69314.stderr new file mode 100644 index 00000000000..7ae6789285b --- /dev/null +++ b/tests/ui/lifetimes/issue-69314.stderr @@ -0,0 +1,26 @@ +error[E0726]: implicit elided lifetime not allowed here + --> $DIR/issue-69314.rs:18:20 + | +LL | async fn f2(m: Msg) {} + | ^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | async fn f2(m: Msg<'_>) {} + | ++++ + +error[E0597]: `buf` does not live long enough + --> $DIR/issue-69314.rs:14:19 + | +LL | let m2 = &buf[..]; + | ^^^ borrowed value does not live long enough +LL | let m = Self::g(m2).await; + | ----------- argument requires that `buf` is borrowed for `'static` +LL | Self::f2(m).await; +LL | } + | - `buf` dropped here while still borrowed + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0597, E0726. +For more information about an error, try `rustc --explain E0597`. diff --git a/tests/ui/lifetimes/unusual-rib-combinations.rs b/tests/ui/lifetimes/unusual-rib-combinations.rs index b4c86aab863..1c122f42e59 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.rs +++ b/tests/ui/lifetimes/unusual-rib-combinations.rs @@ -23,6 +23,6 @@ fn c<T = u8()>() {} // Elided lifetime in path in ConstGeneric fn d<const C: S>() {} //~^ ERROR missing lifetime specifier -//~| ERROR `S<'static>` is forbidden as the type of a const generic parameter +//~| ERROR `S<'_>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr index 6d7b4250698..68f4fce0178 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.stderr +++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr @@ -46,7 +46,7 @@ LL | fn a() -> [u8; foo::()] { = note: expected type `usize` found fn item `fn() {foo}` -error: `S<'static>` is forbidden as the type of a const generic parameter +error: `S<'_>` is forbidden as the type of a const generic parameter --> $DIR/unusual-rib-combinations.rs:24:15 | LL | fn d<const C: S>() {} diff --git a/tests/ui/lint/reasons-forbidden.rs b/tests/ui/lint/reasons-forbidden.rs index 9c2edec4d52..947099fdd13 100644 --- a/tests/ui/lint/reasons-forbidden.rs +++ b/tests/ui/lint/reasons-forbidden.rs @@ -8,7 +8,7 @@ // // The test is much cleaner if we deduplicate, though. -// compile-flags: -Z deduplicate-diagnostics=yes +// compile-flags: -Z deduplicate-diagnostics=true #![forbid( unsafe_code, diff --git a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr index 9ddea162944..b5231823099 100644 --- a/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr +++ b/tests/ui/mismatched_types/issue-74918-missing-lifetime.stderr @@ -16,9 +16,9 @@ LL | fn next(&mut self) -> Option<IteratorChunk<T, S>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'1, T, S>>` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL | - = note: expected `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'static, T, S>>` + = note: expected `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'2, T, S>>` | - = note: expected signature `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'static, T, S>>` + = note: expected signature `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'2, T, S>>` found signature `fn(&'1 mut ChunkingIterator<T, S>) -> Option<IteratorChunk<'1, T, S>>` = help: the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` = help: verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output diff --git a/tests/ui/modules/issue-107649.rs b/tests/ui/modules/issue-107649.rs new file mode 100644 index 00000000000..71b84cd30d6 --- /dev/null +++ b/tests/ui/modules/issue-107649.rs @@ -0,0 +1,106 @@ +// compile-flags: -Z ui-testing=no +#[path = "auxiliary/dummy_lib.rs"] +mod lib; + +/// The function needs to be long enough to +/// ensure `max_line_num_len` to be large enough +/// for no-ui-testing +fn main() { + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + (); + dbg!(lib::Dummy); //~ Error: `Dummy` doesn't implement `Debug` +} diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr new file mode 100644 index 00000000000..1cea71f2829 --- /dev/null +++ b/tests/ui/modules/issue-107649.stderr @@ -0,0 +1,18 @@ +error[E0277]: `Dummy` doesn't implement `Debug` + --> $DIR/issue-107649.rs:105:5 + | +105 | dbg!(lib::Dummy); + | ^^^^^^^^^^^^^^^^ `Dummy` cannot be formatted using `{:?}` + | + = help: the trait `Debug` is not implemented for `Dummy` + = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy` + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `Dummy` with `#[derive(Debug)]` + --> $DIR/auxiliary/dummy_lib.rs:2:1 + | +2 | #[derive(Debug)] + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs index 066048795c8..0ccd441cc64 100644 --- a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs +++ b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.rs @@ -1,8 +1,11 @@ -// compile-flags: -Zunstable-options --crate-type rlib +// gate-test-packed_bundled_libs + +// ignore-wasm32-bare +// compile-flags: --crate-type rlib +// error-pattern: link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs // build-fail -// error-pattern: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs -#[link(name = "mylib", kind = "static", modifiers = "+bundle,+whole-archive")] -extern "C" { } +#[link(name = "rust_test_helpers", kind = "static", modifiers = "+bundle,+whole-archive")] +extern "C" {} -fn main() { } +fn main() {} diff --git a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr index 246efb8d627..8a9fed740b0 100644 --- a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr +++ b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive-link-attr.stderr @@ -1,6 +1,4 @@ -error: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs +error: link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs -error: could not find native static library `mylib`, perhaps an -L flag is missing? - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs index 1d0768d99cf..18d4b52a34c 100644 --- a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs +++ b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.rs @@ -1,7 +1,8 @@ -// Mixing +bundle and +whole-archive is not allowed +// gate-test-packed_bundled_libs -// compile-flags: -l static:+bundle,+whole-archive=mylib -Zunstable-options --crate-type rlib +// ignore-wasm32-bare +// compile-flags: -l static:+bundle,+whole-archive=rust_test_helpers --crate-type rlib +// error-pattern: link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs // build-fail -// error-pattern: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs -fn main() { } +fn main() {} diff --git a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr index 246efb8d627..8a9fed740b0 100644 --- a/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr +++ b/tests/ui/native-library-link-flags/mix-bundle-and-whole-archive.stderr @@ -1,6 +1,4 @@ -error: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs +error: link modifiers combination `+bundle,+whole-archive` is unstable when generating rlibs -error: could not find native static library `mylib`, perhaps an -L flag is missing? - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/nll/issue-52057.rs b/tests/ui/nll/issue-52057.rs index 98f49fe8f55..5991c1104c8 100644 --- a/tests/ui/nll/issue-52057.rs +++ b/tests/ui/nll/issue-52057.rs @@ -1,6 +1,6 @@ // Regression test for #52057. There is an implied bound -// that `I: 'a` where `'a` is the lifetime of `self` in `parse_first`; -// but to observe that, one must normalize first. +// that `I: 'x` where `'x` is the lifetime of the reference `&mut Self::Input` +// in `parse_first`; but to observe that, one must normalize first. // // run-pass diff --git a/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs b/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs index 79d78da3328..0e487a700b8 100644 --- a/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs +++ b/tests/ui/numbers-arithmetic/next-power-of-two-overflow-debug.rs @@ -1,5 +1,5 @@ // run-pass -// compile-flags: -C debug_assertions=yes +// compile-flags: -C debug_assertions=true // needs-unwind // ignore-emscripten dies with an LLVM error diff --git a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs index da95c1bfa27..a56cd17773d 100644 --- a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs +++ b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.rs @@ -17,7 +17,7 @@ fn test2<T1, T2>(arg1 : T1, arg2 : T2) { fn test3<'a>(arg : &'a u32) { let v : Vec<'a = vec![]; //~^ ERROR: expected one of - //~| ERROR: type annotations needed for `Vec<T>` + //~| ERROR: type annotations needed for `Vec<_>` } fn main() {} diff --git a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr index bad241634cb..b2448774ae9 100644 --- a/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr +++ b/tests/ui/parser/missing-closing-angle-bracket-eq-constraint.stderr @@ -39,26 +39,26 @@ help: you might have meant to end the type parameters here LL | let v : Vec<'a> = vec![]; | + -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:7:7 | LL | let v : Vec<(u32,_) = vec![]; | ^ | -help: consider giving `v` an explicit type, where the type for type parameter `T` is specified +help: consider giving `v` an explicit type, where the placeholders `_` are specified | -LL | let v: Vec<T> : Vec<(u32,_) = vec![]; +LL | let v: Vec<_> : Vec<(u32,_) = vec![]; | ++++++++ -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/missing-closing-angle-bracket-eq-constraint.rs:18:7 | LL | let v : Vec<'a = vec![]; | ^ | -help: consider giving `v` an explicit type, where the type for type parameter `T` is specified +help: consider giving `v` an explicit type, where the placeholders `_` are specified | -LL | let v: Vec<T> : Vec<'a = vec![]; +LL | let v: Vec<_> : Vec<'a = vec![]; | ++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/regions/regions-mock-codegen.rs b/tests/ui/regions/regions-mock-codegen.rs index 9d0ca76e409..d5c93f81fd8 100644 --- a/tests/ui/regions/regions-mock-codegen.rs +++ b/tests/ui/regions/regions-mock-codegen.rs @@ -22,15 +22,15 @@ struct Ccx { x: isize, } -fn allocate(_bcx: &arena) -> &Bcx<'_> { +fn allocate(_bcx: &arena) -> &mut Bcx<'_> { unsafe { let layout = Layout::new::<Bcx>(); let ptr = Global.allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); - &*(ptr.as_ptr() as *const _) + &mut *ptr.as_ptr().cast() } } -fn h<'a>(bcx: &'a Bcx<'a>) -> &'a Bcx<'a> { +fn h<'a>(bcx: &'a Bcx<'a>) -> &'a mut Bcx<'a> { return allocate(bcx.fcx.arena); } @@ -38,7 +38,7 @@ fn g(fcx: &Fcx) { let bcx = Bcx { fcx }; let bcx2 = h(&bcx); unsafe { - Global.deallocate(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::<Bcx>()); + Global.deallocate(NonNull::new_unchecked(bcx2 as *mut _ as *mut _), Layout::new::<Bcx>()); } } diff --git a/tests/ui/rfc-2091-track-caller/call-chain.rs b/tests/ui/rfc-2091-track-caller/call-chain.rs index 28b3f76c9d5..a8814ce2852 100644 --- a/tests/ui/rfc-2091-track-caller/call-chain.rs +++ b/tests/ui/rfc-2091-track-caller/call-chain.rs @@ -1,6 +1,6 @@ // run-pass // revisions: default mir-opt -//[default] compile-flags: -Zinline-mir=no +//[default] compile-flags: -Zinline-mir=false //[mir-opt] compile-flags: -Zmir-opt-level=4 use std::panic::Location; diff --git a/tests/ui/simd/intrinsic/generic-cast-pass.rs b/tests/ui/simd/intrinsic/generic-cast-pass.rs index 15f232e2c0f..89436c83e25 100644 --- a/tests/ui/simd/intrinsic/generic-cast-pass.rs +++ b/tests/ui/simd/intrinsic/generic-cast-pass.rs @@ -1,121 +1,59 @@ // run-pass -#![allow(unused_must_use)] // ignore-emscripten FIXME(#45351) hits an LLVM assert -#![feature(repr_simd, platform_intrinsics, concat_idents, test)] -#![allow(non_camel_case_types)] - -extern crate test; - -#[repr(simd)] -#[derive(PartialEq, Debug)] -struct i32x4(i32, i32, i32, i32); -#[repr(simd)] -#[derive(PartialEq, Debug)] -struct i8x4(i8, i8, i8, i8); - -#[repr(simd)] -#[derive(PartialEq, Debug)] -struct u32x4(u32, u32, u32, u32); -#[repr(simd)] -#[derive(PartialEq, Debug)] -struct u8x4(u8, u8, u8, u8); - -#[repr(simd)] -#[derive(PartialEq, Debug)] -struct f32x4(f32, f32, f32, f32); - -#[repr(simd)] -#[derive(PartialEq, Debug)] -struct f64x4(f64, f64, f64, f64); - +#![feature(repr_simd, platform_intrinsics)] extern "platform-intrinsic" { fn simd_cast<T, U>(x: T) -> U; } -const A: i32 = -1234567; -const B: i32 = 12345678; -const C: i32 = -123456789; -const D: i32 = 1234567890; +use std::cmp::{max, min}; -trait Foo { - fn is_float() -> bool { false } - fn in_range(x: i32) -> bool; -} -impl Foo for i32 { - fn in_range(_: i32) -> bool { true } -} -impl Foo for i8 { - fn in_range(x: i32) -> bool { -128 <= x && x < 128 } -} -impl Foo for u32 { - fn in_range(x: i32) -> bool { 0 <= x } -} -impl Foo for u8 { - fn in_range(x: i32) -> bool { 0 <= x && x < 128 } -} -impl Foo for f32 { - fn is_float() -> bool { true } - fn in_range(_: i32) -> bool { true } -} -impl Foo for f64 { - fn is_float() -> bool { true } - fn in_range(_: i32) -> bool { true } -} +#[derive(Copy, Clone)] +#[repr(simd)] +struct V<T>([T; 2]); fn main() { - macro_rules! test { - ($from: ident, $to: ident) => {{ - // force the casts to actually happen, or else LLVM/rustc - // may fold them and get slightly different results. - let (a, b, c, d) = test::black_box((A as $from, B as $from, C as $from, D as $from)); - // the SIMD vectors are all FOOx4, so we can concat_idents - // so we don't have to pass in the extra args to the macro - let mut from = simd_cast(concat_idents!($from, x4)(a, b, c, d)); - let mut to = concat_idents!($to, x4)(a as $to, - b as $to, - c as $to, - d as $to); - // assist type inference, it needs to know what `from` is - // for the `if` statements. - to == from; + unsafe { + let u = V::<u32>([i16::MIN as u32, i16::MAX as u32]); + let i: V<i16> = simd_cast(u); + assert_eq!(i.0[0], u.0[0] as i16); + assert_eq!(i.0[1], u.0[1] as i16); + } - // there are platform differences for some out of range - // casts, so we just normalize such things: it's OK for - // "invalid" calculations to result in nonsense answers. - // (e.g., negative float to unsigned integer goes through a - // library routine on the default i686 platforms, and the - // implementation of that routine differs on e.g., Linux - // vs. macOS, resulting in different answers.) - if $from::is_float() { - if !$to::in_range(A) { from.0 = 0 as $to; to.0 = 0 as $to; } - if !$to::in_range(B) { from.1 = 0 as $to; to.1 = 0 as $to; } - if !$to::in_range(C) { from.2 = 0 as $to; to.2 = 0 as $to; } - if !$to::in_range(D) { from.3 = 0 as $to; to.3 = 0 as $to; } - } + unsafe { + let f = V::<f32>([i16::MIN as f32, i16::MAX as f32]); + let i: V<i16> = simd_cast(f); + assert_eq!(i.0[0], f.0[0] as i16); + assert_eq!(i.0[1], f.0[1] as i16); + } - assert!(to == from, - "{} -> {} ({:?} != {:?})", stringify!($from), stringify!($to), - from, to); - }} + unsafe { + let f = V::<f32>([u8::MIN as f32, u8::MAX as f32]); + let u: V<u8> = simd_cast(f); + assert_eq!(u.0[0], f.0[0] as u8); + assert_eq!(u.0[1], f.0[1] as u8); } - macro_rules! tests { - (: $($to: ident),*) => { () }; - // repeating the list twice is easier than writing a cartesian - // product macro - ($from: ident $(, $from_: ident)*: $($to: ident),*) => { - fn $from() { unsafe { $( test!($from, $to); )* } } - tests!($($from_),*: $($to),*) - }; - ($($types: ident),*) => {{ - tests!($($types),* : $($types),*); - $($types();)* - }} + + unsafe { + // We would like to do isize::MIN..=isize::MAX, but those values are not representable in + // an f64, so we clamp to the range of an i32 to prevent running into UB. + let f = V::<f64>([ + max(isize::MIN, i32::MIN as isize) as f64, + min(isize::MAX, i32::MAX as isize) as f64, + ]); + let i: V<isize> = simd_cast(f); + assert_eq!(i.0[0], f.0[0] as isize); + assert_eq!(i.0[1], f.0[1] as isize); } - // test various combinations, including truncation, - // signed/unsigned extension, and floating point casts. - tests!(i32, i8, u32, u8, f32); - tests!(i32, u32, f32, f64) + unsafe { + let f = V::<f64>([ + max(usize::MIN, u32::MIN as usize) as f64, + min(usize::MAX, u32::MAX as usize) as f64, + ]); + let u: V<usize> = simd_cast(f); + assert_eq!(u.0[0], f.0[0] as usize); + assert_eq!(u.0[1], f.0[1] as usize); + } } diff --git a/tests/ui/simd/intrinsic/generic-gather-pass.rs b/tests/ui/simd/intrinsic/generic-gather-pass.rs index 805caebe5b1..7d4b3dbd7b4 100644 --- a/tests/ui/simd/intrinsic/generic-gather-pass.rs +++ b/tests/ui/simd/intrinsic/generic-gather-pass.rs @@ -24,9 +24,9 @@ fn main() { // reading from *const unsafe { - let pointer = &x[0] as *const f32; + let pointer = x.as_ptr(); let pointers = x4( - pointer.offset(0) as *const f32, + pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) @@ -39,9 +39,9 @@ fn main() { // reading from *mut unsafe { - let pointer = &mut x[0] as *mut f32; + let pointer = x.as_mut_ptr(); let pointers = x4( - pointer.offset(0) as *mut f32, + pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) @@ -54,9 +54,9 @@ fn main() { // writing to *mut unsafe { - let pointer = &mut x[0] as *mut f32; + let pointer = x.as_mut_ptr(); let pointers = x4( - pointer.offset(0) as *mut f32, + pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) @@ -85,9 +85,9 @@ fn main() { // reading from *const unsafe { - let pointer = &y[0] as *const *const f32; + let pointer = y.as_ptr(); let pointers = x4( - pointer.offset(0) as *const *const f32, + pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) @@ -100,9 +100,9 @@ fn main() { // reading from *mut unsafe { - let pointer = &mut y[0] as *mut *const f32; + let pointer = y.as_mut_ptr(); let pointers = x4( - pointer.offset(0) as *mut *const f32, + pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) @@ -115,9 +115,9 @@ fn main() { // writing to *mut unsafe { - let pointer = &mut y[0] as *mut *const f32; + let pointer = y.as_mut_ptr(); let pointers = x4( - pointer.offset(0) as *mut *const f32, + pointer.offset(0), pointer.offset(2), pointer.offset(4), pointer.offset(6) diff --git a/tests/ui/simd/issue-89193.rs b/tests/ui/simd/issue-89193.rs index 79c4e6a312c..cd24d6675b2 100644 --- a/tests/ui/simd/issue-89193.rs +++ b/tests/ui/simd/issue-89193.rs @@ -17,13 +17,14 @@ extern "platform-intrinsic" { fn main() { let x: [usize; 4] = [10, 11, 12, 13]; let default = x4(0_usize, 1, 2, 3); - let mask = x4(1_i32, 1, 1, 1); + let all_set = u8::MAX as i8; // aka -1 + let mask = x4(all_set, all_set, all_set, all_set); let expected = x4(10_usize, 11, 12, 13); unsafe { - let pointer = &x[0] as *const usize; + let pointer = x.as_ptr(); let pointers = x4( - pointer.offset(0) as *const usize, + pointer.offset(0), pointer.offset(1), pointer.offset(2), pointer.offset(3) @@ -38,9 +39,9 @@ fn main() { let expected = x4(10_isize, 11, 12, 13); unsafe { - let pointer = &x[0] as *const isize; + let pointer = x.as_ptr(); let pointers = x4( - pointer.offset(0) as *const isize, + pointer.offset(0), pointer.offset(1), pointer.offset(2), pointer.offset(3) diff --git a/tests/ui/suggestions/issue-104961.fixed b/tests/ui/suggestions/issue-104961.fixed new file mode 100644 index 00000000000..520d638b174 --- /dev/null +++ b/tests/ui/suggestions/issue-104961.fixed @@ -0,0 +1,16 @@ +// run-rustfix + +fn foo(x: &str) -> bool { + x.starts_with(&("hi".to_string() + " you")) + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn foo2(x: &str) -> bool { + x.starts_with(&"hi".to_string()) + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn main() { + foo("hi you"); + foo2("hi"); +} diff --git a/tests/ui/suggestions/issue-104961.rs b/tests/ui/suggestions/issue-104961.rs new file mode 100644 index 00000000000..aeb787abb6f --- /dev/null +++ b/tests/ui/suggestions/issue-104961.rs @@ -0,0 +1,16 @@ +// run-rustfix + +fn foo(x: &str) -> bool { + x.starts_with("hi".to_string() + " you") + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn foo2(x: &str) -> bool { + x.starts_with("hi".to_string()) + //~^ ERROR expected a `FnMut<(char,)>` closure, found `String` +} + +fn main() { + foo("hi you"); + foo2("hi"); +} diff --git a/tests/ui/suggestions/issue-104961.stderr b/tests/ui/suggestions/issue-104961.stderr new file mode 100644 index 00000000000..8cec6a3f827 --- /dev/null +++ b/tests/ui/suggestions/issue-104961.stderr @@ -0,0 +1,37 @@ +error[E0277]: expected a `FnMut<(char,)>` closure, found `String` + --> $DIR/issue-104961.rs:4:19 + | +LL | x.starts_with("hi".to_string() + " you") + | ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | | + | required by a bound introduced by this call + | + = note: the trait bound `String: Pattern<'_>` is not satisfied + = note: required for `String` to implement `Pattern<'_>` +note: required by a bound in `core::str::<impl str>::starts_with` + --> $SRC_DIR/core/src/str/mod.rs:LL:COL +help: consider borrowing here + | +LL | x.starts_with(&("hi".to_string() + " you")) + | ++ + + +error[E0277]: expected a `FnMut<(char,)>` closure, found `String` + --> $DIR/issue-104961.rs:9:19 + | +LL | x.starts_with("hi".to_string()) + | ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String` + | | + | required by a bound introduced by this call + | + = note: the trait bound `String: Pattern<'_>` is not satisfied + = note: required for `String` to implement `Pattern<'_>` +note: required by a bound in `core::str::<impl str>::starts_with` + --> $SRC_DIR/core/src/str/mod.rs:LL:COL +help: consider borrowing here + | +LL | x.starts_with(&"hi".to_string()) + | + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs new file mode 100644 index 00000000000..657dd9c22c2 --- /dev/null +++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs @@ -0,0 +1,16 @@ +enum E { + One(i32, i32), +} + +fn main() { + let var = E::One; + if let E::One(var1, var2) = var { + //~^ ERROR mismatched types + //~| HELP use parentheses to construct this tuple variant + println!("{var1} {var2}"); + } + + let Some(x) = Some; + //~^ ERROR mismatched types + //~| HELP use parentheses to construct this tuple variant +} diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr new file mode 100644 index 00000000000..7338312bab6 --- /dev/null +++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/suggest-call-on-pat-mismatch.rs:7:12 + | +LL | if let E::One(var1, var2) = var { + | ^^^^^^^^^^^^^^^^^^ --- this expression has type `fn(i32, i32) -> E {E::One}` + | | + | expected enum constructor, found `E` + | + = note: expected enum constructor `fn(i32, i32) -> E {E::One}` + found enum `E` +help: use parentheses to construct this tuple variant + | +LL | if let E::One(var1, var2) = var(/* i32 */, /* i32 */) { + | ++++++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/suggest-call-on-pat-mismatch.rs:13:9 + | +LL | let Some(x) = Some; + | ^^^^^^^ ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}` + | | + | expected enum constructor, found `Option<_>` + | + = note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}` + found enum `Option<_>` +help: use parentheses to construct this tuple variant + | +LL | let Some(x) = Some(/* value */); + | +++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs b/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs new file mode 100644 index 00000000000..9b41a8096c4 --- /dev/null +++ b/tests/ui/traits/alias/issue-107747-do-not-assemble-supertraits.rs @@ -0,0 +1,21 @@ +// Regression test for #107747: methods from trait alias supertraits were brought into scope +// +// check-pass + +#![feature(trait_alias)] + +use std::fmt; + +trait Foo: fmt::Debug {} +trait Bar = Foo; + +#[derive(Debug)] +struct Qux(bool); + +impl fmt::Display for Qux { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/alias_eq_cant_be_furthur_normalized.rs b/tests/ui/traits/new-solver/alias_eq_cant_be_furthur_normalized.rs new file mode 100644 index 00000000000..dc726ba51f9 --- /dev/null +++ b/tests/ui/traits/new-solver/alias_eq_cant_be_furthur_normalized.rs @@ -0,0 +1,29 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +// check that a goal such as `alias-eq(<T as TraitB>::Assoc<bool>, <T as TraitB>::Assoc<?0>)` +// succeeds with a constraint that `?0 = bool` + +// FIXME(deferred_projection_equality): add a test that this is true during coherence + +trait TraitA {} + +trait TraitB { + type Assoc<T: ?Sized>; +} + +impl<T: TraitB> TraitA for (T, T::Assoc<bool>) {} + +impl TraitB for i32 { + type Assoc<T: ?Sized> = u32; +} + +fn needs_a<T: TraitA>() {} + +fn bar<T: TraitB>() { + needs_a::<(T, <T as TraitB>::Assoc<_>)>(); +} + +fn main() { + bar::<i32>(); +} diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs new file mode 100644 index 00000000000..fd5d0e3b194 --- /dev/null +++ b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs @@ -0,0 +1,45 @@ +// compile-flags: -Ztrait-solver=next + +// check that when computing `alias-eq(<() as Foo<u16, T>>::Assoc, <() as Foo<?0, T>>::Assoc)` +// we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by +// relating substs as either could be a valid solution. + +trait Foo<T, STOP> { + type Assoc; +} + +impl<STOP> Foo<u8, STOP> for () +where + (): Foo<u16, STOP>, +{ + type Assoc = <() as Foo<u16, STOP>>::Assoc; +} + +impl Foo<u16, i8> for () { + type Assoc = u8; +} + +impl Foo<u16, i16> for () { + type Assoc = u16; +} + +fn output<T, U>() -> <() as Foo<T, U>>::Assoc +where + (): Foo<T, U>, +{ + todo!() +} + +fn incomplete<T>() +where + (): Foo<u16, T>, +{ + // `<() as Foo<u16, STOP>>::Assoc == <() as Foo<_, STOP>>::Assoc` + let _: <() as Foo<u16, T>>::Assoc = output::<_, T>(); + //~^ error: type annotations needed + + // let _: <() as Foo<u16, T>>::Assoc = output::<u8, T>(); // OK + // let _: <() as Foo<u16, T>>::Assoc = output::<u16, T>(); // OK +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr new file mode 100644 index 00000000000..a6712332c37 --- /dev/null +++ b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/alias_eq_dont_use_normalizes_to_if_substs_eq.rs:38:41 + | +LL | let _: <() as Foo<u16, T>>::Assoc = output::<_, T>(); + | ^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `output` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/new-solver/alias_eq_simple.rs b/tests/ui/traits/new-solver/alias_eq_simple.rs new file mode 100644 index 00000000000..6792cf3ce35 --- /dev/null +++ b/tests/ui/traits/new-solver/alias_eq_simple.rs @@ -0,0 +1,22 @@ +// check-pass +// compile-flags: -Ztrait-solver=next + +// test that the new solver can handle `alias-eq(<i32 as TraitB>::Assoc, u32)` + +trait TraitA {} + +trait TraitB { + type Assoc; +} + +impl<T: TraitB> TraitA for (T, T::Assoc) {} + +impl TraitB for i32 { + type Assoc = u32; +} + +fn needs_a<T: TraitA>() {} + +fn main() { + needs_a::<(i32, u32)>(); +} diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs new file mode 100644 index 00000000000..d4cc380fa21 --- /dev/null +++ b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs @@ -0,0 +1,20 @@ +// compile-flags: -Ztrait-solver=next + +// check that a `alias-eq(<?0 as TraitB>::Assoc, <T as TraitB>::Assoc)` goal fails. + +// FIXME(deferred_projection_equality): add a test that this is true during coherence + +trait TraitB { + type Assoc; +} + +fn needs_a<T: TraitB>() -> T::Assoc { + unimplemented!() +} + +fn bar<T: TraitB>() { + let _: <_ as TraitB>::Assoc = needs_a::<T>(); + //~^ error: type annotations needed +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr new file mode 100644 index 00000000000..d063d8fce11 --- /dev/null +++ b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/alias_eq_substs_eq_not_intercrate.rs:16:12 + | +LL | let _: <_ as TraitB>::Assoc = needs_a::<T>(); + | ^^^^^^^^^^^^^^^^^^^^ cannot infer type for associated type `<_ as TraitB>::Assoc` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/new-solver/elaborate-item-bounds.rs b/tests/ui/traits/new-solver/elaborate-item-bounds.rs new file mode 100644 index 00000000000..076aefcf8fc --- /dev/null +++ b/tests/ui/traits/new-solver/elaborate-item-bounds.rs @@ -0,0 +1,12 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo { + type Bar: Bar; +} + +trait Bar: Baz {} + +trait Baz {} + +fn main() {} diff --git a/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.rs b/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.rs new file mode 100644 index 00000000000..46343241b45 --- /dev/null +++ b/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.rs @@ -0,0 +1,40 @@ +// [no_self_infer] check-pass +// compile-flags: -Ztrait-solver=next +// revisions: self_infer no_self_infer + +// checks that the new solver is smart enough to infer `?0 = U` when solving: +// `normalizes-to(<Vec<?0> as Trait>::Assoc, u8)` +// with `normalizes-to(<Vec<U> as Trait>::Assoc, u8)` in the paramenv even when +// there is a separate `Vec<T>: Trait` bound in the paramenv. +// +// FIXME(-Ztrait-solver=next) +// This could also compile for `normalizes-to(<?0 as Trait>::Assoc, u8)` but +// we currently immediately consider a goal ambiguous if the self type is an +// inference variable. + +trait Trait { + type Assoc; +} + +fn foo<T: Trait<Assoc = u8>>(x: T) {} + +#[cfg(self_infer)] +fn unconstrained<T>() -> T { + todo!() +} + +#[cfg(no_self_infer)] +fn unconstrained<T>() -> Vec<T> { + todo!() +} + +fn bar<T, U>() +where + Vec<T>: Trait, + Vec<U>: Trait<Assoc = u8>, +{ + foo(unconstrained()) + //[self_infer]~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr b/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr new file mode 100644 index 00000000000..06283201261 --- /dev/null +++ b/tests/ui/traits/new-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed + --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:36:5 + | +LL | foo(unconstrained()) + | ^^^ cannot infer type of the type parameter `T` declared on the function `foo` + | +help: consider specifying the generic argument + | +LL | foo::<T>(unconstrained()) + | +++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs new file mode 100644 index 00000000000..bdf999ec5dd --- /dev/null +++ b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs @@ -0,0 +1,25 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl<T> Foo for T { + type Assoc = i32; +} + +impl<T> Bar for T where T: Foo<Assoc = i32> {} + +fn require_bar<T: Bar>() {} + +fn foo<T: Foo>() { + // Unlike the classic solver, `<T as Foo>::Assoc = _` will still project + // down to `i32` even though there's a param-env candidate here, since we + // don't assemble any param-env projection candidates for `T: Foo` alone. + require_bar::<T>(); +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/provisional-result-done.rs b/tests/ui/traits/new-solver/provisional-result-done.rs index a3d97927bad..589d34dd7ab 100644 --- a/tests/ui/traits/new-solver/provisional-result-done.rs +++ b/tests/ui/traits/new-solver/provisional-result-done.rs @@ -1,9 +1,5 @@ -// known-bug: unknown // compile-flags: -Ztrait-solver=next -// failure-status: 101 -// normalize-stderr-test "note: .*\n\n" -> "" -// normalize-stderr-test "thread 'rustc' panicked.*\n" -> "" -// rustc-env:RUST_BACKTRACE=0 +// check-pass // This tests checks that we update results in the provisional cache when // we pop a goal from the stack. diff --git a/tests/ui/traits/new-solver/provisional-result-done.stderr b/tests/ui/traits/new-solver/provisional-result-done.stderr deleted file mode 100644 index ffc92b81f08..00000000000 --- a/tests/ui/traits/new-solver/provisional-result-done.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [check_well_formed] checking that `<impl at $DIR/provisional-result-done.rs:20:1: 20:31>` is well-formed -#1 [check_mod_type_wf] checking that types are well-formed in top-level module -end of query stack diff --git a/tests/ui/traits/new-solver/temporary-ambiguity.rs b/tests/ui/traits/new-solver/temporary-ambiguity.rs new file mode 100644 index 00000000000..18ee0545700 --- /dev/null +++ b/tests/ui/traits/new-solver/temporary-ambiguity.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +// Checks that we don't explode when we assemble >1 candidate for a goal. + +struct Wrapper<T>(T); + +trait Foo {} + +impl Foo for Wrapper<i32> {} + +impl Foo for Wrapper<()> {} + +fn needs_foo(_: impl Foo) {} + +fn main() { + let mut x = Default::default(); + let w = Wrapper(x); + needs_foo(w); + x = 1; + drop(x); +} diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs new file mode 100644 index 00000000000..cde2059ca9b --- /dev/null +++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs @@ -0,0 +1,30 @@ +// compile-flags: -Ztrait-solver=next + +// When we're solving `<T as Foo>::Assoc = i32`, we actually first solve +// `<T as Foo>::Assoc = _#1t`, then unify `_#1t` with `i32`. That goal +// with the inference variable is ambiguous when there are >1 param-env +// candidates. + +// We don't unify the RHS of a projection goal eagerly when solving, both +// for caching reasons and partly to make sure that we don't make the new +// trait solver smarter than it should be. + +// This is (as far as I can tell) a forwards-compatible decision, but if you +// make this test go from fail to pass, be sure you understand the implications! + +trait Foo { + type Assoc; +} + +trait Bar {} + +impl<T> Bar for T where T: Foo<Assoc = i32> {} + +fn needs_bar<T: Bar>() {} + +fn foo<T: Foo<Assoc = i32> + Foo<Assoc = u32>>() { + needs_bar::<T>(); + //~^ ERROR type annotations needed: cannot satisfy `T: Bar` +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr new file mode 100644 index 00000000000..fa5e780ee5e --- /dev/null +++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr @@ -0,0 +1,16 @@ +error[E0283]: type annotations needed: cannot satisfy `T: Bar` + --> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:5 + | +LL | needs_bar::<T>(); + | ^^^^^^^^^^^^^^ + | + = note: cannot satisfy `T: Bar` +note: required by a bound in `needs_bar` + --> $DIR/two-projection-param-candidates-are-ambiguous.rs:23:17 + | +LL | fn needs_bar<T: Bar>() {} + | ^^^ required by this bound in `needs_bar` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs b/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs new file mode 100644 index 00000000000..bcfc747ebb1 --- /dev/null +++ b/tests/ui/traits/new-solver/unsafe-auto-trait-impl.rs @@ -0,0 +1,8 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +struct Foo(*mut ()); + +unsafe impl Sync for Foo {} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs index d3e169a70d3..cdd8f6f1976 100644 --- a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs +++ b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.rs @@ -4,7 +4,7 @@ fn main() { let y = 42; let x = wrong_generic(&y); let z: i32 = x; - //~^ ERROR expected generic type parameter, found `&'static i32 + //~^ ERROR expected generic type parameter, found `&i32` } type WrongGeneric<T> = impl 'static; diff --git a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr index 19115fd2866..fa79e51e9f7 100644 --- a/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr +++ b/tests/ui/type-alias-impl-trait/generic_type_does_not_live_long_enough.stderr @@ -4,7 +4,7 @@ error: at least one trait must be specified LL | type WrongGeneric<T> = impl 'static; | ^^^^^^^^^^^^ -error[E0792]: expected generic type parameter, found `&'static i32` +error[E0792]: expected generic type parameter, found `&i32` --> $DIR/generic_type_does_not_live_long_enough.rs:6:18 | LL | let z: i32 = x; diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr index b63d2a3b61c..09c4b2053b2 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec.stderr @@ -1,12 +1,12 @@ -error[E0282]: type annotations needed for `Vec<T>` +error[E0282]: type annotations needed for `Vec<_>` --> $DIR/cannot_infer_local_or_vec.rs:2:9 | LL | let x = vec![]; | ^ | -help: consider giving `x` an explicit type, where the type for type parameter `T` is specified +help: consider giving `x` an explicit type, where the placeholders `_` are specified | -LL | let x: Vec<T> = vec![]; +LL | let x: Vec<_> = vec![]; | ++++++++ error: aborting due to previous error diff --git a/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr b/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr index e544b369515..1fa253052e6 100644 --- a/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr +++ b/tests/ui/type/type-check/cannot_infer_local_or_vec_in_tuples.stderr @@ -1,12 +1,12 @@ -error[E0282]: type annotations needed for `(Vec<T>,)` +error[E0282]: type annotations needed for `(Vec<_>,)` --> $DIR/cannot_infer_local_or_vec_in_tuples.rs:2:9 | LL | let (x, ) = (vec![], ); | ^^^^^ ---------- type must be known at this point | -help: consider giving this pattern a type, where the type for type parameter `T` is specified +help: consider giving this pattern a type, where the placeholders `_` are specified | -LL | let (x, ): (Vec<T>,) = (vec![], ); +LL | let (x, ): (Vec<_>,) = (vec![], ); | +++++++++++ error: aborting due to previous error diff --git a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs index 4fcf8f403bb..0be5127dcc4 100644 --- a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs +++ b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs @@ -13,5 +13,5 @@ pub struct Ref<'a>(&'a u8); impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here extern "C" { - pub fn repro(_: Wrapper<Ref>); //~ ERROR: incompatible lifetime on type + pub fn repro(_: Wrapper<Ref>); } diff --git a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index 94f6dc26624..b10856571a6 100644 --- a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -4,34 +4,11 @@ error[E0726]: implicit elided lifetime not allowed here LL | impl Trait for Ref {} | ^^^ expected lifetime parameter | - = note: assuming a `'static` lifetime... help: indicate the anonymous lifetime | LL | impl Trait for Ref<'_> {} | ++++ -error: incompatible lifetime on type - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 - | -LL | pub fn repro(_: Wrapper<Ref>); - | ^^^^^^^^^^^^ - | -note: because this has an unmet lifetime requirement - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:8:23 - | -LL | pub struct Wrapper<T: Trait>(T); - | ^^^^^ introduces a `'static` lifetime requirement -note: the anonymous lifetime as defined here... - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:29 - | -LL | pub fn repro(_: Wrapper<Ref>); - | ^^^ -note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl` - --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:1 - | -LL | impl Trait for Ref {} - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0726`. |
