diff options
| author | bors <bors@rust-lang.org> | 2024-10-20 05:57:13 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-10-20 05:57:13 +0000 |
| commit | 45a9a7ca5aba461c91afd0423cbf567853bae847 (patch) | |
| tree | 7aebdb581d6b5aa4a67efc5c52d84cc0bf58e802 | |
| parent | 3386ae21ccab4303aa94bf71e2840f9634ffcb13 (diff) | |
| parent | ccd680ee01564d32cad783a94c4dcccf228922c1 (diff) | |
| download | rust-45a9a7ca5aba461c91afd0423cbf567853bae847.tar.gz rust-45a9a7ca5aba461c91afd0423cbf567853bae847.zip | |
Auto merge of #3980 - rust-lang:rustup-2024-10-20, r=RalfJung
Automatic Rustup
675 files changed, 10403 insertions, 4839 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 003c1e5d7eb..2fca71716c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,6 +104,18 @@ jobs: with: fetch-depth: 2 + # Free up disk space on Linux by removing preinstalled components that + # we do not need. We do this to enable some of the less resource + # intensive jobs to run on free runners, which however also have + # less disk space. + - name: free up disk space + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be + if: contains(matrix.os, 'ubuntu') + with: + # Removing packages with APT saves ~5 GiB, but takes several + # minutes (and potentially removes important packages). + large-packages: false + # Rust Log Analyzer can't currently detect the PR number of a GitHub # Actions build on its own, so a hint in the log message is needed to # point it in the right direction. @@ -194,6 +206,11 @@ jobs: - name: create github artifacts run: src/ci/scripts/create-doc-artifacts.sh + - name: print disk usage + run: | + echo "disk usage:" + df -h + - name: upload artifacts to github uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index b170dca88fa..948133cd76e 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,8 @@ build/ /src/tools/x/target # Created by default with `src/ci/docker/run.sh` /obj/ +# Created by nix dev shell / .envrc +src/tools/nix-dev-shell/flake.lock ## ICE reports rustc-ice-*.txt diff --git a/.mailmap b/.mailmap index bdc34c52aa7..56490ca5059 100644 --- a/.mailmap +++ b/.mailmap @@ -256,6 +256,7 @@ Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakub.bukaj@yahoo.com> Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakub@jakub.cc> Jakub Adam Wieczorek <jakub.adam.wieczorek@gmail.com> <jakubw@jakubw.net> +Jakub Beránek <berykubik@gmail.com> <jakub.beranek@vsb.cz> James [Undefined] <tpzker@thepuzzlemaker.info> James Deng <cnjamesdeng@gmail.com> <cnJamesDeng@gmail.com> James Hinshelwood <jameshinshelwood1@gmail.com> <james.hinshelwood@bigpayme.com> diff --git a/Cargo.lock b/Cargo.lock index 5f81a5a8496..f3a505b6c16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,7 +377,7 @@ dependencies = [ "cargo_metadata", "directories", "rustc-build-sysroot", - "rustc_tools_util 0.4.0", + "rustc_tools_util", "rustc_version", "serde", "serde_json", @@ -537,7 +537,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clippy" -version = "0.1.83" +version = "0.1.84" dependencies = [ "anstream", "cargo_metadata", @@ -550,9 +550,11 @@ dependencies = [ "if_chain", "itertools", "parking_lot", + "pulldown-cmark 0.11.3", "quote", "regex", - "rustc_tools_util 0.3.0", + "rinja", + "rustc_tools_util", "serde", "serde_json", "syn 2.0.79", @@ -566,7 +568,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.83" +version = "0.1.84" dependencies = [ "itertools", "serde", @@ -582,20 +584,19 @@ dependencies = [ "clap", "indoc", "itertools", - "opener 0.6.1", + "opener", "shell-escape", "walkdir", ] [[package]] name = "clippy_lints" -version = "0.1.83" +version = "0.1.84" dependencies = [ "arrayvec", "cargo_metadata", "clippy_config", "clippy_utils", - "declare_clippy_lint", "itertools", "quine-mc_cluskey", "regex", @@ -613,7 +614,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.83" +version = "0.1.84" dependencies = [ "arrayvec", "clippy_config", @@ -920,15 +921,6 @@ dependencies = [ ] [[package]] -name = "declare_clippy_lint" -version = "0.1.83" -dependencies = [ - "itertools", - "quote", - "syn 2.0.79", -] - -[[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2163,7 +2155,7 @@ dependencies = [ "log", "memchr", "once_cell", - "opener 0.7.2", + "opener", "pulldown-cmark 0.10.3", "regex", "serde", @@ -2503,17 +2495,6 @@ dependencies = [ [[package]] name = "opener" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" -dependencies = [ - "bstr", - "normpath", - "winapi", -] - -[[package]] -name = "opener" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0812e5e4df08da354c851a3376fead46db31c2214f849d3de356d774d057681" @@ -2879,6 +2860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" dependencies = [ "bitflags 2.6.0", + "getopts", "memchr", "pulldown-cmark-escape 0.11.0", "unicase", @@ -4462,12 +4444,6 @@ dependencies = [ [[package]] name = "rustc_tools_util" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" - -[[package]] -name = "rustc_tools_util" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3316159ab19e19d1065ecc49278e87f767a9dae9fae80348d2b4d4fa4ae02d4d" @@ -4608,6 +4584,7 @@ dependencies = [ "rustdoc-json-types", "serde", "serde_json", + "sha2", "smallvec", "tempfile", "threadpool", @@ -5594,13 +5571,6 @@ dependencies = [ ] [[package]] -name = "unicode-bdd" -version = "0.1.0" -dependencies = [ - "ucd-parse", -] - -[[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5650,6 +5620,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] +name = "unicode-table-generator" +version = "0.1.0" +dependencies = [ + "ucd-parse", +] + +[[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/RELEASES.md b/RELEASES.md index 1213a596024..ac72a1d885c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -123,7 +123,6 @@ Stabilized APIs These APIs are now stable in const contexts: - [`std::task::Waker::from_raw`](https://doc.rust-lang.org/nightly/std/task/struct.Waker.html#method.from_raw) -- [`std::task::Waker::waker`](https://doc.rust-lang.org/nightly/std/task/struct.Waker.html#method.from_raw) - [`std::task::Context::from_waker`](https://doc.rust-lang.org/nightly/std/task/struct.Context.html#method.from_waker) - [`std::task::Context::waker`](https://doc.rust-lang.org/nightly/std/task/struct.Context.html#method.waker) - [`$integer::from_str_radix`](https://doc.rust-lang.org/nightly/std/primitive.u32.html#method.from_str_radix) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 6e1299944a0..59f42425bb9 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -39,7 +39,7 @@ enum NicheBias { End, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LayoutCalculatorError<F> { /// An unsized type was found in a location where a sized type was expected. /// @@ -54,6 +54,36 @@ pub enum LayoutCalculatorError<F> { /// A union had no fields. EmptyUnion, + + /// The fields or variants have irreconcilable reprs + ReprConflict, +} + +impl<F> LayoutCalculatorError<F> { + pub fn without_payload(&self) -> LayoutCalculatorError<()> { + match self { + LayoutCalculatorError::UnexpectedUnsized(_) => { + LayoutCalculatorError::UnexpectedUnsized(()) + } + LayoutCalculatorError::SizeOverflow => LayoutCalculatorError::SizeOverflow, + LayoutCalculatorError::EmptyUnion => LayoutCalculatorError::EmptyUnion, + LayoutCalculatorError::ReprConflict => LayoutCalculatorError::ReprConflict, + } + } + + /// Format an untranslated diagnostic for this type + /// + /// Intended for use by rust-analyzer, as neither it nor `rustc_abi` depend on fluent infra. + pub fn fallback_fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + LayoutCalculatorError::UnexpectedUnsized(_) => { + "an unsized type was found where a sized type was expected" + } + LayoutCalculatorError::SizeOverflow => "size overflow", + LayoutCalculatorError::EmptyUnion => "type is a union with no fields", + LayoutCalculatorError::ReprConflict => "type has an invalid repr", + }) + } } type LayoutCalculatorResult<FieldIdx, VariantIdx, F> = @@ -489,6 +519,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { } let dl = self.cx.data_layout(); + // bail if the enum has an incoherent repr that cannot be computed + if repr.packed() { + return Err(LayoutCalculatorError::ReprConflict); + } let calculate_niche_filling_layout = || -> Option<TmpLayout<FieldIdx, VariantIdx>> { if dont_niche_optimize_enum { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ce744cc56e1..7416a1e39eb 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1574,7 +1574,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .collect(); // Introduce extra lifetimes if late resolution tells us to. - let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id); + let extra_lifetimes = self.resolver.extra_lifetime_params(parent_node_id); params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { self.lifetime_res_to_generic_param( ident, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 00eafeb4d84..4d8d22e09d9 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -268,8 +268,8 @@ impl ResolverAstLowering { /// /// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring /// should appear at the enclosing `PolyTraitRef`. - fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { - self.extra_lifetime_params_map.remove(&id).unwrap_or_default() + fn extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { + self.extra_lifetime_params_map.get(&id).cloned().unwrap_or_default() } } @@ -885,7 +885,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut generic_params: Vec<_> = self .lower_generic_params_mut(generic_params, hir::GenericParamSource::Binder) .collect(); - let extra_lifetimes = self.resolver.take_extra_lifetime_params(binder); + let extra_lifetimes = self.resolver.extra_lifetime_params(binder); debug!(?extra_lifetimes); generic_params.extend(extra_lifetimes.into_iter().filter_map(|(ident, node_id, res)| { self.lifetime_res_to_generic_param(ident, node_id, res, hir::GenericParamSource::Binder) @@ -1495,62 +1495,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - let captured_lifetimes_to_duplicate = if let Some(args) = - // We only look for one `use<...>` syntax since we syntactially reject more than one. - bounds.iter().find_map( - |bound| match bound { - ast::GenericBound::Use(a, _) => Some(a), - _ => None, - }, - ) { - // We'll actually validate these later on; all we need is the list of - // lifetimes to duplicate during this portion of lowering. - args.iter() - .filter_map(|arg| match arg { - PreciseCapturingArg::Lifetime(lt) => Some(*lt), - PreciseCapturingArg::Arg(..) => None, - }) - // Add in all the lifetimes mentioned in the bounds. We will error - // them out later, but capturing them here is important to make sure - // they actually get resolved in resolve_bound_vars. - .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)) - .collect() - } else { - match origin { - hir::OpaqueTyOrigin::TyAlias { .. } => { - // type alias impl trait and associated type position impl trait were - // decided to capture all in-scope lifetimes, which we collect for - // all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } - hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => { - if in_trait_or_impl.is_some() - || self.tcx.features().lifetime_capture_rules_2024 - || span.at_least_rust_2024() - { - // return-position impl trait in trait was decided to capture all - // in-scope lifetimes, which we collect for all opaques during resolution. - self.resolver - .take_extra_lifetime_params(opaque_ty_node_id) - .into_iter() - .map(|(ident, id, _)| Lifetime { id, ident }) - .collect() - } else { - // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` - // example, we only need to duplicate lifetimes that appear in the - // bounds, since those are the only ones that are captured by the opaque. - lifetime_collector::lifetimes_in_bounds(self.resolver, bounds) - } - } - hir::OpaqueTyOrigin::AsyncFn { .. } => { - unreachable!("should be using `lower_async_fn_ret_ty`") - } + // Whether this opaque always captures lifetimes in scope. + // Right now, this is all RPITIT and TAITs, and when `lifetime_capture_rules_2024` + // is enabled. We don't check the span of the edition, since this is done + // on a per-opaque basis to account for nested opaques. + let always_capture_in_scope = match origin { + _ if self.tcx.features().lifetime_capture_rules_2024 => true, + hir::OpaqueTyOrigin::TyAlias { .. } => true, + hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(), + hir::OpaqueTyOrigin::AsyncFn { .. } => { + unreachable!("should be using `lower_coroutine_fn_ret_ty`") } }; + let captured_lifetimes_to_duplicate = lifetime_collector::lifetimes_for_opaque( + self.resolver, + always_capture_in_scope, + opaque_ty_node_id, + bounds, + span, + ); debug!(?captured_lifetimes_to_duplicate); // Feature gate for RPITIT + use<..> @@ -1920,7 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let captured_lifetimes = self .resolver - .take_extra_lifetime_params(opaque_ty_node_id) + .extra_lifetime_params(opaque_ty_node_id) .into_iter() .map(|(ident, id, _)| Lifetime { id, ident }) .collect(); diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index fe64160fb4d..8d47c856bdd 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -1,5 +1,7 @@ use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor}; -use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind}; +use rustc_ast::{ + GenericBound, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind, +}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::{DefKind, LifetimeRes, Res}; use rustc_middle::span_bug; @@ -10,14 +12,41 @@ use rustc_span::symbol::{Ident, kw}; use super::ResolverAstLoweringExt; struct LifetimeCollectVisitor<'ast> { - resolver: &'ast ResolverAstLowering, + resolver: &'ast mut ResolverAstLowering, + always_capture_in_scope: bool, current_binders: Vec<NodeId>, collected_lifetimes: FxIndexSet<Lifetime>, } impl<'ast> LifetimeCollectVisitor<'ast> { - fn new(resolver: &'ast ResolverAstLowering) -> Self { - Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() } + fn new(resolver: &'ast mut ResolverAstLowering, always_capture_in_scope: bool) -> Self { + Self { + resolver, + always_capture_in_scope, + current_binders: Vec::new(), + collected_lifetimes: FxIndexSet::default(), + } + } + + fn visit_opaque(&mut self, opaque_ty_node_id: NodeId, bounds: &'ast GenericBounds, span: Span) { + // If we're edition 2024 or within a TAIT or RPITIT, *and* there is no + // `use<>` statement to override the default capture behavior, then + // capture all of the in-scope lifetimes. + if (self.always_capture_in_scope || span.at_least_rust_2024()) + && bounds.iter().all(|bound| !matches!(bound, GenericBound::Use(..))) + { + for (ident, id, _) in self.resolver.extra_lifetime_params(opaque_ty_node_id) { + self.record_lifetime_use(Lifetime { id, ident }); + } + } + + // We also recurse on the bounds to make sure we capture all the lifetimes + // mentioned in the bounds. These may disagree with the `use<>` list, in which + // case we will error on these later. We will also recurse to visit any + // nested opaques, which may *implicitly* capture lifetimes. + for bound in bounds { + self.visit_param_bound(bound, BoundKind::Bound); + } } fn record_lifetime_use(&mut self, lifetime: Lifetime) { @@ -99,6 +128,9 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { self.record_elided_anchor(t.id, t.span); visit::walk_ty(self, t); } + TyKind::ImplTrait(opaque_ty_node_id, bounds) => { + self.visit_opaque(*opaque_ty_node_id, bounds, t.span) + } _ => { visit::walk_ty(self, t); } @@ -106,13 +138,14 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { } } -pub(crate) fn lifetimes_in_bounds( - resolver: &ResolverAstLowering, +pub(crate) fn lifetimes_for_opaque( + resolver: &mut ResolverAstLowering, + always_capture_in_scope: bool, + opaque_ty_node_id: NodeId, bounds: &GenericBounds, + span: Span, ) -> FxIndexSet<Lifetime> { - let mut visitor = LifetimeCollectVisitor::new(resolver); - for bound in bounds { - visitor.visit_param_bound(bound, BoundKind::Bound); - } + let mut visitor = LifetimeCollectVisitor::new(resolver, always_capture_in_scope); + visitor.visit_opaque(opaque_ty_node_id, bounds, span); visitor.collected_lifetimes } diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 40a6d506ffa..2437a43bd5a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -3,12 +3,16 @@ use std::rc::Rc; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_infer::infer::{ InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt as _, }; use rustc_infer::traits::ObligationCause; +use rustc_infer::traits::query::{ + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, + CanonicalTypeOpProvePredicateGoal, +}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, RePlaceholder, Region, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex, @@ -95,9 +99,7 @@ impl<'tcx> ToUniverseInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tc } } -impl<'tcx> ToUniverseInfo<'tcx> - for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>> -{ +impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpProvePredicateGoal<'tcx> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(PredicateQuery { canonical_query: self, @@ -107,7 +109,7 @@ impl<'tcx> ToUniverseInfo<'tcx> } impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUniverseInfo<'tcx> - for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>> + for CanonicalTypeOpNormalizeGoal<'tcx, T> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(NormalizeQuery { @@ -117,9 +119,7 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable<TyCtxt<'tcx>> + 'tcx> ToUnivers } } -impl<'tcx> ToUniverseInfo<'tcx> - for Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>> -{ +impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpAscribeUserTypeGoal<'tcx> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo(UniverseInfoInner::TypeOp(Rc::new(AscribeUserTypeQuery { canonical_query: self, @@ -128,7 +128,7 @@ impl<'tcx> ToUniverseInfo<'tcx> } } -impl<'tcx, F> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::CustomTypeOp<F>> { +impl<'tcx, F> ToUniverseInfo<'tcx> for CanonicalQueryInput<'tcx, type_op::custom::CustomTypeOp<F>> { fn to_universe_info(self, _base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { // We can't rerun custom type ops. UniverseInfo::other() @@ -211,8 +211,7 @@ trait TypeOpInfo<'tcx> { } struct PredicateQuery<'tcx> { - canonical_query: - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::prove_predicate::ProvePredicate<'tcx>>>, + canonical_query: CanonicalTypeOpProvePredicateGoal<'tcx>, base_universe: ty::UniverseIndex, } @@ -220,7 +219,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { tcx.dcx().create_err(HigherRankedLifetimeError { cause: Some(HigherRankedErrorCause::CouldNotProve { - predicate: self.canonical_query.value.value.predicate.to_string(), + predicate: self.canonical_query.canonical.value.value.predicate.to_string(), }), span, }) @@ -253,7 +252,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> { } struct NormalizeQuery<'tcx, T> { - canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>, + canonical_query: CanonicalTypeOpNormalizeGoal<'tcx, T>, base_universe: ty::UniverseIndex, } @@ -264,7 +263,7 @@ where fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { tcx.dcx().create_err(HigherRankedLifetimeError { cause: Some(HigherRankedErrorCause::CouldNotNormalize { - value: self.canonical_query.value.value.value.to_string(), + value: self.canonical_query.canonical.value.value.value.to_string(), }), span, }) @@ -306,7 +305,7 @@ where } struct AscribeUserTypeQuery<'tcx> { - canonical_query: Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>, + canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>, base_universe: ty::UniverseIndex, } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 20ecc665b1e..a5f3298b02c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1146,6 +1146,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // don't create labels for compiler-generated spans Some(_) => None, + // don't create labels for the span not from user's code + None if opt_assignment_rhs_span + .is_some_and(|span| self.infcx.tcx.sess.source_map().is_imported(span)) => + { + None + } None => { let (has_sugg, decl_span, sugg) = if name != kw::SelfLower { suggest_ampmut( @@ -1198,18 +1204,21 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { sugg.push(s); } - err.multipart_suggestion_verbose( - format!( - "consider changing this to be a mutable {pointer_desc}{}", - if is_trait_sig { - " in the `impl` method and the `trait` definition" - } else { - "" - } - ), - sugg, - Applicability::MachineApplicable, - ); + if sugg.iter().all(|(span, _)| !self.infcx.tcx.sess.source_map().is_imported(*span)) + { + err.multipart_suggestion_verbose( + format!( + "consider changing this to be a mutable {pointer_desc}{}", + if is_trait_sig { + " in the `impl` method and the `trait` definition" + } else { + "" + } + ), + sugg, + Applicability::MachineApplicable, + ); + } } Some((false, err_label_span, message, _)) => { let def_id = self.body.source.def_id(); diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 0c6f8cd7b5b..fde68615cc0 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( locations, category, - param_env.and(type_op::prove_predicate::ProvePredicate::new(predicate)), + param_env.and(type_op::prove_predicate::ProvePredicate { predicate }), ); } @@ -162,7 +162,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let result: Result<_, ErrorGuaranteed> = self.fully_perform_op( location.to_locations(), category, - param_env.and(type_op::normalize::Normalize::new(value)), + param_env.and(type_op::normalize::Normalize { value }), ); result.unwrap_or(value) } @@ -223,7 +223,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( Locations::All(span), ConstraintCategory::Boring, - self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)), + self.param_env.and(type_op::ascribe_user_type::AscribeUserType { mir_ty, user_ty }), ); } 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 cded9935f97..8e1faf025e2 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -280,7 +280,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self .param_env - .and(type_op::normalize::Normalize::new(ty)) + .and(type_op::normalize::Normalize { value: ty }) .fully_perform(self.infcx, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), @@ -318,7 +318,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { let result: Result<_, ErrorGuaranteed> = self .param_env - .and(type_op::normalize::Normalize::new(ty)) + .and(type_op::normalize::Normalize { value: ty }) .fully_perform(self.infcx, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; @@ -373,7 +373,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .param_env - .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) + .and(type_op::ImpliedOutlivesBounds { ty }) .fully_perform(self.infcx, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index a5175e653d8..35963228181 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -11,8 +11,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex}; use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use rustc_span::DUMMY_SP; -use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives; -use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput}; use tracing::debug; use crate::location::RichLocation; @@ -632,7 +631,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { match typeck .param_env - .and(DropckOutlives::new(dropped_ty)) + .and(DropckOutlives { dropped_ty }) .fully_perform(typeck.infcx, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 5c297ebfadb..336934354e1 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -34,7 +34,9 @@ pub(crate) fn unsized_info<'tcx>( { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); - if data_a.principal_def_id() == data_b.principal_def_id() { + let b_principal_def_id = data_b.principal_def_id(); + if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { + // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. debug_assert!( validate_trivial_unsize(fx.tcx, data_a, data_b), "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index c2c261da79b..cee704a9c22 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,3 +1,5 @@ +use std::ffi::CStr; + use itertools::Itertools as _; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -132,7 +134,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) { .collect::<Vec<_>>(); let initializer = cx.const_array(cx.type_ptr(), &name_globals); - let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), "__llvm_coverage_names"); + let array = llvm::add_global(cx.llmod, cx.val_ty(initializer), c"__llvm_coverage_names"); llvm::set_global_constant(array, true); llvm::set_linkage(array, llvm::Linkage::InternalLinkage); llvm::set_initializer(array, initializer); @@ -305,7 +307,7 @@ fn generate_coverage_map<'ll>( /// specific, well-known section and name. fn save_function_record( cx: &CodegenCx<'_, '_>, - covfun_section_name: &str, + covfun_section_name: &CStr, mangled_function_name: &str, source_hash: u64, filenames_ref: u64, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index d7d29eebf85..484a4d00c13 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::ffi::{CStr, CString}; use libc::c_uint; use rustc_codegen_ssa::traits::{ @@ -12,6 +13,7 @@ use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::HasTyCtxt; use rustc_target::abi::{Align, Size}; +use rustc_target::spec::HasTargetSpec; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -284,16 +286,16 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, cov_data_val: &'ll llvm::Value, ) { - let covmap_var_name = llvm::build_string(|s| unsafe { + let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe { llvm::LLVMRustCoverageWriteMappingVarNameToString(s); - }) - .expect("Rust Coverage Mapping var name failed UTF-8 conversion"); + })) + .unwrap(); debug!("covmap var name: {:?}", covmap_var_name); - let covmap_section_name = llvm::build_string(|s| unsafe { + let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe { llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage section name failed UTF-8 conversion"); + })) + .expect("covmap section name should not contain NUL"); debug!("covmap section name: {:?}", covmap_section_name); let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name); @@ -308,7 +310,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, - covfun_section_name: &str, + covfun_section_name: &CStr, func_name_hash: u64, func_record_val: &'ll llvm::Value, is_used: bool, @@ -322,7 +324,8 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( // of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by // appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging. let func_record_var_name = - format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }); + CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" })) + .unwrap(); debug!("function record var name: {:?}", func_record_var_name); debug!("function record section name: {:?}", covfun_section_name); @@ -334,7 +337,9 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( llvm::set_section(llglobal, covfun_section_name); // LLVM's coverage mapping format specifies 8-byte alignment for items in this section. llvm::set_alignment(llglobal, Align::EIGHT); - llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); + if cx.target_spec().supports_comdat() { + llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); + } cx.add_used_global(llglobal); } @@ -349,9 +354,9 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( /// - `__llvm_covfun` on Linux /// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix) /// - `.lcovfun$M` on Windows (includes `$M` sorting suffix) -pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String { - llvm::build_string(|s| unsafe { +pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> CString { + CString::new(llvm::build_byte_buffer(|s| unsafe { llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage function record section name failed UTF-8 conversion") + })) + .expect("covfun section name should not contain NUL") } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index bfe623e7fc3..64f1d21b438 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -787,7 +787,9 @@ fn codegen_msvc_try<'ll>( let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); unsafe { llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); - llvm::SetUniqueComdat(bx.llmod, tydesc); + if bx.cx.tcx.sess.target.supports_comdat() { + llvm::SetUniqueComdat(bx.llmod, tydesc); + } llvm::LLVMSetInitializer(tydesc, type_info); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 661debbb9f1..d0034de06c7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -646,6 +646,7 @@ unsafe extern "C" { pub type Attribute; pub type Metadata; pub type BasicBlock; + pub type Comdat; } #[repr(C)] pub struct Builder<'a>(InvariantOpaque<'a>); @@ -1490,6 +1491,9 @@ unsafe extern "C" { pub fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; + + pub fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + pub fn LLVMSetComdat(V: &Value, C: &Comdat); } #[link(name = "llvm-wrapper", kind = "static")] @@ -2320,7 +2324,6 @@ unsafe extern "C" { pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); - pub fn LLVMRustSetComdat<'a>(M: &'a Module, V: &'a Value, Name: *const c_char, NameLen: size_t); pub fn LLVMRustSetModulePICLevel(M: &Module); pub fn LLVMRustSetModulePIELevel(M: &Module); pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index d0db350a149..e837022044e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -178,10 +178,10 @@ pub fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { // function. // For more details on COMDAT sections see e.g., https://www.airs.com/blog/archives/52 pub fn SetUniqueComdat(llmod: &Module, val: &Value) { - unsafe { - let name = get_value_name(val); - LLVMRustSetComdat(llmod, val, name.as_ptr().cast(), name.len()); - } + let name_buf = get_value_name(val).to_vec(); + let name = + CString::from_vec_with_nul(name_buf).or_else(|buf| CString::new(buf.into_bytes())).unwrap(); + set_comdat(llmod, val, &name); } pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { @@ -210,15 +210,13 @@ impl MemoryEffects { } } -pub fn set_section(llglobal: &Value, section_name: &str) { - let section_name_cstr = CString::new(section_name).expect("unexpected CString error"); +pub fn set_section(llglobal: &Value, section_name: &CStr) { unsafe { - LLVMSetSection(llglobal, section_name_cstr.as_ptr()); + LLVMSetSection(llglobal, section_name.as_ptr()); } } -pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name: &str) -> &'a Value { - let name_cstr = CString::new(name).expect("unexpected CString error"); +pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name_cstr: &CStr) -> &'a Value { unsafe { LLVMAddGlobal(llmod, ty, name_cstr.as_ptr()) } } @@ -252,9 +250,14 @@ pub fn set_alignment(llglobal: &Value, align: Align) { } } -pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &str) { +/// Get the `name`d comdat from `llmod` and assign it to `llglobal`. +/// +/// Inserts the comdat into `llmod` if it does not exist. +/// It is an error to call this if the target does not support comdat. +pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { unsafe { - LLVMRustSetComdat(llmod, llglobal, name.as_ptr().cast(), name.len()); + let comdat = LLVMGetOrInsertComdat(llmod, name.as_ptr()); + LLVMSetComdat(llglobal, comdat); } } diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 02e1995620b..bf6ef219873 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -64,7 +64,9 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); base::set_link_section(lldecl, attrs); - if linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR { + if (linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR) + && self.tcx.sess.target.supports_comdat() + { llvm::SetUniqueComdat(self.llmod, lldecl); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d91c0f0790d..f3d9a7d37e6 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -147,7 +147,7 @@ pub fn validate_trivial_unsize<'tcx>( infcx.leak_check(universe, None).is_ok() }) } - (None, None) => true, + (_, None) => true, _ => false, } } @@ -175,7 +175,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); - if data_a.principal_def_id() == data_b.principal_def_id() { + let b_principal_def_id = data_b.principal_def_id(); + if data_a.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { // Codegen takes advantage of the additional assumption, where if the // principal trait def id of what's being casted doesn't change, // then we don't need to adjust the vtable at all. This diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index fd05664e2f2..6686413bf02 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -11,7 +11,8 @@ use rustc_span::{Span, Symbol}; use super::CompileTimeMachine; use crate::errors::{self, FrameNote, ReportErrorExt}; use crate::interpret::{ - ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType, err_inval, err_machine_stop, + ErrorHandled, Frame, InterpErrorInfo, InterpErrorKind, MachineStopType, err_inval, + err_machine_stop, }; /// The CTFE machine has some custom error kinds. @@ -57,7 +58,7 @@ impl MachineStopType for ConstEvalErrKind { } } -/// The errors become [`InterpError::MachineStop`] when being raised. +/// The errors become [`InterpErrorKind::MachineStop`] when being raised. impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind { fn into(self) -> InterpErrorInfo<'tcx> { err_machine_stop!(self).into() @@ -124,7 +125,7 @@ pub fn get_span_and_frames<'tcx>( /// `get_span_and_frames`. pub(super) fn report<'tcx, C, F, E>( tcx: TyCtxt<'tcx>, - error: InterpError<'tcx>, + error: InterpErrorKind<'tcx>, span: Span, get_span_and_frames: C, mk: F, 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 672353e629d..7319c251bbd 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,7 +18,7 @@ use tracing::{debug, instrument, trace}; use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine}; use crate::const_eval::CheckAlignment; use crate::interpret::{ - CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpError, + CtfeValidationMode, GlobalId, Immediate, InternKind, InternResult, InterpCx, InterpErrorKind, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, StackPopCleanup, create_static_alloc, eval_nullary_intrinsic, intern_const_alloc_recursive, interp_ok, throw_exhaust, }; @@ -463,7 +463,7 @@ fn report_validation_error<'tcx>( error: InterpErrorInfo<'tcx>, alloc_id: AllocId, ) -> ErrorHandled { - if !matches!(error.kind(), InterpError::UndefinedBehavior(_)) { + if !matches!(error.kind(), InterpErrorKind::UndefinedBehavior(_)) { // Some other error happened during validation, e.g. an unsupported operation. return report_eval_error(ecx, cid, error); } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index c943236affc..211668cf055 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{ use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::interpret::{ - CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpError, InvalidMetaKind, + CheckInAllocMsg, CtfeProvenance, ExpectedKind, InterpErrorKind, InvalidMetaKind, InvalidProgramInfo, Misalignment, Pointer, PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, }; @@ -835,23 +835,23 @@ impl ReportErrorExt for UnsupportedOpInfo { } } -impl<'tcx> ReportErrorExt for InterpError<'tcx> { +impl<'tcx> ReportErrorExt for InterpErrorKind<'tcx> { fn diagnostic_message(&self) -> DiagMessage { match self { - InterpError::UndefinedBehavior(ub) => ub.diagnostic_message(), - InterpError::Unsupported(e) => e.diagnostic_message(), - InterpError::InvalidProgram(e) => e.diagnostic_message(), - InterpError::ResourceExhaustion(e) => e.diagnostic_message(), - InterpError::MachineStop(e) => e.diagnostic_message(), + InterpErrorKind::UndefinedBehavior(ub) => ub.diagnostic_message(), + InterpErrorKind::Unsupported(e) => e.diagnostic_message(), + InterpErrorKind::InvalidProgram(e) => e.diagnostic_message(), + InterpErrorKind::ResourceExhaustion(e) => e.diagnostic_message(), + InterpErrorKind::MachineStop(e) => e.diagnostic_message(), } } fn add_args<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { match self { - InterpError::UndefinedBehavior(ub) => ub.add_args(diag), - InterpError::Unsupported(e) => e.add_args(diag), - InterpError::InvalidProgram(e) => e.add_args(diag), - InterpError::ResourceExhaustion(e) => e.add_args(diag), - InterpError::MachineStop(e) => e.add_args(&mut |name, value| { + InterpErrorKind::UndefinedBehavior(ub) => ub.add_args(diag), + InterpErrorKind::Unsupported(e) => e.add_args(diag), + InterpErrorKind::InvalidProgram(e) => e.add_args(diag), + InterpErrorKind::ResourceExhaustion(e) => e.add_args(diag), + InterpErrorKind::MachineStop(e) => e.add_args(&mut |name, value| { diag.arg(name, value); }), } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4945563f4a4..85d99900c6c 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -471,7 +471,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Don't forget to mark "initially live" locals as live. self.storage_live_for_always_live_locals()?; }; - res.inspect_err(|_| { + res.inspect_err_kind(|_| { // Don't show the incomplete stack frame in the error stacktrace. self.stack_mut().pop(); }) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 02dd7821ef6..a1c773a4b80 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -19,7 +19,7 @@ use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument, trace}; use super::{ - Frame, FrameInfo, GlobalId, InterpError, InterpErrorInfo, InterpResult, MPlaceTy, Machine, + Frame, FrameInfo, GlobalId, InterpErrorInfo, InterpErrorKind, InterpResult, MPlaceTy, Machine, MemPlaceMeta, Memory, OpTy, Place, PlaceTy, PointerArithmetic, Projectable, Provenance, err_inval, interp_ok, throw_inval, throw_ub, throw_ub_custom, }; @@ -73,7 +73,7 @@ where } impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { - type LayoutOfResult = Result<TyAndLayout<'tcx>, InterpError<'tcx>>; + type LayoutOfResult = Result<TyAndLayout<'tcx>, InterpErrorKind<'tcx>>; #[inline] fn layout_tcx_at_span(&self) -> Span { @@ -82,20 +82,25 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { } #[inline] - fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> InterpError<'tcx> { + fn handle_layout_err( + &self, + err: LayoutError<'tcx>, + _: Span, + _: Ty<'tcx>, + ) -> InterpErrorKind<'tcx> { err_inval!(Layout(err)) } } impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { - type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpError<'tcx>>; + type FnAbiOfResult = Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, InterpErrorKind<'tcx>>; fn handle_fn_abi_err( &self, err: FnAbiError<'tcx>, _span: Span, _fn_abi_request: FnAbiRequest<'tcx>, - ) -> InterpError<'tcx> { + ) -> InterpErrorKind<'tcx> { match err { FnAbiError::Layout(err) => err_inval!(Layout(err)), FnAbiError::AdjustForForeignAbi(err) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 540898ec645..4e603f57c56 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -324,13 +324,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dist.checked_neg().unwrap(), // i64::MIN is impossible as no allocation can be that large CheckInAllocMsg::OffsetFromTest, ) - .map_err(|_| { + .map_err_kind(|_| { // Make the error more specific. err_ub_custom!( fluent::const_eval_offset_from_different_allocations, name = intrinsic_name, ) - .into() })?; // Perform division by size to compute return value. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 13641ef2bd3..b6120ce82fe 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -17,8 +17,8 @@ use rustc_hir as hir; use rustc_middle::bug; use rustc_middle::mir::interpret::ValidationErrorKind::{self, *}; use rustc_middle::mir::interpret::{ - ExpectedKind, InterpError, InterpErrorInfo, InvalidMetaKind, Misalignment, PointerKind, - Provenance, UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, + ExpectedKind, InterpErrorKind, InvalidMetaKind, Misalignment, PointerKind, Provenance, + UnsupportedOpInfo, ValidationErrorInfo, alloc_range, interp_ok, }; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -37,8 +37,8 @@ use super::{ // for the validation errors #[rustfmt::skip] -use super::InterpError::UndefinedBehavior as Ub; -use super::InterpError::Unsupported as Unsup; +use super::InterpErrorKind::UndefinedBehavior as Ub; +use super::InterpErrorKind::Unsupported as Unsup; use super::UndefinedBehaviorInfo::*; use super::UnsupportedOpInfo::*; @@ -97,20 +97,19 @@ macro_rules! try_validation { ($e:expr, $where:expr, $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)? ) => {{ - $e.map_err(|e| { + $e.map_err_kind(|e| { // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - let (kind, backtrace) = e.into_parts(); - match kind { + match e { $( $($p)|+ => { err_validation_failure!( $where, $kind - ).into() + ) } ),+, - _ => InterpErrorInfo::from_parts(kind, backtrace), + e => e, } })? }}; @@ -1230,11 +1229,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // No need for an alignment check here, this is not an actual memory access. let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0"); - alloc.get_bytes_strip_provenance().map_err(|err| { + alloc.get_bytes_strip_provenance().map_err_kind(|kind| { // Some error happened, try to provide a more detailed description. // For some errors we might be able to provide extra information. // (This custom logic does not fit the `try_validation!` macro.) - let (kind, backtrace) = err.into_parts(); match kind { Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { // Some byte was uninitialized, determine which @@ -1247,14 +1245,14 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path.push(PathElem::ArrayElem(i)); if matches!(kind, Ub(InvalidUninitBytes(_))) { - err_validation_failure!(self.path, Uninit { expected }).into() + err_validation_failure!(self.path, Uninit { expected }) } else { - err_validation_failure!(self.path, PointerAsInt { expected }).into() + err_validation_failure!(self.path, PointerAsInt { expected }) } } // Propagate upwards (that will also check for unexpected errors). - _ => return InterpErrorInfo::from_parts(kind, backtrace), + err => err, } })?; @@ -1368,12 +1366,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { v.reset_padding(val)?; interp_ok(()) }) - .map_err(|err| { + .map_err_info(|err| { if !matches!( err.kind(), err_ub!(ValidationError { .. }) - | InterpError::InvalidProgram(_) - | InterpError::Unsupported(UnsupportedOpInfo::ExternTypeField) + | InterpErrorKind::InvalidProgram(_) + | InterpErrorKind::Unsupported(UnsupportedOpInfo::ExternTypeField) ) { bug!( "Unexpected error during validation: {}", diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index ead72e2a0e1..d4604c27e6d 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -8,6 +8,7 @@ use fluent_syntax::ast::{ Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern, PatternElement, }; use fluent_syntax::parser::ParserError; +use proc_macro::tracked_path::path; use proc_macro::{Diagnostic, Level, Span}; use proc_macro2::TokenStream; use quote::quote; @@ -99,8 +100,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok let crate_name = Ident::new(&crate_name, resource_str.span()); - // As this macro also outputs an `include_str!` for this file, the macro will always be - // re-executed when the file changes. + path(absolute_ftl_path.to_str().unwrap()); let resource_contents = match read_to_string(absolute_ftl_path) { Ok(resource_contents) => resource_contents, Err(e) => { diff --git a/compiler/rustc_fluent_macro/src/lib.rs b/compiler/rustc_fluent_macro/src/lib.rs index 6e5add24bcc..3ad51fa1e64 100644 --- a/compiler/rustc_fluent_macro/src/lib.rs +++ b/compiler/rustc_fluent_macro/src/lib.rs @@ -6,6 +6,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] #![feature(rustdoc_internals)] +#![feature(track_path)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index a9f30ffd6da..507297ce162 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -356,12 +356,14 @@ hir_analysis_only_current_traits_arbitrary = only traits defined in the current hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait -hir_analysis_only_current_traits_label = impl doesn't use only types from inside the current crate - hir_analysis_only_current_traits_name = this is not defined in the current crate because {$name} are always foreign hir_analysis_only_current_traits_note = define and implement a trait or new type instead +hir_analysis_only_current_traits_note_more_info = for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + +hir_analysis_only_current_traits_note_uncovered = impl doesn't have any local type before any uncovered type parameters + hir_analysis_only_current_traits_opaque = type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate hir_analysis_only_current_traits_outside = only traits defined in the current crate can be implemented for types defined outside of the crate diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 8947e7a2216..7f4ab352ef2 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; use rustc_span::Span; use rustc_span::def_id::DefId; -use crate::hir_ty_lowering::OnlySelfBounds; +use crate::hir_ty_lowering::PredicateFilter; /// Collects together a list of type bounds. These lists of bounds occur in many places /// in Rust's syntax: @@ -52,7 +52,7 @@ impl<'tcx> Bounds<'tcx> { span: Span, polarity: ty::PredicatePolarity, constness: ty::BoundConstness, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) { let clause = ( bound_trait_ref @@ -72,9 +72,18 @@ impl<'tcx> Bounds<'tcx> { // FIXME(effects): Lift this out of `push_trait_bound`, and move it somewhere else. // Perhaps moving this into `lower_poly_trait_ref`, just like we lower associated // type bounds. - if !tcx.features().effects || only_self_bounds.0 { + if !tcx.features().effects { return; } + match predicate_filter { + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => { + return; + } + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { + // Ok. + } + } + // For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the // associated type of `<T as Tr>` and make sure that the effect is compatible. let compat_val = match (tcx.def_kind(defining_def_id), constness) { diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index a87b29b3093..421ba40aa88 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -16,7 +16,7 @@ use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; use crate::delegation::inherit_predicates_for_delegation_item; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason}; +use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter, RegionInferReason}; /// Returns a list of all type predicates (explicit and implicit) for the definition with /// ID `def_id`. This includes all predicates returned by `explicit_predicates_of`, plus @@ -270,7 +270,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen bound_pred.bounds.iter(), &mut bounds, bound_vars, - OnlySelfBounds(false), + PredicateFilter::All, ); predicates.extend(bounds.clauses(tcx)); effects_min_tys.extend(bounds.effects_min_tys()); @@ -825,20 +825,6 @@ impl<'tcx> ItemCtxt<'tcx> { continue; }; - // Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we - // want to only consider predicates with `Self: ...`, but we don't want - // `OnlySelfBounds(true)` since we want to collect the nested associated - // type bound as well. - let (only_self_bounds, assoc_name) = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - (OnlySelfBounds(false), None) - } - PredicateFilter::SelfOnly => (OnlySelfBounds(true), None), - PredicateFilter::SelfThatDefines(assoc_name) => { - (OnlySelfBounds(true), Some(assoc_name)) - } - }; - let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) { ty } else if matches!(filter, PredicateFilter::All) { @@ -850,31 +836,13 @@ impl<'tcx> ItemCtxt<'tcx> { let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); self.lowerer().lower_poly_bounds( bound_ty, - predicate.bounds.iter().filter(|bound| { - assoc_name - .map_or(true, |assoc_name| self.bound_defines_assoc_item(bound, assoc_name)) - }), + predicate.bounds.iter(), &mut bounds, bound_vars, - only_self_bounds, + filter, ); } bounds.clauses(self.tcx).collect() } - - #[instrument(level = "trace", skip(self))] - fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool { - match b { - hir::GenericBound::Trait(poly_trait_ref) => { - let trait_ref = &poly_trait_ref.trait_ref; - if let Some(trait_did) = trait_ref.trait_def_id() { - self.tcx.trait_may_define_assoc_item(trait_did, assoc_name) - } else { - false - } - } - _ => false, - } - } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index af4445a7fd4..77e81af3ca9 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1434,24 +1434,27 @@ pub(crate) enum OnlyCurrentTraits { #[diag(hir_analysis_only_current_traits_outside, code = E0117)] Outside { #[primary_span] - #[label(hir_analysis_only_current_traits_label)] span: Span, + #[note(hir_analysis_only_current_traits_note_uncovered)] + #[note(hir_analysis_only_current_traits_note_more_info)] #[note(hir_analysis_only_current_traits_note)] note: (), }, #[diag(hir_analysis_only_current_traits_primitive, code = E0117)] Primitive { #[primary_span] - #[label(hir_analysis_only_current_traits_label)] span: Span, + #[note(hir_analysis_only_current_traits_note_uncovered)] + #[note(hir_analysis_only_current_traits_note_more_info)] #[note(hir_analysis_only_current_traits_note)] note: (), }, #[diag(hir_analysis_only_current_traits_arbitrary, code = E0117)] Arbitrary { #[primary_span] - #[label(hir_analysis_only_current_traits_label)] span: Span, + #[note(hir_analysis_only_current_traits_note_uncovered)] + #[note(hir_analysis_only_current_traits_note_more_info)] #[note(hir_analysis_only_current_traits_note)] note: (), }, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 8f7ca089c91..310f648b980 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -19,9 +19,7 @@ use tracing::{debug, instrument}; use super::errors::GenericsArgsErrExtend; use crate::bounds::Bounds; use crate::errors; -use crate::hir_ty_lowering::{ - AssocItemQSelf, HirTyLowerer, OnlySelfBounds, PredicateFilter, RegionInferReason, -}; +use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer, PredicateFilter, RegionInferReason}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -150,11 +148,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_bounds: I, bounds: &mut Bounds<'tcx>, bound_vars: &'tcx ty::List<ty::BoundVariableKind>, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) where 'tcx: 'hir, { for hir_bound in hir_bounds { + // In order to avoid cycles, when we're lowering `SelfThatDefines`, + // we skip over any traits that don't define the given associated type. + + if let PredicateFilter::SelfThatDefines(assoc_name) = predicate_filter { + if let Some(trait_ref) = hir_bound.trait_ref() + && let Some(trait_did) = trait_ref.trait_def_id() + && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) + { + // Okay + } else { + continue; + } + } + match hir_bound { hir::GenericBound::Trait(poly_trait_ref) => { let (constness, polarity) = match poly_trait_ref.modifiers { @@ -179,7 +191,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { polarity, param_ty, bounds, - only_self_bounds, + predicate_filter, ); } hir::GenericBound::Outlives(lifetime) => { @@ -213,37 +225,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, param_ty: Ty<'tcx>, hir_bounds: &[hir::GenericBound<'tcx>], - filter: PredicateFilter, + predicate_filter: PredicateFilter, ) -> Bounds<'tcx> { let mut bounds = Bounds::default(); - let only_self_bounds = match filter { - PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { - OnlySelfBounds(false) - } - PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), - }; - self.lower_poly_bounds( param_ty, - hir_bounds.iter().filter(|bound| match filter { - PredicateFilter::All - | PredicateFilter::SelfOnly - | PredicateFilter::SelfAndAssociatedTypeBounds => true, - PredicateFilter::SelfThatDefines(assoc_name) => { - if let Some(trait_ref) = bound.trait_ref() - && let Some(trait_did) = trait_ref.trait_def_id() - && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name) - { - true - } else { - false - } - } - }), + hir_bounds.iter(), &mut bounds, ty::List::empty(), - only_self_bounds, + predicate_filter, ); debug!(?bounds); @@ -267,7 +258,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds: &mut Bounds<'tcx>, duplicates: &mut FxIndexMap<DefId, Span>, path_span: Span, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); @@ -444,21 +435,23 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator<Item: Debug>` // to a bound involving a projection: `<T as Iterator>::Item: Debug`. hir::AssocItemConstraintKind::Bound { bounds: hir_bounds } => { - // NOTE: If `only_self_bounds` is true, do NOT expand this associated type bound into - // a trait predicate, since we only want to add predicates for the `Self` type. - if !only_self_bounds.0 { - let projection_ty = projection_term - .map_bound(|projection_term| projection_term.expect_ty(self.tcx())); - // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` - // parameter to have a skipped binder. - let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); - self.lower_poly_bounds( - param_ty, - hir_bounds.iter(), - bounds, - projection_ty.bound_vars(), - only_self_bounds, - ); + match predicate_filter { + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => {} + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { + let projection_ty = projection_term + .map_bound(|projection_term| projection_term.expect_ty(self.tcx())); + // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` + // parameter to have a skipped binder. + let param_ty = + Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); + self.lower_poly_bounds( + param_ty, + hir_bounds.iter(), + bounds, + projection_ty.bound_vars(), + predicate_filter, + ); + } } } } @@ -516,7 +509,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty, trait_segment, false, - ty::BoundConstness::NotConst, ); // SUBTLE: As noted at the end of `try_append_return_type_notation_params` diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 98822eec2ac..2cf97e29060 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -20,7 +20,7 @@ use tracing::{debug, instrument}; use super::HirTyLowerer; use crate::bounds::Bounds; use crate::hir_ty_lowering::{ - GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds, RegionInferReason, + GenericArgCountMismatch, GenericArgCountResult, PredicateFilter, RegionInferReason, }; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { @@ -55,9 +55,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::PredicatePolarity::Positive, dummy_self, &mut bounds, - // True so we don't populate `bounds` with associated type bounds, even - // though they're disallowed from object types. - OnlySelfBounds(true), + PredicateFilter::SelfOnly, ) { potential_assoc_types.extend(cur_potential_assoc_types); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index d760acf53bd..828809726d2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -65,9 +65,6 @@ use crate::require_c_abi_if_c_variadic; pub struct GenericPathSegment(pub DefId, pub usize); #[derive(Copy, Clone, Debug)] -pub struct OnlySelfBounds(pub bool); - -#[derive(Copy, Clone, Debug)] pub enum PredicateFilter { /// All predicates may be implied by the trait. All, @@ -76,7 +73,8 @@ pub enum PredicateFilter { SelfOnly, /// Only traits that reference `Self: ..` and define an associated type - /// with the given ident are implied by the trait. + /// with the given ident are implied by the trait. This mode exists to + /// side-step query cycles when lowering associated types. SelfThatDefines(Ident), /// Only traits that reference `Self: ..` and their associated type bounds. @@ -336,14 +334,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id: DefId, item_segment: &hir::PathSegment<'tcx>, ) -> GenericArgsRef<'tcx> { - let (args, _) = self.lower_generic_args_of_path( - span, - def_id, - &[], - item_segment, - None, - ty::BoundConstness::NotConst, - ); + let (args, _) = self.lower_generic_args_of_path(span, def_id, &[], item_segment, None); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span))); } @@ -392,7 +383,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { parent_args: &[ty::GenericArg<'tcx>], segment: &hir::PathSegment<'tcx>, self_ty: Option<Ty<'tcx>>, - constness: ty::BoundConstness, ) -> (GenericArgsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, @@ -415,7 +405,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert!(self_ty.is_none()); } - let mut arg_count = check_generic_arg_count( + let arg_count = check_generic_arg_count( self, def_id, segment, @@ -573,16 +563,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } } - if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness - && generics.has_self - && !tcx.is_const_trait(def_id) - { - let reported = self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { - span, - modifier: constness.as_str(), - }); - arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] }); - } let mut args_ctx = GenericArgsCtxt { lowerer: self, @@ -614,14 +594,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { debug!(?span, ?item_def_id, ?item_segment); - let (args, _) = self.lower_generic_args_of_path( - span, - item_def_id, - parent_args, - item_segment, - None, - ty::BoundConstness::NotConst, - ); + let (args, _) = + self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None); if let Some(c) = item_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span))); } @@ -647,7 +621,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty, trait_ref.path.segments.last().unwrap(), true, - ty::BoundConstness::NotConst, ) } @@ -683,7 +656,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { polarity: ty::PredicatePolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, - only_self_bounds: OnlySelfBounds, + predicate_filter: PredicateFilter, ) -> GenericArgCountResult { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); let trait_segment = trait_ref.path.segments.last().unwrap(); @@ -700,9 +673,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[], trait_segment, Some(self_ty), - constness, ); + if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness + && !self.tcx().is_const_trait(trait_def_id) + { + self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait { + span: trait_ref.path.span, + modifier: constness.as_str(), + }); + } + let tcx = self.tcx(); let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id); debug!(?bound_vars); @@ -720,7 +701,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, polarity, constness, - only_self_bounds, + predicate_filter, ); let mut dup_constraints = FxIndexMap::default(); @@ -744,7 +725,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bounds, &mut dup_constraints, constraint.span, - only_self_bounds, + predicate_filter, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). } @@ -762,19 +743,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'tcx>, is_impl: bool, - // FIXME(effects): Move all host param things in HIR ty lowering to AST lowering. - constness: ty::BoundConstness, ) -> ty::TraitRef<'tcx> { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); - let (generic_args, _) = self.lower_generic_args_of_path( - span, - trait_def_id, - &[], - trait_segment, - Some(self_ty), - constness, - ); + let (generic_args, _) = + self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty)); if let Some(c) = trait_segment.args().constraints.first() { prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span))); } @@ -1542,7 +1515,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { item_def_id: DefId, trait_segment: &hir::PathSegment<'tcx>, item_segment: &hir::PathSegment<'tcx>, - constness: ty::BoundConstness, ) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1555,7 +1527,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?self_ty); let trait_ref = - self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness); + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); debug!(?trait_ref); let item_args = @@ -1918,7 +1890,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { def_id, &path.segments[path.segments.len() - 2], path.segments.last().unwrap(), - ty::BoundConstness::NotConst, ) } Res::PrimTy(prim_ty) => { @@ -2151,7 +2122,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &[], &hir::PathSegment::invalid(), None, - ty::BoundConstness::NotConst, ); tcx.at(span).type_of(def_id).instantiate(tcx, args) } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7bdd3c95ad1..f4d7b59e9c8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1320,84 +1320,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let expected_ty = expected.coercion_target_type(self, expr.span); if expected_ty == self.tcx.types.bool { - // The expected type is `bool` but this will result in `()` so we can reasonably - // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. - // The likely cause of this is `if foo = bar { .. }`. - let actual_ty = self.tcx.types.unit; - let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err(); - let lhs_ty = self.check_expr(lhs); - let rhs_ty = self.check_expr(rhs); - let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| { - let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs()); - let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs()); - self.may_coerce(rhs, lhs) - }; - let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) { - (Applicability::MachineApplicable, true) - } else if refs_can_coerce(rhs_ty, lhs_ty) { - // The lhs and rhs are likely missing some references in either side. Subsequent - // suggestions will show up. - (Applicability::MaybeIncorrect, true) - } else if let ExprKind::Binary( - Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, - _, - rhs_expr, - ) = lhs.kind - { - // if x == 1 && y == 2 { .. } - // + - let actual_lhs_ty = self.check_expr(rhs_expr); - ( - Applicability::MaybeIncorrect, - self.may_coerce(rhs_ty, actual_lhs_ty) - || refs_can_coerce(rhs_ty, actual_lhs_ty), - ) - } else if let ExprKind::Binary( - Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, - lhs_expr, - _, - ) = rhs.kind - { - // if x == 1 && y == 2 { .. } - // + - let actual_rhs_ty = self.check_expr(lhs_expr); - ( - Applicability::MaybeIncorrect, - self.may_coerce(actual_rhs_ty, lhs_ty) - || refs_can_coerce(actual_rhs_ty, lhs_ty), - ) - } else { - (Applicability::MaybeIncorrect, false) - }; - if !lhs.is_syntactic_place_expr() - && lhs.is_approximately_pattern() - && !matches!(lhs.kind, hir::ExprKind::Lit(_)) - { - // Do not suggest `if let x = y` as `==` is way more likely to be the intention. - if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = - self.tcx.parent_hir_node(expr.hir_id) - { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - applicability, - ); - }; - } - if eq { - err.span_suggestion_verbose( - span.shrink_to_hi(), - "you might have meant to compare for equality", - '=', - applicability, - ); - } - - // If the assignment expression itself is ill-formed, don't - // bother emitting another error - let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()); - return Ty::new_error(self.tcx, reported); + let guar = self.expr_assign_expected_bool_error(expr, lhs, rhs, span); + return Ty::new_error(self.tcx, guar); } let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace); @@ -1450,6 +1374,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// The expected type is `bool` but this will result in `()` so we can reasonably + /// say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. + /// The likely cause of this is `if foo = bar { .. }`. + fn expr_assign_expected_bool_error( + &self, + expr: &'tcx hir::Expr<'tcx>, + lhs: &'tcx hir::Expr<'tcx>, + rhs: &'tcx hir::Expr<'tcx>, + span: Span, + ) -> ErrorGuaranteed { + let actual_ty = self.tcx.types.unit; + let expected_ty = self.tcx.types.bool; + let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err(); + let lhs_ty = self.check_expr(lhs); + let rhs_ty = self.check_expr(rhs); + let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| { + let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs()); + let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs()); + self.may_coerce(rhs, lhs) + }; + let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) { + (Applicability::MachineApplicable, true) + } else if refs_can_coerce(rhs_ty, lhs_ty) { + // The lhs and rhs are likely missing some references in either side. Subsequent + // suggestions will show up. + (Applicability::MaybeIncorrect, true) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + _, + rhs_expr, + ) = lhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_lhs = self.check_expr(rhs_expr); + let may_eq = self.may_coerce(rhs_ty, actual_lhs) || refs_can_coerce(rhs_ty, actual_lhs); + (Applicability::MaybeIncorrect, may_eq) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs_expr, + _, + ) = rhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_rhs = self.check_expr(lhs_expr); + let may_eq = self.may_coerce(actual_rhs, lhs_ty) || refs_can_coerce(actual_rhs, lhs_ty); + (Applicability::MaybeIncorrect, may_eq) + } else { + (Applicability::MaybeIncorrect, false) + }; + + if !lhs.is_syntactic_place_expr() + && lhs.is_approximately_pattern() + && !matches!(lhs.kind, hir::ExprKind::Lit(_)) + { + // Do not suggest `if let x = y` as `==` is way more likely to be the intention. + if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = + self.tcx.parent_hir_node(expr.hir_id) + { + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ", + applicability, + ); + }; + } + if eq { + err.span_suggestion_verbose( + span.shrink_to_hi(), + "you might have meant to compare for equality", + '=', + applicability, + ); + } + + // If the assignment expression itself is ill-formed, don't + // bother emitting another error + err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error()) + } + pub(super) fn check_expr_let( &self, let_expr: &'tcx hir::LetExpr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6b0a897faba..9e36f7a9aea 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -419,7 +419,7 @@ fn report_unexpected_variant_res( } } - err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err.multipart_suggestion_verbose(descr, suggestion, Applicability::HasPlaceholders); err } Res::Def(DefKind::Variant, _) if expr.is_none() => { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index ba6bfd3a5e9..1be711887d9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -340,13 +340,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>, { let mut orig_values = OriginalQueryValues::default(); - let param_env_and_self_ty = self.canonicalize_query( + let query_input = self.canonicalize_query( ParamEnvAnd { param_env: self.param_env, value: self_ty }, &mut orig_values, ); let steps = match mode { - Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty), + Mode::MethodCall => self.tcx.method_autoderef_steps(query_input), Mode::Path => self.probe(|_| { // Mode::Path - the deref steps is "trivial". This turns // our CanonicalQuery into a "trivial" QueryResponse. This @@ -355,11 +355,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let infcx = &self.infcx; let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) = - infcx.instantiate_canonical(span, ¶m_env_and_self_ty); - debug!( - "probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}", - param_env_and_self_ty, self_ty - ); + infcx.instantiate_canonical(span, &query_input.canonical); + debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); MethodAutoderefStepsResult { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { self_ty: self.make_query_response_ignoring_pending_obligations( diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 35ea4233825..e3519dfb028 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -17,7 +17,8 @@ use tracing::debug; use crate::infer::InferCtxt; use crate::infer::canonical::{ - Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, OriginalQueryValues, + Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, + OriginalQueryValues, }; impl<'tcx> InferCtxt<'tcx> { @@ -40,12 +41,12 @@ impl<'tcx> InferCtxt<'tcx> { &self, value: ty::ParamEnvAnd<'tcx, V>, query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>> + ) -> CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, V>> where V: TypeFoldable<TyCtxt<'tcx>>, { let (param_env, value) = value.into_parts(); - let mut param_env = self.tcx.canonical_param_env_cache.get_or_insert( + let param_env = self.tcx.canonical_param_env_cache.get_or_insert( self.tcx, param_env, query_state, @@ -62,9 +63,7 @@ impl<'tcx> InferCtxt<'tcx> { }, ); - param_env.defining_opaque_types = self.defining_opaque_types; - - Canonicalizer::canonicalize_with_base( + let canonical = Canonicalizer::canonicalize_with_base( param_env, value, Some(self), @@ -72,7 +71,8 @@ impl<'tcx> InferCtxt<'tcx> { &CanonicalizeAllFreeRegions, query_state, ) - .unchecked_map(|(param_env, value)| param_env.and(value)) + .unchecked_map(|(param_env, value)| param_env.and(value)); + CanonicalQueryInput { canonical, defining_opaque_types: self.defining_opaque_types() } } /// Canonicalizes a query *response* `V`. When we canonicalize a @@ -544,7 +544,6 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: (), - defining_opaque_types: infcx.map(|i| i.defining_opaque_types).unwrap_or_default(), }; Canonicalizer::canonicalize_with_base( base, @@ -614,15 +613,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .max() .unwrap_or(ty::UniverseIndex::ROOT); - assert!( - !infcx.is_some_and(|infcx| infcx.defining_opaque_types != base.defining_opaque_types) - ); - Canonical { - max_universe, - variables: canonical_variables, - value: (base.value, out_value), - defining_opaque_types: base.defining_opaque_types, - } + Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } } /// Creates a canonical variable replacing `kind` from the input, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 7ef714475fc..5afdf3c2454 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -25,7 +25,7 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ ConstVariableOrigin, ConstVariableValue, ConstVidKey, EffectVarValue, EffectVidKey, }; @@ -606,14 +606,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> { pub fn build_with_canonical<T>( mut self, span: Span, - canonical: &Canonical<'tcx, T>, + input: &CanonicalQueryInput<'tcx, T>, ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) where T: TypeFoldable<TyCtxt<'tcx>>, { - self.defining_opaque_types = canonical.defining_opaque_types; + self.defining_opaque_types = input.defining_opaque_types; let infcx = self.build(); - let (value, args) = infcx.instantiate_canonical(span, canonical); + let (value, args) = infcx.instantiate_canonical(span, &input.canonical); (infcx, value, args) } @@ -899,6 +899,13 @@ impl<'tcx> InferCtxt<'tcx> { ty::Const::new_var(self.tcx, vid) } + fn next_effect_var(&self) -> ty::Const<'tcx> { + let effect_vid = + self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid; + + ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)) + } + pub fn next_int_var(&self) -> Ty<'tcx> { let next_int_var_id = self.inner.borrow_mut().int_unification_table().new_key(ty::IntVarValue::Unknown); @@ -1001,15 +1008,13 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn var_for_effect(&self, param: &ty::GenericParamDef) -> GenericArg<'tcx> { - let effect_vid = - self.inner.borrow_mut().effect_unification_table().new_key(EffectVarValue::Unknown).vid; let ty = self .tcx .type_of(param.def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); debug_assert_eq!(self.tcx.types.bool, ty); - ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(effect_vid)).into() + self.next_effect_var().into() } /// Given a set of generics defined on a type or impl, returns the generic parameters mapping diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 8e330a084c6..613cebc266d 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -4,9 +4,12 @@ use rustc_data_structures::{snapshot_vec as sv, unify as ut}; use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; +use rustc_type_ir::EffectVid; +use rustc_type_ir::visit::TypeVisitableExt; use tracing::instrument; use ut::UnifyKey; +use super::VariableLengths; use crate::infer::type_variable::TypeVariableOrigin; use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable}; @@ -40,26 +43,7 @@ fn const_vars_since_snapshot<'tcx>( ) } -struct VariableLengths { - type_var_len: usize, - const_var_len: usize, - int_var_len: usize, - float_var_len: usize, - region_constraints_len: usize, -} - impl<'tcx> InferCtxt<'tcx> { - fn variable_lengths(&self) -> VariableLengths { - let mut inner = self.inner.borrow_mut(); - VariableLengths { - type_var_len: inner.type_variables().num_vars(), - const_var_len: inner.const_unification_table().len(), - int_var_len: inner.int_unification_table().len(), - float_var_len: inner.float_unification_table().len(), - region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), - } - } - /// This rather funky routine is used while processing expected /// types. What happens here is that we want to propagate a /// coercion through the return type of a fn to its @@ -106,78 +90,94 @@ impl<'tcx> InferCtxt<'tcx> { T: TypeFoldable<TyCtxt<'tcx>>, { let variable_lengths = self.variable_lengths(); - let (mut fudger, value) = self.probe(|_| { - match f() { - Ok(value) => { - let value = self.resolve_vars_if_possible(value); - - // At this point, `value` could in principle refer - // to inference variables that have been created during - // the snapshot. Once we exit `probe()`, those are - // going to be popped, so we will have to - // eliminate any references to them. - - let mut inner = self.inner.borrow_mut(); - let type_vars = - inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); - let int_vars = vars_since_snapshot( - &inner.int_unification_table(), - variable_lengths.int_var_len, - ); - let float_vars = vars_since_snapshot( - &inner.float_unification_table(), - variable_lengths.float_var_len, - ); - let region_vars = inner - .unwrap_region_constraints() - .vars_since_snapshot(variable_lengths.region_constraints_len); - let const_vars = const_vars_since_snapshot( - &mut inner.const_unification_table(), - variable_lengths.const_var_len, - ); - - let fudger = InferenceFudger { - infcx: self, - type_vars, - int_vars, - float_vars, - region_vars, - const_vars, - }; - - Ok((fudger, value)) - } - Err(e) => Err(e), - } + let (snapshot_vars, value) = self.probe(|_| { + let value = f()?; + // At this point, `value` could in principle refer + // to inference variables that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + let snapshot_vars = SnapshotVarData::new(self, variable_lengths); + Ok((snapshot_vars, self.resolve_vars_if_possible(value))) })?; // At this point, we need to replace any of the now-popped // type/region variables that appear in `value` with a fresh // variable of the appropriate kind. We can't do this during // the probe because they would just get popped then too. =) + Ok(self.fudge_inference(snapshot_vars, value)) + } + fn fudge_inference<T: TypeFoldable<TyCtxt<'tcx>>>( + &self, + snapshot_vars: SnapshotVarData, + value: T, + ) -> T { // Micro-optimization: if no variables have been created, then // `value` can't refer to any of them. =) So we can just return it. - if fudger.type_vars.0.is_empty() - && fudger.int_vars.is_empty() - && fudger.float_vars.is_empty() - && fudger.region_vars.0.is_empty() - && fudger.const_vars.0.is_empty() - { - Ok(value) + if snapshot_vars.is_empty() { + value } else { - Ok(value.fold_with(&mut fudger)) + value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars }) } } } -struct InferenceFudger<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, +struct SnapshotVarData { + region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>), type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>), int_vars: Range<IntVid>, float_vars: Range<FloatVid>, - region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>), const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>), + effect_vars: Range<EffectVid>, +} + +impl SnapshotVarData { + fn new(infcx: &InferCtxt<'_>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData { + let mut inner = infcx.inner.borrow_mut(); + let region_vars = inner + .unwrap_region_constraints() + .vars_since_snapshot(vars_pre_snapshot.region_constraints_len); + let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len); + let int_vars = + vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len); + let float_vars = + vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len); + + let const_vars = const_vars_since_snapshot( + &mut inner.const_unification_table(), + vars_pre_snapshot.const_var_len, + ); + let effect_vars = vars_since_snapshot( + &inner.effect_unification_table(), + vars_pre_snapshot.effect_var_len, + ); + let effect_vars = effect_vars.start.vid..effect_vars.end.vid; + + SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars, effect_vars } + } + + fn is_empty(&self) -> bool { + let SnapshotVarData { + region_vars, + type_vars, + int_vars, + float_vars, + const_vars, + effect_vars, + } = self; + region_vars.0.is_empty() + && type_vars.0.is_empty() + && int_vars.is_empty() + && float_vars.is_empty() + && const_vars.0.is_empty() + && effect_vars.is_empty() + } +} + +struct InferenceFudger<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + snapshot_vars: SnapshotVarData, } impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> { @@ -186,68 +186,93 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for InferenceFudger<'a, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Infer(ty::InferTy::TyVar(vid)) => { - if self.type_vars.0.contains(&vid) { - // This variable was created during the fudging. - // Recreate it with a fresh variable here. - let idx = vid.as_usize() - self.type_vars.0.start.as_usize(); - let origin = self.type_vars.1[idx]; - self.infcx.next_ty_var_with_origin(origin) - } else { - // This variable was created before the - // "fudging". Since we refresh all type - // variables to their binding anyhow, we know - // that it is unbound, so we can just return - // it. - debug_assert!( - self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() - ); - ty + if let &ty::Infer(infer_ty) = ty.kind() { + match infer_ty { + ty::TyVar(vid) => { + if self.snapshot_vars.type_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); + let origin = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var_with_origin(origin) + } else { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!( + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() + ); + ty + } } - } - ty::Infer(ty::InferTy::IntVar(vid)) => { - if self.int_vars.contains(&vid) { - self.infcx.next_int_var() - } else { - ty + ty::IntVar(vid) => { + if self.snapshot_vars.int_vars.contains(&vid) { + self.infcx.next_int_var() + } else { + ty + } } - } - ty::Infer(ty::InferTy::FloatVar(vid)) => { - if self.float_vars.contains(&vid) { - self.infcx.next_float_var() - } else { - ty + ty::FloatVar(vid) => { + if self.snapshot_vars.float_vars.contains(&vid) { + self.infcx.next_float_var() + } else { + ty + } + } + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { + unreachable!("unexpected fresh infcx var") } } - _ => ty.super_fold_with(self), + } else if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty } } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if let ty::ReVar(vid) = *r - && self.region_vars.0.contains(&vid) - { - let idx = vid.index() - self.region_vars.0.start.index(); - let origin = self.region_vars.1[idx]; - return self.infcx.next_region_var(origin); + if let ty::ReVar(vid) = r.kind() { + if self.snapshot_vars.region_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index(); + let origin = self.snapshot_vars.region_vars.1[idx]; + self.infcx.next_region_var(origin) + } else { + r + } + } else { + r } - r } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { - if self.const_vars.0.contains(&vid) { - // This variable was created during the fudging. - // Recreate it with a fresh variable here. - let idx = vid.index() - self.const_vars.0.start.index(); - let origin = self.const_vars.1[idx]; - self.infcx.next_const_var_with_origin(origin) - } else { - ct + if let ty::ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + ty::InferConst::Var(vid) => { + if self.snapshot_vars.const_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); + let origin = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var_with_origin(origin) + } else { + ct + } + } + ty::InferConst::EffectVar(vid) => { + if self.snapshot_vars.effect_vars.contains(&vid) { + self.infcx.next_effect_var() + } else { + ct + } + } + ty::InferConst::Fresh(_) => { + unreachable!("unexpected fresh infcx var") + } } - } else { + } else if ct.has_infer() { ct.super_fold_with(self) + } else { + ct } } } diff --git a/compiler/rustc_infer/src/infer/snapshot/mod.rs b/compiler/rustc_infer/src/infer/snapshot/mod.rs index 07a482c2f9a..fa813500c54 100644 --- a/compiler/rustc_infer/src/infer/snapshot/mod.rs +++ b/compiler/rustc_infer/src/infer/snapshot/mod.rs @@ -17,7 +17,28 @@ pub struct CombinedSnapshot<'tcx> { universe: ty::UniverseIndex, } +struct VariableLengths { + region_constraints_len: usize, + type_var_len: usize, + int_var_len: usize, + float_var_len: usize, + const_var_len: usize, + effect_var_len: usize, +} + impl<'tcx> InferCtxt<'tcx> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + type_var_len: inner.type_variables().num_vars(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + const_var_len: inner.const_unification_table().len(), + effect_var_len: inner.effect_unification_table().len(), + } + } + pub fn in_snapshot(&self) -> bool { UndoLogs::<UndoLog<'tcx>>::in_snapshot(&self.inner.borrow_mut().undo_log) } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 89a67fc0d89..95a8e7625ff 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -493,7 +493,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // // This means that this only errors if we're truly lowering the lint // level from forbid. - if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { + if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid { + // Having a deny inside a forbid is fine and is ignored, so we skip this check. + return; + } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { // Backwards compatibility check: // // We used to not consider `forbid(lint_group)` diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index db4413149a4..e9c61a41d6d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -18,6 +18,8 @@ use rustc_target::spec::abi::Abi as SpecAbi; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; +mod improper_ctypes; + use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, @@ -983,15 +985,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Empty enums are okay... although sort of useless. return FfiSafe; } - - if def.is_variant_list_non_exhaustive() && !def.did().is_local() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_non_exhaustive, - help: None, - }; - } - // Check for a repr() attribute to specify the size of the // discriminant. if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() @@ -1010,21 +1003,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } + use improper_ctypes::{ + check_non_exhaustive_variant, non_local_and_non_exhaustive, + }; + + let non_local_def = non_local_and_non_exhaustive(def); // Check the contained variants. - for variant in def.variants() { - let is_non_exhaustive = variant.is_field_list_non_exhaustive(); - if is_non_exhaustive && !variant.def_id.is_local() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_non_exhaustive_variant, - help: None, - }; - } + let ret = def.variants().iter().try_for_each(|variant| { + check_non_exhaustive_variant(non_local_def, variant) + .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; match self.check_variant_for_ffi(acc, ty, def, variant, args) { - FfiSafe => (), - r => return r, + FfiSafe => ControlFlow::Continue(()), + r => ControlFlow::Break(r), } + }); + if let ControlFlow::Break(result) = ret { + return result; } FfiSafe diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs new file mode 100644 index 00000000000..1030101c545 --- /dev/null +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -0,0 +1,51 @@ +use std::ops::ControlFlow; + +use rustc_errors::DiagMessage; +use rustc_hir::def::CtorKind; +use rustc_middle::ty; + +use crate::fluent_generated as fluent; + +/// Check a variant of a non-exhaustive enum for improper ctypes +/// +/// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added". +/// This includes linting, on a best-effort basis. There are valid additions that are unlikely. +/// +/// Adding a data-carrying variant to an existing C-like enum that is passed to C is "unlikely", +/// so we don't need the lint to account for it. +/// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }. +pub(crate) fn check_non_exhaustive_variant( + non_local_def: bool, + variant: &ty::VariantDef, +) -> ControlFlow<DiagMessage, ()> { + // non_exhaustive suggests it is possible that someone might break ABI + // see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 + // so warn on complex enums being used outside their crate + if non_local_def { + // which is why we only warn about really_tagged_union reprs from https://rust.tf/rfc2195 + // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` + // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) + if variant_has_complex_ctor(variant) { + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); + } + } + + let non_exhaustive_variant_fields = variant.is_field_list_non_exhaustive(); + if non_exhaustive_variant_fields && !variant.def_id.is_local() { + return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); + } + + ControlFlow::Continue(()) +} + +fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { + // CtorKind::Const means a "unit" ctor + !matches!(variant.ctor_kind(), Some(CtorKind::Const)) +} + +// non_exhaustive suggests it is possible that someone might break ABI +// see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 +// so warn on complex enums being used outside their crate +pub(crate) fn non_local_and_non_exhaustive(def: ty::AdtDef<'_>) -> bool { + def.is_variant_list_non_exhaustive() && !def.did().is_local() +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 45a5ce0ca20..c11571182fe 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -156,7 +156,7 @@ declare_lint! { /// /// ```rust /// #![forbid(warnings)] - /// #![deny(bad_style)] + /// #![warn(bad_style)] /// /// fn main() {} /// ``` diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 8f0b1b81276..4b303511dbc 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -490,13 +490,13 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); auto Arg0 = std::string(ArgsCstrBuff); buffer_offset = Arg0.size() + 1; - auto ArgsCppStr = - std::string(ArgsCstrBuff + buffer_offset, ArgsCstrBuffLen - 1); + auto ArgsCppStr = std::string(ArgsCstrBuff + buffer_offset, + ArgsCstrBuffLen - buffer_offset); auto i = 0; while (i != std::string::npos) { i = ArgsCppStr.find('\0', i + 1); if (i != std::string::npos) - ArgsCppStr.replace(i, i + 1, " "); + ArgsCppStr.replace(i, 1, " "); } Options.MCOptions.Argv0 = Arg0; Options.MCOptions.CommandlineArgs = ArgsCppStr; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 72b03fa0560..910c27da954 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1658,16 +1658,6 @@ extern "C" void LLVMRustPositionBuilderAtStart(LLVMBuilderRef B, unwrap(B)->SetInsertPoint(unwrap(BB), Point); } -extern "C" void LLVMRustSetComdat(LLVMModuleRef M, LLVMValueRef V, - const char *Name, size_t NameLen) { - Triple TargetTriple = Triple(unwrap(M)->getTargetTriple()); - GlobalObject *GV = unwrap<GlobalObject>(V); - if (TargetTriple.supportsCOMDAT()) { - StringRef NameRef(Name, NameLen); - GV->setComdat(unwrap(M)->getOrInsertComdat(NameRef)); - } -} - enum class LLVMRustLinkage { ExternalLinkage = 0, AvailableExternallyLinkage = 1, diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 99c673b021a..a4a69ae9514 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -499,8 +499,11 @@ impl<'a> CrateLocator<'a> { dylibs: FxIndexMap<PathBuf, PathKind>, ) -> Result<Option<(Svh, Library)>, CrateError> { let mut slot = None; - // Order here matters, rmeta should come first. See comment in - // `extract_one` below. + // Order here matters, rmeta should come first. + // + // Make sure there's at most one rlib and at most one dylib. + // + // See comment in `extract_one` below. let source = CrateSource { rmeta: self.extract_one(rmetas, CrateFlavor::Rmeta, &mut slot)?, rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, @@ -706,54 +709,58 @@ impl<'a> CrateLocator<'a> { let mut rmetas = FxIndexMap::default(); let mut dylibs = FxIndexMap::default(); for loc in &self.exact_paths { - if !loc.canonicalized().exists() { - return Err(CrateError::ExternLocationNotExist( - self.crate_name, - loc.original().clone(), - )); + let loc_canon = loc.canonicalized(); + let loc_orig = loc.original(); + if !loc_canon.exists() { + return Err(CrateError::ExternLocationNotExist(self.crate_name, loc_orig.clone())); } - if !loc.original().is_file() { - return Err(CrateError::ExternLocationNotFile( - self.crate_name, - loc.original().clone(), - )); + if !loc_orig.is_file() { + return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); } - let Some(file) = loc.original().file_name().and_then(|s| s.to_str()) else { - return Err(CrateError::ExternLocationNotFile( - self.crate_name, - loc.original().clone(), - )); + // Note to take care and match against the non-canonicalized name: + // some systems save build artifacts into content-addressed stores + // that do not preserve extensions, and then link to them using + // e.g. symbolic links. If we canonicalize too early, we resolve + // the symlink, the file type is lost and we might treat rlibs and + // rmetas as dylibs. + let Some(file) = loc_orig.file_name().and_then(|s| s.to_str()) else { + return Err(CrateError::ExternLocationNotFile(self.crate_name, loc_orig.clone())); }; - - if file.starts_with("lib") && (file.ends_with(".rlib") || file.ends_with(".rmeta")) - || file.starts_with(self.target.dll_prefix.as_ref()) - && file.ends_with(self.target.dll_suffix.as_ref()) - { - // Make sure there's at most one rlib and at most one dylib. - // Note to take care and match against the non-canonicalized name: - // some systems save build artifacts into content-addressed stores - // that do not preserve extensions, and then link to them using - // e.g. symbolic links. If we canonicalize too early, we resolve - // the symlink, the file type is lost and we might treat rlibs and - // rmetas as dylibs. - let loc_canon = loc.canonicalized().clone(); - let loc = loc.original(); - if loc.file_name().unwrap().to_str().unwrap().ends_with(".rlib") { - rlibs.insert(loc_canon, PathKind::ExternFlag); - } else if loc.file_name().unwrap().to_str().unwrap().ends_with(".rmeta") { - rmetas.insert(loc_canon, PathKind::ExternFlag); - } else { - dylibs.insert(loc_canon, PathKind::ExternFlag); + // FnMut cannot return reference to captured value, so references + // must be taken outside the closure. + let rlibs = &mut rlibs; + let rmetas = &mut rmetas; + let dylibs = &mut dylibs; + let type_via_filename = (|| { + if file.starts_with("lib") { + if file.ends_with(".rlib") { + return Some(rlibs); + } + if file.ends_with(".rmeta") { + return Some(rmetas); + } + } + let dll_prefix = self.target.dll_prefix.as_ref(); + let dll_suffix = self.target.dll_suffix.as_ref(); + if file.starts_with(dll_prefix) && file.ends_with(dll_suffix) { + return Some(dylibs); + } + None + })(); + match type_via_filename { + Some(type_via_filename) => { + type_via_filename.insert(loc_canon.clone(), PathKind::ExternFlag); + } + None => { + self.crate_rejections + .via_filename + .push(CrateMismatch { path: loc_orig.clone(), got: String::new() }); } - } else { - self.crate_rejections - .via_filename - .push(CrateMismatch { path: loc.original().clone(), got: String::new() }); } } // Extract the dylib/rlib/rmeta triple. - Ok(self.extract_lib(rlibs, rmetas, dylibs)?.map(|(_, lib)| lib)) + self.extract_lib(rlibs, rmetas, dylibs).map(|opt| opt.map(|(_, lib)| lib)) } pub(crate) fn into_error(self, root: Option<CratePaths>) -> CrateError { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 69707fdbe8f..7bb40996d58 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -437,9 +437,6 @@ provide! { tcx, def_id, other, cdata, pub(in crate::rmeta) fn provide(providers: &mut Providers) { provide_cstore_hooks(providers); - // FIXME(#44234) - almost all of these queries have no sub-queries and - // therefore no actual inputs, they're just reading tables calculated in - // resolve! Does this work? Unsure! That's what the issue is about providers.queries = rustc_middle::query::Providers { allocator_kind: |tcx, ()| CStore::from_tcx(tcx).allocator_kind(), alloc_error_handler_kind: |tcx, ()| CStore::from_tcx(tcx).alloc_error_handler_kind(), diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index d4314978819..ac55497f8b3 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -34,6 +34,7 @@ use crate::infer::MemberConstraint; use crate::mir::ConstraintCategory; use crate::ty::{self, GenericArg, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; +pub type CanonicalQueryInput<'tcx, V> = ir::CanonicalQueryInput<TyCtxt<'tcx>, V>; pub type Canonical<'tcx, V> = ir::Canonical<TyCtxt<'tcx>, V>; pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo<TyCtxt<'tcx>>; pub type CanonicalVarValues<'tcx> = ir::CanonicalVarValues<TyCtxt<'tcx>>; @@ -182,7 +183,6 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { max_universe: ty::UniverseIndex::ROOT, variables: List::empty(), value: key, - defining_opaque_types: ty::List::empty(), }; } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 04d035e27ba..ac3baf74ca7 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -18,9 +18,9 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ - AllocId, BadBytesAccess, CtfeProvenance, InterpError, InterpResult, Pointer, PointerArithmetic, - Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, - UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, + AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, + PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, + UndefinedBehaviorInfo, UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -199,22 +199,22 @@ impl From<ScalarSizeMismatch> for AllocError { } impl AllocError { - pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpError<'tcx> { + pub fn to_interp_error<'tcx>(self, alloc_id: AllocId) -> InterpErrorKind<'tcx> { use AllocError::*; match self { ScalarSizeMismatch(s) => { - InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) } - ReadPointerAsInt(info) => InterpError::Unsupported( + ReadPointerAsInt(info) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - OverwritePartialPointer(offset) => InterpError::Unsupported( + OverwritePartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), ), - ReadPartialPointer(offset) => InterpError::Unsupported( + ReadPartialPointer(offset) => InterpErrorKind::Unsupported( UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), - InvalidUninitBytes(info) => InterpError::UndefinedBehavior( + InvalidUninitBytes(info) => InterpErrorKind::UndefinedBehavior( UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))), ), } @@ -318,7 +318,7 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> { pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { Self::uninit_inner(size, align, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); - InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) + InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) .into() } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index fcb87e19435..b520f21ce20 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -113,7 +113,7 @@ pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>); #[derive(Debug)] struct InterpErrorInfoInner<'tcx> { - kind: InterpError<'tcx>, + kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace, } @@ -154,21 +154,21 @@ impl InterpErrorBacktrace { } impl<'tcx> InterpErrorInfo<'tcx> { - pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { + pub fn into_parts(self) -> (InterpErrorKind<'tcx>, InterpErrorBacktrace) { let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) } - pub fn into_kind(self) -> InterpError<'tcx> { + pub fn into_kind(self) -> InterpErrorKind<'tcx> { self.0.kind } - pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { + pub fn from_parts(kind: InterpErrorKind<'tcx>, backtrace: InterpErrorBacktrace) -> Self { Self(Box::new(InterpErrorInfoInner { kind, backtrace })) } #[inline] - pub fn kind(&self) -> &InterpError<'tcx> { + pub fn kind(&self) -> &InterpErrorKind<'tcx> { &self.0.kind } } @@ -179,13 +179,13 @@ fn print_backtrace(backtrace: &Backtrace) { impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { fn from(err: ErrorGuaranteed) -> Self { - InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() + InterpErrorKind::InvalidProgram(InvalidProgramInfo::AlreadyReported(err.into())).into() } } impl From<ErrorHandled> for InterpErrorInfo<'_> { fn from(err: ErrorHandled) -> Self { - InterpError::InvalidProgram(match err { + InterpErrorKind::InvalidProgram(match err { ErrorHandled::Reported(r, _span) => InvalidProgramInfo::AlreadyReported(r), ErrorHandled::TooGeneric(_span) => InvalidProgramInfo::TooGeneric, }) @@ -193,8 +193,8 @@ impl From<ErrorHandled> for InterpErrorInfo<'_> { } } -impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> { - fn from(kind: InterpError<'tcx>) -> Self { +impl<'tcx> From<InterpErrorKind<'tcx>> for InterpErrorInfo<'tcx> { + fn from(kind: InterpErrorKind<'tcx>) -> Self { InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace: InterpErrorBacktrace::new(), @@ -590,7 +590,7 @@ impl dyn MachineStopType { } #[derive(Debug)] -pub enum InterpError<'tcx> { +pub enum InterpErrorKind<'tcx> { /// The program caused undefined behavior. UndefinedBehavior(UndefinedBehaviorInfo<'tcx>), /// The program did something the interpreter does not support (some of these *might* be UB @@ -606,25 +606,25 @@ pub enum InterpError<'tcx> { MachineStop(Box<dyn MachineStopType>), } -impl InterpError<'_> { +impl InterpErrorKind<'_> { /// Some errors do string formatting even if the error is never printed. /// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors, /// so this method lets us detect them and `bug!` on unexpected errors. pub fn formatted_string(&self) -> bool { matches!( self, - InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) + InterpErrorKind::Unsupported(UnsupportedOpInfo::Unsupported(_)) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) + | InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) ) } } -// Macros for constructing / throwing `InterpError` +// Macros for constructing / throwing `InterpErrorKind` #[macro_export] macro_rules! err_unsup { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::Unsupported( + $crate::mir::interpret::InterpErrorKind::Unsupported( $crate::mir::interpret::UnsupportedOpInfo::$($tt)* ) }; @@ -638,7 +638,7 @@ macro_rules! err_unsup_format { #[macro_export] macro_rules! err_inval { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::InvalidProgram( + $crate::mir::interpret::InterpErrorKind::InvalidProgram( $crate::mir::interpret::InvalidProgramInfo::$($tt)* ) }; @@ -647,7 +647,7 @@ macro_rules! err_inval { #[macro_export] macro_rules! err_ub { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::UndefinedBehavior( + $crate::mir::interpret::InterpErrorKind::UndefinedBehavior( $crate::mir::interpret::UndefinedBehaviorInfo::$($tt)* ) }; @@ -680,7 +680,7 @@ macro_rules! err_ub_custom { #[macro_export] macro_rules! err_exhaust { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::ResourceExhaustion( + $crate::mir::interpret::InterpErrorKind::ResourceExhaustion( $crate::mir::interpret::ResourceExhaustionInfo::$($tt)* ) }; @@ -689,7 +689,7 @@ macro_rules! err_exhaust { #[macro_export] macro_rules! err_machine_stop { ($($tt:tt)*) => { - $crate::mir::interpret::InterpError::MachineStop(Box::new($($tt)*)) + $crate::mir::interpret::InterpErrorKind::MachineStop(Box::new($($tt)*)) }; } @@ -792,9 +792,9 @@ impl<'tcx, T> ops::FromResidual for InterpResult_<'tcx, T> { } // Allow `yeet`ing `InterpError` in functions returning `InterpResult_`. -impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpError<'tcx>>> for InterpResult_<'tcx, T> { +impl<'tcx, T> ops::FromResidual<ops::Yeet<InterpErrorKind<'tcx>>> for InterpResult_<'tcx, T> { #[inline] - fn from_residual(ops::Yeet(e): ops::Yeet<InterpError<'tcx>>) -> Self { + fn from_residual(ops::Yeet(e): ops::Yeet<InterpErrorKind<'tcx>>) -> Self { Self::new(Err(e.into())) } } @@ -856,7 +856,7 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn map_err( + pub fn map_err_info( self, f: impl FnOnce(InterpErrorInfo<'tcx>) -> InterpErrorInfo<'tcx>, ) -> InterpResult<'tcx, T> { @@ -864,8 +864,19 @@ impl<'tcx, T> InterpResult_<'tcx, T> { } #[inline] - pub fn inspect_err(self, f: impl FnOnce(&InterpErrorInfo<'tcx>)) -> InterpResult<'tcx, T> { - InterpResult_::new(self.disarm().inspect_err(f)) + pub fn map_err_kind( + self, + f: impl FnOnce(InterpErrorKind<'tcx>) -> InterpErrorKind<'tcx>, + ) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().map_err(|mut e| { + e.0.kind = f(e.0.kind); + e + })) + } + + #[inline] + pub fn inspect_err_kind(self, f: impl FnOnce(&InterpErrorKind<'tcx>)) -> InterpResult<'tcx, T> { + InterpResult_::new(self.disarm().inspect_err(|e| f(&e.0.kind))) } #[inline] diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 115bcdbc589..790ff3e2fe0 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -36,7 +36,7 @@ pub use self::allocation::{ pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, - InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo, + InterpErrorInfo, InterpErrorKind, InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind, interp_ok, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 80adbe74fe7..ba7b57c891c 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -7,7 +7,7 @@ use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{DUMMY_SP, Span}; use rustc_target::abi; -use crate::infer::canonical::Canonical; +use crate::infer::canonical::CanonicalQueryInput; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::{TyAndLayout, ValidityRequirement}; use crate::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt}; @@ -485,7 +485,7 @@ impl Key for Option<Symbol> { /// Canonical query goals correspond to abstract trait operations that /// are not tied to any crate in particular. -impl<'tcx, T: Clone> Key for Canonical<'tcx, T> { +impl<'tcx, T: Clone> Key for CanonicalQueryInput<'tcx, T> { type Cache<V> = DefaultCache<Self, V>; fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f0be70e00df..f8ba606e087 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -65,10 +65,11 @@ use crate::query::plumbing::{ CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, }; use crate::traits::query::{ - CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal, - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, DropckConstraint, DropckOutlivesResult, - MethodAutoderefStepsResult, NoSolution, NormalizationResult, OutlivesBound, + CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, + CanonicalPredicateGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, + CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, + DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, + OutlivesBound, }; use crate::traits::{ CodegenObligationError, DynCompatibilityViolation, EvaluationResult, ImplSource, @@ -569,6 +570,7 @@ rustc_queries! { /// either `#[coverage(on)]` or no coverage attribute was found. query coverage_attr_on(key: LocalDefId) -> bool { desc { |tcx| "checking for `#[coverage(..)]` on `{}`", tcx.def_path_str(key) } + feedable } /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass @@ -2010,7 +2012,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value } + desc { "normalizing `{}`", goal.canonical.value.value } } /// <div class="warning"> @@ -2024,7 +2026,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value } + desc { "normalizing `{}`", goal.canonical.value.value } } /// <div class="warning"> @@ -2038,7 +2040,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value } + desc { "normalizing `{}`", goal.canonical.value.value } } /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead. @@ -2049,32 +2051,32 @@ rustc_queries! { } query implied_outlives_bounds_compat( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, NoSolution, > { - desc { "computing implied outlives bounds for `{}`", goal.value.value } + desc { "computing implied outlives bounds for `{}`", goal.canonical.value.value.ty } } query implied_outlives_bounds( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, NoSolution, > { - desc { "computing implied outlives bounds v2 for `{}`", goal.value.value } + desc { "computing implied outlives bounds v2 for `{}`", goal.canonical.value.value.ty } } /// Do not call this query directly: /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead. query dropck_outlives( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalDropckOutlivesGoal<'tcx> ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution, > { - desc { "computing dropck types for `{}`", goal.value.value } + desc { "computing dropck types for `{}`", goal.canonical.value.value.dropped_ty } } /// Do not call this query directly: invoke `infcx.predicate_may_hold()` or @@ -2082,7 +2084,7 @@ rustc_queries! { query evaluate_obligation( goal: CanonicalPredicateGoal<'tcx> ) -> Result<EvaluationResult, OverflowError> { - desc { "evaluating trait selection obligation `{}`", goal.value.value } + desc { "evaluating trait selection obligation `{}`", goal.canonical.value.value } } /// Do not call this query directly: part of the `Eq` type-op @@ -2092,7 +2094,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution, > { - desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.value.value } + desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal.canonical.value.value } } /// Do not call this query directly: part of the `ProvePredicate` type-op @@ -2102,7 +2104,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>, NoSolution, > { - desc { "evaluating `type_op_prove_predicate` `{:?}`", goal.value.value } + desc { "evaluating `type_op_prove_predicate` `{:?}`", goal.canonical.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2112,7 +2114,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>, NoSolution, > { - desc { "normalizing `{}`", goal.value.value.value } + desc { "normalizing `{}`", goal.canonical.value.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2122,7 +2124,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Clause<'tcx>>>, NoSolution, > { - desc { "normalizing `{:?}`", goal.value.value.value } + desc { "normalizing `{:?}`", goal.canonical.value.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2132,7 +2134,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>, NoSolution, > { - desc { "normalizing `{:?}`", goal.value.value.value } + desc { "normalizing `{:?}`", goal.canonical.value.value.value } } /// Do not call this query directly: part of the `Normalize` type-op @@ -2142,7 +2144,7 @@ rustc_queries! { &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>, NoSolution, > { - desc { "normalizing `{:?}`", goal.value.value.value } + desc { "normalizing `{:?}`", goal.canonical.value.value.value } } query instantiate_and_check_impossible_predicates(key: (DefId, GenericArgsRef<'tcx>)) -> bool { @@ -2163,7 +2165,7 @@ rustc_queries! { query method_autoderef_steps( goal: CanonicalTyGoal<'tcx> ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{}`", goal.value.value } + desc { "computing autoderef types for `{}`", goal.canonical.value.value } } query supported_target_features(_: CrateNum) -> &'tcx UnordMap<String, Option<Symbol>> { diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 81a543e647a..eeed5118592 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -11,16 +11,13 @@ use rustc_span::Span; pub use rustc_type_ir::solve::NoSolution; use crate::error::DropCheckOverflow; -use crate::infer::canonical::{Canonical, QueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; use crate::ty::{self, GenericArg, Ty, TyCtxt}; pub mod type_op { - use std::fmt; - use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; - use crate::ty::fold::TypeFoldable; - use crate::ty::{Predicate, Ty, TyCtxt, UserType}; + use crate::ty::{Predicate, Ty, UserType}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct AscribeUserType<'tcx> { @@ -28,12 +25,6 @@ pub mod type_op { pub user_ty: UserType<'tcx>, } - impl<'tcx> AscribeUserType<'tcx> { - pub fn new(mir_ty: Ty<'tcx>, user_ty: UserType<'tcx>) -> Self { - Self { mir_ty, user_ty } - } - } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Eq<'tcx> { pub a: Ty<'tcx>, @@ -51,46 +42,50 @@ pub mod type_op { pub predicate: Predicate<'tcx>, } - impl<'tcx> ProvePredicate<'tcx> { - pub fn new(predicate: Predicate<'tcx>) -> Self { - ProvePredicate { predicate } - } - } - #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Normalize<T> { pub value: T, } - impl<'tcx, T> Normalize<T> - where - T: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>, - { - pub fn new(value: T) -> Self { - Self { value } - } + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] + pub struct ImpliedOutlivesBounds<'tcx> { + pub ty: Ty<'tcx>, + } + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] + pub struct DropckOutlives<'tcx> { + pub dropped_ty: Ty<'tcx>, } } -pub type CanonicalAliasGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; +pub type CanonicalAliasGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; -pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; +pub type CanonicalTyGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; -pub type CanonicalPredicateGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; +pub type CanonicalPredicateGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; pub type CanonicalTypeOpAscribeUserTypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::AscribeUserType<'tcx>>>; -pub type CanonicalTypeOpEqGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; +pub type CanonicalTypeOpEqGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Eq<'tcx>>>; pub type CanonicalTypeOpSubtypeGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Subtype<'tcx>>>; pub type CanonicalTypeOpProvePredicateGoal<'tcx> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::ProvePredicate<'tcx>>>; pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = - Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>; + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>; + +pub type CanonicalImpliedOutlivesBoundsGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::ImpliedOutlivesBounds<'tcx>>>; + +pub type CanonicalDropckOutlivesGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::DropckOutlives<'tcx>>>; #[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)] pub struct DropckOutlivesResult<'tcx> { diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index d889bc90c9d..84c8a91b082 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -51,11 +51,20 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { // This will filter to functions with `extern "C-unwind"` ABIs, for // example. for block in body.basic_blocks.as_mut() { + let Some(terminator) = &mut block.terminator else { continue }; + let span = terminator.source_info.span; + + // If we see an `UnwindResume` terminator inside a function that cannot unwind, we need + // to replace it with `UnwindTerminate`. + if let TerminatorKind::UnwindResume = &terminator.kind + && !body_can_unwind + { + terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); + } + if block.is_cleanup { continue; } - let Some(terminator) = &block.terminator else { continue }; - let span = terminator.source_info.span; let call_can_unwind = match &terminator.kind { TerminatorKind::Call { func, .. } => { @@ -87,14 +96,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { if !call_can_unwind { // If this function call can't unwind, then there's no need for it // to have a landing pad. This means that we can remove any cleanup - // registered for it. + // registered for it (and turn it into `UnwindAction::Unreachable`). let cleanup = block.terminator_mut().unwind_mut().unwrap(); *cleanup = UnwindAction::Unreachable; - } else if !body_can_unwind { + } else if !body_can_unwind + && matches!(terminator.unwind(), Some(UnwindAction::Continue)) + { // Otherwise if this function can unwind, then if the outer function // can also unwind there's nothing to do. If the outer function - // can't unwind, however, we need to change the landing pad for this - // function call to one that aborts. + // can't unwind, however, we need to ensure that any `UnwindAction::Continue` + // is replaced with terminate. For those with `UnwindAction::Cleanup`, + // cleanup will still happen, and terminate will happen afterwards handled by + // the `UnwindResume` -> `UnwindTerminate` terminator replacement. let cleanup = block.terminator_mut().unwind_mut().unwrap(); *cleanup = UnwindAction::Terminate(UnwindTerminateReason::Abi); } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index cc4b7689d40..2c622b1927e 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -223,6 +223,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); + body_def.coverage_attr_on(tcx.coverage_attr_on(coroutine_def_id)); body_def.constness(tcx.constness(coroutine_def_id)); body_def.coroutine_kind(tcx.coroutine_kind(coroutine_def_id)); body_def.def_ident_span(tcx.def_ident_span(coroutine_def_id)); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d0f30314e79..2e4c503f3ce 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -524,6 +524,11 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back // to HIR for it. + // HACK: For synthetic MIR bodies (async closures), use the def id of the HIR body. + if tcx.is_synthetic_mir(def_id) { + return extract_hir_info(tcx, tcx.local_parent(def_id)); + } + let hir_node = tcx.hir_node_by_def_id(def_id); let fn_body_id = hir_node.body_id().expect("HIR node is a function with body"); let hir_body = tcx.hir().body(fn_body_id); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8f490094d60..2f97c408f2a 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -232,7 +232,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, { f(self) - .map_err(|err| { + .map_err_info(|err| { trace!("InterpCx operation failed: {:?}", err); // Some errors shouldn't come up because creating them causes // an allocation, which we should avoid. When that happens, diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 0bf9d7b9249..23634d35c07 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -83,8 +83,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { let (max_universe, variables) = canonicalizer.finalize(); - let defining_opaque_types = delegate.defining_opaque_types(); - Canonical { defining_opaque_types, max_universe, variables, value } + Canonical { max_universe, variables, value } } fn get_or_insert_bound_var( diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 6a3d58b5906..4da1e7fa711 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -4,9 +4,8 @@ use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::solve::{Certainty, Goal, NoSolution, SolverMode}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; -pub trait SolverDelegate: - Deref<Target: InferCtxtLike<Interner = <Self as SolverDelegate>::Interner>> + Sized -{ +pub trait SolverDelegate: Deref<Target = <Self as SolverDelegate>::Infcx> + Sized { + type Infcx: InferCtxtLike<Interner = <Self as SolverDelegate>::Interner>; type Interner: Interner; fn cx(&self) -> Self::Interner { (**self).cx() @@ -17,7 +16,7 @@ pub trait SolverDelegate: fn build_with_canonical<V>( cx: Self::Interner, solver_mode: SolverMode, - canonical: &ty::Canonical<Self::Interner, V>, + canonical: &ty::CanonicalQueryInput<Self::Interner, V>, ) -> (Self, V, ty::CanonicalVarValues<Self::Interner>) where V: TypeFoldable<Self::Interner>; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index fdefec33eeb..f49f3a1a3bf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -60,7 +60,7 @@ where (goal, opaque_types).fold_with(&mut EagerResolver::new(self.delegate)); let mut orig_values = Default::default(); - let canonical_goal = Canonicalizer::canonicalize( + let canonical = Canonicalizer::canonicalize( self.delegate, CanonicalizeMode::Input, &mut orig_values, @@ -71,7 +71,11 @@ where .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), }, ); - (orig_values, canonical_goal) + let query_input = ty::CanonicalQueryInput { + canonical, + defining_opaque_types: self.delegate.defining_opaque_types(), + }; + (orig_values, query_input) } /// To return the constraints of a canonical query to the caller, we canonicalize: diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 0f8b796d602..cbefc826fb7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -283,11 +283,11 @@ where let mut ecx = EvalCtxt { delegate, - variables: canonical_input.variables, + variables: canonical_input.canonical.variables, var_values, is_normalizes_to_goal: false, predefined_opaques_in_body: input.predefined_opaques_in_body, - max_input_universe: canonical_input.max_universe, + max_input_universe: canonical_input.canonical.max_universe, search_graph, nested_goals: NestedGoals::new(), tainted: Ok(()), diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8fe39bb4ee1..ff91fa13fd0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -313,6 +313,5 @@ fn response_no_constraints_raw<I: Interner>( external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), certainty, }, - defining_opaque_types: Default::default(), } } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 0e3f179b0c8..843200ca184 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -96,14 +96,19 @@ where } fn step_is_coinductive(cx: I, input: CanonicalInput<I>) -> bool { - input.value.goal.predicate.is_coinductive(cx) + input.canonical.value.goal.predicate.is_coinductive(cx) } } fn response_no_constraints<I: Interner>( cx: I, - goal: CanonicalInput<I>, + input: CanonicalInput<I>, certainty: Certainty, ) -> QueryResult<I> { - Ok(super::response_no_constraints_raw(cx, goal.max_universe, goal.variables, certainty)) + Ok(super::response_no_constraints_raw( + cx, + input.canonical.max_universe, + input.canonical.variables, + certainty, + )) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 5828b2ecf34..2cbed0bceb2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -785,7 +785,8 @@ where let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). - if a_data.principal_def_id() == b_data.principal_def_id() { + let b_principal_def_id = b_data.principal_def_id(); + if a_data.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() { responses.extend(self.consider_builtin_upcast_to_principal( goal, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 3080a0c9892..e36055e8575 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -184,11 +184,11 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { // If the extern crate isn't in the extern prelude, // there is no way it can be written as a `use`. - if !self + if self .r .extern_prelude .get(&extern_crate.ident) - .is_some_and(|entry| !entry.introduced_by_item) + .is_none_or(|entry| entry.introduced_by_item) { continue; } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b84cbf9c629..98db36b12be 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4011,6 +4011,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let instead = res.is_some(); let suggestion = if let Some((start, end)) = this.diag_metadata.in_range && path[0].ident.span.lo() == end.span.lo() + && !matches!(start.kind, ExprKind::Lit(_)) { let mut sugg = "."; let mut span = start.span.between(end.span); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 82e11a3afce..f4b45a08195 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2515,6 +2515,13 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati } impl TargetOptions { + pub fn supports_comdat(&self) -> bool { + // XCOFF and MachO don't support COMDAT. + !self.is_like_aix && !self.is_like_osx + } +} + +impl TargetOptions { fn link_args(flavor: LinkerFlavor, args: &[&'static str]) -> LinkArgs { let mut link_args = LinkArgs::new(); add_link_args(&mut link_args, flavor, args); diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index e869314d4d8..68d51193564 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD, + supports_xray: true, direct_access_external_data: Some(false), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs index 70e8bf633a9..25d3559d920 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs @@ -24,6 +24,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD, + supports_xray: true, + direct_access_external_data: Some(false), ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs index 90bcd9a45cf..12e026294cf 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs @@ -1,4 +1,4 @@ -use crate::spec::{SanitizerSet, Target, TargetOptions, base}; +use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,6 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { + code_model: Some(CodeModel::Medium), cpu: "generic".into(), features: "+f,+d".into(), llvm_abiname: "lp64d".into(), @@ -22,6 +23,8 @@ pub(crate) fn target() -> Target { | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD, + supports_xray: true, + direct_access_external_data: Some(false), ..base::linux_ohos::opts() }, } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5076152dbff..44373ca4866 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1277,19 +1277,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let normalized_term = ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); - let is_normalized_term_expected = !matches!( - obligation.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - | ObligationCauseCode::Coercion { .. } - ); - - let (expected, actual) = if is_normalized_term_expected { - (normalized_term, data.term) - } else { - (data.term, normalized_term) - }; - // constrain inference variables a bit more to nested obligations from normalize so // we can have more helpful errors. // @@ -1298,12 +1285,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let _ = ocx.select_where_possible(); if let Err(new_err) = - ocx.eq(&obligation.cause, obligation.param_env, expected, actual) + ocx.eq(&obligation.cause, obligation.param_env, data.term, normalized_term) { ( Some(( data.projection_term, - is_normalized_term_expected, + false, self.resolve_vars_if_possible(normalized_term), data.term, )), @@ -1444,12 +1431,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &mut diag, &obligation.cause, secondary_span, - values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { - infer::ValuePairs::Terms(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - expected_ty, - )) + values.map(|(_, _, normalized_ty, expected_ty)| { + infer::ValuePairs::Terms(ExpectedFound::new(true, expected_ty, normalized_ty)) }), err, false, diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index f232a896f96..bacb3b1b1b8 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -5,7 +5,9 @@ use rustc_hir::lang_items::LangItem; pub use rustc_infer::infer::*; use rustc_macros::extension; use rustc_middle::arena::ArenaAllocatable; -use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; +use rustc_middle::infer::canonical::{ + Canonical, CanonicalQueryInput, CanonicalQueryResponse, QueryResponse, +}; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; use rustc_span::DUMMY_SP; @@ -132,7 +134,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { /// `K: TypeFoldable<TyCtxt<'tcx>>`.) fn enter_canonical_trait_query<K, R>( self, - canonical_key: &Canonical<'tcx, K>, + canonical_key: &CanonicalQueryInput<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> where diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index df9ac2b80fd..5793ac2fc31 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ - Canonical, CanonicalExt as _, CanonicalVarInfo, CanonicalVarValues, + Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues, }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::Goal; @@ -36,6 +36,7 @@ impl<'tcx> Deref for SolverDelegate<'tcx> { } impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> { + type Infcx = InferCtxt<'tcx>; type Interner = TyCtxt<'tcx>; fn cx(&self) -> TyCtxt<'tcx> { @@ -47,7 +48,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn build_with_canonical<V>( interner: TyCtxt<'tcx>, solver_mode: SolverMode, - canonical: &Canonical<'tcx, V>, + canonical: &CanonicalQueryInput<'tcx, V>, ) -> (Self, V, CanonicalVarValues<'tcx>) where V: TypeFoldable<TyCtxt<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 339e4bf1f22..88c11e55b7a 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,6 +1,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_macros::extension; use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; use rustc_middle::span_bug; @@ -54,11 +55,12 @@ fn implied_outlives_bounds<'a, 'tcx>( assert!(!ty.has_non_region_infer()); let mut canonical_var_values = OriginalQueryValues::default(); - let canonical_ty = infcx.canonicalize_query(param_env.and(ty), &mut canonical_var_values); + let input = ImpliedOutlivesBounds { ty }; + let canonical = infcx.canonicalize_query(param_env.and(input), &mut canonical_var_values); let implied_bounds_result = if compat { - infcx.tcx.implied_outlives_bounds_compat(canonical_ty) + infcx.tcx.implied_outlives_bounds_compat(canonical) } else { - infcx.tcx.implied_outlives_bounds(canonical_ty) + infcx.tcx.implied_outlives_bounds(canonical) }; let Ok(canonical_result) = implied_bounds_result else { return vec![]; diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index c70fe13fc69..4ff0910c9b9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -1,4 +1,5 @@ use rustc_data_structures::fx::FxHashSet; +use rustc_infer::traits::query::type_op::DropckOutlives; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::{DUMMY_SP, Span}; @@ -88,10 +89,10 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn compute_dropck_outlives_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, - goal: ParamEnvAnd<'tcx, Ty<'tcx>>, + goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>, ) -> Result<DropckOutlivesResult<'tcx>, NoSolution> { let tcx = ocx.infcx.tcx; - let ParamEnvAnd { param_env, value: for_ty } = goal; + let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal; let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; @@ -99,7 +100,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // something from the stack and invoke // `dtorck_constraint_for_ty_inner`. This may produce new types that // have to be pushed on the stack. This continues until we have explored - // all the reachable types from the type `for_ty`. + // all the reachable types from the type `dropped_ty`. // // Example: Imagine that we have the following code: // @@ -129,7 +130,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // lead to us trying to push `A` a second time -- to prevent // infinite recursion, we notice that `A` was already pushed // once and stop. - let mut ty_stack = vec![(for_ty, 0)]; + let mut ty_stack = vec![(dropped_ty, 0)]; // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index c84c3147a38..dc3f5544613 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, User use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { @@ -22,7 +22,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { tcx.type_op_ascribe_user_type(canonicalized) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index bab038af9ed..dfd0cab6905 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,7 +1,7 @@ -use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::canonical::CanonicalQueryInput; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::OutlivesBound; -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; +use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; @@ -14,11 +14,6 @@ use tracing::debug; use crate::traits::query::NoSolution; use crate::traits::{ObligationCtxt, wf}; -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] -pub struct ImpliedOutlivesBounds<'tcx> { - pub ty: Ty<'tcx>, -} - impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { type QueryResponse = Vec<OutlivesBound<'tcx>>; @@ -38,16 +33,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { - // FIXME this `unchecked_map` is only necessary because the - // query is defined as taking a `ParamEnvAnd<Ty>`; it should - // take an `ImpliedOutlivesBounds` instead - let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { - let ImpliedOutlivesBounds { ty } = value; - param_env.and(ty) - }); - if tcx.sess.opts.unstable_opts.no_implied_bounds_compat { tcx.implied_outlives_bounds(canonicalized) } else { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 5ae8c87ec02..a618d96ce95 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -1,7 +1,6 @@ use std::fmt; use rustc_errors::ErrorGuaranteed; -use rustc_infer::infer::canonical::Certainty; use rustc_infer::traits::PredicateObligations; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::TypeFoldable; @@ -9,7 +8,8 @@ use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; use rustc_span::Span; use crate::infer::canonical::{ - Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, + CanonicalQueryInput, CanonicalQueryResponse, Certainty, OriginalQueryValues, + QueryRegionConstraints, }; use crate::infer::{InferCtxt, InferOk}; use crate::traits::{ObligationCause, ObligationCtxt}; @@ -80,7 +80,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't /// not captured in the return value. fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>; /// In the new trait solver, we already do caching in the solver itself, @@ -102,7 +102,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't ) -> Result< ( Self::QueryResponse, - Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, + Option<CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>>, PredicateObligations<'tcx>, Certainty, ), @@ -135,7 +135,7 @@ where Q: QueryTypeOp<'tcx>, { type Output = Q::QueryResponse; - type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; + type ErrorInfo = CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Q>>; fn fully_perform( self, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 62d5655922b..94df222932e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -6,7 +6,7 @@ pub use rustc_middle::traits::query::type_op::Normalize; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<T> @@ -21,7 +21,7 @@ where fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { T::type_op_method(tcx, canonicalized) } @@ -40,14 +40,14 @@ pub trait Normalizable<'tcx>: { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution>; } impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { tcx.type_op_normalize_ty(canonicalized) } @@ -56,7 +56,7 @@ impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { tcx.type_op_normalize_clause(canonicalized) } @@ -65,7 +65,7 @@ impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { tcx.type_op_normalize_poly_fn_sig(canonicalized) } @@ -74,7 +74,7 @@ impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { tcx.type_op_normalize_fn_sig(canonicalized) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index d891d4ca06f..fa05f901f66 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,23 +1,12 @@ -use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; -use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; use crate::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, trivial_dropck_outlives, }; - -#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] -pub struct DropckOutlives<'tcx> { - dropped_ty: Ty<'tcx>, -} - -impl<'tcx> DropckOutlives<'tcx> { - pub fn new(dropped_ty: Ty<'tcx>) -> Self { - DropckOutlives { dropped_ty } - } -} +use crate::traits::query::type_op::DropckOutlives; impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { type QueryResponse = DropckOutlivesResult<'tcx>; @@ -31,16 +20,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { - // FIXME convert to the type expected by the `dropck_outlives` - // query. This should eventually be fixed by changing the - // *underlying query*. - let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { - let DropckOutlives { dropped_ty } = value; - param_env.and(dropped_ty) - }); - tcx.dropck_outlives(canonicalized) } @@ -48,6 +29,6 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, ) -> Result<Self::QueryResponse, NoSolution> { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value.dropped_ty)) + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value)) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 7cdb9ee691e..b2dab379262 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -5,7 +5,7 @@ use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { @@ -49,7 +49,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { tcx.type_op_prove_predicate(canonicalized) } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 52048ca79f9..aa313a526c1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -1018,7 +1018,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // #2 (region bounds). let principal_def_id_a = a_data.principal_def_id(); let principal_def_id_b = b_data.principal_def_id(); - if principal_def_id_a == principal_def_id_b { + if principal_def_id_a == principal_def_id_b || principal_def_id_b.is_none() { // We may upcast to auto traits that are either explicitly listed in // the object type's bounds, or implied by the principal trait ref's // supertraits. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index cc5c7532b50..0ba3b4e6e55 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1153,6 +1153,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let iter = data_a .principal() + .filter(|_| { + // optionally drop the principal, if we're unsizing to no principal + data_b.principal().is_some() + }) .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) .into_iter() .chain( diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 6e6f948a2cd..ed221e2a183 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -154,18 +154,17 @@ fn prepare_vtable_segments_inner<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { + let has_entries = + has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()); + segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: inner_most_trait_ref, - emit_vptr: emit_vptr && !tcx.sess.opts.unstable_opts.no_trait_vptr, + emit_vptr: emit_vptr && has_entries && !tcx.sess.opts.unstable_opts.no_trait_vptr, })?; // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, // we'll need to emit vptrs from now on. - if !emit_vptr_on_new_entry - && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) - { - emit_vptr_on_new_entry = true; - } + emit_vptr_on_new_entry |= has_entries; if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.upcast(tcx))) diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 0d052ecf0df..4e5309eea28 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, dtorck_constraint_for_ty_inner, }; -use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::query::{CanonicalDropckOutlivesGoal, NoSolution}; use tracing::debug; pub(crate) fn provide(p: &mut Providers) { @@ -19,7 +19,7 @@ pub(crate) fn provide(p: &mut Providers) { fn dropck_outlives<'tcx>( tcx: TyCtxt<'tcx>, - canonical_goal: CanonicalTyGoal<'tcx>, + canonical_goal: CanonicalDropckOutlivesGoal<'tcx>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { debug!("dropck_outlives(goal={:#?})", canonical_goal); diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index f9e1db567c2..a51eefd908c 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -5,13 +5,14 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::canonical::{self, Canonical}; use rustc_infer::traits::query::OutlivesBound; +use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, }; -use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution}; +use rustc_trait_selection::traits::query::{CanonicalImpliedOutlivesBoundsGoal, NoSolution}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { implied_outlives_bounds_compat, ..*p }; @@ -20,26 +21,26 @@ pub(crate) fn provide(p: &mut Providers) { fn implied_outlives_bounds_compat<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx>, ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, NoSolution, > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { - let (param_env, ty) = key.into_parts(); + let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) }) } fn implied_outlives_bounds<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, + goal: CanonicalImpliedOutlivesBoundsGoal<'tcx>, ) -> Result< &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>, NoSolution, > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { - let (param_env, ty) = key.into_parts(); + let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); compute_implied_outlives_bounds_inner(ocx, param_env, ty) }) } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index c982cd66bca..71088a598bb 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::canonical::{Canonical, QueryResponse}; +use rustc_infer::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; @@ -28,7 +28,7 @@ pub(crate) fn provide(p: &mut Providers) { fn type_op_ascribe_user_type<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { type_op_ascribe_user_type_with_span(ocx, key, None) @@ -51,35 +51,35 @@ where fn type_op_normalize_ty<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Ty<'tcx>>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Ty<'tcx>>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_normalize_clause<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Clause<'tcx>>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Clause<'tcx>>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Clause<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_normalize_fn_sig<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<FnSig<'tcx>>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<FnSig<'tcx>>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, FnSig<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_normalize_poly_fn_sig<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<PolyFnSig<'tcx>>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<PolyFnSig<'tcx>>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, PolyFnSig<'tcx>>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize) } fn type_op_prove_predicate<'tcx>( tcx: TyCtxt<'tcx>, - canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { type_op_prove_predicate_with_cause(ocx, key, ObligationCause::dummy()); diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 7354ea5fb6a..13691204c96 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -728,6 +728,49 @@ fn fn_abi_adjust_for_abi<'tcx>( }; } + if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 { + // Return values larger than 2 registers using a return area + // pointer. LLVM and Cranelift disagree about how to return + // values that don't fit in the registers designated for return + // values. LLVM will force the entire return value to be passed + // by return area pointer, while Cranelift will look at each IR level + // return value independently and decide to pass it in a + // register or not, which would result in the return value + // being passed partially in registers and partially through a + // return area pointer. + // + // While Cranelift may need to be fixed as the LLVM behavior is + // generally more correct with respect to the surface language, + // forcing this behavior in rustc itself makes it easier for + // other backends to conform to the Rust ABI and for the C ABI + // rustc already handles this behavior anyway. + // + // In addition LLVM's decision to pass the return value in + // registers or using a return area pointer depends on how + // exactly the return type is lowered to an LLVM IR type. For + // example `Option<u128>` can be lowered as `{ i128, i128 }` + // in which case the x86_64 backend would use a return area + // pointer, or it could be passed as `{ i32, i128 }` in which + // case the x86_64 backend would pass it in registers by taking + // advantage of an LLVM ABI extension that allows using 3 + // registers for the x86_64 sysv call conv rather than the + // officially specified 2 registers. + // + // FIXME: Technically we should look at the amount of available + // return registers rather than guessing that there are 2 + // registers for return values. In practice only a couple of + // architectures have less than 2 return registers. None of + // which supported by Cranelift. + // + // NOTE: This adjustment is only necessary for the Rust ABI as + // for other ABI's the calling convention implementations in + // rustc_target already ensure any return value which doesn't + // fit in the available amount of return registers is passed in + // the right way for the current target. + arg.make_indirect(); + return; + } + match arg.layout.abi { Abi::Aggregate { .. } => {} diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index afdfa2e80c1..38b292afe8d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -30,7 +30,8 @@ use {rustc_abi as abi, rustc_hir as hir}; use crate::errors::{ MultipleArrayFieldsSimdType, NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType, }; -use crate::layout_sanity_check::sanity_check_layout; + +mod invariant; pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { layout_of, ..*providers }; @@ -79,7 +80,7 @@ fn layout_of<'tcx>( record_layout_for_printing(&cx, layout); } - sanity_check_layout(&cx, &layout); + invariant::partially_check_layout(&cx, &layout); Ok(layout) } @@ -115,6 +116,11 @@ fn map_error<'tcx>( cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}")); LayoutError::Unknown(ty) } + LayoutCalculatorError::ReprConflict => { + // packed enums are the only known trigger of this, but others might arise + cx.tcx().dcx().delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}")); + LayoutError::Unknown(ty) + } }; error(cx, err) } diff --git a/compiler/rustc_ty_utils/src/layout_sanity_check.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index be0a7c5ee89..6cf114b74c1 100644 --- a/compiler/rustc_ty_utils/src/layout_sanity_check.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; use rustc_target::abi::*; /// Enforce some basic invariants on layouts. -pub(super) fn sanity_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { +pub(super) fn partially_check_layout<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); // Type-level uninhabitedness should always imply ABI uninhabitedness. diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index dc5303317a8..8be1611bb9a 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -29,7 +29,6 @@ mod errors; mod implied_bounds; mod instance; mod layout; -mod layout_sanity_check; mod needs_drop; mod opaque_types; mod representability; diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index d609e5add14..07cb8b037ec 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -10,6 +10,18 @@ use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Gen use crate::inherent::*; use crate::{self as ty, Interner, UniverseIndex}; +#[derive_where(Clone; I: Interner, V: Clone)] +#[derive_where(Hash; I: Interner, V: Hash)] +#[derive_where(PartialEq; I: Interner, V: PartialEq)] +#[derive_where(Eq; I: Interner, V: Eq)] +#[derive_where(Debug; I: Interner, V: fmt::Debug)] +#[derive_where(Copy; I: Interner, V: Copy)] +#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))] +pub struct CanonicalQueryInput<I: Interner, V> { + pub canonical: Canonical<I, V>, + pub defining_opaque_types: I::DefiningOpaqueTypes, +} + /// A "canonicalized" type `V` is one where all free inference /// variables have been rewritten to "canonical vars". These are /// numbered starting from 0 in order of first appearance. @@ -24,8 +36,6 @@ use crate::{self as ty, Interner, UniverseIndex}; pub struct Canonical<I: Interner, V> { pub value: V, pub max_universe: UniverseIndex, - // FIXME(lcnr, oli-obk): try moving this into the query inputs instead - pub defining_opaque_types: I::DefiningOpaqueTypes, pub variables: I::CanonicalVars, } @@ -54,27 +64,17 @@ impl<I: Interner, V> Canonical<I, V> { /// let b: Canonical<I, (T, Ty<I>)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map<W>(self, map_op: impl FnOnce(V) -> W) -> Canonical<I, W> { - let Canonical { defining_opaque_types, max_universe, variables, value } = self; - Canonical { defining_opaque_types, max_universe, variables, value: map_op(value) } - } - - /// Allows you to map the `value` of a canonical while keeping the same set of - /// bound variables. - /// - /// **WARNING:** This function is very easy to mis-use, hence the name! See - /// the comment of [Canonical::unchecked_map] for more details. - pub fn unchecked_rebind<W>(self, value: W) -> Canonical<I, W> { - let Canonical { defining_opaque_types, max_universe, variables, value: _ } = self; - Canonical { defining_opaque_types, max_universe, variables, value } + let Canonical { max_universe, variables, value } = self; + Canonical { max_universe, variables, value: map_op(value) } } } impl<I: Interner, V: fmt::Display> fmt::Display for Canonical<I, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { value, max_universe, variables, defining_opaque_types } = self; + let Self { value, max_universe, variables } = self; write!( f, - "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?}, defining_opaque_types: {defining_opaque_types:?} }}", + "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?} }}", ) } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index f4fb03562de..3fd2bb61ba5 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -714,7 +714,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // current goal is already part of the same cycle. This check could be // improved but seems to be good enough for now. let last = self.stack.raw.last().unwrap(); - if !last.heads.opt_lowest_cycle_head().is_some_and(|lowest| lowest <= head) { + if last.heads.opt_lowest_cycle_head().is_none_or(|lowest| lowest > head) { continue; } } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index f02c7a32071..b3f8390bbf0 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -71,7 +71,8 @@ pub enum SolverMode { Coherence, } -pub type CanonicalInput<I, T = <I as Interner>::Predicate> = Canonical<I, QueryInput<I, T>>; +pub type CanonicalInput<I, T = <I as Interner>::Predicate> = + ty::CanonicalQueryInput<I, QueryInput<I, T>>; pub type CanonicalResponse<I> = Canonical<I, Response<I>>; /// The result of evaluating a canonical query. /// diff --git a/config.example.toml b/config.example.toml index 4b591b949b3..168ac353cff 100644 --- a/config.example.toml +++ b/config.example.toml @@ -414,6 +414,11 @@ # Specify the location of the Android NDK. Used when targeting Android. #android-ndk = "/path/to/android-ndk-r26d" +# Number of parallel jobs to be used for building and testing. If set to `0` or +# omitted, it will be automatically determined. This is the `-j`/`--jobs` flag +# passed to cargo invocations. +#jobs = 0 + # ============================================================================= # General install configuration options # ============================================================================= diff --git a/library/Cargo.lock b/library/Cargo.lock index 59b76d8d442..db60a484081 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.133" +version = "0.1.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10bf45b2ed1b4f4c25401527a61684142c042b3c86ace7da7ea6881e26741b" +checksum = "2f743e6f7410a78c261505c729f389583de40eec62332cc8cdf2c8b9bf73049a" dependencies = [ "cc", "rustc-std-workspace-core", @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.30.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -158,9 +158,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" dependencies = [ "rustc-std-workspace-core", ] @@ -340,7 +340,6 @@ dependencies = [ "object", "panic_abort", "panic_unwind", - "profiler_builtins", "r-efi", "r-efi-alloc", "rand", @@ -368,6 +367,7 @@ name = "sysroot" version = "0.0.0" dependencies = [ "proc_macro", + "profiler_builtins", "std", "test", ] @@ -406,12 +406,12 @@ dependencies = [ [[package]] name = "unwinding" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55842d0db6329a669d55a623c674b02d677b16bfb2d24857d4089d41eba882" +checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987" dependencies = [ "compiler_builtins", - "gimli 0.30.0", + "gimli 0.31.1", "rustc-std-workspace-core", ] diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 259a3ed2beb..6301ade2775 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.133", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.134", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 0cd410c0fb7..ca0ea1ec8b2 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1082,7 +1082,7 @@ impl<T, A: Allocator> LinkedList<T, A> { /// Retains only the elements specified by the predicate. /// - /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. /// This method operates in place, visiting each element exactly once in the /// original order, and preserves the order of the retained elements. /// diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 54739c50d1d..cf51a84bb6f 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2122,7 +2122,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// Retains only the elements specified by the predicate. /// - /// In other words, remove all elements `e` for which `f(&e)` returns false. + /// In other words, remove all elements `e` for which `f(&mut e)` returns false. /// This method operates in place, visiting each element exactly once in the /// original order, and preserves the order of the retained elements. /// diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 0a4a5160d82..ae9b3739858 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -112,7 +112,6 @@ #![feature(const_eval_select)] #![feature(const_heap)] #![feature(const_maybe_uninit_write)] -#![feature(const_pin)] #![feature(const_size_of_val)] #![feature(const_vec_string_slice)] #![feature(core_intrinsics)] diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 42501f9c315..52ceb8b45f9 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -20,7 +20,7 @@ pub use core::str::SplitInclusive; pub use core::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; -use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher}; +use core::str::pattern::{DoubleEndedSearcher, Pattern, ReverseSearcher, Searcher, Utf8Pattern}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{Bytes, CharIndices, Chars, from_utf8, from_utf8_mut}; #[stable(feature = "str_escape", since = "1.34.0")] @@ -269,6 +269,18 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn replace<P: Pattern>(&self, from: P, to: &str) -> String { + // Fast path for ASCII to ASCII case. + + if let Some(from_byte) = match from.as_utf8_pattern() { + Some(Utf8Pattern::StringPattern([from_byte])) => Some(*from_byte), + Some(Utf8Pattern::CharPattern(c)) => c.as_ascii().map(|ascii_char| ascii_char.to_u8()), + _ => None, + } { + if let [to_byte] = to.as_bytes() { + return unsafe { replace_ascii(self.as_bytes(), from_byte, *to_byte) }; + } + } + let mut result = String::new(); let mut last_end = 0; for (start, part) in self.match_indices(from) { @@ -686,3 +698,14 @@ pub fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, &str) { (ascii_string, rest) } } +#[inline] +#[cfg(not(test))] +#[cfg(not(no_global_oom_handling))] +#[allow(dead_code)] +/// Faster implementation of string replacement for ASCII to ASCII cases. +/// Should produce fast vectorized code. +unsafe fn replace_ascii(utf8_bytes: &[u8], from: u8, to: u8) -> String { + let result: Vec<u8> = utf8_bytes.iter().map(|b| if *b == from { to } else { *b }).collect(); + // SAFETY: We replaced ascii with ascii on valid utf8 strings. + unsafe { String::from_utf8_unchecked(result) } +} diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 82dbf030608..b042720933b 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -53,7 +53,7 @@ use core::ops::AddAssign; #[cfg(not(no_global_oom_handling))] use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Range, RangeBounds}; -use core::str::pattern::Pattern; +use core::str::pattern::{Pattern, Utf8Pattern}; use core::{fmt, hash, ptr, slice}; #[cfg(not(no_global_oom_handling))] @@ -2436,6 +2436,11 @@ impl<'b> Pattern for &'b String { { self[..].strip_suffix_of(haystack) } + + #[inline] + fn as_utf8_pattern(&self) -> Option<Utf8Pattern<'_>> { + Some(Utf8Pattern::StringPattern(self.as_bytes())) + } } macro_rules! impl_eq { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 69ad4f41519..38e858626b9 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -3138,7 +3138,7 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons /// [violate memory safety][read-ownership]. /// /// Note that even if the effectively copied size (`count * size_of::<T>()`) is -/// `0`, the pointers must be non-null and properly aligned. +/// `0`, the pointers must be properly aligned. /// /// [`read`]: crate::ptr::read /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value @@ -3261,7 +3261,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us /// [violate memory safety][read-ownership]. /// /// Note that even if the effectively copied size (`count * size_of::<T>()`) is -/// `0`, the pointers must be non-null and properly aligned. +/// `0`, the pointers must be properly aligned. /// /// [`read`]: crate::ptr::read /// [read-ownership]: crate::ptr::read#ownership-of-the-returned-value @@ -3342,7 +3342,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { /// * `dst` must be properly aligned. /// /// Note that even if the effectively copied size (`count * size_of::<T>()`) is -/// `0`, the pointer must be non-null and properly aligned. +/// `0`, the pointer must be properly aligned. /// /// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB) /// later if the written bytes are not a valid representation of some `T`. For instance, the diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 7e162ff387b..cc089c617c0 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -8,9 +8,7 @@ use crate::num::NonZero; /// The `repeat_n()` function repeats a single value exactly `n` times. /// /// This is very similar to using [`repeat()`] with [`Iterator::take()`], -/// but there are two differences: -/// - `repeat_n()` can return the original value, rather than always cloning. -/// - `repeat_n()` produces an [`ExactSizeIterator`]. +/// but `repeat_n()` can return the original value, rather than always cloning. /// /// [`repeat()`]: crate::iter::repeat /// diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6ed9ccaa694..ad034d3e576 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -129,7 +129,7 @@ #![feature(const_nonnull_new)] #![feature(const_num_midpoint)] #![feature(const_option_ext)] -#![feature(const_pin)] +#![feature(const_pin_2)] #![feature(const_pointer_is_aligned)] #![feature(const_ptr_is_null)] #![feature(const_ptr_sub_ptr)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 6a4f2af10ef..771c2d31b60 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1550,7 +1550,7 @@ pub(crate) mod builtin { /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. /// OUTPUT_ACTIVITY must not be set if we implicitely return nothing (or explicitely return - /// `-> ()`. Otherwise it must be set to one of the allowed activities. + /// `-> ()`). Otherwise it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index 64214eae377..dce3514a159 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -45,7 +45,8 @@ impl IndexRange { #[inline] pub const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap - unsafe { self.end.unchecked_sub(self.start) } + // Using the intrinsic because a UB check here impedes LLVM optimization. (#131563) + unsafe { crate::intrinsics::unchecked_sub(self.end, self.start) } } /// # Safety @@ -82,7 +83,8 @@ impl IndexRange { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. - unsafe { self.start.unchecked_add(n) } + // Using the intrinsic avoids a superfluous UB check. + unsafe { crate::intrinsics::unchecked_add(self.start, n) } } else { self.end }; @@ -100,8 +102,9 @@ impl IndexRange { pub fn take_suffix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, - // and thus the addition cannot overflow. - unsafe { self.end.unchecked_sub(n) } + // and thus the subtraction cannot overflow. + // Using the intrinsic avoids a superfluous UB check. + unsafe { crate::intrinsics::unchecked_sub(self.end, n) } } else { self.start }; diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index fac789dbd99..5d5733d38fc 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1186,7 +1186,7 @@ impl<Ptr: Deref<Target: Unpin>> Pin<Ptr> { /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val); /// ``` #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn new(pointer: Ptr) -> Pin<Ptr> { // SAFETY: the value pointed to is `Unpin`, and so has no requirements @@ -1214,7 +1214,7 @@ impl<Ptr: Deref<Target: Unpin>> Pin<Ptr> { /// assert_eq!(*r, 5); /// ``` #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_unstable(feature = "const_pin_2", issue = "76654")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const fn into_inner(pin: Pin<Ptr>) -> Ptr { pin.__pointer @@ -1351,7 +1351,7 @@ impl<Ptr: Deref> Pin<Ptr> { /// [`pin` module docs]: self #[lang = "new_unchecked"] #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const unsafe fn new_unchecked(pointer: Ptr) -> Pin<Ptr> { Pin { __pointer: pointer } @@ -1503,7 +1503,7 @@ impl<Ptr: Deref> Pin<Ptr> { /// If the underlying data is [`Unpin`], [`Pin::into_inner`] should be used /// instead. #[inline(always)] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_unstable(feature = "const_pin_2", issue = "76654")] #[stable(feature = "pin_into_inner", since = "1.39.0")] pub const unsafe fn into_inner_unchecked(pin: Pin<Ptr>) -> Ptr { pin.__pointer @@ -1559,7 +1559,7 @@ impl<'a, T: ?Sized> Pin<&'a T> { /// ["pinning projections"]: self#projections-and-structural-pinning #[inline(always)] #[must_use] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn get_ref(self) -> &'a T { self.__pointer @@ -1570,7 +1570,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { /// Converts this `Pin<&mut T>` into a `Pin<&T>` with the same lifetime. #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] #[stable(feature = "pin", since = "1.33.0")] pub const fn into_ref(self) -> Pin<&'a T> { Pin { __pointer: self.__pointer } @@ -1588,7 +1588,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn get_mut(self) -> &'a mut T where T: Unpin, @@ -1609,7 +1609,7 @@ impl<'a, T: ?Sized> Pin<&'a mut T> { #[inline(always)] #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "pin", since = "1.33.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn get_unchecked_mut(self) -> &'a mut T { self.__pointer } @@ -1652,7 +1652,7 @@ impl<T: ?Sized> Pin<&'static T> { /// This is safe because `T` is borrowed immutably for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn static_ref(r: &'static T) -> Pin<&'static T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). @@ -1666,7 +1666,7 @@ impl<T: ?Sized> Pin<&'static mut T> { /// This is safe because `T` is borrowed for the `'static` lifetime, which /// never ends. #[stable(feature = "pin_static_ref", since = "1.61.0")] - #[rustc_const_unstable(feature = "const_pin", issue = "76654")] + #[rustc_const_stable(feature = "const_pin", since = "CURRENT_RUSTC_VERSION")] pub const fn static_mut(r: &'static mut T) -> Pin<&'static mut T> { // SAFETY: The 'static borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 89936dc12ac..22fd47b0596 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -505,9 +505,11 @@ impl () {} /// /// *[See also the `std::ptr` module](ptr).* /// -/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. -/// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is -/// dereferenced (using the `*` operator), it must be non-null and aligned. +/// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. Raw pointers +/// can be out-of-bounds, unaligned, or [`null`]. However, when loading from or storing to a raw +/// pointer, it must be [valid] for the given access and aligned. When using a field expression, +/// tuple index expression, or array/slice index expression on a raw pointer, it follows the rules +/// of [in-bounds pointer arithmetic][`offset`]. /// /// Storing through a raw pointer using `*ptr = data` calls `drop` on the old value, so /// [`write`] must be used if the type has drop glue and memory is not already @@ -613,6 +615,7 @@ impl () {} /// [`offset`]: pointer::offset /// [`into_raw`]: ../std/boxed/struct.Box.html#method.into_raw /// [`write`]: ptr::write +/// [valid]: ptr#safety #[stable(feature = "rust1", since = "1.0.0")] mod prim_pointer {} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 09ff7f8cab1..f7036f30a99 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1024,7 +1024,7 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] { /// /// * Both `x` and `y` must be properly aligned. /// -/// Note that even if `T` has size `0`, the pointers must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointers must be properly aligned. /// /// [valid]: self#safety /// @@ -1110,7 +1110,7 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) { /// beginning at `y` with the same size. /// /// Note that even if the effectively copied size (`count * size_of::<T>()`) is `0`, -/// the pointers must be non-null and properly aligned. +/// the pointers must be properly aligned. /// /// [valid]: self#safety /// @@ -1243,7 +1243,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, coun /// /// * `dst` must point to a properly initialized value of type `T`. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// @@ -1300,7 +1300,7 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T { /// /// * `src` must point to a properly initialized value of type `T`. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// # Examples /// @@ -1555,7 +1555,7 @@ pub const unsafe fn read_unaligned<T>(src: *const T) -> T { /// * `dst` must be properly aligned. Use [`write_unaligned`] if this is not the /// case. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// @@ -1774,7 +1774,7 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) { /// However, storing non-[`Copy`] types in volatile memory is almost certainly /// incorrect. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// [read-ownership]: read#ownership-of-the-returned-value @@ -1853,7 +1853,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { /// /// * `dst` must be properly aligned. /// -/// Note that even if `T` has size `0`, the pointer must be non-null and properly aligned. +/// Note that even if `T` has size `0`, the pointer must be properly aligned. /// /// [valid]: self#safety /// diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 9f1294d7606..eb60effe813 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -160,6 +160,19 @@ pub trait Pattern: Sized { None } } + + /// Returns the pattern as utf-8 bytes if possible. + fn as_utf8_pattern(&self) -> Option<Utf8Pattern<'_>>; +} +/// Result of calling [`Pattern::as_utf8_pattern()`]. +/// Can be used for inspecting the contents of a [`Pattern`] in cases +/// where the underlying representation can be represented as UTF-8. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Utf8Pattern<'a> { + /// Type returned by String and str types. + StringPattern(&'a [u8]), + /// Type returned by char types. + CharPattern(char), } // Searcher @@ -599,6 +612,11 @@ impl Pattern for char { { self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) } + + #[inline] + fn as_utf8_pattern(&self) -> Option<Utf8Pattern<'_>> { + Some(Utf8Pattern::CharPattern(*self)) + } } ///////////////////////////////////////////////////////////////////////////// @@ -657,6 +675,11 @@ impl<C: MultiCharEq> Pattern for MultiCharEqPattern<C> { fn into_searcher(self, haystack: &str) -> MultiCharEqSearcher<'_, C> { MultiCharEqSearcher { haystack, char_eq: self.0, char_indices: haystack.char_indices() } } + + #[inline] + fn as_utf8_pattern(&self) -> Option<Utf8Pattern<'_>> { + None + } } unsafe impl<'a, C: MultiCharEq> Searcher<'a> for MultiCharEqSearcher<'a, C> { @@ -747,6 +770,11 @@ macro_rules! pattern_methods { { ($pmap)(self).strip_suffix_of(haystack) } + + #[inline] + fn as_utf8_pattern(&self) -> Option<Utf8Pattern<'_>> { + None + } }; } @@ -1022,6 +1050,11 @@ impl<'b> Pattern for &'b str { None } } + + #[inline] + fn as_utf8_pattern(&self) -> Option<Utf8Pattern<'_>> { + Some(Utf8Pattern::StringPattern(self.as_bytes())) + } } ///////////////////////////////////////////////////////////////////////////// diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index 711511eaf4a..32d0ac51f03 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -114,6 +114,7 @@ fn lazy_type_inference() { } #[test] +#[cfg(panic = "unwind")] #[should_panic = "LazyCell instance has previously been poisoned"] fn lazy_force_mut_panic() { let mut lazy = LazyCell::<String>::new(|| panic!()); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index bfc0b638b7e..443090097c0 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -21,7 +21,7 @@ #![feature(const_likely)] #![feature(const_nonnull_new)] #![feature(const_option_ext)] -#![feature(const_pin)] +#![feature(const_pin_2)] #![feature(const_pointer_is_aligned)] #![feature(const_three_way_compare)] #![feature(const_trait_impl)] diff --git a/library/core/tests/pin.rs b/library/core/tests/pin.rs index 7a6af46a743..026d2ca8de2 100644 --- a/library/core/tests/pin.rs +++ b/library/core/tests/pin.rs @@ -19,6 +19,10 @@ fn pin_const() { const REF: &'static usize = PINNED.get_ref(); assert_eq!(REF, POINTER); + const INT: u8 = 42; + const STATIC_REF: Pin<&'static u8> = Pin::static_ref(&INT); + assert_eq!(*STATIC_REF, INT); + // Note: `pin_mut_const` tests that the methods of `Pin<&mut T>` are usable in a const context. // A const fn is used because `&mut` is not (yet) usable in constants. const fn pin_mut_const() { diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 5a1086527a1..f6d4825c67b 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -18,17 +18,10 @@ macro_rules! define_client_handles { $(pub(super) $ity: AtomicU32,)* } - impl HandleCounters { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - extern "C" fn get() -> &'static Self { - static COUNTERS: HandleCounters = HandleCounters { - $($oty: AtomicU32::new(1),)* - $($ity: AtomicU32::new(1),)* - }; - &COUNTERS - } - } + static COUNTERS: HandleCounters = HandleCounters { + $($oty: AtomicU32::new(1),)* + $($ity: AtomicU32::new(1),)* + }; $( pub(crate) struct $oty { @@ -259,9 +252,7 @@ pub(crate) fn is_available() -> bool { /// and forcing the use of APIs that take/return `S::TokenStream`, server-side. #[repr(C)] pub struct Client<I, O> { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters, + pub(super) handle_counters: &'static HandleCounters, pub(super) run: extern "C" fn(BridgeConfig<'_>) -> Buffer, @@ -346,7 +337,7 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>( impl Client<crate::TokenStream, crate::TokenStream> { pub const fn expand1(f: impl Fn(crate::TokenStream) -> crate::TokenStream + Copy) -> Self { Client { - get_handle_counters: HandleCounters::get, + handle_counters: &COUNTERS, run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { run_client(bridge, |input| f(crate::TokenStream(Some(input))).0) }), @@ -360,7 +351,7 @@ impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> { f: impl Fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream + Copy, ) -> Self { Client { - get_handle_counters: HandleCounters::get, + handle_counters: &COUNTERS, run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { run_client(bridge, |(input, input2)| { f(crate::TokenStream(Some(input)), crate::TokenStream(Some(input2))).0 diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index 692b6038a38..97e5a603c3a 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -400,10 +400,10 @@ impl client::Client<crate::TokenStream, crate::TokenStream> { S: Server, S::TokenStream: Default, { - let client::Client { get_handle_counters, run, _marker } = *self; + let client::Client { handle_counters, run, _marker } = *self; run_server( strategy, - get_handle_counters(), + handle_counters, server, <MarkedTypes<S> as Types>::TokenStream::mark(input), run, @@ -426,10 +426,10 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream S: Server, S::TokenStream: Default, { - let client::Client { get_handle_counters, run, _marker } = *self; + let client::Client { handle_counters, run, _marker } = *self; run_server( strategy, - get_handle_counters(), + handle_counters, server, ( <MarkedTypes<S> as Types>::TokenStream::mark(input), diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 358bd25ff1b..bfb9df7d781 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,8 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.133" } -profiler_builtins = { path = "../profiler_builtins", optional = true } +compiler_builtins = { version = "0.1.134" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -39,7 +38,7 @@ miniz_oxide = { version = "0.7.0", optional = true, default-features = false } addr2line = { version = "0.22.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.159", default-features = false, features = [ +libc = { version = "0.2.161", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } @@ -98,7 +97,6 @@ backtrace = [ ] panic-unwind = ["panic_unwind"] -profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 453b2708daa..30d43c8bbfd 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -2146,10 +2146,13 @@ mod unsafe_keyword {} #[doc(keyword = "use")] // -/// Import or rename items from other crates or modules. +/// Import or rename items from other crates or modules, or specify precise capturing +/// with `use<..>`. /// -/// Usually a `use` keyword is used to shorten the path required to refer to a module item. -/// The keyword may appear in modules, blocks and even functions, usually at the top. +/// ## Importing items +/// +/// The `use` keyword is employed to shorten the path required to refer to a module item. +/// The keyword may appear in modules, blocks, and even functions, typically at the top. /// /// The most basic usage of the keyword is `use path::to::item;`, /// though a number of convenient shortcuts are supported: @@ -2190,19 +2193,48 @@ mod unsafe_keyword {} /// // Compiles. /// let _ = VariantA; /// -/// // Does not compile ! +/// // Does not compile! /// let n = new(); /// ``` /// -/// For more information on `use` and paths in general, see the [Reference]. +/// For more information on `use` and paths in general, see the [Reference][ref-use-decls]. /// /// The differences about paths and the `use` keyword between the 2015 and 2018 editions -/// can also be found in the [Reference]. +/// can also be found in the [Reference][ref-use-decls]. +/// +/// ## Precise capturing +/// +/// The `use<..>` syntax is used within certain `impl Trait` bounds to control which generic +/// parameters are captured. This is important for return-position `impl Trait` (RPIT) types, +/// as it affects borrow checking by controlling which generic parameters can be used in the +/// hidden type. +/// +/// For example, the following function demonstrates an error without precise capturing in +/// Rust 2021 and earlier editions: +/// +/// ```rust,compile_fail,edition2021 +/// fn f(x: &()) -> impl Sized { x } +/// ``` +/// +/// By using `use<'_>` for precise capturing, it can be resolved: +/// +/// ```rust +/// fn f(x: &()) -> impl Sized + use<'_> { x } +/// ``` +/// +/// This syntax specifies that the elided lifetime be captured and therefore available for +/// use in the hidden type. +/// +/// In Rust 2024, opaque types automatically capture all lifetime parameters in scope. +/// `use<..>` syntax serves as an important way of opting-out of that default. +/// +/// For more details about precise capturing, see the [Reference][ref-impl-trait]. /// /// [`crate`]: keyword.crate.html /// [`self`]: keyword.self.html /// [`super`]: keyword.super.html -/// [Reference]: ../reference/items/use-declarations.html +/// [ref-use-decls]: ../reference/items/use-declarations.html +/// [ref-impl-trait]: ../reference/types/impl-trait.html mod use_keyword {} #[doc(keyword = "where")] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 9aadd949116..ef5adaf2290 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -154,6 +154,7 @@ pub trait CommandExt: Sealed { /// required to gracefully handle errors it is recommended to use the /// cross-platform `spawn` instead. #[stable(feature = "process_exec2", since = "1.9.0")] + #[must_use] fn exec(&mut self) -> io::Error; /// Set executable argument diff --git a/library/std/src/os/xous/ffi.rs b/library/std/src/os/xous/ffi.rs index 1a4a940bf35..1db314e9dda 100644 --- a/library/std/src/os/xous/ffi.rs +++ b/library/std/src/os/xous/ffi.rs @@ -615,7 +615,7 @@ pub(crate) fn thread_id() -> Result<ThreadId, Error> { /// An error is generated if the `knob` is not a valid limit, or if the call /// would not succeed. pub(crate) fn adjust_limit(knob: Limits, current: usize, new: usize) -> Result<usize, Error> { - let mut a0 = Syscall::JoinThread as usize; + let mut a0 = Syscall::AdjustProcessLimit as usize; let mut a1 = knob as usize; let a2 = current; let a3 = new; diff --git a/library/std/src/random.rs b/library/std/src/random.rs index 604fa4df110..cdb88c795bf 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -40,6 +40,7 @@ use crate::sys::random as sys; /// Horizon | `getrandom` shim /// Hurd, L4Re, QNX | `/dev/urandom` /// Redox | `/scheme/rand` +/// RTEMS | [`arc4random_buf`](https://docs.rtems.org/branches/master/bsp-howto/getentropy.html) /// SGX | [`rdrand`](https://en.wikipedia.org/wiki/RDRAND) /// SOLID | `SOLID_RNG_SampleRandomBytes` /// TEEOS | `TEE_GenerateRandom` diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index 9ea43445d02..321d30e0b11 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use crate::alloc::{GlobalAlloc, Layout, System}; #[cfg(not(test))] diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 21c5facd52f..670383b45ac 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -3,9 +3,14 @@ use crate::ptr::null; use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU32; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU32; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 4ced7065c82..abc8e69a285 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,7 +10,7 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; -use r_efi::protocols::{device_path, device_path_to_text}; +use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_io_error}; @@ -424,3 +424,24 @@ pub(crate) fn os_string_to_raw(s: &OsStr) -> Option<Box<[r_efi::efi::Char16]>> { let temp = s.encode_wide().chain(Some(0)).collect::<Box<[r_efi::efi::Char16]>>(); if temp[..temp.len() - 1].contains(&0) { None } else { Some(temp) } } + +pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> { + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::<shell::Protocol>(handle, shell::PROTOCOL_GUID) { + return Some(protocol); + } + } + + let handles = locate_handles(shell::PROTOCOL_GUID).ok()?; + for handle in handles { + if let Ok(protocol) = open_protocol::<shell::Protocol>(handle, shell::PROTOCOL_GUID) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return Some(protocol); + } + } + + None +} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 4eb7698b43a..27395f7c3c0 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -125,7 +125,7 @@ pub fn error_string(errno: RawOsError) -> String { } pub fn getcwd() -> io::Result<PathBuf> { - match uefi_shell::open_shell() { + match helpers::open_shell() { Some(shell) => { // SAFETY: path_ptr is managed by UEFI shell and should not be deallocated let path_ptr = unsafe { ((*shell.as_ptr()).get_cur_dir)(crate::ptr::null_mut()) }; @@ -144,7 +144,7 @@ pub fn getcwd() -> io::Result<PathBuf> { } pub fn chdir(p: &path::Path) -> io::Result<()> { - let shell = uefi_shell::open_shell().ok_or(unsupported_err())?; + let shell = helpers::open_shell().ok_or(unsupported_err())?; let mut p = helpers::os_string_to_raw(p.as_os_str()) .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))?; @@ -192,44 +192,58 @@ pub fn current_exe() -> io::Result<PathBuf> { helpers::device_path_to_text(protocol).map(PathBuf::from) } -pub struct Env(!); +pub struct EnvStrDebug<'a> { + iter: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = f.debug_list(); + for (a, b) in self.iter { + list.entry(&(a.to_str().unwrap(), b.to_str().unwrap())); + } + list.finish() + } +} + +pub struct Env(crate::vec::IntoIter<(OsString, OsString)>); impl Env { // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} + EnvStrDebug { iter: self.0.as_slice() } } } impl Iterator for Env { type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 + self.0.next() } } impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) } } pub fn env() -> Env { - panic!("not supported on this platform") + let env = uefi_env::get_all().expect("not supported on this platform"); + Env(env.into_iter()) } -pub fn getenv(_: &OsStr) -> Option<OsString> { - None +pub fn getenv(key: &OsStr) -> Option<OsString> { + uefi_env::get(key) } -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +pub unsafe fn setenv(key: &OsStr, val: &OsStr) -> io::Result<()> { + uefi_env::set(key, val) } -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +pub unsafe fn unsetenv(key: &OsStr) -> io::Result<()> { + uefi_env::unset(key) } pub fn temp_dir() -> PathBuf { @@ -261,36 +275,84 @@ pub fn getpid() -> u32 { panic!("no pids on this platform") } -mod uefi_shell { - use r_efi::protocols::shell; - - use super::super::helpers; +mod uefi_env { + use crate::ffi::{OsStr, OsString}; + use crate::io; + use crate::os::uefi::ffi::OsStringExt; use crate::ptr::NonNull; - use crate::sync::atomic::{AtomicPtr, Ordering}; - - pub fn open_shell() -> Option<NonNull<shell::Protocol>> { - static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = - AtomicPtr::new(crate::ptr::null_mut()); - - if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { - if let Ok(protocol) = helpers::open_protocol::<shell::Protocol>( - handle, - r_efi::protocols::shell::PROTOCOL_GUID, - ) { - return Some(protocol); - } + use crate::sys::{helpers, unsupported_err}; + + pub(crate) fn get(key: &OsStr) -> Option<OsString> { + let shell = helpers::open_shell()?; + let mut key_ptr = helpers::os_string_to_raw(key)?; + unsafe { get_raw(shell, key_ptr.as_mut_ptr()) } + } + + pub(crate) fn set(key: &OsStr, val: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + let mut val_ptr = helpers::os_string_to_raw(val) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), val_ptr.as_mut_ptr()) } + } + + pub(crate) fn unset(key: &OsStr) -> io::Result<()> { + let mut key_ptr = helpers::os_string_to_raw(key) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Key"))?; + unsafe { set_raw(key_ptr.as_mut_ptr(), crate::ptr::null_mut()) } + } + + pub(crate) fn get_all() -> io::Result<Vec<(OsString, OsString)>> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + + let mut vars = Vec::new(); + let val = unsafe { ((*shell.as_ptr()).get_env)(crate::ptr::null_mut()) }; + + if val.is_null() { + return Ok(vars); } - let handles = helpers::locate_handles(shell::PROTOCOL_GUID).ok()?; - for handle in handles { - if let Ok(protocol) = - helpers::open_protocol::<shell::Protocol>(handle, shell::PROTOCOL_GUID) - { - LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); - return Some(protocol); + let mut start = 0; + + // UEFI Shell returns all keys seperated by NULL. + // End of string is denoted by two NULLs + for i in 0.. { + if unsafe { *val.add(i) } == 0 { + // Two NULL signal end of string + if i == start { + break; + } + + let key = OsString::from_wide(unsafe { + crate::slice::from_raw_parts(val.add(start), i - start) + }); + // SAFETY: val.add(start) is always NULL terminated + let val = unsafe { get_raw(shell, val.add(start)) } + .ok_or(io::const_io_error!(io::ErrorKind::InvalidInput, "Invalid Value"))?; + + vars.push((key, val)); + start = i + 1; } } - None + Ok(vars) + } + + unsafe fn get_raw( + shell: NonNull<r_efi::efi::protocols::shell::Protocol>, + key_ptr: *mut r_efi::efi::Char16, + ) -> Option<OsString> { + let val = unsafe { ((*shell.as_ptr()).get_env)(key_ptr) }; + helpers::os_string_from_raw(val) + } + + unsafe fn set_raw( + key_ptr: *mut r_efi::efi::Char16, + val_ptr: *mut r_efi::efi::Char16, + ) -> io::Result<()> { + let shell = helpers::open_shell().ok_or(unsupported_err())?; + let r = + unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 567577b2b4d..f1f843a5f7a 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -189,7 +189,7 @@ cfg_has_statx! {{ // See: https://github.com/rust-lang/rust/issues/65662 // // FIXME what about transient conditions like `ENOMEM`? - let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) + let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut())) .err() .and_then(|e| e.raw_os_error()); if err2 == Some(libc::EFAULT) { @@ -910,7 +910,7 @@ impl DirEntry { fd, name, libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } @@ -1194,7 +1194,7 @@ impl File { fd, c"".as_ptr() as *const c_char, libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } @@ -1767,7 +1767,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> { libc::AT_FDCWD, p.as_ptr(), libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } @@ -1786,7 +1786,7 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> { libc::AT_FDCWD, p.as_ptr(), libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, - libc::STATX_ALL, + libc::STATX_BASIC_STATS | libc::STATX_BTIME, ) } { return ret; } diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index cc725045c48..0fc765dc87a 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -11,9 +11,14 @@ use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU32; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU32; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; /// Waits for a `futex_wake` operation to wake us. diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index ac0858e1de8..69b31da427f 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -265,9 +265,7 @@ mod imp { /// Modern kernels on modern hardware can have dynamic signal stack sizes. #[cfg(any(target_os = "linux", target_os = "android"))] fn sigstack_size() -> usize { - // FIXME: reuse const from libc when available? - const AT_MINSIGSTKSZ: crate::ffi::c_ulong = 51; - let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) }; + let dynamic_sigstksz = unsafe { libc::getauxval(libc::AT_MINSIGSTKSZ) }; // If getauxval couldn't find the entry, it returns 0, // so take the higher of the "constant" and auxval. // This transparently supports older kernels which don't provide AT_MINSIGSTKSZ diff --git a/library/std/src/sys/pal/wasm/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs index 42913a99ee9..bdad0da73f0 100644 --- a/library/std/src/sys/pal/wasm/atomics/futex.rs +++ b/library/std/src/sys/pal/wasm/atomics/futex.rs @@ -6,9 +6,14 @@ use core::arch::wasm64 as wasm; use crate::sync::atomic::AtomicU32; use crate::time::Duration; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU32; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU32; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; /// Wait for a futex_wake operation to wake us. diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index 4d6c4df9a5a..38afb8c043b 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -9,22 +9,27 @@ use core::{mem, ptr}; use super::api::{self, WinError}; use crate::sys::{c, dur2timeout}; +/// An atomic for use as a futex that is at least 32-bits but may be larger +pub type Futex = AtomicU32; +/// Must be the underlying type of Futex +pub type Primitive = u32; + /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallAtomic = AtomicU8; -/// Must be the underlying type of SmallAtomic +pub type SmallFutex = AtomicU8; +/// Must be the underlying type of SmallFutex pub type SmallPrimitive = u8; -pub unsafe trait Futex {} +pub unsafe trait Futexable {} pub unsafe trait Waitable { - type Atomic; + type Futex; } macro_rules! unsafe_waitable_int { ($(($int:ty, $atomic:ty)),*$(,)?) => { $( unsafe impl Waitable for $int { - type Atomic = $atomic; + type Futex = $atomic; } - unsafe impl Futex for $atomic {} + unsafe impl Futexable for $atomic {} )* }; } @@ -42,15 +47,15 @@ unsafe_waitable_int! { (usize, AtomicUsize), } unsafe impl<T> Waitable for *const T { - type Atomic = AtomicPtr<T>; + type Futex = AtomicPtr<T>; } unsafe impl<T> Waitable for *mut T { - type Atomic = AtomicPtr<T>; + type Futex = AtomicPtr<T>; } -unsafe impl<T> Futex for AtomicPtr<T> {} +unsafe impl<T> Futexable for AtomicPtr<T> {} pub fn wait_on_address<W: Waitable>( - address: &W::Atomic, + address: &W::Futex, compare: W, timeout: Option<Duration>, ) -> bool { @@ -63,30 +68,30 @@ pub fn wait_on_address<W: Waitable>( } } -pub fn wake_by_address_single<T: Futex>(address: &T) { +pub fn wake_by_address_single<T: Futexable>(address: &T) { unsafe { let addr = ptr::from_ref(address).cast::<c_void>(); c::WakeByAddressSingle(addr); } } -pub fn wake_by_address_all<T: Futex>(address: &T) { +pub fn wake_by_address_all<T: Futexable>(address: &T) { unsafe { let addr = ptr::from_ref(address).cast::<c_void>(); c::WakeByAddressAll(addr); } } -pub fn futex_wait<W: Waitable>(futex: &W::Atomic, expected: W, timeout: Option<Duration>) -> bool { +pub fn futex_wait<W: Waitable>(futex: &W::Futex, expected: W, timeout: Option<Duration>) -> bool { // return false only on timeout wait_on_address(futex, expected, timeout) || api::get_last_error() != WinError::TIMEOUT } -pub fn futex_wake<T: Futex>(futex: &T) -> bool { +pub fn futex_wake<T: Futexable>(futex: &T) -> bool { wake_by_address_single(futex); false } -pub fn futex_wake_all<T: Futex>(futex: &T) { +pub fn futex_wake_all<T: Futexable>(futex: &T) { wake_by_address_all(futex) } diff --git a/library/std/src/sys/pal/xous/args.rs b/library/std/src/sys/pal/xous/args.rs new file mode 100644 index 00000000000..00c44ca220a --- /dev/null +++ b/library/std/src/sys/pal/xous/args.rs @@ -0,0 +1,53 @@ +use crate::ffi::OsString; +use crate::sys::pal::xous::os::get_application_parameters; +use crate::sys::pal::xous::os::params::ArgumentList; +use crate::{fmt, vec}; + +pub struct Args { + parsed_args_list: vec::IntoIter<OsString>, +} + +pub fn args() -> Args { + let Some(params) = get_application_parameters() else { + return Args { parsed_args_list: vec![].into_iter() }; + }; + + for param in params { + if let Ok(args) = ArgumentList::try_from(¶m) { + let mut parsed_args = vec![]; + for arg in args { + parsed_args.push(arg.into()); + } + return Args { parsed_args_list: parsed_args.into_iter() }; + } + } + Args { parsed_args_list: vec![].into_iter() } +} + +impl fmt::Debug for Args { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.parsed_args_list.as_slice().fmt(f) + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { + self.parsed_args_list.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.parsed_args_list.size_hint() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.parsed_args_list.next_back() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.parsed_args_list.len() + } +} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index b211e94db65..a64cd068560 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,6 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -#[path = "../unsupported/args.rs"] pub mod args; #[path = "../unsupported/env.rs"] pub mod env; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/pal/xous/net/dns.rs index d0083c61837..1a2b56b4da5 100644 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ b/library/std/src/sys/pal/xous/net/dns.rs @@ -6,6 +6,7 @@ use crate::os::xous::ffi::lend_mut; use crate::os::xous::services::{DnsLendMut, dns_server}; pub struct DnsError { + #[allow(dead_code)] pub code: u8, } diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/pal/xous/net/mod.rs index dd8b765aa74..3e18ed24208 100644 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ b/library/std/src/sys/pal/xous/net/mod.rs @@ -60,6 +60,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -72,6 +73,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 8f8f35428c4..b0ab01a6383 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -1,29 +1,35 @@ use super::unsupported; +use crate::collections::HashMap; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; -use crate::{fmt, io}; +use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::{Mutex, Once}; +use crate::{fmt, io, vec}; + +pub(crate) mod params; + +static PARAMS_ADDRESS: AtomicPtr<u8> = AtomicPtr::new(core::ptr::null_mut()); #[cfg(not(test))] #[cfg(feature = "panic_unwind")] mod eh_unwinding { - pub(crate) struct EhFrameFinder(usize /* eh_frame */); - pub(crate) static mut EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder(0); - impl EhFrameFinder { - pub(crate) unsafe fn init(&mut self, eh_frame: usize) { - unsafe { - EH_FRAME_SETTINGS.0 = eh_frame; - } - } - } + pub(crate) struct EhFrameFinder; + pub(crate) static mut EH_FRAME_ADDRESS: usize = 0; + pub(crate) static EH_FRAME_SETTINGS: EhFrameFinder = EhFrameFinder; + unsafe impl unwind::EhFrameFinder for EhFrameFinder { fn find(&self, _pc: usize) -> Option<unwind::FrameInfo> { - Some(unwind::FrameInfo { - text_base: None, - kind: unwind::FrameInfoKind::EhFrame(self.0), - }) + if unsafe { EH_FRAME_ADDRESS == 0 } { + None + } else { + Some(unwind::FrameInfo { + text_base: None, + kind: unwind::FrameInfoKind::EhFrame(unsafe { EH_FRAME_ADDRESS }), + }) + } } } } @@ -41,12 +47,21 @@ mod c_compat { } #[no_mangle] - pub extern "C" fn _start(eh_frame: usize) { + pub extern "C" fn _start(eh_frame: usize, params_address: usize) { #[cfg(feature = "panic_unwind")] - unsafe { - super::eh_unwinding::EH_FRAME_SETTINGS.init(eh_frame); + { + unsafe { super::eh_unwinding::EH_FRAME_ADDRESS = eh_frame }; unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); } + + if params_address != 0 { + let params_address = crate::ptr::with_exposed_provenance_mut::<u8>(params_address); + if unsafe { + super::params::ApplicationParameters::new_from_ptr(params_address).is_some() + } { + super::PARAMS_ADDRESS.store(params_address, core::sync::atomic::Ordering::Relaxed); + } + } exit(unsafe { main() }); } @@ -116,44 +131,103 @@ pub fn current_exe() -> io::Result<PathBuf> { unsupported() } -pub struct Env(!); +pub(crate) fn get_application_parameters() -> Option<params::ApplicationParameters> { + let params_address = PARAMS_ADDRESS.load(Ordering::Relaxed); + unsafe { params::ApplicationParameters::new_from_ptr(params_address) } +} + +// ---------- Environment handling ---------- // +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex<HashMap<OsString, OsString>>; + +fn get_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + let env_store = EnvStore::default(); + if let Some(params) = get_application_parameters() { + for param in params { + if let Ok(envs) = params::EnvironmentBlock::try_from(¶m) { + let mut env_store = env_store.lock().unwrap(); + for env in envs { + env_store.insert(env.key.into(), env.value.into()); + } + break; + } + } + } + ENV.store(Box::into_raw(Box::new(env_store)) as _, Ordering::Relaxed) + }); + unsafe { &*core::ptr::with_exposed_provenance::<EnvStore>(ENV.load(Ordering::Relaxed)) } +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +// FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. +pub struct EnvStrDebug<'a> { + slice: &'a [(OsString, OsString)], +} + +impl fmt::Debug for EnvStrDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { slice } = self; + f.debug_list() + .entries(slice.iter().map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap()))) + .finish() + } +} impl Env { // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt. pub fn str_debug(&self) -> impl fmt::Debug + '_ { - let Self(inner) = self; - match *inner {} + let Self { iter } = self; + EnvStrDebug { slice: iter.as_slice() } } } impl fmt::Debug for Env { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self(inner) = self; - match *inner {} + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { iter } = self; + f.debug_list().entries(iter.as_slice()).finish() } } +impl !Send for Env {} +impl !Sync for Env {} + impl Iterator for Env { type Item = (OsString, OsString); fn next(&mut self) -> Option<(OsString, OsString)> { - self.0 + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() } } pub fn env() -> Env { - panic!("not supported on this platform") + let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + }; + + let iter = clone_to_vec(&*get_env_store().lock().unwrap()).into_iter(); + Env { iter } } -pub fn getenv(_: &OsStr) -> Option<OsString> { - None +pub fn getenv(k: &OsStr) -> Option<OsString> { + get_env_store().lock().unwrap().get(k).cloned() } -pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + get_env_store().lock().unwrap().insert(k, v); + Ok(()) } -pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { - Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + get_env_store().lock().unwrap().remove(k); + Ok(()) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/pal/xous/os/params.rs b/library/std/src/sys/pal/xous/os/params.rs new file mode 100644 index 00000000000..0d02cf35477 --- /dev/null +++ b/library/std/src/sys/pal/xous/os/params.rs @@ -0,0 +1,271 @@ +/// Xous passes a pointer to the parameter block as the second argument. +/// This is used for passing flags such as environment variables. The +/// format of the argument block is: +/// +/// #[repr(C)] +/// struct BlockHeader { +/// /// Magic number that identifies this block. Must be printable ASCII. +/// magic: [u8; 4], +/// +/// /// The size of the data block. Does not include this header. May be 0. +/// size: u32, +/// +/// /// The contents of this block. Varies depending on the block type. +/// data: [u8; 0], +/// } +/// +/// There is a BlockHeader at the start that has magic `AppP`, and the data +/// that follows is the number of blocks present: +/// +/// #[repr(C)] +/// struct ApplicationParameters { +/// magic: b"AppP", +/// size: 4u32, +/// +/// /// The size of the entire application slice, in bytes, including all headers +/// length: u32, +/// +/// /// Number of application parameters present. Must be at least 1 (this block) +/// entries: (parameter_count as u32).to_bytes_le(), +/// } +/// +/// #[repr(C)] +/// struct EnvironmentBlock { +/// magic: b"EnvB", +/// +/// /// Total number of bytes, excluding this header +/// size: 2+data.len(), +/// +/// /// The number of environment variables +/// count: u16, +/// +/// /// Environment variable iteration +/// data: [u8; 0], +/// } +/// +/// Environment variables are present in an `EnvB` block. The `data` section is +/// a sequence of bytes of the form: +/// +/// (u16 /* key_len */; [0u8; key_len as usize] /* key */, +/// u16 /* val_len */ [0u8; val_len as usize]) +/// +/// #[repr(C)] +/// struct ArgumentList { +/// magic: b"ArgL", +/// +/// /// Total number of bytes, excluding this header +/// size: 2+data.len(), +/// +/// /// The number of arguments variables +/// count: u16, +/// +/// /// Argument variable iteration +/// data: [u8; 0], +/// } +/// +/// Args are just an array of strings that represent command line arguments. +/// They are a sequence of the form: +/// +/// (u16 /* val_len */ [0u8; val_len as usize]) +use core::slice; + +use crate::ffi::OsString; + +/// Magic number indicating we have an environment block +const ENV_MAGIC: [u8; 4] = *b"EnvB"; + +/// Command line arguments list +const ARGS_MAGIC: [u8; 4] = *b"ArgL"; + +/// Magic number indicating the loader has passed application parameters +const PARAMS_MAGIC: [u8; 4] = *b"AppP"; + +#[cfg(test)] +mod tests; + +pub(crate) struct ApplicationParameters { + data: &'static [u8], + offset: usize, + _entries: usize, +} + +impl ApplicationParameters { + pub(crate) unsafe fn new_from_ptr(data: *const u8) -> Option<ApplicationParameters> { + if data.is_null() { + return None; + } + + let magic = unsafe { core::slice::from_raw_parts(data, 4) }; + let block_length = unsafe { + u32::from_le_bytes(slice::from_raw_parts(data.add(4), 4).try_into().ok()?) as usize + }; + let data_length = unsafe { + u32::from_le_bytes(slice::from_raw_parts(data.add(8), 4).try_into().ok()?) as usize + }; + let entries = unsafe { + u32::from_le_bytes(slice::from_raw_parts(data.add(12), 4).try_into().ok()?) as usize + }; + + // Check for the main header + if data_length < 16 || magic != PARAMS_MAGIC || block_length != 8 { + return None; + } + + let data = unsafe { slice::from_raw_parts(data, data_length) }; + + Some(ApplicationParameters { data, offset: 0, _entries: entries }) + } +} + +impl Iterator for ApplicationParameters { + type Item = ApplicationParameter; + + fn next(&mut self) -> Option<Self::Item> { + // Fetch magic, ensuring we don't run off the end + if self.offset + 4 > self.data.len() { + return None; + } + let magic = &self.data[self.offset..self.offset + 4]; + self.offset += 4; + + // Fetch header size + if self.offset + 4 > self.data.len() { + return None; + } + let size = u32::from_le_bytes(self.data[self.offset..self.offset + 4].try_into().unwrap()) + as usize; + self.offset += 4; + + // Fetch data contents + if self.offset + size > self.data.len() { + return None; + } + let data = &self.data[self.offset..self.offset + size]; + self.offset += size; + + Some(ApplicationParameter { data, magic: magic.try_into().unwrap() }) + } +} + +pub(crate) struct ApplicationParameter { + data: &'static [u8], + magic: [u8; 4], +} + +pub(crate) struct ApplicationParameterError; + +pub(crate) struct EnvironmentBlock { + _count: usize, + data: &'static [u8], + offset: usize, +} + +impl TryFrom<&ApplicationParameter> for EnvironmentBlock { + type Error = ApplicationParameterError; + + fn try_from(value: &ApplicationParameter) -> Result<Self, Self::Error> { + if value.data.len() < 2 || value.magic != ENV_MAGIC { + return Err(ApplicationParameterError); + } + + let count = u16::from_le_bytes(value.data[0..2].try_into().unwrap()) as usize; + + Ok(EnvironmentBlock { data: &value.data[2..], offset: 0, _count: count }) + } +} + +pub(crate) struct EnvironmentEntry { + pub key: &'static str, + pub value: &'static str, +} + +impl Iterator for EnvironmentBlock { + type Item = EnvironmentEntry; + + fn next(&mut self) -> Option<Self::Item> { + if self.offset + 2 > self.data.len() { + return None; + } + let key_len = + u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; + self.offset += 2; + + if self.offset + key_len > self.data.len() { + return None; + } + let key = core::str::from_utf8(&self.data[self.offset..self.offset + key_len]).ok()?; + self.offset += key_len; + + if self.offset + 2 > self.data.len() { + return None; + } + let value_len = + u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; + self.offset += 2; + + if self.offset + value_len > self.data.len() { + return None; + } + let value = core::str::from_utf8(&self.data[self.offset..self.offset + value_len]).ok()?; + self.offset += value_len; + + Some(EnvironmentEntry { key, value }) + } +} + +pub(crate) struct ArgumentList { + data: &'static [u8], + _count: usize, + offset: usize, +} + +impl TryFrom<&ApplicationParameter> for ArgumentList { + type Error = ApplicationParameterError; + + fn try_from(value: &ApplicationParameter) -> Result<Self, Self::Error> { + if value.data.len() < 2 || value.magic != ARGS_MAGIC { + return Err(ApplicationParameterError); + } + let count = + u16::from_le_bytes(value.data[0..2].try_into().or(Err(ApplicationParameterError))?) + as usize; + Ok(ArgumentList { data: &value.data[2..], _count: count, offset: 0 }) + } +} + +pub(crate) struct ArgumentEntry { + value: &'static str, +} + +impl Into<&str> for ArgumentEntry { + fn into(self) -> &'static str { + self.value + } +} + +impl Into<OsString> for ArgumentEntry { + fn into(self) -> OsString { + self.value.into() + } +} + +impl Iterator for ArgumentList { + type Item = ArgumentEntry; + + fn next(&mut self) -> Option<Self::Item> { + if self.offset + 2 > self.data.len() { + return None; + } + let value_len = + u16::from_le_bytes(self.data[self.offset..self.offset + 2].try_into().ok()?) as usize; + self.offset += 2; + + if self.offset + value_len > self.data.len() { + return None; + } + let value = core::str::from_utf8(&self.data[self.offset..self.offset + value_len]).ok()?; + self.offset += value_len; + + Some(ArgumentEntry { value }) + } +} diff --git a/library/std/src/sys/pal/xous/os/params/tests.rs b/library/std/src/sys/pal/xous/os/params/tests.rs new file mode 100644 index 00000000000..0ef04ee3091 --- /dev/null +++ b/library/std/src/sys/pal/xous/os/params/tests.rs @@ -0,0 +1,75 @@ +use super::*; +use crate::collections::HashMap; +use crate::io::Write; + +fn create_args_test() -> std::io::Result<Vec<u8>> { + let mut sample_data = vec![]; + let mut h = HashMap::new(); + + h.insert("foo", "bar"); + h.insert("baz", "qux"); + h.insert("some", "val"); + + // Magic number + sample_data.write_all(&PARAMS_MAGIC)?; + // Size of the AppP block + sample_data.write_all(&4u32.to_le_bytes())?; + // Number of blocks + sample_data.write_all(&2u32.to_le_bytes())?; + + // Magic number + sample_data.write_all(&ENV_MAGIC)?; + let mut data = vec![]; + for (key, value) in h.iter() { + data.extend_from_slice(&(key.len() as u16).to_le_bytes()); + data.extend_from_slice(key.as_bytes()); + data.extend_from_slice(&(value.len() as u16).to_le_bytes()); + data.extend_from_slice(value.as_bytes()); + } + // Size of the EnvB block + sample_data.write_all(&(data.len() as u32 + 2).to_le_bytes())?; + + // Number of environment variables + sample_data.write_all(&(h.len() as u16).to_le_bytes())?; + + // Environment variables + sample_data.write_all(&data)?; + + // Write command line arguments + let args = vec!["some", "command", "line variable", "entries"]; + sample_data.write_all(&ARGS_MAGIC)?; + let mut args_size = 0; + for entry in args.iter() { + args_size += entry.len() + 2; + } + sample_data.write_all(&(args_size as u32 + 2).to_le_bytes())?; + sample_data.write_all(&(args.len() as u16).to_le_bytes())?; + for entry in args { + sample_data.write_all(&(entry.len() as u16).to_le_bytes())?; + sample_data.write_all(entry.as_bytes())?; + } + + Ok(sample_data) +} + +#[test] +fn basic_arg_parsing() { + let arg_data = create_args_test().expect("couldn't create test data"); + for byte in &arg_data { + print!("{:02x} ", byte); + } + println!(); + + let args = ApplicationParameters::new(&arg_data).expect("Unable to parse arguments"); + for arg in args { + if let Ok(env) = EnvironmentBlock::try_from(&arg) { + for env in env { + println!("{}={}", env.key, env.value); + } + } else if let Ok(args) = ArgumentList::try_from(&arg) { + for arg in args { + println!("Arg: {}", arg.value); + } + } + } +} diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs index 32467e9ebaa..ffabaafbee8 100644 --- a/library/std/src/sys/random/arc4random.rs +++ b/library/std/src/sys/random/arc4random.rs @@ -12,6 +12,7 @@ #[cfg(not(any( target_os = "haiku", target_os = "illumos", + target_os = "rtems", target_os = "solaris", target_os = "vita", )))] @@ -21,6 +22,7 @@ use libc::arc4random_buf; #[cfg(any( target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random + target_os = "rtems", // See https://docs.rtems.org/branches/master/bsp-howto/getentropy.html target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 ))] diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index d625814d15b..edc2cacdfd8 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -17,6 +17,7 @@ cfg_if::cfg_if! { target_os = "illumos", target_os = "netbsd", target_os = "openbsd", + target_os = "rtems", target_os = "solaris", target_os = "vita", ))] { diff --git a/library/std/src/sys/sync/condvar/futex.rs b/library/std/src/sys/sync/condvar/futex.rs index 39cd97c01ea..0d0c5f0dbe7 100644 --- a/library/std/src/sys/sync/condvar/futex.rs +++ b/library/std/src/sys/sync/condvar/futex.rs @@ -1,6 +1,5 @@ -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; +use crate::sys::futex::{Futex, futex_wait, futex_wake, futex_wake_all}; use crate::sys::sync::Mutex; use crate::time::Duration; @@ -8,13 +7,13 @@ pub struct Condvar { // The value of this atomic is simply incremented on every notification. // This is used by `.wait()` to not miss any notifications after // unlocking the mutex and before waiting for notifications. - futex: AtomicU32, + futex: Futex, } impl Condvar { #[inline] pub const fn new() -> Self { - Self { futex: AtomicU32::new(0) } + Self { futex: Futex::new(0) } } // All the memory orderings here are `Relaxed`, diff --git a/library/std/src/sys/sync/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs index 81afa94b147..ce9b2daa5f8 100644 --- a/library/std/src/sys/sync/mutex/futex.rs +++ b/library/std/src/sys/sync/mutex/futex.rs @@ -1,11 +1,11 @@ use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sys::futex::{self, futex_wait, futex_wake}; -type Atomic = futex::SmallAtomic; +type Futex = futex::SmallFutex; type State = futex::SmallPrimitive; pub struct Mutex { - futex: Atomic, + futex: Futex, } const UNLOCKED: State = 0; @@ -15,7 +15,7 @@ const CONTENDED: State = 2; // locked, and other threads waiting (contended) impl Mutex { #[inline] pub const fn new() -> Self { - Self { futex: Atomic::new(UNLOCKED) } + Self { futex: Futex::new(UNLOCKED) } } #[inline] diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 25588a4217b..10bfa81a6d7 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -1,39 +1,38 @@ use crate::cell::Cell; use crate::sync as public; -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::once::ExclusiveState; -use crate::sys::futex::{futex_wait, futex_wake_all}; +use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. // This means we only need one atomic value with 4 states: /// No initialization has run yet, and no thread is currently using the Once. -const INCOMPLETE: u32 = 0; +const INCOMPLETE: Primitive = 0; /// Some thread has previously attempted to initialize the Once, but it panicked, /// so the Once is now poisoned. There are no other threads currently accessing /// this Once. -const POISONED: u32 = 1; +const POISONED: Primitive = 1; /// Some thread is currently attempting to run initialization. It may succeed, /// so all future threads need to wait for it to finish. -const RUNNING: u32 = 2; +const RUNNING: Primitive = 2; /// Initialization has completed and all future calls should finish immediately. -const COMPLETE: u32 = 3; +const COMPLETE: Primitive = 3; // An additional bit indicates whether there are waiting threads: /// May only be set if the state is not COMPLETE. -const QUEUED: u32 = 4; +const QUEUED: Primitive = 4; // Threads wait by setting the QUEUED bit and calling `futex_wait` on the state // variable. When the running thread finishes, it will wake all waiting threads using // `futex_wake_all`. -const STATE_MASK: u32 = 0b11; +const STATE_MASK: Primitive = 0b11; pub struct OnceState { poisoned: bool, - set_state_to: Cell<u32>, + set_state_to: Cell<Primitive>, } impl OnceState { @@ -49,8 +48,8 @@ impl OnceState { } struct CompletionGuard<'a> { - state_and_queued: &'a AtomicU32, - set_state_on_drop_to: u32, + state_and_queued: &'a Futex, + set_state_on_drop_to: Primitive, } impl<'a> Drop for CompletionGuard<'a> { @@ -65,13 +64,13 @@ impl<'a> Drop for CompletionGuard<'a> { } pub struct Once { - state_and_queued: AtomicU32, + state_and_queued: Futex, } impl Once { #[inline] pub const fn new() -> Once { - Once { state_and_queued: AtomicU32::new(INCOMPLETE) } + Once { state_and_queued: Futex::new(INCOMPLETE) } } #[inline] diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 17abaf0bf26..3e83a4a088f 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -23,7 +23,7 @@ // You'll find a few more details in the implementation, but that's the gist of // it! // -// Atomic orderings: +// Futex orderings: // When running `Once` we deal with multiple atomics: // `Once.state_and_queue` and an unknown number of `Waiter.signaled`. // * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the diff --git a/library/std/src/sys/sync/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs index 75ecc2ab5c5..df22c36dd5a 100644 --- a/library/std/src/sys/sync/rwlock/futex.rs +++ b/library/std/src/sys/sync/rwlock/futex.rs @@ -1,6 +1,5 @@ -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; +use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake, futex_wake_all}; pub struct RwLock { // The state consists of a 30-bit reader counter, a 'readers waiting' flag, and a 'writers waiting' flag. @@ -10,41 +9,41 @@ pub struct RwLock { // 0x3FFF_FFFF: Write locked // Bit 30: Readers are waiting on this futex. // Bit 31: Writers are waiting on the writer_notify futex. - state: AtomicU32, + state: Futex, // The 'condition variable' to notify writers through. // Incremented on every signal. - writer_notify: AtomicU32, + writer_notify: Futex, } -const READ_LOCKED: u32 = 1; -const MASK: u32 = (1 << 30) - 1; -const WRITE_LOCKED: u32 = MASK; -const MAX_READERS: u32 = MASK - 1; -const READERS_WAITING: u32 = 1 << 30; -const WRITERS_WAITING: u32 = 1 << 31; +const READ_LOCKED: Primitive = 1; +const MASK: Primitive = (1 << 30) - 1; +const WRITE_LOCKED: Primitive = MASK; +const MAX_READERS: Primitive = MASK - 1; +const READERS_WAITING: Primitive = 1 << 30; +const WRITERS_WAITING: Primitive = 1 << 31; #[inline] -fn is_unlocked(state: u32) -> bool { +fn is_unlocked(state: Primitive) -> bool { state & MASK == 0 } #[inline] -fn is_write_locked(state: u32) -> bool { +fn is_write_locked(state: Primitive) -> bool { state & MASK == WRITE_LOCKED } #[inline] -fn has_readers_waiting(state: u32) -> bool { +fn has_readers_waiting(state: Primitive) -> bool { state & READERS_WAITING != 0 } #[inline] -fn has_writers_waiting(state: u32) -> bool { +fn has_writers_waiting(state: Primitive) -> bool { state & WRITERS_WAITING != 0 } #[inline] -fn is_read_lockable(state: u32) -> bool { +fn is_read_lockable(state: Primitive) -> bool { // This also returns false if the counter could overflow if we tried to read lock it. // // We don't allow read-locking if there's readers waiting, even if the lock is unlocked @@ -55,14 +54,14 @@ fn is_read_lockable(state: u32) -> bool { } #[inline] -fn has_reached_max_readers(state: u32) -> bool { +fn has_reached_max_readers(state: Primitive) -> bool { state & MASK == MAX_READERS } impl RwLock { #[inline] pub const fn new() -> Self { - Self { state: AtomicU32::new(0), writer_notify: AtomicU32::new(0) } + Self { state: Futex::new(0), writer_notify: Futex::new(0) } } #[inline] @@ -225,7 +224,7 @@ impl RwLock { /// If both are waiting, this will wake up only one writer, but will fall /// back to waking up readers if there was no writer to wake up. #[cold] - fn wake_writer_or_readers(&self, mut state: u32) { + fn wake_writer_or_readers(&self, mut state: Primitive) { assert!(is_unlocked(state)); // The readers waiting bit might be turned on at any point now, @@ -290,7 +289,7 @@ impl RwLock { /// Spin for a while, but stop directly at the given condition. #[inline] - fn spin_until(&self, f: impl Fn(u32) -> bool) -> u32 { + fn spin_until(&self, f: impl Fn(Primitive) -> bool) -> Primitive { let mut spin = 100; // Chosen by fair dice roll. loop { let state = self.state.load(Relaxed); @@ -303,13 +302,13 @@ impl RwLock { } #[inline] - fn spin_write(&self) -> u32 { + fn spin_write(&self) -> Primitive { // Stop spinning when it's unlocked or when there's waiting writers, to keep things somewhat fair. self.spin_until(|state| is_unlocked(state) || has_writers_waiting(state)) } #[inline] - fn spin_read(&self) -> u32 { + fn spin_read(&self) -> Primitive { // Stop spinning when it's unlocked or read locked, or when there's waiting threads. self.spin_until(|state| { !is_write_locked(state) || has_readers_waiting(state) || has_writers_waiting(state) diff --git a/library/std/src/sys/sync/thread_parking/futex.rs b/library/std/src/sys/sync/thread_parking/futex.rs index ce852eaadc4..c8f7f26386a 100644 --- a/library/std/src/sys/sync/thread_parking/futex.rs +++ b/library/std/src/sys/sync/thread_parking/futex.rs @@ -4,7 +4,7 @@ use crate::sync::atomic::Ordering::{Acquire, Release}; use crate::sys::futex::{self, futex_wait, futex_wake}; use crate::time::Duration; -type Atomic = futex::SmallAtomic; +type Futex = futex::SmallFutex; type State = futex::SmallPrimitive; const PARKED: State = State::MAX; @@ -12,7 +12,7 @@ const EMPTY: State = 0; const NOTIFIED: State = 1; pub struct Parker { - state: Atomic, + state: Futex, } // Notes about memory ordering: @@ -39,7 +39,7 @@ impl Parker { /// Constructs the futex parker. The UNIX parker implementation /// requires this to happen in-place. pub unsafe fn new_in_place(parker: *mut Parker) { - unsafe { parker.write(Self { state: Atomic::new(EMPTY) }) }; + unsafe { parker.write(Self { state: Futex::new(EMPTY) }) }; } // Assumes this is only called by the thread that owns the Parker, diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index f498dee0899..a5dffe3c458 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -49,20 +49,21 @@ pub use lazy::Storage as LazyStorage; #[unstable(feature = "thread_local_internals", issue = "none")] #[rustc_macro_transparency = "semitransparent"] pub macro thread_local_inner { - // used to generate the `LocalKey` value for const-initialized thread locals + // NOTE: we cannot import `LocalKey`, `LazyStorage` or `EagerStorage` with a `use` because that + // can shadow user provided type or type alias with a matching name. Please update the shadowing + // test in `tests/thread.rs` if these types are renamed. + + // Used to generate the `LocalKey` value for const-initialized thread locals. (@key $t:ty, const $init:expr) => {{ const __INIT: $t = $init; unsafe { - use $crate::mem::needs_drop; - use $crate::thread::LocalKey; - use $crate::thread::local_impl::EagerStorage; - - LocalKey::new(const { - if needs_drop::<$t>() { + $crate::thread::LocalKey::new(const { + if $crate::mem::needs_drop::<$t>() { |_| { #[thread_local] - static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); + static VAL: $crate::thread::local_impl::EagerStorage<$t> + = $crate::thread::local_impl::EagerStorage::new(__INIT); VAL.get() } } else { @@ -84,21 +85,19 @@ pub macro thread_local_inner { } unsafe { - use $crate::mem::needs_drop; - use $crate::thread::LocalKey; - use $crate::thread::local_impl::LazyStorage; - - LocalKey::new(const { - if needs_drop::<$t>() { + $crate::thread::LocalKey::new(const { + if $crate::mem::needs_drop::<$t>() { |init| { #[thread_local] - static VAL: LazyStorage<$t, ()> = LazyStorage::new(); + static VAL: $crate::thread::local_impl::LazyStorage<$t, ()> + = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) } } else { |init| { #[thread_local] - static VAL: LazyStorage<$t, !> = LazyStorage::new(); + static VAL: $crate::thread::local_impl::LazyStorage<$t, !> + = $crate::thread::local_impl::LazyStorage::new(); VAL.get_or_init(init, __init) } } diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 26ce3322a16..f5a2aaa6c6a 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -15,19 +15,24 @@ pub macro thread_local_inner { $crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR }) }, - // used to generate the `LocalKey` value for `thread_local!` + // NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user + // provided type or type alias with a matching name. Please update the shadowing test in + // `tests/thread.rs` if these types are renamed. + + // used to generate the `LocalKey` value for `thread_local!`. (@key $t:ty, $init:expr) => {{ #[inline] fn __init() -> $t { $init } + // NOTE: this cannot import `LocalKey` or `Storage` with a `use` because that can shadow + // user provided type or type alias with a matching name. Please update the shadowing test + // in `tests/thread.rs` if these types are renamed. unsafe { - use $crate::thread::LocalKey; - use $crate::thread::local_impl::Storage; - // Inlining does not work on windows-gnu due to linking errors around // dllimports. See https://github.com/rust-lang/rust/issues/109797. - LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { - static VAL: Storage<$t> = Storage::new(); + $crate::thread::LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { + static VAL: $crate::thread::local_impl::Storage<$t> + = $crate::thread::local_impl::Storage::new(); VAL.get(init, __init) }) } diff --git a/library/std/tests/thread.rs b/library/std/tests/thread.rs index 83574176186..1bb17d149fa 100644 --- a/library/std/tests/thread.rs +++ b/library/std/tests/thread.rs @@ -39,6 +39,29 @@ fn thread_local_containing_const_statements() { } #[test] +fn thread_local_hygeiene() { + // Previously `thread_local_inner!` had use imports for `LocalKey`, `Storage`, `EagerStorage` + // and `LazyStorage`. The use imports will shadow a user-provided type or type alias if the + // user-provided type or type alias has the same name. Make sure that this does not happen. See + // <https://github.com/rust-lang/rust/issues/131863>. + // + // NOTE: if the internal implementation details change (i.e. get renamed), this test should be + // updated. + + #![allow(dead_code)] + type LocalKey = (); + type Storage = (); + type LazyStorage = (); + type EagerStorage = (); + thread_local! { + static A: LocalKey = const { () }; + static B: Storage = const { () }; + static C: LazyStorage = const { () }; + static D: EagerStorage = const { () }; + } +} + +#[test] // Include an ignore list on purpose, so that new platforms don't miss it #[cfg_attr( any( diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 7165c3e48af..aa6c3dc32e2 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # this is a dummy crate to ensure that all required crates appear in the sysroot [dependencies] proc_macro = { path = "../proc_macro" } +profiler_builtins = { path = "../profiler_builtins", optional = true } std = { path = "../std" } test = { path = "../test" } @@ -23,7 +24,7 @@ system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] optimize_for_size = ["std/optimize_for_size"] -profiler = ["std/profiler"] +profiler = ["dep:profiler_builtins"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] std_detect_env_override = ["std/std_detect_env_override"] diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 590de31a678..569a1b3299e 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -22,7 +22,7 @@ cfg-if = "1.0" libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false } [target.'cfg(target_os = "xous")'.dependencies] -unwinding = { version = "0.2.1", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } +unwinding = { version = "0.2.3", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false } [features] diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index a555a26367d..15137fbb2b5 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -193,7 +193,8 @@ if '--help' in sys.argv or '-h' in sys.argv: if option.value: print('\t{:30} {}'.format('--{}=VAL'.format(option.name), option.desc)) else: - print('\t{:30} {}'.format('--enable-{} OR --disable-{}'.format(option.name, option.name), option.desc)) + print('\t--enable-{:25} OR --disable-{}'.format(option.name, option.name)) + print('\t\t' + option.desc) print('') print('This configure script is a thin configuration shim over the true') print('configuration system, `config.toml`. You can explore the comments') diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 90e6a10d9d6..80ba9f44448 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1591,9 +1591,15 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - prepare("clippy"); - prepare("rust-analyzer"); - for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { + + for tool in &[ + "clippy", + "rustfmt", + "rust-analyzer", + "rust-docs", + "miri", + "rustc-codegen-cranelift", + ] { if built_tools.contains(tool) { prepare(tool); } @@ -1633,6 +1639,8 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() + } else if name == "rustfmt" { + "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1652,7 +1660,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1770,6 +1778,24 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } + if built_tools.contains("rustfmt") { + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rustfmt") + .args(heat_flags) + .arg("-cg") + .arg("RustFmtGroup") + .arg("-dr") + .arg("RustFmt") + .arg("-var") + .arg("var.RustFmtDir") + .arg("-out") + .arg(exe.join("RustFmtGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1841,6 +1867,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } + if built_tools.contains("rustfmt") { + cmd.arg("-dRustFmtDir=rustfmt"); + } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1867,6 +1896,9 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } + if built_tools.contains("rustfmt") { + candle("RustFmtGroup.wxs".as_ref()); + } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1905,6 +1937,9 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } + if built_tools.contains("rustfmt") { + cmd.arg("RustFmtGroup.wixobj"); + } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index c7bcd76cadd..a6dff7fde80 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -283,3 +283,25 @@ impl Step for GenerateCompletions { run.builder.ensure(GenerateCompletions); } } + +#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +pub struct UnicodeTableGenerator; + +impl Step for UnicodeTableGenerator { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/unicode-table-generator") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(UnicodeTableGenerator); + } + + fn run(self, builder: &Builder<'_>) { + let mut cmd = builder.tool_cmd(Tool::UnicodeTableGenerator); + cmd.arg(builder.src.join("library/core/src/unicode/unicode_data.rs")); + cmd.run(builder); + } +} diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index f1a10c3296e..f5afa6c4c6c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -360,6 +360,7 @@ bootstrap_tool!( CoverageDump, "src/tools/coverage-dump", "coverage-dump"; RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper"; WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization"; + UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator"; ); /// These are the submodules that are required for rustbook to work due to diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 15c6f303f94..8cea01434fe 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1010,6 +1010,7 @@ impl<'a> Builder<'a> { run::GenerateCopyright, run::GenerateWindowsSys, run::GenerateCompletions, + run::UnicodeTableGenerator, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index c2ab439891e..aeb81b14638 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -891,6 +891,7 @@ define_config! { metrics: Option<bool> = "metrics", android_ndk: Option<PathBuf> = "android-ndk", optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins", + jobs: Option<u32> = "jobs", } } @@ -1289,7 +1290,6 @@ impl Config { config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; config.on_fail = flags.on_fail; - config.jobs = Some(threads_from_config(flags.jobs as u32)); config.cmd = flags.cmd; config.incremental = flags.incremental; config.dry_run = if flags.dry_run { DryRun::UserSelected } else { DryRun::Disabled }; @@ -1511,8 +1511,11 @@ impl Config { metrics: _, android_ndk, optimized_compiler_builtins, + jobs, } = toml.build.unwrap_or_default(); + config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0)))); + if let Some(file_build) = build { config.build = TargetSelection::from_user(&file_build); }; diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index 3aefe517a5b..bfeb811508c 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -110,11 +110,10 @@ pub struct Flags { short, long, value_hint = clap::ValueHint::Other, - default_value_t = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get), value_name = "JOBS" )] /// number of jobs to run in parallel - pub jobs: usize, + pub jobs: Option<u32>, // This overrides the deny-warnings configuration option, // which passes -Dwarnings to the compiler invocations. #[arg(global = true, long)] diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 2611b6cf51b..1f02757682c 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -352,3 +352,61 @@ fn parse_rust_std_features_empty() { fn parse_rust_std_features_invalid() { parse("rust.std-features = \"backtrace\""); } + +#[test] +fn parse_jobs() { + assert_eq!(parse("build.jobs = 1").jobs, Some(1)); +} + +#[test] +fn jobs_precedence() { + // `--jobs` should take precedence over using `--set build.jobs`. + + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--jobs=67890".to_owned(), + "--set=build.jobs=12345".to_owned(), + ]), + |&_| toml::from_str(""), + ); + assert_eq!(config.jobs, Some(67890)); + + // `--set build.jobs` should take precedence over `config.toml`. + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=build.jobs=12345".to_owned(), + ]), + |&_| { + toml::from_str( + r#" + [build] + jobs = 67890 + "#, + ) + }, + ); + assert_eq!(config.jobs, Some(12345)); + + // `--jobs` > `--set build.jobs` > `config.toml` + let config = Config::parse_inner( + Flags::parse(&[ + "check".to_owned(), + "--jobs=123".to_owned(), + "--config=/does/not/exist".to_owned(), + "--set=build.jobs=456".to_owned(), + ]), + |&_| { + toml::from_str( + r#" + [build] + jobs = 789 + "#, + ) + }, + ); + assert_eq!(config.jobs, Some(123)); +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b37786496cb..9169bc90a45 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -275,4 +275,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.", }, + ChangeInfo { + change_id: 131838, + severity: ChangeSeverity::Info, + summary: "Allow setting `--jobs` in config.toml with `build.jobs`.", + }, ]; diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 303a2f26c0f..8324d1ec586 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -59,6 +59,8 @@ case $HOST_TARGET in # "error: cannot produce cdylib for ... as the target ... does not support these crate types". # Only run "pass" tests, which is quite a bit faster. #FIXME: Re-enable this once CI issues are fixed + # See <https://github.com/rust-lang/rust/issues/127883> + # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. #python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass #python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass ;; diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 8f49f623afa..d84b904442c 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -5,6 +5,11 @@ runners: env: { } - &job-linux-4c + os: ubuntu-20.04 + <<: *base-job + + # Large runner used mainly for its bigger disk capacity + - &job-linux-4c-largedisk os: ubuntu-20.04-4core-16gb <<: *base-job @@ -127,7 +132,7 @@ auto: - image: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-android <<: *job-linux-4c @@ -148,28 +153,28 @@ auto: <<: *job-linux-4c - image: dist-loongarch64-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-loongarch64-musl - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-ohos <<: *job-linux-4c - image: dist-powerpc-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-powerpc64-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-powerpc64le-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-riscv64-linux <<: *job-linux-4c - image: dist-s390x-linux - <<: *job-linux-4c + <<: *job-linux-4c-largedisk - image: dist-various-1 <<: *job-linux-4c @@ -381,6 +386,8 @@ auto: <<: *job-windows-8c # Temporary builder to workaround CI issues + # See <https://github.com/rust-lang/rust/issues/127883> + #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed. - image: x86_64-msvc-ext2 env: SCRIPT: > diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index f29e1e4d27a..2d155bf0b10 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,6 +172,11 @@ <!-- tool-rust-docs-end --> <Directory Id="Cargo" Name="." /> <Directory Id="Std" Name="." /> + <Directory Id="RustFmt" Name="." /> + <Directory Id="RustAnalyzer" Name="." /> + <Directory Id="Miri" Name="." /> + <Directory Id="Analysis" Name="." /> + <Directory Id="Clippy" Name="." /> </Directory> </Directory> @@ -279,7 +284,41 @@ <ComponentRef Id="PathEnvPerMachine" /> <ComponentRef Id="PathEnvPerUser" /> </Feature> - + <Feature Id="RustFmt" + Title="Formatter for rust" + Display="7" + Level="1" + AllowAdvertise="no"> + <ComponentGroupRef Id="RustFmtGroup" /> + </Feature> + <Feature Id="Clippy" + Title="Formatter and checker for rust" + Display="8" + Level="1" + AllowAdvertise="no"> + <ComponentGroupRef Id="ClippyGroup" /> + </Feature> + <Feature Id="Miri" + Title="Soundness checker for rust" + Display="9" + Level="1" + AllowAdvertise="no"> + <ComponentGroupRef Id="MiriGroup" /> + </Feature> + <Feature Id="RustAnalyzer" + Title="Analyzer for rust" + Display="10" + Level="1" + AllowAdvertise="no"> + <ComponentGroupRef Id="RustAnalyzerGroup" /> + </Feature> + <Feature Id="Analysis" + Title="Analysis for rust" + Display="11" + Level="1" + AllowAdvertise="no"> + <ComponentGroupRef Id="AnalysisGroup" /> + </Feature> <UIRef Id="RustUI" /> </Product> </Wix> diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 34332de80b3..42df0b28381 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -24,6 +24,7 @@ tracing = "0.1" tracing-tree = "0.3.0" threadpool = "1.8.1" unicode-segmentation = "1.9" +sha2 = "0.10.8" [dependencies.tracing-subscriber] version = "0.3.3" diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 315b7742a4c..7826a5d8394 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -37,7 +37,7 @@ use std::sync::OnceLock; use pulldown_cmark::{ BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, DiagMessage}; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; @@ -57,6 +57,7 @@ use crate::html::length_limit::HtmlWithLimit; use crate::html::render::small_url_encode; use crate::html::toc::{Toc, TocBuilder}; +mod footnotes; #[cfg(test)] mod tests; @@ -646,81 +647,6 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> { } } -/// Moves all footnote definitions to the end and add back links to the -/// references. -struct Footnotes<'a, I> { - inner: I, - footnotes: FxIndexMap<String, (Vec<Event<'a>>, u16)>, -} - -impl<'a, I> Footnotes<'a, I> { - fn new(iter: I) -> Self { - Footnotes { inner: iter, footnotes: FxIndexMap::default() } - } - - fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) { - let new_id = self.footnotes.len() + 1; - let key = key.to_owned(); - self.footnotes.entry(key).or_insert((Vec::new(), new_id as u16)) - } -} - -impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> { - type Item = SpannedEvent<'a>; - - fn next(&mut self) -> Option<Self::Item> { - loop { - match self.inner.next() { - Some((Event::FootnoteReference(ref reference), range)) => { - let entry = self.get_entry(reference); - let reference = format!( - "<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>", - (*entry).1 - ); - return Some((Event::Html(reference.into()), range)); - } - Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => { - let mut content = Vec::new(); - for (event, _) in &mut self.inner { - if let Event::End(TagEnd::FootnoteDefinition) = event { - break; - } - content.push(event); - } - let entry = self.get_entry(&def); - (*entry).0 = content; - } - Some(e) => return Some(e), - None => { - if !self.footnotes.is_empty() { - let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect(); - v.sort_by(|a, b| a.1.cmp(&b.1)); - let mut ret = String::from("<div class=\"footnotes\"><hr><ol>"); - for (mut content, id) in v { - write!(ret, "<li id=\"fn{id}\">").unwrap(); - let mut is_paragraph = false; - if let Some(&Event::End(TagEnd::Paragraph)) = content.last() { - content.pop(); - is_paragraph = true; - } - html::push_html(&mut ret, content.into_iter()); - write!(ret, " <a href=\"#fnref{id}\">↩</a>").unwrap(); - if is_paragraph { - ret.push_str("</p>"); - } - ret.push_str("</li>"); - } - ret.push_str("</ol></div>"); - return Some((Event::Html(ret.into()), 0..0)); - } else { - return None; - } - } - } - } - } -} - /// A newtype that represents a relative line number in Markdown. /// /// In other words, this represents an offset from the first line of Markdown @@ -1408,7 +1334,7 @@ impl Markdown<'_> { let mut s = String::with_capacity(md.len() * 3 / 2); let p = HeadingLinks::new(p, None, ids, heading_offset); - let p = Footnotes::new(p); + let p = footnotes::Footnotes::new(p); let p = LinkReplacer::new(p.map(|(ev, _)| ev), links); let p = TableWrapper::new(p); let p = CodeBlocks::new(p, codes, edition, playground); @@ -1443,7 +1369,7 @@ impl MarkdownWithToc<'_> { { let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1); - let p = Footnotes::new(p); + let p = footnotes::Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = CodeBlocks::new(p, codes, edition, playground); html::push_html(&mut s, p); @@ -1476,7 +1402,7 @@ impl MarkdownItemInfo<'_> { let mut s = String::with_capacity(md.len() * 3 / 2); let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1); - let p = Footnotes::new(p); + let p = footnotes::Footnotes::new(p); let p = TableWrapper::new(p.map(|(ev, _)| ev)); let p = p.filter(|event| { !matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph)) diff --git a/src/librustdoc/html/markdown/footnotes.rs b/src/librustdoc/html/markdown/footnotes.rs new file mode 100644 index 00000000000..3f0e586b8e3 --- /dev/null +++ b/src/librustdoc/html/markdown/footnotes.rs @@ -0,0 +1,113 @@ +//! Markdown footnote handling. +use std::fmt::Write as _; + +use pulldown_cmark::{Event, Tag, TagEnd, html}; +use rustc_data_structures::fx::FxIndexMap; + +use super::SpannedEvent; + +/// Moves all footnote definitions to the end and add back links to the +/// references. +pub(super) struct Footnotes<'a, I> { + inner: I, + footnotes: FxIndexMap<String, FootnoteDef<'a>>, +} + +/// The definition of a single footnote. +struct FootnoteDef<'a> { + content: Vec<Event<'a>>, + /// The number that appears in the footnote reference and list. + id: u16, +} + +impl<'a, I> Footnotes<'a, I> { + pub(super) fn new(iter: I) -> Self { + Footnotes { inner: iter, footnotes: FxIndexMap::default() } + } + + fn get_entry(&mut self, key: &str) -> (&mut Vec<Event<'a>>, u16) { + let new_id = self.footnotes.len() + 1; + let key = key.to_owned(); + let FootnoteDef { content, id } = self + .footnotes + .entry(key) + .or_insert(FootnoteDef { content: Vec::new(), id: new_id as u16 }); + // Don't allow changing the ID of existing entrys, but allow changing the contents. + (content, *id) + } +} + +impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> { + type Item = SpannedEvent<'a>; + + fn next(&mut self) -> Option<Self::Item> { + loop { + match self.inner.next() { + Some((Event::FootnoteReference(ref reference), range)) => { + // When we see a reference (to a footnote we may not know) the definition of, + // reserve a number for it, and emit a link to that number. + let (_, id) = self.get_entry(reference); + let reference = + format!("<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>", id); + return Some((Event::Html(reference.into()), range)); + } + Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => { + // When we see a footnote definition, collect the assocated content, and store + // that for rendering later. + let content = collect_footnote_def(&mut self.inner); + let (entry_content, _) = self.get_entry(&def); + *entry_content = content; + } + Some(e) => return Some(e), + None => { + if !self.footnotes.is_empty() { + // After all the markdown is emmited, emit an <hr> then all the footnotes + // in a list. + let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect(); + let defs_html = render_footnotes_defs(defs); + return Some((Event::Html(defs_html.into()), 0..0)); + } else { + return None; + } + } + } + } + } +} + +fn collect_footnote_def<'a>(events: impl Iterator<Item = SpannedEvent<'a>>) -> Vec<Event<'a>> { + let mut content = Vec::new(); + for (event, _) in events { + if let Event::End(TagEnd::FootnoteDefinition) = event { + break; + } + content.push(event); + } + content +} + +fn render_footnotes_defs(mut footnotes: Vec<FootnoteDef<'_>>) -> String { + let mut ret = String::from("<div class=\"footnotes\"><hr><ol>"); + + // Footnotes must listed in order of id, so the numbers the + // browser generated for <li> are right. + footnotes.sort_by_key(|x| x.id); + + for FootnoteDef { mut content, id } in footnotes { + write!(ret, "<li id=\"fn{id}\">").unwrap(); + let mut is_paragraph = false; + if let Some(&Event::End(TagEnd::Paragraph)) = content.last() { + content.pop(); + is_paragraph = true; + } + html::push_html(&mut ret, content.into_iter()); + write!(ret, " <a href=\"#fnref{id}\">↩</a>").unwrap(); + if is_paragraph { + ret.push_str("</p>"); + } + ret.push_str("</li>"); + } + ret.push_str("</ol></div>"); + + ret +} diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index df9776ff5f8..2c17fd54006 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -44,7 +44,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 400; src: local('Fira Sans'), - url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2"); + url("FiraSans-Regular-0fe48ade.woff2") format("woff2"); font-display: swap; } @font-face { @@ -52,7 +52,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 500; src: local('Fira Sans Medium'), - url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2"); + url("FiraSans-Medium-e1aa3f0a.woff2") format("woff2"); font-display: swap; } @@ -62,7 +62,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 400; src: local('Source Serif 4'), - url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2"); + url("SourceSerif4-Regular-6b053e98.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -70,7 +70,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: italic; font-weight: 400; src: local('Source Serif 4 Italic'), - url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2"); + url("SourceSerif4-It-ca3b17ed.ttf.woff2") format("woff2"); font-display: swap; } @font-face { @@ -78,7 +78,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-style: normal; font-weight: 700; src: local('Source Serif 4 Bold'), - url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2"); + url("SourceSerif4-Bold-6d4fd4c0.ttf.woff2") format("woff2"); font-display: swap; } @@ -89,28 +89,28 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ font-weight: 400; /* Avoid using locally installed font because bad versions are in circulation: * see https://github.com/rust-lang/rust/issues/24355 */ - src: url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2"); + src: url("SourceCodePro-Regular-8badfe75.ttf.woff2") format("woff2"); font-display: swap; } @font-face { font-family: 'Source Code Pro'; font-style: italic; font-weight: 400; - src: url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2"); + src: url("SourceCodePro-It-fc8b9304.ttf.woff2") format("woff2"); font-display: swap; } @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 600; - src: url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2"); + src: url("SourceCodePro-Semibold-aa29a496.ttf.woff2") format("woff2"); font-display: swap; } /* Avoid using legacy CJK serif fonts in Windows like Batang. */ @font-face { font-family: 'NanumBarunGothic'; - src: url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2"); + src: url("NanumBarunGothic-13b3dcba.ttf.woff2") format("woff2"); font-display: swap; unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF; } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 6157598ba38..9e0803f5d3f 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -3,12 +3,9 @@ //! All the static files are included here for centralized access in case anything other than the //! HTML rendering code (say, the theme checker) needs to access one of these files. -use std::hash::Hasher; use std::path::{Path, PathBuf}; use std::{fmt, str}; -use rustc_data_structures::fx::FxHasher; - pub(crate) struct StaticFile { pub(crate) filename: PathBuf, pub(crate) bytes: &'static [u8], @@ -64,9 +61,11 @@ pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf { } fn static_suffix(bytes: &[u8]) -> String { - let mut hasher = FxHasher::default(); - hasher.write(bytes); - format!("-{:016x}", hasher.finish()) + use sha2::Digest; + let bytes = sha2::Sha256::digest(bytes); + let mut digest = format!("-{bytes:x}"); + digest.truncate(9); + digest } macro_rules! static_files { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 77e7d83090b..0130f2ce517 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -672,12 +672,12 @@ impl FromClean<clean::Trait> for Trait { let tcx = renderer.tcx; let is_auto = trait_.is_auto(tcx); let is_unsafe = trait_.safety(tcx) == rustc_hir::Safety::Unsafe; - let is_object_safe = trait_.is_dyn_compatible(tcx); + let is_dyn_compatible = trait_.is_dyn_compatible(tcx); let clean::Trait { items, generics, bounds, .. } = trait_; Trait { is_auto, is_unsafe, - is_object_safe, + is_dyn_compatible, items: renderer.ids(items), generics: generics.into_json(renderer), bounds: bounds.into_json(renderer), diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index cbc6e351fac..2f0ea8d618c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1091,7 +1091,6 @@ impl LinkCollector<'_, '_> { // resolutions are cached, for other links we want to report an error every // time so they are not cached. matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut), - false, )?; if resolved.len() > 1 { @@ -1404,9 +1403,6 @@ impl LinkCollector<'_, '_> { // If errors are cached then they are only reported on first occurrence // which we want in some cases but not in others. cache_errors: bool, - // If this call is intended to be recoverable, then pass true to silence. - // This is only recoverable when path is failed to resolved. - recoverable: bool, ) -> Option<Vec<(Res, Option<UrlFragment>)>> { if let Some(res) = self.visited_links.get(&key) { if res.is_some() || cache_errors { @@ -1414,7 +1410,7 @@ impl LinkCollector<'_, '_> { } } - let mut candidates = self.resolve_with_disambiguator(&key, diag.clone(), recoverable); + let mut candidates = self.resolve_with_disambiguator(&key, diag.clone()); // FIXME: it would be nice to check that the feature gate was enabled in the original crate, not just ignore it altogether. // However I'm not sure how to check that across crates. @@ -1463,14 +1459,10 @@ impl LinkCollector<'_, '_> { } /// After parsing the disambiguator, resolve the main part of the link. - // FIXME(jynelson): wow this is just so much fn resolve_with_disambiguator( &mut self, key: &ResolutionInfo, diag: DiagnosticInfo<'_>, - // If this call is intended to be recoverable, then pass true to silence. - // This is only recoverable when path is failed to resolved. - recoverable: bool, ) -> Vec<(Res, Option<DefId>)> { let disambiguator = key.dis; let path_str = &key.path_str; @@ -1500,9 +1492,7 @@ impl LinkCollector<'_, '_> { } } } - if !recoverable { - resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); - } + resolution_failure(self, diag, path_str, disambiguator, smallvec![err]); return vec![]; } } @@ -1539,15 +1529,13 @@ impl LinkCollector<'_, '_> { .fold(0, |acc, res| if let Ok(res) = res { acc + res.len() } else { acc }); if len == 0 { - if !recoverable { - resolution_failure( - self, - diag, - path_str, - disambiguator, - candidates.into_iter().filter_map(|res| res.err()).collect(), - ); - } + resolution_failure( + self, + diag, + path_str, + disambiguator, + candidates.into_iter().filter_map(|res| res.err()).collect(), + ); return vec![]; } else if len == 1 { candidates.into_iter().filter_map(|res| res.ok()).flatten().collect::<Vec<_>>() diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index d3548036d4c..7f7cd3672b7 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -6,9 +6,12 @@ edition = "2021" [lib] path = "lib.rs" +[features] +default = ["rustc-hash"] + [dependencies] serde = { version = "1.0", features = ["derive"] } -rustc-hash = "1.1.0" +rustc-hash = { version = "1.1.0", optional = true } [dev-dependencies] serde_json = "1.0" diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index fc64bc98bb9..c4e142342a8 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -3,17 +3,22 @@ //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`] //! struct is the root of the JSON blob and all other items are contained within. +#[cfg(not(feature = "rustc-hash"))] +use std::collections::HashMap; use std::path::PathBuf; -pub use rustc_hash::FxHashMap; +#[cfg(feature = "rustc-hash")] +use rustc_hash::FxHashMap as HashMap; use serde::{Deserialize, Serialize}; +pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc + /// The version of JSON output that this crate represents. /// /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 35; +pub const FORMAT_VERSION: u32 = 36; /// The root of the emitted JSON blob. /// @@ -30,11 +35,11 @@ pub struct Crate { pub includes_private: bool, /// A collection of all items in the local crate as well as some external traits and their /// items that are referenced locally. - pub index: FxHashMap<Id, Item>, + pub index: HashMap<Id, Item>, /// Maps IDs to fully qualified paths and other info helpful for generating links. - pub paths: FxHashMap<Id, ItemSummary>, + pub paths: HashMap<Id, ItemSummary>, /// Maps `crate_id` of items to a crate name and html_root_url if it exists. - pub external_crates: FxHashMap<u32, ExternalCrate>, + pub external_crates: HashMap<u32, ExternalCrate>, /// A single version number to be used in the future when making backwards incompatible changes /// to the JSON output. pub format_version: u32, @@ -95,7 +100,7 @@ pub struct Item { /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`). pub docs: Option<String>, /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs - pub links: FxHashMap<String, Id>, + pub links: HashMap<String, Id>, /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`) pub attrs: Vec<String>, /// Information about the item’s deprecation, if present. @@ -1082,8 +1087,11 @@ pub struct Trait { pub is_auto: bool, /// Whether the trait is marked as `unsafe`. pub is_unsafe: bool, - /// Whether the trait is [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety). - pub is_object_safe: bool, + // FIXME(dyn_compat_renaming): Update the URL once the Reference is updated and hits stable. + /// Whether the trait is [dyn compatible](https://doc.rust-lang.org/reference/items/traits.html#object-safety)[^1]. + /// + /// [^1]: Formerly known as "object safe". + pub is_dyn_compatible: bool, /// Associated [`Item`]s that can/must be implemented by the `impl` blocks. pub items: Vec<Id>, /// Information about the type parameters and `where` clauses of the trait. diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 8c30ce53688e25f7e9d860b33cc914fb2957ca9 +Subproject cf53cc54bb593b5ec3dc2be4b1702f50c36d24d diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh index 5b4b4be4e36..ea118a3b6fc 100644 --- a/src/tools/clippy/.github/deploy.sh +++ b/src/tools/clippy/.github/deploy.sh @@ -8,8 +8,8 @@ rm -rf out/master/ || exit 0 echo "Making the docs for master" mkdir out/master/ cp util/gh-pages/index.html out/master +cp util/gh-pages/theme.js out/master cp util/gh-pages/script.js out/master -cp util/gh-pages/lints.json out/master cp util/gh-pages/style.css out/master if [[ -n $TAG_NAME ]]; then diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore index 181b71a658b..a7c25b29021 100644 --- a/src/tools/clippy/.gitignore +++ b/src/tools/clippy/.gitignore @@ -34,6 +34,7 @@ out # gh pages docs util/gh-pages/lints.json +util/gh-pages/index.html # rustfmt backups *.rs.bk diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 5d253d52531..4bdbc91db93 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,46 @@ document. ## Unreleased / Beta / In Rust Nightly -[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master) +[0f8eabd6...master](https://github.com/rust-lang/rust-clippy/compare/0f8eabd6...master) + +## Rust 1.82 + +Current stable, released 2024-10-17 + +[View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-07-11T20%3A12%3A07Z..2024-08-24T20%3A55%3A35Z+base%3Amaster) + +### New Lints + +* Added [`too_long_first_doc_paragraph`] to `nursery` + [#12993](https://github.com/rust-lang/rust-clippy/pull/12993) +* Added [`unused_result_ok`] to `restriction` + [#12150](https://github.com/rust-lang/rust-clippy/pull/12150) +* Added [`pathbuf_init_then_push`] to `restriction` + [#11700](https://github.com/rust-lang/rust-clippy/pull/11700) + +### Enhancements + +* [`explicit_iter_loop`]: Now respects the `msrv` configuration + [#13288](https://github.com/rust-lang/rust-clippy/pull/13288) +* [`assigning_clones`]: No longer lints in test code + [#13273](https://github.com/rust-lang/rust-clippy/pull/13273) +* [`inconsistent_struct_constructor`]: Lint attributes now work on the struct definition + [#13211](https://github.com/rust-lang/rust-clippy/pull/13211) +* [`set_contains_or_insert`]: Now also checks for `BTreeSet` + [#13053](https://github.com/rust-lang/rust-clippy/pull/13053) +* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]: AccessKit, + CoreFoundation, CoreGraphics, CoreText, Direct2D, Direct3D, DirectWrite, PostScript, + OpenAL, OpenType, WebRTC, WebSocket, WebTransport, NetBSD, and OpenBSD + [#13093](https://github.com/rust-lang/rust-clippy/pull/13093) + +### ICE Fixes + +* [`uninit_vec`] + [rust#128720](https://github.com/rust-lang/rust/pull/128720) ## Rust 1.81 -Current stable, released 2024-09-05 +Released 2024-09-05 ### New Lints @@ -5621,6 +5656,7 @@ Released 2018-09-13 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_hash_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one +[`manual_ignore_case_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ignore_case_cmp [`manual_inspect`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect [`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check @@ -5874,6 +5910,7 @@ Released 2018-09-13 [`ref_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns +[`regex_creation_in_loops`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_creation_in_loops [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once @@ -6027,6 +6064,7 @@ Released 2018-09-13 [`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations +[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap [`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index cf810798d8c..1f7784fc489 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.83" +version = "0.1.84" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -23,7 +23,7 @@ path = "src/driver.rs" [dependencies] clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } -rustc_tools_util = "0.3.0" +rustc_tools_util = "0.4.0" tempfile = { version = "3.3", optional = true } termize = "0.1" color-print = "0.3.4" @@ -39,6 +39,8 @@ toml = "0.7.3" walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" +pulldown-cmark = "0.11" +rinja = { version = "0.3", default-features = false, features = ["config"] } # UI test dependencies clippy_utils = { path = "clippy_utils" } @@ -50,7 +52,7 @@ parking_lot = "0.12" tokio = { version = "1", features = ["io-util"] } [build-dependencies] -rustc_tools_util = "0.3.0" +rustc_tools_util = "0.4.0" [features] integration = ["tempfile"] diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 07a56fb33df..43b551ae216 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -329,7 +329,7 @@ arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] ## `array-size-threshold` The maximum allowed size for arrays on the stack -**Default Value:** `512000` +**Default Value:** `16384` --- **Affected lints:** diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 9da7112345d..d21df202dca 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.83" +version = "0.1.84" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index e4e2c97fdc1..4757c0b1339 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -97,6 +97,30 @@ impl ConfError { } } +// Remove code tags and code behind '# 's, as they are not needed for the lint docs and --explain +pub fn sanitize_explanation(raw_docs: &str) -> String { + // Remove tags and hidden code: + let mut explanation = String::with_capacity(128); + let mut in_code = false; + for line in raw_docs.lines().map(str::trim) { + if let Some(lang) = line.strip_prefix("```") { + let tag = lang.split_once(',').map_or(lang, |(left, _)| left); + if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { + explanation += "```rust\n"; + } else { + explanation += line; + explanation.push('\n'); + } + in_code = !in_code; + } else if !(in_code && line.starts_with("# ")) { + explanation += line; + explanation.push('\n'); + } + } + + explanation +} + macro_rules! wrap_option { () => { None @@ -366,7 +390,7 @@ define_Conf! { arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(), /// The maximum allowed size for arrays on the stack #[lints(large_const_arrays, large_stack_arrays)] - array_size_threshold: u64 = 512_000, + array_size_threshold: u64 = 16 * 1024, /// Suppress lints whenever the suggested change would cause breakage for other crates. #[lints( box_collection, diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index c63d98a0a13..42651521f8d 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -26,5 +26,5 @@ mod metadata; pub mod msrvs; pub mod types; -pub use conf::{Conf, get_configuration_metadata, lookup_conf_file}; +pub use conf::{Conf, get_configuration_metadata, lookup_conf_file, sanitize_explanation}; pub use metadata::ClippyConfiguration; diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index 68a3b11d384..2f4da4cba3d 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -17,8 +17,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,83,0 { CONST_EXTERN_FN } - 1,83,0 { CONST_FLOAT_BITS_CONV } + 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } 1,82,0 { IS_NONE_OR } 1,81,0 { LINT_REASONS_STABILIZATION } 1,80,0 { BOX_INTO_ITER} diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index a5d72c3a559..952a8711fb4 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -9,7 +9,7 @@ aho-corasick = "1.0" clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" -opener = "0.6" +opener = "0.7" shell-escape = "0.1" walkdir = "2.3" diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 5fd65017cfb..e15ba339717 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -207,13 +207,13 @@ pub(crate) fn get_stabilization_version() -> String { fn get_test_file_contents(lint_name: &str, msrv: bool) -> String { let mut test = formatdoc!( - r#" + r" #![warn(clippy::{lint_name})] fn main() {{ // test code goes here }} - "# + " ); if msrv { @@ -272,23 +272,23 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( - r#" + r" use clippy_config::msrvs::{{self, Msrv}}; use clippy_config::Conf; {pass_import} use rustc_lint::{{{context_import}, {pass_type}, LintContext}}; use rustc_session::impl_lint_pass; - "# + " ) } else { formatdoc!( - r#" + r" {pass_import} use rustc_lint::{{{context_import}, {pass_type}}}; use rustc_session::declare_lint_pass; - "# + " ) }); @@ -296,7 +296,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { result.push_str(&if enable_msrv { formatdoc!( - r#" + r" pub struct {name_camel} {{ msrv: Msrv, }} @@ -315,15 +315,15 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed. // TODO: Update msrv config comment in `clippy_config/src/conf.rs` - "# + " ) } else { formatdoc!( - r#" + r" declare_lint_pass!({name_camel} => [{name_upper}]); impl {pass_type}{pass_lifetimes} for {name_camel} {{}} - "# + " ) }); @@ -416,7 +416,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R } else { let _: fmt::Result = writedoc!( lint_file_contents, - r#" + r" use rustc_lint::{{{context_import}, LintContext}}; use super::{name_upper}; @@ -425,7 +425,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R pub(super) fn check(cx: &{context_import}{pass_lifetimes}) {{ todo!(); }} - "# + " ); } diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index cc14cd8dae6..d367fefec61 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -19,7 +19,9 @@ pub fn run(port: u16, lint: Option<String>) -> ! { }); loop { - if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") { + let index_time = mtime("util/gh-pages/index.html"); + + if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") { Command::new(env::var("CARGO").unwrap_or("cargo".into())) .arg("collect-metadata") .spawn() diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index d1188940b46..63ea6faf60d 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.83" +version = "0.1.84" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -13,7 +13,6 @@ arrayvec = { version = "0.7", default-features = false } cargo_metadata = "0.18" clippy_config = { path = "../clippy_config" } clippy_utils = { path = "../clippy_utils" } -declare_clippy_lint = { path = "../declare_clippy_lint" } itertools = "0.12" quine-mc_cluskey = "0.2" regex-syntax = "0.8" diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 40d154c0bdf..bf1d077fec2 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -47,7 +47,7 @@ impl LateLintPass<'_> for BoxDefault { // And the call is that of a `Box` method && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box()) // And the single argument to the call is another function call - // This is the `T::default()` of `Box::new(T::default())` + // This is the `T::default()` (or default equivalent) of `Box::new(T::default())` && let ExprKind::Call(arg_path, _) = arg.kind // And we are not in a foreign crate's macro && !in_external_macro(cx.sess(), expr.span) diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs index dd2620b0b9d..d88c0711b39 100644 --- a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs +++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs @@ -41,7 +41,7 @@ impl EarlyLintPass for ByteCharSlice { "can be more succinctly written as a byte str", "try", format!("b\"{slice}\""), - Applicability::MaybeIncorrect, + Applicability::MachineApplicable, ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs index d4d5ee37bcc..b7b63250864 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -19,7 +19,7 @@ pub(super) fn check( if msrv.meets(msrvs::UNSIGNED_ABS) && let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() - && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind + && let ExprKind::MethodCall(method_path, receiver, [], _) = cast_expr.kind && method_path.ident.name.as_str() == "abs" { let span = if from.bit_width() == to.bit_width() { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index 960c81045e3..b11b967f8bc 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, ..) = &expr.kind { + } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind { if method_path.ident.name == sym!(cast) && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 24570d8f440..b43906903a0 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -34,7 +34,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]); impl LateLintPass<'_> for CreateDir { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Call(func, [arg, ..]) = expr.kind + if let ExprKind::Call(func, [arg]) = expr.kind && let ExprKind::Path(ref path) = func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id) diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs new file mode 100644 index 00000000000..b1e39c70baa --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs @@ -0,0 +1,162 @@ +#[macro_export] +#[allow(clippy::crate_in_macro_def)] +macro_rules! declare_clippy_lint { + (@ + $(#[doc = $lit:literal])* + pub $lint_name:ident, + $category:ident, + $lintcategory:expr, + $desc:literal, + $version_expr:expr, + $version_lit:literal + ) => { + rustc_session::declare_tool_lint! { + $(#[doc = $lit])* + #[clippy::version = $version_lit] + pub clippy::$lint_name, + $category, + $desc, + report_in_external_macro:true + } + + pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo { + lint: &$lint_name, + category: $lintcategory, + explanation: concat!($($lit,"\n",)*), + location: concat!(file!(), "#L", line!()), + version: $version_expr + }; + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + restriction, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Restriction, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + style, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Style, $desc, + Some($version), $version + + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + correctness, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Deny, crate::LintCategory::Correctness, $desc, + Some($version), $version + + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + perf, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Perf, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + complexity, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Complexity, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + suspicious, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + nursery, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Nursery, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + pedantic, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc, + Some($version), $version + } + }; + ( + $(#[doc = $lit:literal])* + #[clippy::version = $version:literal] + pub $lint_name:ident, + cargo, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Cargo, $desc, + Some($version), $version + } + }; + + ( + $(#[doc = $lit:literal])* + pub $lint_name:ident, + internal, + $desc:literal + ) => { + declare_clippy_lint! {@ + $(#[doc = $lit])* + pub $lint_name, Allow, crate::LintCategory::Internal, $desc, + None, "0.0.0" + } + }; +} diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 9cec672beb0..3c4e75df8ab 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -306,6 +306,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, + crate::manual_ignore_case_cmp::MANUAL_IGNORE_CASE_CMP_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, @@ -639,6 +640,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::ref_patterns::REF_PATTERNS_INFO, crate::reference::DEREF_ADDROF_INFO, crate::regex::INVALID_REGEX_INFO, + crate::regex::REGEX_CREATION_IN_LOOPS_INFO, crate::regex::TRIVIAL_REGEX_INFO, crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO, crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO, @@ -736,6 +738,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unit_types::UNIT_CMP_INFO, crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO, + crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO, crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index dc10b64698b..de775b64795 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { if !expr.span.from_expansion() // Avoid cases already linted by `field_reassign_with_default` && !self.reassigned_linted.contains(&expr.span) - && let ExprKind::Call(path, ..) = expr.kind + && let ExprKind::Call(path, []) = expr.kind && !in_automatically_derived(cx.tcx, expr.hir_id) && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() @@ -253,7 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { - if let ExprKind::Call(fn_expr, _) = &expr.kind + if let ExprKind::Call(fn_expr, []) = &expr.kind && let ExprKind::Path(qpath) = &fn_expr.kind && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index e090644ae44..89c6a4e08dc 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -452,7 +452,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, - style, + nursery, "ensure that the first line of a documentation paragraph isn't too long" } diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index f37d11f7eb9..3c235fab009 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -43,7 +43,7 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(path_expr, _args) = e.kind + if let ExprKind::Call(path_expr, [_]) = e.kind && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 5b423a96918..4e4434ec7d1 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { && unwrap_fun.ident.name == sym::unwrap // match call to write_fmt && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind) - && let ExprKind::Call(write_recv_path, _) = write_recv.kind + && let ExprKind::Call(write_recv_path, []) = write_recv.kind && write_fun.ident.name == sym!(write_fmt) && let Some(def_id) = path_def_id(cx, write_recv_path) { diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 8da6623f34d..daa199779e3 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -436,12 +436,12 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { lhs, rhs, ) = expr.kind + && let ExprKind::MethodCall(path, self_arg, [], _) = &lhs.kind + && path.ident.name.as_str() == "exp" && cx.typeck_results().expr_ty(lhs).is_floating_point() && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) - && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && cx.typeck_results().expr_ty(self_arg).is_floating_point() - && path.ident.name.as_str() == "exp" { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 7c0515b8c56..5619cb0ab1b 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -151,7 +151,7 @@ struct FormatImplExpr<'a, 'tcx> { impl FormatImplExpr<'_, '_> { fn check_to_string_in_display(&self) { if self.format_trait_impl.name == sym::Display - && let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind + && let ExprKind::MethodCall(path, self_arg, [], _) = self.expr.kind // Get the hir_id of the object we are calling the method on // Is the method to_string() ? && path.ident.name == sym::to_string diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index 1c52514a330..ba80c099a01 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -82,7 +82,7 @@ fn mutex_lock_call<'tcx>( expr: &'tcx Expr<'_>, op_mutex: Option<&'tcx Expr<'_>>, ) -> ControlFlow<&'tcx Expr<'tcx>> { - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.as_str() == "lock" && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() && is_type_diagnostic_item(cx, ty, sym::Mutex) diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index f4a64f5c20b..3b84b569c3e 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -139,6 +139,13 @@ fn check_manual_check<'tcx>( if_block, else_block, msrv, + matches!( + clippy_utils::get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::If(..), + .. + }) + ), ), BinOpKind::Lt | BinOpKind::Le => check_gt( cx, @@ -149,6 +156,13 @@ fn check_manual_check<'tcx>( if_block, else_block, msrv, + matches!( + clippy_utils::get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::If(..), + .. + }) + ), ), _ => {}, } @@ -165,6 +179,7 @@ fn check_gt( if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: &Msrv, + is_composited: bool, ) { if let Some(big_var) = Var::new(big_var) && let Some(little_var) = Var::new(little_var) @@ -178,6 +193,7 @@ fn check_gt( if_block, else_block, msrv, + is_composited, ); } } @@ -206,6 +222,7 @@ fn check_subtraction( if_block: &Expr<'_>, else_block: &Expr<'_>, msrv: &Msrv, + is_composited: bool, ) { let if_block = peel_blocks(if_block); let else_block = peel_blocks(else_block); @@ -226,6 +243,7 @@ fn check_subtraction( else_block, if_block, msrv, + is_composited, ); return; } @@ -242,13 +260,18 @@ fn check_subtraction( && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST)) { + let sugg = format!( + "{}{big_var_snippet}.saturating_sub({little_var_snippet}){}", + if is_composited { "{ " } else { "" }, + if is_composited { " }" } else { "" } + ); span_lint_and_sugg( cx, IMPLICIT_SATURATING_SUB, expr_span, "manual arithmetic check found", "replace it with", - format!("{big_var_snippet}.saturating_sub({little_var_snippet})"), + sugg, Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 590d9afd1b4..f4c00d8287d 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -3,8 +3,8 @@ use clippy_utils::source::snippet; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ - AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, - TyKind, WherePredicate, + AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind, + WherePredicate, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 66a8a3167a4..dd90e2a6e94 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -50,11 +50,28 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Detects type names that are prefixed or suffixed by the - /// containing module's name. + /// Detects public item names that are prefixed or suffixed by the + /// containing public module's name. /// /// ### Why is this bad? - /// It requires the user to type the module name twice. + /// It requires the user to type the module name twice in each usage, + /// especially if they choose to import the module rather than its contents. + /// + /// Lack of such repetition is also the style used in the Rust standard library; + /// e.g. `io::Error` and `fmt::Error` rather than `io::IoError` and `fmt::FmtError`; + /// and `array::from_ref` rather than `array::array_from_ref`. + /// + /// ### Known issues + /// Glob re-exports are ignored; e.g. this will not warn even though it should: + /// + /// ```no_run + /// pub mod foo { + /// mod iteration { + /// pub struct FooIter {} + /// } + /// pub use iteration::*; // creates the path `foo::FooIter` + /// } + /// ``` /// /// ### Example /// ```no_run @@ -71,7 +88,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.33.0"] pub MODULE_NAME_REPETITIONS, - pedantic, + restriction, "type names prefixed/postfixed with their containing module's name" } @@ -389,12 +406,12 @@ impl LateLintPass<'_> for ItemNameRepetitions { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let [.., (mod_name, mod_camel, owner_id)] = &*self.modules { + if let [.., (mod_name, mod_camel, mod_owner_id)] = &*self.modules { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name && let ItemKind::Mod(..) = item.kind - && (!self.allow_private_module_inception || cx.tcx.visibility(owner_id.def_id).is_public()) + && (!self.allow_private_module_inception || cx.tcx.visibility(mod_owner_id.def_id).is_public()) { span_lint( cx, @@ -403,9 +420,13 @@ impl LateLintPass<'_> for ItemNameRepetitions { "module has the same name as its containing module", ); } + // The `module_name_repetitions` lint should only trigger if the item has the module in its // name. Having the same name is accepted. - if cx.tcx.visibility(item.owner_id).is_public() && item_camel.len() > mod_camel.len() { + if cx.tcx.visibility(item.owner_id).is_public() + && cx.tcx.visibility(mod_owner_id.def_id).is_public() + && item_camel.len() > mod_camel.len() + { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs index 6f5065e4936..25f9be8b2d7 100644 --- a/src/tools/clippy/clippy_lints/src/large_futures.rs +++ b/src/tools/clippy/clippy_lints/src/large_futures.rs @@ -57,7 +57,7 @@ impl_lint_pass!(LargeFuture => [LARGE_FUTURES]); impl<'tcx> LateLintPass<'tcx> for LargeFuture { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind - && let ExprKind::Call(func, [arg, ..]) = scrutinee.kind + && let ExprKind::Call(func, [arg]) = scrutinee.kind && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind && !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(arg) diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 0f061d6de50..4ef881f11d5 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -1,3 +1,5 @@ +use std::num::Saturating; + use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; @@ -30,6 +32,7 @@ declare_clippy_lint! { pub struct LargeStackArrays { maximum_allowed_size: u64, prev_vec_macro_callsite: Option<Span>, + const_item_counter: Saturating<u16>, } impl LargeStackArrays { @@ -37,6 +40,7 @@ impl LargeStackArrays { Self { maximum_allowed_size: conf.array_size_threshold, prev_vec_macro_callsite: None, + const_item_counter: Saturating(0), } } @@ -60,8 +64,21 @@ impl LargeStackArrays { impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) { + self.const_item_counter += 1; + } + } + + fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) { + self.const_item_counter -= 1; + } + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind + if self.const_item_counter.0 == 0 + && let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind && !self.is_from_vec_macro(cx, expr.span) && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 035ee40348c..47c65ee6d0b 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -513,7 +513,7 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> return; } - if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { + if let (&ExprKind::MethodCall(method_path, receiver, [], _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { @@ -521,29 +521,17 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_> } } - check_len( - cx, - span, - method_path.ident.name, - receiver, - args, - &lit.node, - op, - compare_to, - ); + check_len(cx, span, method_path.ident.name, receiver, &lit.node, op, compare_to); } else { check_empty_expr(cx, span, method, lit, op); } } -// FIXME(flip1995): Figure out how to reduce the number of arguments -#[allow(clippy::too_many_arguments)] fn check_len( cx: &LateContext<'_>, span: Span, method_name: Symbol, receiver: &Expr<'_>, - args: &[Expr<'_>], lit: &LitKind, op: &str, compare_to: u32, @@ -554,7 +542,7 @@ fn check_len( return; } - if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) { + if method_name == sym::len && has_is_empty(cx, receiver) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 6ee064a6124..6e29dde2211 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -1,6 +1,7 @@ #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] +#![feature(macro_metavar_expr_concat)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] @@ -56,9 +57,10 @@ extern crate rustc_trait_selection; extern crate thin_vec; #[macro_use] -extern crate clippy_utils; +mod declare_clippy_lint; + #[macro_use] -extern crate declare_clippy_lint; +extern crate clippy_utils; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; @@ -203,6 +205,7 @@ mod manual_clamp; mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; +mod manual_ignore_case_cmp; mod manual_is_ascii_check; mod manual_is_power_of_two; mod manual_let_else; @@ -360,6 +363,7 @@ mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; mod unnecessary_box_returns; +mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; @@ -391,7 +395,7 @@ mod zero_sized_map_values; mod zombie_processes; // end lints modules, do not remove this comment, it’s used in `update_lints` -use clippy_config::{Conf, get_configuration_metadata}; +use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -519,8 +523,9 @@ impl LintInfo { pub fn explain(name: &str) -> i32 { let target = format!("clippy::{}", name.to_ascii_uppercase()); + if let Some(info) = declared_lints::LINTS.iter().find(|info| info.lint.name == target) { - println!("{}", info.explanation); + println!("{}", sanitize_explanation(info.explanation)); // Check if the lint has configuration let mut mdconf = get_configuration_metadata(); let name = name.to_ascii_lowercase(); @@ -896,7 +901,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_early_pass(|| Box::new(visibility::Visibility)); store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))); - store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); + store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))); @@ -941,5 +946,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); + store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); + store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); // 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 a7c48eb216a..5a3930b8bb8 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate, + Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref, + walk_trait_ref, walk_ty, walk_where_predicate, }; use rustc_hir::{ BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 81f2a03fb55..e2dcb20f906 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -412,7 +412,6 @@ impl LiteralDigitGrouping { } } -#[expect(clippy::module_name_repetitions)] pub struct DecimalLiteralRepresentation { threshold: u64, } diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index 858e3be5093..e25c03db534 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -42,6 +42,7 @@ pub(super) fn check<'tcx>( let mut loop_visitor = LoopVisitor { cx, label, + inner_labels: label.into_iter().collect(), is_finite: false, loop_depth: 0, }; @@ -93,6 +94,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option struct LoopVisitor<'hir, 'tcx> { cx: &'hir LateContext<'tcx>, label: Option<Label>, + inner_labels: Vec<Label>, loop_depth: usize, is_finite: bool, } @@ -108,11 +110,24 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> { self.is_finite = true; } }, + ExprKind::Continue(hir::Destination { label, .. }) => { + // Check whether we are leaving this loop by continuing into an outer loop + // whose label we did not encounter. + if label.is_some_and(|label| !self.inner_labels.contains(&label)) { + self.is_finite = true; + } + }, ExprKind::Ret(..) => self.is_finite = true, - ExprKind::Loop(..) => { + ExprKind::Loop(_, label, _, _) => { + if let Some(label) = label { + self.inner_labels.push(*label); + } self.loop_depth += 1; walk_expr(self, ex); - self.loop_depth = self.loop_depth.saturating_sub(1); + self.loop_depth -= 1; + if label.is_some() { + self.inner_labels.pop(); + } }, _ => { // Calls to a function that never return diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs index 7476a87267f..4473a3343c7 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs @@ -47,8 +47,9 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>, ); } -fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool { - if let ExprKind::MethodCall(..) = expr.kind +fn match_method_call<const ARGS_COUNT: usize>(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool { + if let ExprKind::MethodCall(_, _, args, _) = expr.kind + && args.len() == ARGS_COUNT && let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { cx.tcx.is_diagnostic_item(method, id) @@ -58,9 +59,9 @@ fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> b } fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr<'_>) -> bool { - if (match_method_call(cx, expr, sym::option_unwrap) || match_method_call(cx, expr, sym::option_expect)) + if (match_method_call::<0>(cx, expr, sym::option_unwrap) || match_method_call::<1>(cx, expr, sym::option_expect)) && let ExprKind::MethodCall(_, unwrap_recv, ..) = expr.kind - && match_method_call(cx, unwrap_recv, sym::vec_pop) + && match_method_call::<0>(cx, unwrap_recv, sym::vec_pop) && let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind { // make sure they're the same `Vec` @@ -96,7 +97,7 @@ fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &E pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, full_cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>, loop_span: Span) { if let ExprKind::Unary(UnOp::Not, cond) = full_cond.kind && let ExprKind::MethodCall(_, is_empty_recv, _, _) = cond.kind - && match_method_call(cx, cond, sym::vec_is_empty) + && match_method_call::<0>(cx, cond, sym::vec_is_empty) && let ExprKind::Block(body, _) = body.kind && let Some(stmt) = body.stmts.first() { diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index f8659897ffe..d255fea3af2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -172,10 +172,8 @@ fn get_vec_push<'tcx>( stmt: &'tcx Stmt<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> { if let StmtKind::Semi(semi_stmt) = &stmt.kind - // Extract method being called - && let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind - // Figure out the parameters for the method call - && let Some(pushed_item) = args.first() + // Extract method being called and figure out the parameters for the method call + && let ExprKind::MethodCall(path, self_expr, [pushed_item], _) = &semi_stmt.kind // Check that the method being called is push() on a Vec && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec) && path.ident.name.as_str() == "push" diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 50680331fbc..22aa681b681 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -43,7 +43,6 @@ impl MacroRefData { } #[derive(Default)] -#[expect(clippy::module_name_repetitions)] pub struct MacroUseImports { /// the actual import path used and the span of the attribute above it. The value is /// the location, where the lint should be emitted. diff --git a/src/tools/clippy/clippy_lints/src/main_recursion.rs b/src/tools/clippy/clippy_lints/src/main_recursion.rs index 72807b4b284..01ea2f5debe 100644 --- a/src/tools/clippy/clippy_lints/src/main_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/main_recursion.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for MainRecursion { return; } - if let ExprKind::Call(func, _) = &expr.kind + if let ExprKind::Call(func, []) = &expr.kind && let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind && let Some(def_id) = path.res.opt_def_id() && is_entrypoint_fn(cx, def_id) diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index c0e87e8a1fa..1bd8813e348 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -95,7 +95,7 @@ fn get_one_size_of_ty<'tcx>( } fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { - if let ExprKind::Call(count_func, _func_args) = expr.kind + if let ExprKind::Call(count_func, []) = expr.kind && let ExprKind::Path(ref count_func_qpath) = count_func.kind && let QPath::Resolved(_, count_func_path) = count_func_qpath && let Some(segment_zero) = count_func_path.segments.first() diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 9d3ddab60bb..a269ea11397 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -1,3 +1,5 @@ +use clippy_config::msrvs::Msrv; +use clippy_config::{Conf, msrvs}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; @@ -6,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Constness, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does @@ -56,7 +58,7 @@ declare_clippy_lint! { style, "use dedicated method to check if a float is finite" } -declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]); +impl_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]); #[derive(Clone, Copy)] enum Variant { @@ -80,6 +82,18 @@ impl Variant { } } +pub struct ManualFloatMethods { + msrv: Msrv, +} + +impl ManualFloatMethods { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Binary(kind, lhs, rhs) = expr.kind @@ -92,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { && !in_external_macro(cx.sess(), expr.span) && ( matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst) - || cx.tcx.features().declared(sym!(const_float_classify)) + || self.msrv.meets(msrvs::CONST_FLOAT_CLASSIFY) ) && let [first, second, const_1, const_2] = exprs && let ecx = ConstEvalCtxt::new(cx) @@ -150,6 +164,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { }); } } + + extract_msrv_attr!(LateContext); } fn is_infinity(constant: &Constant<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs new file mode 100644 index 00000000000..dabfac3f613 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -0,0 +1,127 @@ +use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item}; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::ExprKind::{Binary, Lit, MethodCall}; +use rustc_hir::{BinOpKind, Expr, LangItem}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::{Ty, UintTy}; +use rustc_session::declare_lint_pass; +use rustc_span::{Span, sym}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for manual case-insensitive ASCII comparison. + /// + /// ### Why is this bad? + /// The `eq_ignore_ascii_case` method is faster because it does not allocate + /// memory for the new strings, and it is more readable. + /// + /// ### Example + /// ```no_run + /// fn compare(a: &str, b: &str) -> bool { + /// a.to_ascii_lowercase() == b.to_ascii_lowercase() || a.to_ascii_lowercase() == "abc" + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn compare(a: &str, b: &str) -> bool { + /// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc") + /// } + /// ``` + #[clippy::version = "1.82.0"] + pub MANUAL_IGNORE_CASE_CMP, + perf, + "manual case-insensitive ASCII comparison" +} + +declare_lint_pass!(ManualIgnoreCaseCmp => [MANUAL_IGNORE_CASE_CMP]); + +enum MatchType<'a, 'b> { + ToAscii(bool, Ty<'a>), + Literal(&'b LitKind), +} + +fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> { + if let MethodCall(path, expr, _, _) = kind { + let is_lower = match path.ident.name.as_str() { + "to_ascii_lowercase" => true, + "to_ascii_uppercase" => false, + _ => return None, + }; + let ty_raw = cx.typeck_results().expr_ty(expr); + let ty = ty_raw.peel_refs(); + if needs_ref_to_cmp(cx, ty) + || ty.is_str() + || ty.is_slice() + || matches!(get_type_diagnostic_name(cx, ty), Some(sym::OsStr | sym::OsString)) + { + return Some((expr.span, ToAscii(is_lower, ty_raw))); + } + } else if let Lit(expr) = kind { + return Some((expr.span, Literal(&expr.node))); + } + None +} + +/// Returns true if the type needs to be dereferenced to be compared +fn needs_ref_to_cmp(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + ty.is_char() + || *ty.kind() == ty::Uint(UintTy::U8) + || is_type_diagnostic_item(cx, ty, sym::Vec) + || is_type_lang_item(cx, ty, LangItem::String) +} + +impl LateLintPass<'_> for ManualIgnoreCaseCmp { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + // check if expression represents a comparison of two strings + // using .to_ascii_lowercase() or .to_ascii_uppercase() methods, + // or one of the sides is a literal + // Offer to replace it with .eq_ignore_ascii_case() method + if let Binary(op, left, right) = &expr.kind + && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) + && let Some((left_span, left_val)) = get_ascii_type(cx, left.kind) + && let Some((right_span, right_val)) = get_ascii_type(cx, right.kind) + && match (&left_val, &right_val) { + (ToAscii(l_lower, ..), ToAscii(r_lower, ..)) if l_lower == r_lower => true, + (ToAscii(..), Literal(..)) | (Literal(..), ToAscii(..)) => true, + _ => false, + } + { + let deref = match right_val { + ToAscii(_, ty) if needs_ref_to_cmp(cx, ty) => "&", + ToAscii(..) => "", + Literal(ty) => { + if let LitKind::Char(_) | LitKind::Byte(_) = ty { + "&" + } else { + "" + } + }, + }; + let neg = if op.node == BinOpKind::Ne { "!" } else { "" }; + span_lint_and_then( + cx, + MANUAL_IGNORE_CASE_CMP, + expr.span, + "manual case-insensitive ASCII comparison", + |diag| { + let mut app = Applicability::MachineApplicable; + diag.span_suggestion_verbose( + expr.span, + "consider using `.eq_ignore_ascii_case()` instead", + format!( + "{neg}{}.eq_ignore_ascii_case({deref}{})", + snippet_with_applicability(cx, left_span, "_", &mut app), + snippet_with_applicability(cx, right_span, "_", &mut app) + ), + app, + ); + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index da2a982ee17..a11d3e4624c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -11,10 +11,12 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual + /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which may be manual /// reimplementations of `x.is_power_of_two()`. + /// /// ### Why is this bad? /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit. + /// /// ### Example /// ```no_run /// let a: u32 = 4; @@ -27,7 +29,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.82.0"] pub MANUAL_IS_POWER_OF_TWO, - complexity, + pedantic, "manually reimplementing `is_power_of_two`" } @@ -41,7 +43,7 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo { && bin_op.node == BinOpKind::Eq { // a.count_ones() == 1 - if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind + if let ExprKind::MethodCall(method_name, reciever, [], _) = left.kind && method_name.ident.as_str() == "count_ones" && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() && check_lit(right, 1) @@ -50,7 +52,7 @@ impl LateLintPass<'_> for ManualIsPowerOfTwo { } // 1 == a.count_ones() - if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind + if let ExprKind::MethodCall(method_name, reciever, [], _) = right.kind && method_name.ident.as_str() == "count_ones" && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() && check_lit(left, 1) diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs index b24a0f4695a..18901f7399d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs +++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs @@ -45,10 +45,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { && !expr.span.from_expansion() // Does not apply inside const because size_of_val is not cost in stable. && !is_in_const_context(cx) - && let Some(receiver) = simplify(cx, left, right) + && let Some((receiver, refs_count)) = simplify(cx, left, right) { let ctxt = expr.span.ctxt(); let mut app = Applicability::MachineApplicable; + let deref = "*".repeat(refs_count - 1); let val_name = snippet_with_context(cx, receiver.span, ctxt, "slice", &mut app).0; let Some(sugg) = std_or_core(cx) else { return }; @@ -58,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation { expr.span, "manual slice size calculation", "try", - format!("{sugg}::mem::size_of_val({val_name})"), + format!("{sugg}::mem::size_of_val({deref}{val_name})"), app, ); } @@ -69,7 +70,7 @@ fn simplify<'tcx>( cx: &LateContext<'tcx>, expr1: &'tcx Expr<'tcx>, expr2: &'tcx Expr<'tcx>, -) -> Option<&'tcx Expr<'tcx>> { +) -> Option<(&'tcx Expr<'tcx>, usize)> { let expr1 = expr_or_init(cx, expr1); let expr2 = expr_or_init(cx, expr2); @@ -80,15 +81,16 @@ fn simplify_half<'tcx>( cx: &LateContext<'tcx>, expr1: &'tcx Expr<'tcx>, expr2: &'tcx Expr<'tcx>, -) -> Option<&'tcx Expr<'tcx>> { +) -> Option<(&'tcx Expr<'tcx>, usize)> { if !expr1.span.from_expansion() // expr1 is `[T1].len()`? - && let ExprKind::MethodCall(method_path, receiver, _, _) = expr1.kind + && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind && method_path.ident.name == sym::len && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let ty::Slice(ty1) = receiver_ty.peel_refs().kind() + && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty) + && let ty::Slice(ty1) = receiver_ty.kind() // expr2 is `size_of::<T2>()`? - && let ExprKind::Call(func, _) = expr2.kind + && let ExprKind::Call(func, []) = expr2.kind && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) @@ -96,7 +98,7 @@ fn simplify_half<'tcx>( // T1 == T2? && *ty1 == ty2 { - Some(receiver) + Some((receiver, refs_count)) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs index 198f7aaddc7..5c2a711b5cb 100644 --- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -52,8 +52,8 @@ impl LateLintPass<'_> for ManualStringNew { } match expr.kind { - ExprKind::Call(func, args) => { - parse_call(cx, expr.span, func, args); + ExprKind::Call(func, [arg]) => { + parse_call(cx, expr.span, func, arg); }, ExprKind::MethodCall(path_segment, receiver, ..) => { parse_method_call(cx, expr.span, path_segment, receiver); @@ -93,20 +93,15 @@ fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegmen let method_arg_kind = &receiver.kind; if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) { warn_then_suggest(cx, span); - } else if let ExprKind::Call(func, args) = method_arg_kind { + } else if let ExprKind::Call(func, [arg]) = method_arg_kind { // If our first argument is a function call itself, it could be an `unwrap`-like function. // E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc. - parse_call(cx, span, func, args); + parse_call(cx, span, func, arg); } } /// Tries to parse an expression as a function call, emitting the warning if necessary. -fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) { - if args.len() != 1 { - return; - } - - let arg_kind = &args[0].kind; +fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, arg: &Expr<'_>) { if let ExprKind::Path(qpath) = &func.kind { // String::from(...) or String::try_from(...) if let QPath::TypeRelative(ty, path_seg) = qpath @@ -115,13 +110,13 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_ && let QPath::Resolved(_, path) = qpath && let [path_seg] = path.segments && path_seg.ident.name == sym::String - && is_expr_kind_empty_str(arg_kind) + && is_expr_kind_empty_str(&arg.kind) { warn_then_suggest(cx, span); } else if let QPath::Resolved(_, path) = qpath { // From::from(...) or TryFrom::try_from(...) if let [path_seg1, path_seg2] = path.segments - && is_expr_kind_empty_str(arg_kind) + && is_expr_kind_empty_str(&arg.kind) && ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) || (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)) { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index b646e87a439..9ca75fb2615 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -210,7 +210,7 @@ fn find_method_sugg_for_if_let<'tcx>( // check that `while_let_on_iterator` lint does not trigger if keyword == "while" - && let ExprKind::MethodCall(method_path, ..) = let_expr.kind + && let ExprKind::MethodCall(method_path, _, [], _) = let_expr.kind && method_path.ident.name == sym::next && is_trait_method(cx, let_expr, sym::Iterator) { diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs index b7ffa8b8a78..c7e1b70d19e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -21,10 +21,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine // #[allow(unreachable_code)] // val, // }; - if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind + if let ExprKind::Call(match_fun, [try_arg]) = scrutinee.kind && let ExprKind::Path(ref match_fun_path) = match_fun.kind && matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)) - && let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind + && let ExprKind::Call(err_fun, [err_arg]) = try_arg.kind && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 79a473e0e6f..c9604c7b2e2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -58,7 +58,7 @@ pub(super) fn check( return; }, // ? is a Call, makes sure not to rec *x?, but rather (*x)? - ExprKind::Call(hir_callee, _) => matches!( + ExprKind::Call(hir_callee, [_]) => matches!( hir_callee.kind, ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, ..)) ), diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 2922086522c..c288dbdabe9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -143,7 +143,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("use of `{name}` followed by a function call"), + format!("function call inside of `{name}`"), "try", format!("unwrap_or_else({closure_args} panic!({sugg}))"), applicability, @@ -161,7 +161,7 @@ pub(super) fn check<'tcx>( cx, EXPECT_FUN_CALL, span_replace_word, - format!("use of `{name}` followed by a function call"), + format!("function call inside of `{name}`"), "try", format!("unwrap_or_else({closure_args} {{ panic!(\"{{}}\", {arg_root_snippet}) }})"), applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index f6612c984a7..30387ba62a7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -106,9 +106,9 @@ fn is_method( fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { if let Some(expr) = get_parent_expr(cx, expr) - && is_trait_method(cx, expr, sym::Iterator) - && let ExprKind::MethodCall(path, _, _, _) = expr.kind + && let ExprKind::MethodCall(path, _, [_], _) = expr.kind && path.ident.name == sym::map + && is_trait_method(cx, expr, sym::Iterator) { return true; } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs index 96af9db1af7..22f4748de70 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs @@ -6,6 +6,7 @@ use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; +use rustc_span::edition::Edition::Edition2021; use rustc_span::{Span, Symbol, sym}; use super::MANUAL_C_STR_LITERALS; @@ -25,6 +26,7 @@ pub(super) fn check_as_ptr<'tcx>( ) { if let ExprKind::Lit(lit) = receiver.kind && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node + && cx.tcx.sess.edition() >= Edition2021 && let casts_removed = peel_ptr_cast_ancestors(cx, expr) && !get_parent_expr(cx, casts_removed).is_some_and( |parent| matches!(parent.kind, ExprKind::Call(func, _) if is_c_str_function(cx, func).is_some()), @@ -66,6 +68,7 @@ fn is_c_str_function(cx: &LateContext<'_>, func: &Expr<'_>) -> Option<Symbol> { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) { if let Some(fn_name) = is_c_str_function(cx, func) && let [arg] = args + && cx.tcx.sess.edition() >= Edition2021 && msrv.meets(msrvs::C_STR_LITERALS) { match fn_name.as_str() { @@ -84,7 +87,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args /// Checks `CStr::from_ptr(b"foo\0".as_ptr().cast())` fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) { - if let ExprKind::MethodCall(method, lit, ..) = peel_ptr_cast(arg).kind + if let ExprKind::MethodCall(method, lit, [], _) = peel_ptr_cast(arg).kind && method.ident.name == sym::as_ptr && !lit.span.from_expansion() && let ExprKind::Lit(lit) = lit.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 9e3b313156e..13918ed11b8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -68,8 +68,7 @@ enum MinMax { fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> { // `T::max_value()` `T::min_value()` inherent methods - if let hir::ExprKind::Call(func, args) = &expr.kind - && args.is_empty() + if let hir::ExprKind::Call(func, []) = &expr.kind && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind { match segment.ident.as_str() { diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index ac378ff3702..515d4a11ed5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -86,9 +86,8 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ } } }, - hir::ExprKind::Call(call, args) => { + hir::ExprKind::Call(call, [arg]) => { if let hir::ExprKind::Path(qpath) = call.kind - && let [arg] = args && ident_eq(name, arg) { handle_path(cx, call, &qpath, e, recv); diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 7696dd16b25..2a391870d70 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4046,7 +4046,7 @@ declare_clippy_lint! { /// Checks the usage of `.get().is_some()` or `.get().is_none()` on std map types. /// /// ### Why is this bad? - /// It can be done in one call with `.contains()`/`.contains_keys()`. + /// It can be done in one call with `.contains()`/`.contains_key()`. /// /// ### Example /// ```no_run @@ -5182,6 +5182,7 @@ impl ShouldImplTraitCase { } #[rustfmt::skip] +#[expect(clippy::large_const_arrays, reason = "`Span` is not sync, so this can't be static")] const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true), diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index c58e27e37ad..96a31812ca4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -321,7 +321,10 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { // Check function calls on our collection if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind { - if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) { + if args.is_empty() + && method_name.ident.name == sym!(collect) + && is_trait_method(self.cx, expr, sym::Iterator) + { self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv)); self.visit_expr(recv); return; diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index b971f60d416..b685a466b72 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -183,7 +183,7 @@ pub(super) fn check<'tcx>( cx, OR_FUN_CALL, span_replace_word, - format!("use of `{name}` followed by a function call"), + format!("function call inside of `{name}`"), "try", format!("{name}_{suffix}({sugg})"), app, @@ -259,7 +259,7 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) if body.params.is_empty() && let hir::Expr { kind, .. } = &body.value - && let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, _, _) = kind + && let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, [], _) = kind && ident.name == sym::to_string && let hir::Expr { kind, .. } = self_arg && let hir::ExprKind::Lit(lit) = kind diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs index 0c8b6284284..65e545ed03a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs +++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs @@ -43,7 +43,8 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr< for_each_local_use_after_expr(cx, local_id, call.hir_id, |expr| { if let Some(parent) = get_parent_expr(cx, expr) { let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind { - if segment.ident.name == sym!(parse) + if args.is_empty() + && segment.ident.name == sym!(parse) && let parse_result_ty = cx.typeck_results().expr_ty(parent) && is_type_diagnostic_item(cx, parse_result_ty, sym::Result) && let ty::Adt(_, substs) = parse_result_ty.kind() diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs index 774aaec1afd..40b6becd453 100644 --- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::{Location, START_BLOCK}; use rustc_span::sym; fn is_unwrap_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ExprKind::MethodCall(path, receiver, ..) = expr.kind + if let ExprKind::MethodCall(path, receiver, [], _) = expr.kind && path.ident.name == sym::unwrap { is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver).peel_refs(), sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs index 7ef07fe899c..d318462e584 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs @@ -34,14 +34,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' } fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - if let ExprKind::Call(f, args) = expr.kind + if let ExprKind::Call(f, [arg]) = expr.kind && let ExprKind::Path(ref path) = f.kind && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id() && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id) { // check if argument of `SeekFrom::Current` is `0` - if args.len() == 1 - && let ExprKind::Lit(lit) = args[0].kind + if let ExprKind::Lit(lit) = arg.kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node { return true; diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 9c966f010f1..7b1dd9e58c5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -26,12 +26,11 @@ pub(super) fn check<'tcx>( if let Some(seek_trait_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) && implements_trait(cx, ty, seek_trait_id, &[]) - && let ExprKind::Call(func, args1) = arg.kind + && let ExprKind::Call(func, [arg]) = arg.kind && let ExprKind::Path(ref path) = func.kind && let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id) - && args1.len() == 1 - && let ExprKind::Lit(lit) = args1[0].kind + && let ExprKind::Lit(lit) = arg.kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node { let method_call_span = expr.span.with_lo(name_span.lo()); diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs index e2f76ac114c..4a1d25deade 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_insert_string.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: } if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind - && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind && path_segment.ident.name == rustc_span::sym::to_string && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) { diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs index 4ae8634305d..bc271d59392 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_push_string.rs @@ -26,7 +26,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: } if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind - && let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind + && let ExprKind::MethodCall(path_segment, method_arg, [], _) = &arg.kind && path_segment.ident.name == rustc_span::sym::to_string && (is_ref_char(cx, method_arg) || is_char(cx, method_arg)) { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 69032776b2b..a2a7de905ca 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -333,7 +333,7 @@ fn parse_iter_usage<'tcx>( kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)), .. }, - _, + [_], ) => { let parent_span = e.span.parent_callsite().unwrap(); if parent_span.ctxt() == ctxt { diff --git a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs index 1ee655d61e1..6371fe64428 100644 --- a/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs @@ -9,8 +9,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let hir::ExprKind::Call(callee, args) = recv.kind - && args.is_empty() + if let hir::ExprKind::Call(callee, []) = recv.kind && is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit) && !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)) { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs index 7ae1bb54e60..d322909bef3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -50,7 +50,7 @@ pub(super) fn check( ), "replace this with", suggestion, - Applicability::MaybeIncorrect, + Applicability::MachineApplicable, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index eafe7486bb0..c309e778116 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -86,12 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, // changing the type, then we can move forward. && rcv_ty.peel_refs() == res_ty.peel_refs() && let Some(parent) = get_parent_expr(cx, expr) - && let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind + // Check that it only has one argument. + && let hir::ExprKind::MethodCall(segment, _, [arg], _) = parent.kind && segment.ident.span != expr.span // We check that the called method name is `map`. && segment.ident.name == sym::map - // And that it only has one argument. - && let [arg] = args && is_calling_clone(cx, arg) // And that we are not recommending recv.clone() over Arc::clone() or similar && !should_call_clone_as_function(cx, rcv_ty) diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index c56a4014b34..d78299fe08b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -139,7 +139,7 @@ fn assert_len_expr<'hir>( && let ExprKind::Binary(bin_op, left, right) = &condition.kind && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right) - && let ExprKind::MethodCall(method, recv, ..) = &slice_len.kind + && let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() && method.ident.name == sym::len diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 007bcebdff6..ce508d85d63 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) => {} + | hir::ItemKind::Union(..) => {}, hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::GlobalAsm(..) diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index 8118c14bd4a..745f81d1c51 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::trait_ref_of_method; use clippy_utils::ty::InteriorMut; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -132,8 +133,14 @@ impl<'tcx> MutableKeyType<'tcx> { ) { let subst_ty = args.type_at(0); - if self.interior_mut.is_interior_mut_ty(cx, subst_ty) { - span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); + if let Some(chain) = self.interior_mut.interior_mut_ty_chain(cx, subst_ty) { + span_lint_and_then(cx, MUTABLE_KEY_TYPE, span, "mutable key type", |diag| { + for ty in chain.iter().rev() { + diag.note(with_forced_trimmed_paths!(format!( + "... because it contains `{ty}`, which has interior mutability" + ))); + } + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 3c0f06f66d1..c382fb8fce1 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { if let ExprKind::Path(ref path) = fn_expr.kind { check_arguments( cx, - arguments.iter().collect(), + &mut arguments.iter(), cx.typeck_results().expr_ty(fn_expr), &rustc_hir_pretty::qpath_to_string(&cx.tcx, path), "function", @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let method_type = cx.tcx.type_of(def_id).instantiate(cx.tcx, args); check_arguments( cx, - iter::once(receiver).chain(arguments.iter()).collect(), + &mut iter::once(receiver).chain(arguments.iter()), method_type, path.ident.as_str(), "method", @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { fn check_arguments<'tcx>( cx: &LateContext<'tcx>, - arguments: Vec<&Expr<'_>>, + arguments: &mut dyn Iterator<Item = &'tcx Expr<'tcx>>, type_definition: Ty<'tcx>, name: &str, fn_kind: &str, diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index a56024f08d5..68c9af07465 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -64,7 +64,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item .iter() .enumerate() .filter_map(move |(bound_pos, bound)| match bound { - &GenericBound::Trait(ref trait_bound) => Some(Bound { + GenericBound::Trait(trait_bound) => Some(Bound { param, ident, trait_bound, diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index de6a1a36f3e..94855c46567 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -281,7 +281,7 @@ fn self_cmp_call<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_self, _other]) => path_res(cx, path) + ExprKind::Call(path, [_, _]) => path_res(cx, path) .opt_def_id() .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), ExprKind::MethodCall(_, _, [_other], ..) => { diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs index 90a9f2e994b..aefb665b52e 100644 --- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs +++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs @@ -71,7 +71,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit if let ExprKind::Call(func, [arg]) = expr.kind && let ExprKind::Path(qpath) = &func.kind && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() - && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + && let ExprKind::MethodCall(rcv_path, receiver, [], _) = &arg.kind && rcv_path.ident.name.as_str() == "get" { let fn_name = cx.tcx.item_name(def_id); diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index df6e6745596..8e394944c21 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -106,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { return is_signum(cx, child_expr); } - if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind + if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind && sym!(signum) == method_name.ident.name // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index 9f84686a0b1..d2529d4d9f8 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// // or /// let path_buf = PathBuf::new().join("foo"); /// ``` - #[clippy::version = "1.81.0"] + #[clippy::version = "1.82.0"] pub PATHBUF_INIT_THEN_PUSH, restriction, "`push` immediately after `PathBuf` creation" diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index bb8a9b6fca8..f5fcf521b96 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core}; use hir::LifetimeName; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::hir_id::{HirId, HirIdMap}; @@ -294,14 +294,16 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }; for &arg_idx in arg_indices { - if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) { + if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) + && let Some(std_or_core) = std_or_core(cx) + { span_lint_and_sugg( cx, INVALID_NULL_PTR_USAGE, arg.span, "pointer must be non-null", "change this to", - "core::ptr::NonNull::dangling().as_ptr()".to_string(), + format!("{std_or_core}::ptr::NonNull::dangling().as_ptr()"), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs index 87a52cb2186..56d07aeae17 100644 --- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs +++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs @@ -91,7 +91,7 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1, ..], _) = &expr.kind { + if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind { if is_expr_ty_raw_ptr(cx, arg_0) { if path_segment.ident.name == sym::offset { return Some((arg_0, arg_1, Method::Offset)); diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index aa9a9001afb..9344cb41993 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -206,12 +206,11 @@ fn expr_return_none_or_err( sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), _ => false, }, - ExprKind::Call(call_expr, args_expr) => { + ExprKind::Call(call_expr, [arg]) => { if smbl == sym::Result && let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind && let Some(segment) = path.segments.first() && let Some(err_sym) = err_sym - && let Some(arg) = args_expr.first() && let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind && let Some(PathSegment { ident, .. }) = arg_path.segments.first() { @@ -241,7 +240,7 @@ fn expr_return_none_or_err( fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) && !is_else_clause(cx.tcx, expr) - && let ExprKind::MethodCall(segment, caller, ..) = &cond.kind + && let ExprKind::MethodCall(segment, caller, [], _) = &cond.kind && let caller_ty = cx.typeck_results().expr_ty(caller) && let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then) && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block)) @@ -332,7 +331,7 @@ impl QuestionMark { fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool { if let Some(expr) = bl.expr - && let ExprKind::Call(callee, _) = expr.kind + && let ExprKind::Call(callee, [_]) = expr.kind { is_path_lang_item(cx, callee, LangItem::TryTraitFromOutput) } else { diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index 3c19ee3522d..23d0e768c2f 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, snippet_opt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -71,6 +71,23 @@ impl RawStrings { impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::FormatArgs(format_args) = &expr.kind + && !in_external_macro(cx.sess(), format_args.span) + && format_args.span.check_source_text(cx, |src| src.starts_with('r')) + && let Some(str) = snippet_opt(cx.sess(), format_args.span) + && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count() + && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1) + { + self.check_raw_string( + cx, + str, + format_args.span, + "r", + u8::try_from(count_hash).unwrap(), + "string", + ); + } + if let ExprKind::Lit(lit) = expr.kind && let (prefix, max) = match lit.kind { LitKind::StrRaw(max) => ("r", max), @@ -81,94 +98,105 @@ impl EarlyLintPass for RawStrings { && !in_external_macro(cx.sess(), expr.span) && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) { - let str = lit.symbol.as_str(); - let descr = lit.kind.descr(); - - if !str.contains(['\\', '"']) { - span_lint_and_then( - cx, - NEEDLESS_RAW_STRINGS, - expr.span, - "unnecessary raw string literal", - |diag| { - let (start, end) = hash_spans(expr.span, prefix.len(), 0, max); - - // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text - let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); - let start = start.with_lo(r_pos); - - let mut remove = vec![(start, String::new())]; - // avoid debug ICE from empty suggestions - if !end.is_empty() { - remove.push((end, String::new())); - } + self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr()); + } + } +} - diag.multipart_suggestion_verbose( - format!("use a plain {descr} literal instead"), - remove, - Applicability::MachineApplicable, - ); - }, - ); - if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { - return; - } +impl RawStrings { + fn check_raw_string( + &mut self, + cx: &EarlyContext<'_>, + str: &str, + lit_span: Span, + prefix: &str, + max: u8, + descr: &str, + ) { + if !str.contains(['\\', '"']) { + span_lint_and_then( + cx, + NEEDLESS_RAW_STRINGS, + lit_span, + "unnecessary raw string literal", + |diag| { + let (start, end) = hash_spans(lit_span, prefix.len(), 0, max); + + // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text + let r_pos = lit_span.lo() + BytePos::from_usize(prefix.len() - 1); + let start = start.with_lo(r_pos); + + let mut remove = vec![(start, String::new())]; + // avoid debug ICE from empty suggestions + if !end.is_empty() { + remove.push((end, String::new())); + } + + diag.multipart_suggestion_verbose( + format!("use a plain {descr} literal instead"), + remove, + Applicability::MachineApplicable, + ); + }, + ); + if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { + return; } + } - let mut req = { - let mut following_quote = false; - let mut req = 0; - // `once` so a raw string ending in hashes is still checked - let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| { - match b { - b'"' if !following_quote => (following_quote, req) = (true, 1), - b'#' => req += u8::from(following_quote), - _ => { - if following_quote { - following_quote = false; - - if req == max { - return ControlFlow::Break(req); - } - - return ControlFlow::Continue(acc.max(req)); + let mut req = { + let mut following_quote = false; + let mut req = 0; + // `once` so a raw string ending in hashes is still checked + let num = str.as_bytes().iter().chain(once(&0)).try_fold(0u8, |acc, &b| { + match b { + b'"' if !following_quote => (following_quote, req) = (true, 1), + b'#' => req += u8::from(following_quote), + _ => { + if following_quote { + following_quote = false; + + if req == max { + return ControlFlow::Break(req); } - }, - } - ControlFlow::Continue(acc) - }); - - match num { - ControlFlow::Continue(num) | ControlFlow::Break(num) => num, - } - }; - if self.allow_one_hash_in_raw_strings { - req = req.max(1); - } - if req < max { - span_lint_and_then( - cx, - NEEDLESS_RAW_STRING_HASHES, - expr.span, - "unnecessary hashes around raw string literal", - |diag| { - let (start, end) = hash_spans(expr.span, prefix.len(), req, max); - - let message = match max - req { - _ if req == 0 => format!("remove all the hashes around the {descr} literal"), - 1 => format!("remove one hash from both sides of the {descr} literal"), - n => format!("remove {n} hashes from both sides of the {descr} literal"), - }; - - diag.multipart_suggestion( - message, - vec![(start, String::new()), (end, String::new())], - Applicability::MachineApplicable, - ); + return ControlFlow::Continue(acc.max(req)); + } }, - ); + } + + ControlFlow::Continue(acc) + }); + + match num { + ControlFlow::Continue(num) | ControlFlow::Break(num) => num, } + }; + if self.allow_one_hash_in_raw_strings { + req = req.max(1); + } + if req < max { + span_lint_and_then( + cx, + NEEDLESS_RAW_STRING_HASHES, + lit_span, + "unnecessary hashes around raw string literal", + |diag| { + let (start, end) = hash_spans(lit_span, prefix.len(), req, max); + + let message = match max - req { + _ if req == 0 => format!("remove all the hashes around the {descr} literal"), + 1 => format!("remove one hash from both sides of the {descr} literal"), + n => format!("remove {n} hashes from both sides of the {descr} literal"), + }; + + diag.multipart_suggestion( + message, + vec![(start, String::new()), (end, String::new())], + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs index e877f5d6ed4..6bb7650a7e1 100644 --- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs @@ -65,11 +65,11 @@ impl LateLintPass<'_> for RcCloneInVecInit { fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { format!( - r#"{{ + r"{{ {indent} let mut v = Vec::with_capacity({len}); {indent} (0..{len}).for_each(|_| v.push({elem})); {indent} v -{indent}}}"# +{indent}}}" ) } diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 12cbdb854ef..6a5bf1b8045 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; -use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Span}; @@ -55,6 +55,44 @@ declare_clippy_lint! { "trivial regular expressions" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for [regex](https://crates.io/crates/regex) compilation inside a loop with a literal. + /// + /// ### Why is this bad? + /// + /// Compiling a regex is a much more expensive operation than using one, and a compiled regex can be used multiple times. + /// This is documented as an antipattern [on the regex documentation](https://docs.rs/regex/latest/regex/#avoid-re-compiling-regexes-especially-in-a-loop) + /// + /// ### Example + /// ```no_run + /// # let haystacks = [""]; + /// # const MY_REGEX: &str = "a.b"; + /// for haystack in haystacks { + /// let regex = regex::Regex::new(MY_REGEX).unwrap(); + /// if regex.is_match(haystack) { + /// // Perform operation + /// } + /// } + /// ``` + /// can be replaced with + /// ```no_run + /// # let haystacks = [""]; + /// # const MY_REGEX: &str = "a.b"; + /// let regex = regex::Regex::new(MY_REGEX).unwrap(); + /// for haystack in haystacks { + /// if regex.is_match(haystack) { + /// // Perform operation + /// } + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub REGEX_CREATION_IN_LOOPS, + perf, + "regular expression compilation performed in a loop" +} + #[derive(Copy, Clone)] enum RegexKind { Unicode, @@ -66,9 +104,10 @@ enum RegexKind { #[derive(Default)] pub struct Regex { definitions: DefIdMap<RegexKind>, + loop_stack: Vec<(OwnerId, Span)>, } -impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); +impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]); impl<'tcx> LateLintPass<'tcx> for Regex { fn check_crate(&mut self, cx: &LateContext<'tcx>) { @@ -99,12 +138,34 @@ impl<'tcx> LateLintPass<'tcx> for Regex { && let Some(def_id) = path_def_id(cx, fun) && let Some(regex_kind) = self.definitions.get(&def_id) { + if let Some(&(loop_item_id, loop_span)) = self.loop_stack.last() + && loop_item_id == fun.hir_id.owner + && (matches!(arg.kind, ExprKind::Lit(_)) || const_str(cx, arg).is_some()) + { + span_lint_and_help( + cx, + REGEX_CREATION_IN_LOOPS, + fun.span, + "compiling a regex in a loop", + Some(loop_span), + "move the regex construction outside this loop", + ); + } + match regex_kind { RegexKind::Unicode => check_regex(cx, arg, true), RegexKind::UnicodeSet => check_set(cx, arg, true), RegexKind::Bytes => check_regex(cx, arg, false), RegexKind::BytesSet => check_set(cx, arg, false), } + } else if let ExprKind::Loop(block, _, _, span) = expr.kind { + self.loop_stack.push((block.hir_id.owner, span)); + } + } + + fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if matches!(expr.kind, ExprKind::Loop(..)) { + self.loop_stack.pop(); } } } diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 662745e4b5d..110dea8fb8e 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -357,7 +357,7 @@ fn check_final_expr<'tcx>( let replacement = if let Some(inner_expr) = inner { // if desugar of `do yeet`, don't lint - if let ExprKind::Call(path_expr, _) = inner_expr.kind + if let ExprKind::Call(path_expr, [_]) = inner_expr.kind && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind { return; diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 0eece922143..abd8363456d 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -421,11 +421,10 @@ fn dummy_stmt_expr<'any>(expr: &'any hir::Expr<'any>) -> hir::Stmt<'any> { } fn has_drop(expr: &hir::Expr<'_>, first_bind_ident: &Ident, lcx: &LateContext<'_>) -> bool { - if let hir::ExprKind::Call(fun, args) = expr.kind + if let hir::ExprKind::Call(fun, [first_arg]) = expr.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind && let Res::Def(DefKind::Fn, did) = fun_path.res && lcx.tcx.is_diagnostic_item(sym::mem_drop, did) - && let [first_arg, ..] = args { let has_ident = |local_expr: &hir::Expr<'_>| { if let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &local_expr.kind diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index 7750d8909d3..db1c75fc3de 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -34,7 +34,7 @@ declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { match expr.kind { - ExprKind::Call(count_func, _func_args) => { + ExprKind::Call(count_func, _) => { if !inverted && let ExprKind::Path(ref count_func_qpath) = count_func.kind && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index fc799cad67e..ec6e45256d1 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -152,7 +152,7 @@ impl SlowVectorInit { && is_path_diagnostic_item(cx, func, sym::vec_with_capacity) { Some(InitializedSize::Initialized(len_expr)) - } else if matches!(expr.kind, ExprKind::Call(func, _) if is_path_diagnostic_item(cx, func, sym::vec_new)) { + } else if matches!(expr.kind, ExprKind::Call(func, []) if is_path_diagnostic_item(cx, func, sym::vec_new)) { Some(InitializedSize::Uninitialized) } else { None @@ -268,7 +268,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool { - if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind + if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind && take_path.ident.name == sym!(take) // Check that take is applied to `repeat(0)` && self.is_repeat_zero(recv) diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 1fb82b66ab8..bf49bb60162 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -253,18 +253,17 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use rustc_ast::LitKind; - if let ExprKind::Call(fun, args) = e.kind + if let ExprKind::Call(fun, [bytes_arg]) = e.kind // Find std::str::converts::from_utf8 && is_path_diagnostic_item(cx, fun, sym::str_from_utf8) // Find string::as_bytes - && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind + && let ExprKind::AddrOf(BorrowKind::Ref, _, args) = bytes_arg.kind && let ExprKind::Index(left, right, _) = args.kind && let (method_names, expressions, _) = method_calls(left, 1) - && method_names.len() == 1 + && method_names == [sym!(as_bytes)] && expressions.len() == 1 && expressions[0].1.is_empty() - && method_names[0] == sym!(as_bytes) // Check for slicer && let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), _, _) = right.kind @@ -393,7 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { return; } - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) && let ty::Ref(_, ty, ..) = ty.kind() @@ -449,7 +448,7 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { return; } - if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind + if let ExprKind::MethodCall(path, self_arg, [], _) = &expr.kind && path.ident.name == sym::to_string && let ty = cx.typeck_results().expr_ty(self_arg) && is_type_lang_item(cx, ty, LangItem::String) diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index 4f96a566b63..569812d8106 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -51,9 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { None } }, - hir::ExprKind::Call(to_digits_call, to_digit_args) => { - if let [char_arg, radix_arg] = *to_digit_args - && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind + hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => { + if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() && match_def_path(cx, to_digits_def_id, &[ diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 38befdee574..7f528b9d17b 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -10,7 +10,7 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericArg, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, + GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath, TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; @@ -152,7 +152,10 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { .filter_map(get_trait_info_from_bound) .for_each(|(trait_item_res, trait_item_segments, span)| { if let Some(self_segments) = self_bounds_map.get(&trait_item_res) { - if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) { + if SpanlessEq::new(cx) + .paths_by_resolution() + .eq_path_segments(self_segments, trait_item_segments) + { span_lint_and_help( cx, TRAIT_DUPLICATION_IN_BOUNDS, @@ -302,7 +305,7 @@ impl TraitBounds { } } -fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_>) { +fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Generics<'tcx>) { if generics.span.from_expansion() { return; } @@ -314,6 +317,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_ // | // collects each of these where clauses into a set keyed by generic name and comparable trait // eg. (T, Clone) + #[expect(clippy::mutable_key_type)] let where_predicates = generics .predicates .iter() @@ -367,11 +371,27 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_ } } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -struct ComparableTraitRef(Res, Vec<Res>); -impl Default for ComparableTraitRef { - fn default() -> Self { - Self(Res::Err, Vec::new()) +struct ComparableTraitRef<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + trait_ref: &'tcx TraitRef<'tcx>, + modifier: TraitBoundModifier, +} + +impl PartialEq for ComparableTraitRef<'_, '_> { + fn eq(&self, other: &Self) -> bool { + self.modifier == other.modifier + && SpanlessEq::new(self.cx) + .paths_by_resolution() + .eq_path(self.trait_ref.path, other.trait_ref.path) + } +} +impl Eq for ComparableTraitRef<'_, '_> {} +impl Hash for ComparableTraitRef<'_, '_> { + fn hash<H: Hasher>(&self, state: &mut H) { + let mut s = SpanlessHash::new(self.cx).paths_by_resolution(); + s.hash_path(self.trait_ref.path); + state.write_u64(s.finish()); + self.modifier.hash(state); } } @@ -392,69 +412,41 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &' } } -fn get_ty_res(ty: Ty<'_>) -> Option<Res> { - match ty.kind { - TyKind::Path(QPath::Resolved(_, path)) => Some(path.res), - TyKind::Path(QPath::TypeRelative(ty, _)) => get_ty_res(*ty), - _ => None, - } -} - -// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds -fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { - ComparableTraitRef( - trait_ref.path.res, - trait_ref - .path - .segments - .iter() - .filter_map(|segment| { - // get trait bound type arguments - Some(segment.args?.args.iter().filter_map(|arg| { - if let GenericArg::Type(ty) = arg { - return get_ty_res(**ty); - } - None - })) - }) - .flatten() - .collect(), - ) -} - -fn rollup_traits( - cx: &LateContext<'_>, - bounds: &[GenericBound<'_>], +fn rollup_traits<'cx, 'tcx>( + cx: &'cx LateContext<'tcx>, + bounds: &'tcx [GenericBound<'tcx>], msg: &'static str, -) -> Vec<(ComparableTraitRef, Span)> { +) -> Vec<(ComparableTraitRef<'cx, 'tcx>, Span)> { + // Source order is needed for joining spans let mut map = FxIndexMap::default(); let mut repeated_res = false; - let only_comparable_trait_refs = |bound: &GenericBound<'_>| { + let only_comparable_trait_refs = |bound: &'tcx GenericBound<'tcx>| { if let GenericBound::Trait(t) = bound { - Some((into_comparable_trait_ref(&t.trait_ref), t.span)) + Some(( + ComparableTraitRef { + cx, + trait_ref: &t.trait_ref, + modifier: t.modifiers, + }, + t.span, + )) } else { None } }; - let mut i = 0usize; for bound in bounds.iter().filter_map(only_comparable_trait_refs) { let (comparable_bound, span_direct) = bound; match map.entry(comparable_bound) { IndexEntry::Occupied(_) => repeated_res = true, IndexEntry::Vacant(e) => { - e.insert((span_direct, i)); - i += 1; + e.insert(span_direct); }, } } - // Put bounds in source order - let mut comparable_bounds = vec![Default::default(); map.len()]; - for (k, (v, i)) in map { - comparable_bounds[i] = (k, v); - } + let comparable_bounds: Vec<_> = map.into_iter().collect(); if repeated_res && let [first_trait, .., last_trait] = bounds { let all_trait_span = first_trait.span().to(last_trait.span()); diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 41b2ca5d268..e7d26fa238e 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -25,14 +25,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { return; } - let args: Vec<_> = match expr.kind { - ExprKind::Call(_, args) => args.iter().collect(), - ExprKind::MethodCall(_, receiver, args, _) => std::iter::once(receiver).chain(args.iter()).collect(), + let (reciever, args) = match expr.kind { + ExprKind::Call(_, args) => (None, args), + ExprKind::MethodCall(_, receiver, args, _) => (Some(receiver), args), _ => return, }; - let args_to_recover = args + let args_to_recover = reciever .into_iter() + .chain(args) .filter(|arg| { if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) { !matches!( diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs new file mode 100644 index 00000000000..80ce6711126 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnecessary_literal_bound.rs @@ -0,0 +1,158 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_res; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{FnKind, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, Lit, MutTy, Mutability, PrimTy, Ty, TyKind, intravisit}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects functions that are written to return `&str` that could return `&'static str` but instead return a `&'a str`. + /// + /// ### Why is this bad? + /// + /// This leaves the caller unable to use the `&str` as `&'static str`, causing unneccessary allocations or confusion. + /// This is also most likely what you meant to write. + /// + /// ### Example + /// ```no_run + /// # struct MyType; + /// impl MyType { + /// fn returns_literal(&self) -> &str { + /// "Literal" + /// } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # struct MyType; + /// impl MyType { + /// fn returns_literal(&self) -> &'static str { + /// "Literal" + /// } + /// } + /// ``` + /// Or, in case you may return a non-literal `str` in future: + /// ```no_run + /// # struct MyType; + /// impl MyType { + /// fn returns_literal<'a>(&'a self) -> &'a str { + /// "Literal" + /// } + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub UNNECESSARY_LITERAL_BOUND, + pedantic, + "detects &str that could be &'static str in function return types" +} + +declare_lint_pass!(UnnecessaryLiteralBound => [UNNECESSARY_LITERAL_BOUND]); + +fn extract_anonymous_ref<'tcx>(hir_ty: &Ty<'tcx>) -> Option<&'tcx Ty<'tcx>> { + let TyKind::Ref(lifetime, MutTy { ty, mutbl }) = hir_ty.kind else { + return None; + }; + + if !lifetime.is_anonymous() || !matches!(mutbl, Mutability::Not) { + return None; + } + + Some(ty) +} + +fn is_str_literal(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Lit(Lit { + node: LitKind::Str(..), + .. + }), + ) +} + +struct FindNonLiteralReturn; + +impl<'hir> Visitor<'hir> for FindNonLiteralReturn { + type Result = std::ops::ControlFlow<()>; + type NestedFilter = intravisit::nested_filter::None; + + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) -> Self::Result { + if let ExprKind::Ret(Some(ret_val_expr)) = expr.kind + && !is_str_literal(ret_val_expr) + { + Self::Result::Break(()) + } else { + intravisit::walk_expr(self, expr) + } + } +} + +fn check_implicit_returns_static_str(body: &Body<'_>) -> bool { + // TODO: Improve this to the same complexity as the Visitor to catch more implicit return cases. + if let ExprKind::Block(block, _) = body.value.kind + && let Some(implicit_ret) = block.expr + { + return is_str_literal(implicit_ret); + } + + false +} + +fn check_explicit_returns_static_str(expr: &Expr<'_>) -> bool { + let mut visitor = FindNonLiteralReturn; + visitor.visit_expr(expr).is_continue() +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryLiteralBound { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + _: LocalDefId, + ) { + if span.from_expansion() { + return; + } + + // Checking closures would be a little silly + if matches!(kind, FnKind::Closure) { + return; + } + + // Check for `-> &str` + let FnRetTy::Return(ret_hir_ty) = decl.output else { + return; + }; + + let Some(inner_hir_ty) = extract_anonymous_ref(ret_hir_ty) else { + return; + }; + + if path_res(cx, inner_hir_ty) != Res::PrimTy(PrimTy::Str) { + return; + } + + // Check for all return statements returning literals + if check_explicit_returns_static_str(body.value) && check_implicit_returns_static_str(body) { + span_lint_and_sugg( + cx, + UNNECESSARY_LITERAL_BOUND, + ret_hir_ty.span, + "returning a `str` unnecessarily tied to the lifetime of arguments", + "try", + "&'static str".into(), // how ironic, a lint about `&'static str` requiring a `String` alloc... + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index 8f1eb5019f0..d3700d05b01 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -38,13 +38,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { if expr.span.from_expansion() { return; } - if let hir::ExprKind::MethodCall(path, recv, args, ..) = expr.kind + if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind && let Some(sym::Option | sym::Result) = get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(recv)) { - let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, constructor_args) = - recv.kind + let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind - && let Some(arg) = constructor_args.first() { if constructor.span.from_expansion() || arg.span.from_expansion() { return; @@ -70,9 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { _ => return, } - if let Some(map_arg) = args.first() - && let hir::ExprKind::Path(fun) = map_arg.kind - { + if let hir::ExprKind::Path(fun) = map_arg.kind { if map_arg.span.from_expansion() { return; } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs index f01cb457af8..7d996775a58 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -52,8 +52,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { Applicability::MachineApplicable, ); } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id) - && let [.., last_arg] = args - && let ExprKind::Lit(spanned) = &last_arg.kind + && let [arg] = args + && let ExprKind::Lit(spanned) = &arg.kind && let LitKind::Str(symbol, _) = spanned.node && symbol.is_empty() && let inner_expr_type = cx.typeck_results().expr_ty(inner_expr) 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 d2a21b11ef4..cf406b817da 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -222,7 +222,7 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { } fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - while let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind + while let ExprKind::Call(func, [ref arg_0]) = expr.kind && matches!( func.kind, ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..)) @@ -244,7 +244,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { /// waited on. Otherwise return None. fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { - if let ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { + if let ExprKind::Call(func, [ref arg_0]) = expr.kind { if matches!( func.kind, ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) diff --git a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs index 297288db0a5..0c0d10eac5b 100644 --- a/src/tools/clippy/clippy_lints/src/unused_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/unused_result_ok.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// # fn some_function() -> Result<(), ()> { Ok(()) } /// let _ = some_function(); /// ``` - #[clippy::version = "1.70.0"] + #[clippy::version = "1.82.0"] pub UNUSED_RESULT_OK, restriction, "Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead." diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 6fe660b6a47..096b3ff9a2e 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -153,13 +153,12 @@ fn collect_unwrap_info<'tcx>( } } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind { return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false); - } else if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind + } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind && let Some(local_id) = path_to_local(receiver) && let ty = cx.typeck_results().expr_ty(receiver) && let name = method_name.ident.as_str() && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) { - assert!(args.is_empty()); let unwrappable = match name { "is_some" | "is_ok" => true, "is_err" | "is_none" => false, @@ -208,7 +207,7 @@ struct MutationVisitor<'tcx> { /// expression: that will be where the actual method call is. fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool { if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id) - && let ExprKind::MethodCall(path, ..) = mutating_expr.kind + && let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind { path.ident.name.as_str() == "as_mut" } else { @@ -275,7 +274,7 @@ enum AsRefKind { /// Checks if the expression is a method call to `as_{ref,mut}` and returns the receiver of it. /// If it isn't, the expression itself is returned. fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option<AsRefKind>) { - if let ExprKind::MethodCall(path, recv, ..) = expr.kind { + if let ExprKind::MethodCall(path, recv, [], _) = expr.kind { if path.ident.name == sym::as_ref { (recv, Some(AsRefKind::AsRef)) } else if path.ident.name.as_str() == "as_mut" { @@ -303,7 +302,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { self.visit_branch(expr, cond, else_inner, true); } } else { - // find `unwrap[_err]()` calls: + // find `unwrap[_err]()` or `expect("...")` calls: if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && let (self_arg, as_ref_kind) = consume_option_as_ref(self_arg) && let Some(id) = path_to_local(self_arg) diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 29a7949b343..ec3a693d2ef 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -129,7 +129,7 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { - if let ExprKind::MethodCall(name, recv, _, _) = expr.kind + if let ExprKind::MethodCall(name, recv, [], _) = expr.kind && is_trait_method(cx, expr, sym::IntoIterator) && name.ident.name == sym::into_iter { @@ -173,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } }, - ExprKind::MethodCall(name, recv, ..) => { + ExprKind::MethodCall(name, recv, [], _) => { if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index d8f101a8614..a3f9abe4f96 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -5,6 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use std::borrow::{Borrow, Cow}; @@ -76,19 +77,19 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { return; } - if let ExprKind::Call(func, and_then_args) = expr.kind + if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]) - && and_then_args.len() == 5 - && let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind + && let ExprKind::Closure(&Closure { body, .. }) = &call_f.kind && let body = cx.tcx.hir().body(body) && let only_expr = peel_blocks_with_stmt(body.value) && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind && let ExprKind::Path(..) = recv.kind { - let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let and_then_snippets = + get_and_then_snippets(cx, call_cx.span, call_lint.span, call_sp.span, call_msg.span); let mut sle = SpanlessEq::new(cx).deny_side_effects(); match ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + "span_suggestion" if sle.eq_expr(call_sp, &span_call_args[0]) => { suggest_suggestion( cx, expr, @@ -96,11 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { &span_suggestion_snippets(cx, span_call_args), ); }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + "span_help" if sle.eq_expr(call_sp, &span_call_args[0]) => { let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => { + "span_note" if sle.eq_expr(call_sp, &span_call_args[0]) => { let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); }, @@ -125,11 +126,17 @@ struct AndThenSnippets<'a> { msg: Cow<'a, str>, } -fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { - let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); - let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); - let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); - let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); +fn get_and_then_snippets( + cx: &LateContext<'_>, + cx_span: Span, + lint_span: Span, + span_span: Span, + msg_span: Span, +) -> AndThenSnippets<'static> { + let cx_snippet = snippet(cx, cx_span, "cx"); + let lint_snippet = snippet(cx, lint_span, ".."); + let span_snippet = snippet(cx, span_span, "span"); + let msg_snippet = snippet(cx, msg_span, r#""...""#); AndThenSnippets { cx: cx_snippet, 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 dd456022212..51235de9f29 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 @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{is_lint_allowed, match_def_path, paths}; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::Visitor; @@ -87,8 +87,8 @@ declare_clippy_lint! { #[derive(Clone, Debug, Default)] pub struct LintWithoutLintPass { - declared_lints: FxHashMap<Symbol, Span>, - registered_lints: FxHashSet<Symbol>, + declared_lints: FxIndexMap<Symbol, Span>, + registered_lints: FxIndexSet<Symbol>, } impl_lint_pass!(LintWithoutLintPass => [ @@ -266,7 +266,7 @@ pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item< } struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet<Symbol>, + output: &'a mut FxIndexSet<Symbol>, cx: &'a LateContext<'tcx>, } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index ce4f41e854d..9bcff9d7bce 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -17,7 +17,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::{DesugaringKind, Span, sym}; -#[expect(clippy::module_name_repetitions)] pub struct UselessVec { too_large_for_stack: u64, msrv: Msrv, @@ -244,7 +243,7 @@ fn adjusts_to_slice(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { pub fn is_allowed_vec_method(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { const ALLOWED_METHOD_NAMES: &[&str] = &["len", "as_ptr", "is_empty"]; - if let ExprKind::MethodCall(path, ..) = e.kind { + if let ExprKind::MethodCall(path, _, [], _) = e.kind { ALLOWED_METHOD_NAMES.contains(&path.ident.name.as_str()) } else { is_trait_method(cx, e, sym::IntoIterator) diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index fe30b10c435..d8d5733da1c 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.83" +version = "0.1.84" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 510034876e0..a1cfb7be647 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -484,10 +484,9 @@ impl<'tcx> ConstEvalCtxt<'tcx> { }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), ExprKind::Binary(op, left, right) => self.binop(op, left, right), - ExprKind::Call(callee, args) => { + ExprKind::Call(callee, []) => { // We only handle a few const functions for now. - if args.is_empty() - && let ExprKind::Path(qpath) = &callee.kind + if let ExprKind::Path(qpath) = &callee.kind && let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id() { match self.tcx.get_diagnostic_name(did) { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index a19c1555d16..27c57808ece 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -5,7 +5,7 @@ use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::MatchSource::TryDesugar; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, @@ -17,11 +17,33 @@ use rustc_middle::ty::TypeckResults; use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym}; use std::hash::{Hash, Hasher}; use std::ops::Range; +use std::slice; /// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but /// other conditions would make them equal. type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a; +/// Determines how paths are hashed and compared for equality. +#[derive(Copy, Clone, Debug, Default)] +pub enum PathCheck { + /// Paths must match exactly and are hashed by their exact HIR tree. + /// + /// Thus, `std::iter::Iterator` and `Iterator` are not considered equal even though they refer + /// to the same item. + #[default] + Exact, + /// Paths are compared and hashed based on their resolution. + /// + /// They can appear different in the HIR tree but are still considered equal + /// and have equal hashes as long as they refer to the same item. + /// + /// Note that this is currently only partially implemented specifically for paths that are + /// resolved before type-checking, i.e. the final segment must have a non-error resolution. + /// If a path with an error resolution is encountered, it falls back to the default exact + /// matching behavior. + Resolution, +} + /// Type used to check whether two ast are the same. This is different from the /// operator `==` on ast types as this operator would compare true equality with /// ID and span. @@ -33,6 +55,7 @@ pub struct SpanlessEq<'a, 'tcx> { maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, allow_side_effects: bool, expr_fallback: Option<Box<SpanlessEqCallback<'a>>>, + path_check: PathCheck, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -42,6 +65,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), allow_side_effects: true, expr_fallback: None, + path_check: PathCheck::default(), } } @@ -54,6 +78,16 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } + /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more + /// details. + #[must_use] + pub fn paths_by_resolution(self) -> Self { + Self { + path_check: PathCheck::Resolution, + ..self + } + } + #[must_use] pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { Self { @@ -498,7 +532,7 @@ impl HirEqInterExpr<'_, '_, '_> { match (left.res, right.res) { (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r), (Res::Local(_), _) | (_, Res::Local(_)) => false, - _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)), + _ => self.eq_path_segments(left.segments, right.segments), } } @@ -511,17 +545,39 @@ impl HirEqInterExpr<'_, '_, '_> { } } - pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { - left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r)) + pub fn eq_path_segments<'tcx>( + &mut self, + mut left: &'tcx [PathSegment<'tcx>], + mut right: &'tcx [PathSegment<'tcx>], + ) -> bool { + if let PathCheck::Resolution = self.inner.path_check + && let Some(left_seg) = generic_path_segments(left) + && let Some(right_seg) = generic_path_segments(right) + { + // If we compare by resolution, then only check the last segments that could possibly have generic + // arguments + left = left_seg; + right = right_seg; + } + + over(left, right, |l, r| self.eq_path_segment(l, r)) } pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { - // The == of idents doesn't work with different contexts, - // we have to be explicit about hygiene - left.ident.name == right.ident.name - && both(left.args.as_ref(), right.args.as_ref(), |l, r| { - self.eq_path_parameters(l, r) - }) + if !self.eq_path_parameters(left.args(), right.args()) { + return false; + } + + if let PathCheck::Resolution = self.inner.path_check + && left.res != Res::Err + && right.res != Res::Err + { + left.res == right.res + } else { + // The == of idents doesn't work with different contexts, + // we have to be explicit about hygiene + left.ident.name == right.ident.name + } } pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { @@ -684,6 +740,21 @@ pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) } +/// Returns the segments of a path that might have generic parameters. +/// Usually just the last segment for free items, except for when the path resolves to an associated +/// item, in which case it is the last two +fn generic_path_segments<'tcx>(segments: &'tcx [PathSegment<'tcx>]) -> Option<&'tcx [PathSegment<'tcx>]> { + match segments.last()?.res { + Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => { + // <Ty as module::Trait<T>>::assoc::<U> + // ^^^^^^^^^^^^^^^^ ^^^^^^^^^^ segments: [module, Trait<T>, assoc<U>] + Some(&segments[segments.len().checked_sub(2)?..]) + }, + Res::Err => None, + _ => Some(slice::from_ref(segments.last()?)), + } +} + /// Type used to hash an ast element. This is different from the `Hash` trait /// on ast types as this /// trait would consider IDs and spans. @@ -694,6 +765,7 @@ pub struct SpanlessHash<'a, 'tcx> { cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, s: FxHasher, + path_check: PathCheck, } impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { @@ -701,10 +773,21 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), + path_check: PathCheck::default(), s: FxHasher::default(), } } + /// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more + /// details. + #[must_use] + pub fn paths_by_resolution(self) -> Self { + Self { + path_check: PathCheck::Resolution, + ..self + } + } + pub fn finish(self) -> u64 { self.s.finish() } @@ -1042,9 +1125,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // even though the binding names are different and they have different `HirId`s. Res::Local(_) => 1_usize.hash(&mut self.s), _ => { - for seg in path.segments { - self.hash_name(seg.ident.name); - self.hash_generic_args(seg.args().args); + if let PathCheck::Resolution = self.path_check + && let [.., last] = path.segments + && let Some(segments) = generic_path_segments(path.segments) + { + for seg in segments { + self.hash_generic_args(seg.args().args); + } + last.res.hash(&mut self.s); + } else { + for seg in path.segments { + self.hash_name(seg.ident.name); + self.hash_generic_args(seg.args().args); + } } }, } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 10e258444a6..ad85dfa2d1e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -3,6 +3,7 @@ #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] +#![feature(macro_metavar_expr_concat)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_private)] @@ -128,7 +129,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; - use rustc_middle::hir::nested_filter; #[macro_export] diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 21c2b19f4bd..b7a3569ccf0 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -1168,7 +1168,7 @@ pub fn make_normalized_projection<'tcx>( pub struct InteriorMut<'tcx> { ignored_def_ids: FxHashSet<DefId>, ignore_pointers: bool, - tys: FxHashMap<Ty<'tcx>, Option<bool>>, + tys: FxHashMap<Ty<'tcx>, Option<&'tcx ty::List<Ty<'tcx>>>>, } impl<'tcx> InteriorMut<'tcx> { @@ -1194,25 +1194,24 @@ impl<'tcx> InteriorMut<'tcx> { } } - /// Check if given type has inner mutability such as [`std::cell::Cell`] or - /// [`std::cell::RefCell`] etc. - pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + /// Check if given type has interior mutability such as [`std::cell::Cell`] or + /// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes + /// this type to be interior mutable + pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> { match self.tys.entry(ty) { - Entry::Occupied(o) => return *o.get() == Some(true), + Entry::Occupied(o) => return *o.get(), // Temporarily insert a `None` to break cycles Entry::Vacant(v) => v.insert(None), }; - let interior_mut = match *ty.kind() { - ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.is_interior_mut_ty(cx, inner_ty), - ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.is_interior_mut_ty(cx, inner_ty), - ty::Array(inner_ty, size) => { - size.try_eval_target_usize(cx.tcx, cx.param_env) - .map_or(true, |u| u != 0) - && self.is_interior_mut_ty(cx, inner_ty) + let chain = match *ty.kind() { + ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty), + ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty), + ty::Array(inner_ty, size) if size.try_eval_target_usize(cx.tcx, cx.param_env) != Some(0) => { + self.interior_mut_ty_chain(cx, inner_ty) }, - ty::Tuple(fields) => fields.iter().any(|ty| self.is_interior_mut_ty(cx, ty)), - ty::Adt(def, _) if def.is_unsafe_cell() => true, + ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)), + ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()), ty::Adt(def, args) => { let is_std_collection = matches!( cx.tcx.get_diagnostic_name(def.did()), @@ -1231,19 +1230,28 @@ impl<'tcx> InteriorMut<'tcx> { if is_std_collection || def.is_box() { // Include the types from std collections that are behind pointers internally - args.types().any(|ty| self.is_interior_mut_ty(cx, ty)) + args.types().find_map(|ty| self.interior_mut_ty_chain(cx, ty)) } else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() { - false + None } else { def.all_fields() - .any(|f| self.is_interior_mut_ty(cx, f.ty(cx.tcx, args))) + .find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args))) } }, - _ => false, + _ => None, }; - self.tys.insert(ty, Some(interior_mut)); - interior_mut + chain.map(|chain| { + let list = cx.tcx.mk_type_list_from_iter(chain.iter().chain([ty])); + self.tys.insert(ty, Some(list)); + list + }) + } + + /// Check if given type has interior mutability such as [`std::cell::Cell`] or + /// [`std::cell::RefCell`] etc. + pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + self.interior_mut_ty_chain(cx, ty).is_some() } } diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml deleted file mode 100644 index 67a1f7cc72c..00000000000 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "declare_clippy_lint" -version = "0.1.83" -edition = "2021" -publish = false - -[lib] -proc-macro = true - -[dependencies] -itertools = "0.12" -quote = "1.0.21" -syn = "2.0" diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs deleted file mode 100644 index fefc1a0a6c4..00000000000 --- a/src/tools/clippy/declare_clippy_lint/src/lib.rs +++ /dev/null @@ -1,182 +0,0 @@ -#![feature(let_chains, proc_macro_span)] -// warn on lints, that are included in `rust-lang/rust`s bootstrap -#![warn(rust_2018_idioms, unused_lifetimes)] - -use proc_macro::TokenStream; -use quote::{format_ident, quote}; -use syn::parse::{Parse, ParseStream}; -use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token, parse_macro_input}; - -fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) -> Option<LitStr> { - if let Meta::NameValue(name_value) = &attr.meta { - let path_idents = name_value.path.segments.iter().map(|segment| &segment.ident); - - if itertools::equal(path_idents, path) - && let Expr::Lit(ExprLit { lit: Lit::Str(s), .. }) = &name_value.value - { - return Some(s.clone()); - } - } - - None -} - -struct ClippyLint { - attrs: Vec<Attribute>, - version: Option<LitStr>, - explanation: String, - name: Ident, - category: Ident, - description: LitStr, -} - -impl Parse for ClippyLint { - fn parse(input: ParseStream<'_>) -> Result<Self> { - let attrs = input.call(Attribute::parse_outer)?; - - let mut in_code = false; - let mut explanation = String::new(); - let mut version = None; - for attr in &attrs { - if let Some(lit) = parse_attr(["doc"], attr) { - let value = lit.value(); - let line = value.strip_prefix(' ').unwrap_or(&value); - - if let Some(lang) = line.strip_prefix("```") { - let tag = lang.split_once(',').map_or(lang, |(left, _)| left); - if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") { - explanation += "```rust\n"; - } else { - explanation += line; - explanation.push('\n'); - } - in_code = !in_code; - } else if !(in_code && line.starts_with("# ")) { - explanation += line; - explanation.push('\n'); - } - } else if let Some(lit) = parse_attr(["clippy", "version"], attr) { - if let Some(duplicate) = version.replace(lit) { - return Err(Error::new_spanned(duplicate, "duplicate clippy::version")); - } - } else { - return Err(Error::new_spanned(attr, "unexpected attribute")); - } - } - - input.parse::<Token![pub]>()?; - let name = input.parse()?; - input.parse::<Token![,]>()?; - - let category = input.parse()?; - input.parse::<Token![,]>()?; - - let description = input.parse()?; - - Ok(Self { - attrs, - version, - explanation, - name, - category, - description, - }) - } -} - -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website and `cargo clippy --explain` -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the lint -/// is triggered. -/// -/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are -/// enabled by default. As said in the README.md of this repository, if the lint level mapping -/// changes, please update README.md. -/// -/// # Example -/// -/// ```ignore -/// use rustc_session::declare_tool_lint; -/// -/// declare_clippy_lint! { -/// /// ### What it does -/// /// Checks for ... (describe what the lint matches). -/// /// -/// /// ### Why is this bad? -/// /// Supply the reason for linting the code. -/// /// -/// /// ### Example -/// /// ```rust -/// /// Insert a short example of code that triggers the lint -/// /// ``` -/// /// -/// /// Use instead: -/// /// ```rust -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// #[clippy::version = "1.65.0"] -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -#[proc_macro] -pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { - let ClippyLint { - attrs, - version, - explanation, - name, - category, - description, - } = parse_macro_input!(input as ClippyLint); - - let mut category = category.to_string(); - - let level = format_ident!("{}", match category.as_str() { - "correctness" => "Deny", - "style" | "suspicious" | "complexity" | "perf" => "Warn", - "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", - _ => panic!("unknown category {category}"), - },); - - let info_name = format_ident!("{name}_INFO"); - - (&mut category[0..1]).make_ascii_uppercase(); - let category_variant = format_ident!("{category}"); - - let name_span = name.span().unwrap(); - let location = format!("{}#L{}", name_span.source_file().path().display(), name_span.line()); - - let version = match version { - Some(version) => quote!(Some(#version)), - None => quote!(None), - }; - - let output = quote! { - rustc_session::declare_tool_lint! { - #(#attrs)* - pub clippy::#name, - #level, - #description, - report_in_external_macro: true - } - - pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo { - lint: &#name, - category: crate::LintCategory::#category_variant, - explanation: #explanation, - location: #location, - version: #version, - }; - }; - - TokenStream::from(output) -} diff --git a/src/tools/clippy/lintcheck/ci_crates.toml b/src/tools/clippy/lintcheck/ci_crates.toml index 9e3dbef6a9e..6299823451d 100644 --- a/src/tools/clippy/lintcheck/ci_crates.toml +++ b/src/tools/clippy/lintcheck/ci_crates.toml @@ -88,17 +88,17 @@ errno = { name = 'errno', version = '0.3.9' } uuid = { name = 'uuid', version = '1.10.0' } unicode-normalization = { name = 'unicode-normalization', version = '0.1.23' } ppv-lite86 = { name = 'ppv-lite86', version = '0.2.17' } -futures-core = { name = 'futures-core', version = '0.3.30' } +futures-core = { name = 'futures-core', version = '0.3.31' } http-body = { name = 'http-body', version = '1.0.1' } tinyvec = { name = 'tinyvec', version = '1.8.0' } -futures-util = { name = 'futures-util', version = '0.3.30' } -futures-task = { name = 'futures-task', version = '0.3.30' } +futures-util = { name = 'futures-util', version = '0.3.31' } +futures-task = { name = 'futures-task', version = '0.3.31' } sha2 = { name = 'sha2', version = '0.11.0-pre.3' } ring = { name = 'ring', version = '0.17.8' } slab = { name = 'slab', version = '0.4.9' } chrono = { name = 'chrono', version = '0.4.38' } -futures-sink = { name = 'futures-sink', version = '0.3.30' } -futures-channel = { name = 'futures-channel', version = '0.3.30' } +futures-sink = { name = 'futures-sink', version = '0.3.31' } +futures-channel = { name = 'futures-channel', version = '0.3.31' } num_cpus = { name = 'num_cpus', version = '1.16.0' } untrusted = { name = 'untrusted', version = '0.9.0' } tinyvec_macros = { name = 'tinyvec_macros', version = '0.1.1' } @@ -106,7 +106,7 @@ mio = { name = 'mio', version = '1.0.0' } byteorder = { name = 'byteorder', version = '1.5.0' } form_urlencoded = { name = 'form_urlencoded', version = '1.2.1' } unicode-bidi = { name = 'unicode-bidi', version = '0.3.15' } -futures-io = { name = 'futures-io', version = '0.3.30' } +futures-io = { name = 'futures-io', version = '0.3.31' } tokio-util = { name = 'tokio-util', version = '0.7.11' } rustls-pemfile = { name = 'rustls-pemfile', version = '2.1.2' } generic-array = { name = 'generic-array', version = '1.1.0' } @@ -116,7 +116,7 @@ tracing-core = { name = 'tracing-core', version = '0.1.32' } pin-utils = { name = 'pin-utils', version = '0.1.0' } tempfile = { name = 'tempfile', version = '3.10.1' } h2 = { name = 'h2', version = '0.4.5' } -futures = { name = 'futures', version = '0.3.30' } +futures = { name = 'futures', version = '0.3.31' } typenum = { name = 'typenum', version = '1.17.0' } winnow = { name = 'winnow', version = '0.6.13' } cpufeatures = { name = 'cpufeatures', version = '0.2.12' } @@ -131,9 +131,9 @@ pkg-config = { name = 'pkg-config', version = '0.3.30' } redox_syscall = { name = 'redox_syscall', version = '0.5.3' } nom = { name = 'nom', version = '8.0.0-alpha2' } rustc_version = { name = 'rustc_version', version = '0.4.0' } -futures-macro = { name = 'futures-macro', version = '0.3.30' } +futures-macro = { name = 'futures-macro', version = '0.3.31' } clap_derive = { name = 'clap_derive', version = '4.5.8' } -futures-executor = { name = 'futures-executor', version = '0.3.30' } +futures-executor = { name = 'futures-executor', version = '0.3.31' } event-listener = { name = 'event-listener', version = '5.3.1' } num-integer = { name = 'num-integer', version = '0.1.46' } time-macros = { name = 'time-macros', version = '0.2.18' } diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs index ee0c80aea52..3a68f2c9243 100644 --- a/src/tools/clippy/lintcheck/src/json.rs +++ b/src/tools/clippy/lintcheck/src/json.rs @@ -133,7 +133,7 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { println!(); print!( - r##"{}, {}, {}"##, + r"{}, {}, {}", count_string(name, "added", lint.added.len()), count_string(name, "removed", lint.removed.len()), count_string(name, "changed", lint.changed.len()), diff --git a/src/tools/clippy/rinja.toml b/src/tools/clippy/rinja.toml new file mode 100644 index 00000000000..a10da6e1f28 --- /dev/null +++ b/src/tools/clippy/rinja.toml @@ -0,0 +1,3 @@ +[general] +dirs = ["util/gh-pages/"] +whitespace = "suppress" diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index f0c8651efce..e11ab40b9de 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-10-03" +channel = "nightly-2024-10-18" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/CHANGELOG.md b/src/tools/clippy/rustc_tools_util/CHANGELOG.md index 1b351da2e7b..7f628178ea6 100644 --- a/src/tools/clippy/rustc_tools_util/CHANGELOG.md +++ b/src/tools/clippy/rustc_tools_util/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Version 0.4.0 + +* The commit hashes are now always 10 characters long [#13222](https://github.com/rust-lang/rust-clippy/pull/13222) +* `get_commit_date` and `get_commit_hash` now return `None` if the `git` command fails instead of `Some("")` + [#13217](https://github.com/rust-lang/rust-clippy/pull/13217) +* `setup_version_info` will now re-run when the git commit changes + [#13329](https://github.com/rust-lang/rust-clippy/pull/13329) +* New `rerun_if_git_changes` function was added [#13329](https://github.com/rust-lang/rust-clippy/pull/13329) + ## Version 0.3.0 * Added `setup_version_info!();` macro for automated scripts. diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml index 37b592da132..b63632916ba 100644 --- a/src/tools/clippy/rustc_tools_util/Cargo.toml +++ b/src/tools/clippy/rustc_tools_util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustc_tools_util" -version = "0.3.0" +version = "0.4.0" description = "small helper to generate version information for git packages" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md index 56f62b867a6..1b11dfe0619 100644 --- a/src/tools/clippy/rustc_tools_util/README.md +++ b/src/tools/clippy/rustc_tools_util/README.md @@ -13,10 +13,10 @@ build = "build.rs" List rustc_tools_util as regular AND build dependency. ````toml [dependencies] -rustc_tools_util = "0.3.0" +rustc_tools_util = "0.4.0" [build-dependencies] -rustc_tools_util = "0.3.0" +rustc_tools_util = "0.4.0" ```` In `build.rs`, generate the data in your `main()` diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs index 2cc38130472..16be02f4a40 100644 --- a/src/tools/clippy/rustc_tools_util/src/lib.rs +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; +use std::process::Command; use std::str; /// This macro creates the version string during compilation from the @@ -32,6 +34,7 @@ macro_rules! get_version_info { #[macro_export] macro_rules! setup_version_info { () => {{ + let _ = $crate::rerun_if_git_changes(); println!( "cargo:rustc-env=GIT_HASH={}", $crate::get_commit_hash().unwrap_or_default() @@ -100,24 +103,52 @@ impl std::fmt::Debug for VersionInfo { } #[must_use] -pub fn get_commit_hash() -> Option<String> { - let output = std::process::Command::new("git") - .args(["rev-parse", "HEAD"]) - .output() - .ok()?; +fn get_output(cmd: &str, args: &[&str]) -> Option<String> { + let output = Command::new(cmd).args(args).output().ok()?; let mut stdout = output.status.success().then_some(output.stdout)?; - stdout.truncate(10); + // Remove trailing newlines. + while stdout.last().copied() == Some(b'\n') { + stdout.pop(); + } String::from_utf8(stdout).ok() } #[must_use] +pub fn rerun_if_git_changes() -> Option<()> { + // Make sure we get rerun when the git commit changes. + // We want to watch two files: HEAD, which tracks which branch we are on, + // and the file for that branch that tracks which commit is is on. + + // First, find the `HEAD` file. This should work even with worktrees. + let git_head_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", "HEAD"])?); + if git_head_file.exists() { + println!("cargo::rerun-if-changed={}", git_head_file.display()); + } + + // Determine the name of the current ref. + // This will quit if HEAD is detached. + let git_head_ref = get_output("git", &["symbolic-ref", "-q", "HEAD"])?; + // Ask git where this ref is stored. + let git_head_ref_file = PathBuf::from(get_output("git", &["rev-parse", "--git-path", &git_head_ref])?); + // If this ref is packed, the file does not exist. However, the checked-out branch is never (?) + // packed, so we should always be able to find this file. + if git_head_ref_file.exists() { + println!("cargo::rerun-if-changed={}", git_head_ref_file.display()); + } + + Some(()) +} + +#[must_use] +pub fn get_commit_hash() -> Option<String> { + let mut stdout = get_output("git", &["rev-parse", "HEAD"])?; + stdout.truncate(10); + Some(stdout) +} + +#[must_use] pub fn get_commit_date() -> Option<String> { - let output = std::process::Command::new("git") - .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) - .output() - .ok()?; - let stdout = output.status.success().then_some(output.stdout)?; - String::from_utf8(stdout).ok() + get_output("git", &["log", "-1", "--date=short", "--pretty=format:%cd"]) } #[must_use] @@ -127,15 +158,11 @@ pub fn get_channel() -> String { } // if that failed, try to ask rustc -V, do some parsing and find out - if let Ok(output) = std::process::Command::new("rustc").arg("-V").output() { - if output.status.success() { - if let Ok(rustc_output) = str::from_utf8(&output.stdout) { - if rustc_output.contains("beta") { - return String::from("beta"); - } else if rustc_output.contains("stable") { - return String::from("stable"); - } - } + if let Some(rustc_output) = get_output("rustc", &["-V"]) { + if rustc_output.contains("beta") { + return String::from("beta"); + } else if rustc_output.contains("stable") { + return String::from("stable"); } } @@ -151,7 +178,7 @@ mod test { fn test_struct_local() { let vi = get_version_info!(); assert_eq!(vi.major, 0); - assert_eq!(vi.minor, 3); + assert_eq!(vi.minor, 4); assert_eq!(vi.patch, 0); assert_eq!(vi.crate_name, "rustc_tools_util"); // hard to make positive tests for these since they will always change @@ -162,7 +189,7 @@ mod test { #[test] fn test_display_local() { let vi = get_version_info!(); - assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0"); + assert_eq!(vi.to_string(), "rustc_tools_util 0.4.0"); } #[test] @@ -171,7 +198,7 @@ mod test { let s = format!("{vi:?}"); assert_eq!( s, - "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 3, patch: 0 }" + "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 4, patch: 0 }" ); } } diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index af2aa519257..5774e20e0be 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -8,7 +8,10 @@ use clippy_config::ClippyConfiguration; use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; -use serde::{Deserialize, Serialize}; +use pulldown_cmark::{Options, Parser, html}; +use rinja::Template; +use rinja::filters::Safe; +use serde::Deserialize; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; use ui_test::custom_flags::rustfix::RustfixMode; @@ -385,6 +388,22 @@ fn ui_cargo_toml_metadata() { } } +#[derive(Template)] +#[template(path = "index_template.html")] +struct Renderer<'a> { + lints: &'a Vec<LintMetadata>, +} + +impl Renderer<'_> { + fn markdown(input: &str) -> Safe<String> { + let parser = Parser::new_ext(input, Options::all()); + let mut html_output = String::new(); + html::push_html(&mut html_output, parser); + // Oh deer, what a hack :O + Safe(html_output.replace("<table", "<table class=\"table\"")) + } +} + #[derive(Deserialize)] #[serde(untagged)] enum DiagnosticOrMessage { @@ -445,10 +464,14 @@ impl DiagnosticCollector { .map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)), ) .collect(); + metadata.sort_unstable_by(|a, b| a.id.cmp(&b.id)); - let json = serde_json::to_string_pretty(&metadata).unwrap(); - fs::write("util/gh-pages/lints.json", json).unwrap(); + fs::write( + "util/gh-pages/index.html", + Renderer { lints: &metadata }.render().unwrap(), + ) + .unwrap(); }); (Self { sender }, handle) @@ -487,7 +510,7 @@ impl Flag for DiagnosticCollector { } } -#[derive(Debug, Serialize)] +#[derive(Debug)] struct LintMetadata { id: String, id_location: Option<&'static str>, @@ -559,4 +582,14 @@ impl LintMetadata { applicability: Applicability::Unspecified, } } + + fn applicability_str(&self) -> &str { + match self.applicability { + Applicability::MachineApplicable => "MachineApplicable", + Applicability::HasPlaceholders => "HasPlaceholders", + Applicability::MaybeIncorrect => "MaybeIncorrect", + Applicability::Unspecified => "Unspecified", + _ => panic!("needs to update this code"), + } + } } diff --git a/src/tools/clippy/tests/config-metadata.rs b/src/tools/clippy/tests/config-metadata.rs index 628dfc8f758..af9fe064dc7 100644 --- a/src/tools/clippy/tests/config-metadata.rs +++ b/src/tools/clippy/tests/config-metadata.rs @@ -20,7 +20,7 @@ fn book() { let configs = metadata().map(|conf| conf.to_markdown_paragraph()).join("\n"); let expected = format!( - r#"<!-- + r"<!-- This file is generated by `cargo bless --test config-metadata`. Please use that command to update the file and do not edit it by hand. --> @@ -33,7 +33,7 @@ and lints affected. --- {} -"#, +", configs.trim(), ); diff --git a/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr index 009153bc4a1..41cb85b67df 100644 --- a/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr +++ b/src/tools/clippy/tests/ui-toml/array_size_threshold/array_size_threshold.stderr @@ -10,22 +10,14 @@ LL | const ABOVE: [u8; 11] = [0; 11]; = help: to override `-D warnings` add `#[allow(clippy::large_const_arrays)]` error: allocating a local array larger than 10 bytes - --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:4:25 - | -LL | const ABOVE: [u8; 11] = [0; 11]; - | ^^^^^^^ - | - = help: consider allocating on the heap with `vec![0; 11].into_boxed_slice()` - = note: `-D clippy::large-stack-arrays` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]` - -error: allocating a local array larger than 10 bytes --> tests/ui-toml/array_size_threshold/array_size_threshold.rs:8:17 | LL | let above = [0u8; 11]; | ^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u8; 11].into_boxed_slice()` + = note: `-D clippy::large-stack-arrays` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs index 4142ced5f6b..2ae673a6def 100644 --- a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs @@ -1,7 +1,7 @@ #![warn(clippy::module_name_repetitions)] #![allow(dead_code)] -mod foo { +pub mod foo { // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name. // In this test, allowed prefixes are configured to be ["bar"]. diff --git a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs index b132305d01c..dbd61992c0d 100644 --- a/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs +++ b/src/tools/clippy/tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs @@ -1,7 +1,7 @@ #![warn(clippy::module_name_repetitions)] #![allow(dead_code)] -mod foo { +pub mod foo { // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name. // In this test, allowed prefixes are configured to be all of the default prefixes and ["bar"]. diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr index bae853ac5c1..050c039f834 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.stderr +++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr @@ -1,4 +1,4 @@ -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:35:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); @@ -7,85 +7,85 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = note: `-D clippy::expect-fun-call` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::expect_fun_call)]` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:38:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:41:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:51:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:54:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:66:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:87:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:88:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:89:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:92:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:96:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:102:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:106:20 | LL | format_capture.expect(&format!("{error_code}")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))` -error: use of `expect` followed by a function call +error: function call inside of `expect` --> tests/ui/expect_fun_call.rs:109:30 | LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed index 81cc1494914..136238f9eca 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -222,3 +222,9 @@ fn main() { a - b }; } + +fn regression_13524(a: usize, b: usize, c: bool) -> usize { + if c { + 123 + } else { b.saturating_sub(a) } +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs index f73396ebd27..e371e37fb2f 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -268,3 +268,13 @@ fn main() { a - b }; } + +fn regression_13524(a: usize, b: usize, c: bool) -> usize { + if c { + 123 + } else if a >= b { + 0 + } else { + b - a + } +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr index 59a9ddbff2d..61319851228 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr @@ -185,5 +185,16 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: aborting due to 23 previous errors +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:275:12 + | +LL | } else if a >= b { + | ____________^ +LL | | 0 +LL | | } else { +LL | | b - a +LL | | } + | |_____^ help: replace it with: `{ b.saturating_sub(a) }` + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs index b2d522fa011..b6cb7ff49b0 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.rs +++ b/src/tools/clippy/tests/ui/infinite_loops.rs @@ -390,4 +390,42 @@ fn span_inside_fn() { } } +fn continue_outer() { + // Should not lint (issue #13511) + let mut count = 0; + 'outer: loop { + if count != 0 { + break; + } + + loop { + count += 1; + continue 'outer; + } + } + + // This should lint as we continue the loop itself + 'infinite: loop { + //~^ ERROR: infinite loop detected + loop { + continue 'infinite; + } + } + // This should lint as we continue an inner loop + loop { + //~^ ERROR: infinite loop detected + 'inner: loop { + loop { + continue 'inner; + } + } + } + + // This should lint as we continue the loop itself + loop { + //~^ ERROR: infinite loop detected + continue; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr index ec6bd81dc17..7635a7442f4 100644 --- a/src/tools/clippy/tests/ui/infinite_loops.stderr +++ b/src/tools/clippy/tests/ui/infinite_loops.stderr @@ -255,5 +255,67 @@ LL | | }) | = help: if this is not intended, try adding a `break` or `return` condition in the loop -error: aborting due to 17 previous errors +error: infinite loop detected + --> tests/ui/infinite_loops.rs:408:5 + | +LL | / 'infinite: loop { +LL | | +LL | | loop { +LL | | continue 'infinite; +LL | | } +LL | | } + | |_____^ + | +help: if this is intentional, consider specifying `!` as function return + | +LL | fn continue_outer() -> ! { + | ++++ + +error: infinite loop detected + --> tests/ui/infinite_loops.rs:415:5 + | +LL | / loop { +LL | | +LL | | 'inner: loop { +LL | | loop { +... | +LL | | } +LL | | } + | |_____^ + | +help: if this is intentional, consider specifying `!` as function return + | +LL | fn continue_outer() -> ! { + | ++++ + +error: infinite loop detected + --> tests/ui/infinite_loops.rs:417:9 + | +LL | / 'inner: loop { +LL | | loop { +LL | | continue 'inner; +LL | | } +LL | | } + | |_________^ + | +help: if this is intentional, consider specifying `!` as function return + | +LL | fn continue_outer() -> ! { + | ++++ + +error: infinite loop detected + --> tests/ui/infinite_loops.rs:425:5 + | +LL | / loop { +LL | | +LL | | continue; +LL | | } + | |_____^ + | +help: if this is intentional, consider specifying `!` as function return + | +LL | fn continue_outer() -> ! { + | ++++ + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed index 092e875a255..ba225102c98 100644 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed +++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.fixed @@ -1,44 +1,44 @@ fn main() { unsafe { - let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); - let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0); - let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::NonNull::dangling().as_ptr(), 0); - std::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - std::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); - std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); struct A; // zero sized struct assert_eq!(std::mem::size_of::<A>(), 0); - let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); - let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read(std::ptr::NonNull::dangling().as_ptr()); - let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); - let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_unaligned(std::ptr::NonNull::dangling().as_ptr()); - let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); - let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_volatile(std::ptr::NonNull::dangling().as_ptr()); - let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); + let _a: A = std::ptr::replace(std::ptr::NonNull::dangling().as_ptr(), A); let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); // shouldn't lint let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); - std::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A); - std::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr()); + std::ptr::swap::<A>(std::ptr::NonNull::dangling().as_ptr(), &mut A); + std::ptr::swap::<A>(&mut A, std::ptr::NonNull::dangling().as_ptr()); - std::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); - std::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::swap_nonoverlapping::<A>(std::ptr::NonNull::dangling().as_ptr(), &mut A, 0); + std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::NonNull::dangling().as_ptr(), 0); - std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); + std::ptr::write(std::ptr::NonNull::dangling().as_ptr(), A); - std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); + std::ptr::write_unaligned(std::ptr::NonNull::dangling().as_ptr(), A); - std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); + std::ptr::write_volatile(std::ptr::NonNull::dangling().as_ptr(), A); - std::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0); + std::ptr::write_bytes::<usize>(std::ptr::NonNull::dangling().as_ptr(), 42, 0); } } diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr index a0be2c0ad75..613a2cc3688 100644 --- a/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr +++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage.stderr @@ -2,7 +2,7 @@ error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:3:59 | LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` | = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default @@ -10,127 +10,127 @@ error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:4:59 | LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:6:63 | LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:8:33 | LL | std::ptr::copy::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:9:73 | LL | std::ptr::copy::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:11:48 | LL | std::ptr::copy_nonoverlapping::<usize>(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:12:88 | LL | std::ptr::copy_nonoverlapping::<usize>(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:17:36 | LL | let _a: A = std::ptr::read(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:18:36 | LL | let _a: A = std::ptr::read(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:20:46 | LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:21:46 | LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:23:45 | LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); - | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:24:45 | LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:26:39 | LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:30:29 | LL | std::ptr::swap::<A>(std::ptr::null_mut(), &mut A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:31:37 | LL | std::ptr::swap::<A>(&mut A, std::ptr::null_mut()); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:33:44 | LL | std::ptr::swap_nonoverlapping::<A>(std::ptr::null_mut(), &mut A, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:34:52 | LL | std::ptr::swap_nonoverlapping::<A>(&mut A, std::ptr::null_mut(), 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:36:25 | LL | std::ptr::write(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:38:35 | LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:40:34 | LL | std::ptr::write_volatile(std::ptr::null_mut(), A); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: pointer must be non-null --> tests/ui/invalid_null_ptr_usage.rs:42:40 | LL | std::ptr::write_bytes::<usize>(std::ptr::null_mut(), 42, 0); - | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `std::ptr::NonNull::dangling().as_ptr()` error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed new file mode 100644 index 00000000000..2bbfe727424 --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.fixed @@ -0,0 +1,57 @@ +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn main() { + unsafe { + let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + struct A; // zero sized struct + assert_eq!(core::mem::size_of::<A>(), 0); + + let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = core::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = core::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = core::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = core::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); + let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint + let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); + + core::ptr::swap::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A); + core::ptr::swap::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr()); + + core::ptr::swap_nonoverlapping::<A>(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); + core::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); + + core::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); + + core::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); + + core::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); + + core::ptr::write_bytes::<usize>(core::ptr::NonNull::dangling().as_ptr(), 42, 0); + } +} diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs new file mode 100644 index 00000000000..cbce44f7c0d --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.rs @@ -0,0 +1,57 @@ +#![no_std] +#![feature(lang_items)] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn main() { + unsafe { + let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); + let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); + + let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); + + core::ptr::copy::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); + core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); + + core::ptr::copy_nonoverlapping::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); + core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); + + struct A; // zero sized struct + assert_eq!(core::mem::size_of::<A>(), 0); + + let _a: A = core::ptr::read(core::ptr::null()); + let _a: A = core::ptr::read(core::ptr::null_mut()); + + let _a: A = core::ptr::read_unaligned(core::ptr::null()); + let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); + + let _a: A = core::ptr::read_volatile(core::ptr::null()); + let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); + + let _a: A = core::ptr::replace(core::ptr::null_mut(), A); + let _slice: *const [usize] = core::ptr::slice_from_raw_parts(core::ptr::null_mut(), 0); // shouldn't lint + let _slice: *const [usize] = core::ptr::slice_from_raw_parts_mut(core::ptr::null_mut(), 0); + + core::ptr::swap::<A>(core::ptr::null_mut(), &mut A); + core::ptr::swap::<A>(&mut A, core::ptr::null_mut()); + + core::ptr::swap_nonoverlapping::<A>(core::ptr::null_mut(), &mut A, 0); + core::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::null_mut(), 0); + + core::ptr::write(core::ptr::null_mut(), A); + + core::ptr::write_unaligned(core::ptr::null_mut(), A); + + core::ptr::write_volatile(core::ptr::null_mut(), A); + + core::ptr::write_bytes::<usize>(core::ptr::null_mut(), 42, 0); + } +} diff --git a/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr new file mode 100644 index 00000000000..df0d40e9e07 --- /dev/null +++ b/src/tools/clippy/tests/ui/invalid_null_ptr_usage_no_std.stderr @@ -0,0 +1,136 @@ +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:16:60 + | +LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | + = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:17:60 + | +LL | let _slice: &[usize] = core::slice::from_raw_parts(core::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:19:64 + | +LL | let _slice: &[usize] = core::slice::from_raw_parts_mut(core::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:21:34 + | +LL | core::ptr::copy::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:22:75 + | +LL | core::ptr::copy::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:24:49 + | +LL | core::ptr::copy_nonoverlapping::<usize>(core::ptr::null(), core::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:25:90 + | +LL | core::ptr::copy_nonoverlapping::<usize>(core::ptr::NonNull::dangling().as_ptr(), core::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:30:37 + | +LL | let _a: A = core::ptr::read(core::ptr::null()); + | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:31:37 + | +LL | let _a: A = core::ptr::read(core::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:33:47 + | +LL | let _a: A = core::ptr::read_unaligned(core::ptr::null()); + | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:34:47 + | +LL | let _a: A = core::ptr::read_unaligned(core::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:36:46 + | +LL | let _a: A = core::ptr::read_volatile(core::ptr::null()); + | ^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:37:46 + | +LL | let _a: A = core::ptr::read_volatile(core::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:39:40 + | +LL | let _a: A = core::ptr::replace(core::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:43:30 + | +LL | core::ptr::swap::<A>(core::ptr::null_mut(), &mut A); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:44:38 + | +LL | core::ptr::swap::<A>(&mut A, core::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:46:45 + | +LL | core::ptr::swap_nonoverlapping::<A>(core::ptr::null_mut(), &mut A, 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:47:53 + | +LL | core::ptr::swap_nonoverlapping::<A>(&mut A, core::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:49:26 + | +LL | core::ptr::write(core::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:51:36 + | +LL | core::ptr::write_unaligned(core::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:53:35 + | +LL | core::ptr::write_volatile(core::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> tests/ui/invalid_null_ptr_usage_no_std.rs:55:41 + | +LL | core::ptr::write_bytes::<usize>(core::ptr::null_mut(), 42, 0); + | ^^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: aborting due to 22 previous errors + diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed index 6011bb99dec..543ce460e7b 100644 --- a/src/tools/clippy/tests/ui/large_const_arrays.fixed +++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed @@ -12,9 +12,9 @@ pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; static FOO: [u32; 1_000_000] = [0u32; 1_000_000]; // Good -pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000]; -pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000]; -const G_FOO: [u32; 1_000] = [0u32; 1_000]; +pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250]; +pub const G_FOO_PUB: [u32; 250] = [0u32; 250]; +const G_FOO: [u32; 250] = [0u32; 250]; fn main() { // Should lint @@ -26,10 +26,10 @@ fn main() { static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; // Good - pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000]; - const G_BAR: [u32; 1_000] = [0u32; 1_000]; - pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500]; - const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500]; - pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200]; - const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200]; + pub const G_BAR_PUB: [u32; 250] = [0u32; 250]; + const G_BAR: [u32; 250] = [0u32; 250]; + pub const G_BAR_STRUCT_PUB: [S; 4] = [S { data: [0; 32] }; 4]; + const G_BAR_STRUCT: [S; 4] = [S { data: [0; 32] }; 4]; + pub const G_BAR_S_PUB: [Option<&str>; 50] = [Some("str"); 50]; + const G_BAR_S: [Option<&str>; 50] = [Some("str"); 50]; } diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs index a78425d7bc6..e23a8081171 100644 --- a/src/tools/clippy/tests/ui/large_const_arrays.rs +++ b/src/tools/clippy/tests/ui/large_const_arrays.rs @@ -12,9 +12,9 @@ pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000]; const FOO: [u32; 1_000_000] = [0u32; 1_000_000]; // Good -pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000]; -pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000]; -const G_FOO: [u32; 1_000] = [0u32; 1_000]; +pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250]; +pub const G_FOO_PUB: [u32; 250] = [0u32; 250]; +const G_FOO: [u32; 250] = [0u32; 250]; fn main() { // Should lint @@ -26,10 +26,10 @@ fn main() { const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000]; // Good - pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000]; - const G_BAR: [u32; 1_000] = [0u32; 1_000]; - pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500]; - const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500]; - pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200]; - const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200]; + pub const G_BAR_PUB: [u32; 250] = [0u32; 250]; + const G_BAR: [u32; 250] = [0u32; 250]; + pub const G_BAR_STRUCT_PUB: [S; 4] = [S { data: [0; 32] }; 4]; + const G_BAR_STRUCT: [S; 4] = [S { data: [0; 32] }; 4]; + pub const G_BAR_S_PUB: [Option<&str>; 50] = [Some("str"); 50]; + const G_BAR_S: [Option<&str>; 50] = [Some("str"); 50]; } diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs index 6bcaf481c9f..cd72b9bfa47 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.rs +++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs @@ -15,6 +15,12 @@ enum E { T(u32), } +const STATIC_PROMOTED_LARGE_ARRAY: &[u8; 512001] = &[0; 512001]; +const STATIC_PROMOTED_LARGE_ARRAY_WITH_NESTED: &[u8; 512001] = { + const NESTED: () = (); + &[0; 512001] +}; + pub static DOESNOTLINT: [u8; 512_001] = [0; 512_001]; pub static DOESNOTLINT2: [u8; 512_001] = { let x = 0; @@ -23,38 +29,38 @@ pub static DOESNOTLINT2: [u8; 512_001] = { fn issue_10741() { #[derive(Copy, Clone)] - struct Large([u32; 100_000]); + struct Large([u32; 2048]); fn build() -> Large { - Large([0; 100_000]) + Large([0; 2048]) } let _x = [build(); 3]; - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes let _y = [build(), build(), build()]; - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes } fn main() { let bad = ( [0u32; 20_000_000], - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes [S { data: [0; 32] }; 5000], - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes [Some(""); 20_000_000], - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes [E::T(0); 5000], - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes [0u8; usize::MAX], - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes ); let good = ( - [0u32; 1000], - [S { data: [0; 32] }; 1000], - [Some(""); 1000], - [E::T(0); 1000], + [0u32; 50], + [S { data: [0; 32] }; 4], + [Some(""); 50], + [E::T(0); 2], [(); 20_000_000], ); } @@ -68,7 +74,7 @@ fn issue_12586() { // Weird rule to test help messages. ($a:expr => $b:expr) => { [$a, $b, $a, $b] - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes }; ($id:ident; $n:literal) => { dummy!(::std::vec![$id;$n]) @@ -80,26 +86,26 @@ fn issue_12586() { macro_rules! create_then_move { ($id:ident; $n:literal) => {{ let _x_ = [$id; $n]; - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes _x_ }}; } - let x = [0u32; 50_000]; + let x = [0u32; 4096]; let y = vec![x, x, x, x, x]; let y = vec![dummy![x, x, x, x, x]]; let y = vec![dummy![[x, x, x, x, x]]]; let y = dummy![x, x, x, x, x]; let y = [x, x, dummy!(x), x, x]; - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes let y = dummy![x => x]; let y = dummy![x;5]; let y = dummy!(vec![dummy![x, x, x, x, x]]); let y = dummy![[x, x, x, x, x]]; - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes let y = proc_macros::make_it_big!([x; 1]); - //~^ ERROR: allocating a local array larger than 512000 bytes + //~^ ERROR: allocating a local array larger than 16384 bytes let y = vec![proc_macros::make_it_big!([x; 10])]; let y = vec![create_then_move![x; 5]; 5]; } diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr index 06294ee8b8c..f48706415e6 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.stderr +++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr @@ -1,5 +1,5 @@ -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:32:14 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:38:14 | LL | let _x = [build(); 3]; | ^^^^^^^^^^^^ @@ -8,64 +8,64 @@ LL | let _x = [build(); 3]; = note: `-D clippy::large-stack-arrays` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:35:14 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:41:14 | LL | let _y = [build(), build(), build()]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:41:9 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:47:9 | LL | [0u32; 20_000_000], | ^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:43:9 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:49:9 | LL | [S { data: [0; 32] }; 5000], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:45:9 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:51:9 | LL | [Some(""); 20_000_000], | ^^^^^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:47:9 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:53:9 | LL | [E::T(0); 5000], | ^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:49:9 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:55:9 | LL | [0u8; usize::MAX], | ^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:93:13 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:99:13 | LL | let y = [x, x, dummy!(x), x, x]; | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![x, x, dummy!(x), x, x].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:70:13 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:76:13 | LL | [$a, $b, $a, $b] | ^^^^^^^^^^^^^^^^ @@ -75,22 +75,22 @@ LL | let y = dummy![x => x]; | = note: this error originates in the macro `dummy` (in Nightly builds, run with -Z macro-backtrace for more info) -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:98:20 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:104:20 | LL | let y = dummy![[x, x, x, x, x]]; | ^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()` -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:101:39 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:107:39 | LL | let y = proc_macros::make_it_big!([x; 1]); | ^^^^^^ -error: allocating a local array larger than 512000 bytes - --> tests/ui/large_stack_arrays.rs:82:23 +error: allocating a local array larger than 16384 bytes + --> tests/ui/large_stack_arrays.rs:88:23 | LL | let _x_ = [$id; $n]; | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.fixed b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.fixed index a24d7088c88..391c63bb4b8 100644 --- a/src/tools/clippy/tests/ui/manual_c_str_literals.fixed +++ b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.fixed @@ -1,3 +1,6 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 #![warn(clippy::manual_c_str_literals)] #![allow(clippy::no_effect)] diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.stderr b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.stderr index 9c70bddb81c..beab29ccdda 100644 --- a/src/tools/clippy/tests/ui/manual_c_str_literals.stderr +++ b/src/tools/clippy/tests/ui/manual_c_str_literals.edition2021.stderr @@ -1,5 +1,5 @@ error: calling `CStr::new` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:31:5 + --> tests/ui/manual_c_str_literals.rs:34:5 | LL | CStr::from_bytes_with_nul(b"foo\0"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` @@ -8,73 +8,73 @@ LL | CStr::from_bytes_with_nul(b"foo\0"); = help: to override `-D warnings` add `#[allow(clippy::manual_c_str_literals)]` error: calling `CStr::new` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:35:5 + --> tests/ui/manual_c_str_literals.rs:38:5 | LL | CStr::from_bytes_with_nul(b"foo\0"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` error: calling `CStr::new` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:36:5 + --> tests/ui/manual_c_str_literals.rs:39:5 | LL | CStr::from_bytes_with_nul(b"foo\x00"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` error: calling `CStr::new` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:37:5 + --> tests/ui/manual_c_str_literals.rs:40:5 | LL | CStr::from_bytes_with_nul(b"foo\0").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` error: calling `CStr::new` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:38:5 + --> tests/ui/manual_c_str_literals.rs:41:5 | LL | CStr::from_bytes_with_nul(b"foo\\0sdsd\0").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo\\0sdsd"` error: calling `CStr::from_ptr` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:43:14 + --> tests/ui/manual_c_str_literals.rs:46:14 | LL | unsafe { CStr::from_ptr(b"foo\0".as_ptr().cast()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` error: calling `CStr::from_ptr` with a byte string literal - --> tests/ui/manual_c_str_literals.rs:44:14 + --> tests/ui/manual_c_str_literals.rs:47:14 | LL | unsafe { CStr::from_ptr(b"foo\0".as_ptr() as *const _) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"` error: manually constructing a nul-terminated string - --> tests/ui/manual_c_str_literals.rs:45:23 + --> tests/ui/manual_c_str_literals.rs:48:23 | LL | let _: *const _ = b"foo\0".as_ptr(); | ^^^^^^^^ help: use a `c""` literal: `c"foo"` error: manually constructing a nul-terminated string - --> tests/ui/manual_c_str_literals.rs:46:23 + --> tests/ui/manual_c_str_literals.rs:49:23 | LL | let _: *const _ = "foo\0".as_ptr(); | ^^^^^^^ help: use a `c""` literal: `c"foo"` error: manually constructing a nul-terminated string - --> tests/ui/manual_c_str_literals.rs:49:23 + --> tests/ui/manual_c_str_literals.rs:52:23 | LL | let _: *const _ = b"foo\0".as_ptr().cast::<i8>(); | ^^^^^^^^ help: use a `c""` literal: `c"foo"` error: manually constructing a nul-terminated string - --> tests/ui/manual_c_str_literals.rs:52:13 + --> tests/ui/manual_c_str_literals.rs:55:13 | LL | let _ = "电脑\\\0".as_ptr(); | ^^^^^^^^^^ help: use a `c""` literal: `c"电脑\\"` error: manually constructing a nul-terminated string - --> tests/ui/manual_c_str_literals.rs:53:13 + --> tests/ui/manual_c_str_literals.rs:56:13 | LL | let _ = "电脑\0".as_ptr(); | ^^^^^^^^ help: use a `c""` literal: `c"电脑"` error: manually constructing a nul-terminated string - --> tests/ui/manual_c_str_literals.rs:54:13 + --> tests/ui/manual_c_str_literals.rs:57:13 | LL | let _ = "电脑\x00".as_ptr(); | ^^^^^^^^^^ help: use a `c""` literal: `c"电脑"` diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.rs b/src/tools/clippy/tests/ui/manual_c_str_literals.rs index 0a007786720..39b62258077 100644 --- a/src/tools/clippy/tests/ui/manual_c_str_literals.rs +++ b/src/tools/clippy/tests/ui/manual_c_str_literals.rs @@ -1,3 +1,6 @@ +//@revisions: edition2018 edition2021 +//@[edition2018] edition:2018 +//@[edition2021] edition:2021 #![warn(clippy::manual_c_str_literals)] #![allow(clippy::no_effect)] diff --git a/src/tools/clippy/tests/ui/manual_float_methods.rs b/src/tools/clippy/tests/ui/manual_float_methods.rs index ee3daa12834..66545d180ef 100644 --- a/src/tools/clippy/tests/ui/manual_float_methods.rs +++ b/src/tools/clippy/tests/ui/manual_float_methods.rs @@ -39,8 +39,11 @@ fn main() { if x != f64::INFINITY && x != fn_test() {} // Not -inf if x != f64::INFINITY && x != fn_test_not_inf() {} + const { + let x = 1.0f64; + if x == f64::INFINITY || x == f64::NEG_INFINITY {} + } const X: f64 = 1.0f64; - // Will be linted if `const_float_classify` is enabled if const { X == f64::INFINITY || X == f64::NEG_INFINITY } {} if const { X != f64::INFINITY && X != f64::NEG_INFINITY } {} external! { diff --git a/src/tools/clippy/tests/ui/manual_float_methods.stderr b/src/tools/clippy/tests/ui/manual_float_methods.stderr index 70057620a4a..676a4485ab4 100644 --- a/src/tools/clippy/tests/ui/manual_float_methods.stderr +++ b/src/tools/clippy/tests/ui/manual_float_methods.stderr @@ -78,5 +78,11 @@ help: or, for conciseness LL | if !x.is_infinite() {} | ~~~~~~~~~~~~~~~~ -error: aborting due to 6 previous errors +error: manually checking if a float is infinite + --> tests/ui/manual_float_methods.rs:44:12 + | +LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed new file mode 100644 index 00000000000..53a124f59c8 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed @@ -0,0 +1,107 @@ +#![allow(clippy::all)] +#![deny(clippy::manual_ignore_case_cmp)] + +use std::ffi::{OsStr, OsString}; + +fn main() {} + +fn variants(a: &str, b: &str) { + if a.eq_ignore_ascii_case(b) { + return; + } + if a.eq_ignore_ascii_case(b) { + return; + } + let r = a.eq_ignore_ascii_case(b); + let r = r || a.eq_ignore_ascii_case(b); + r && a.eq_ignore_ascii_case(&b.to_uppercase()); + // != + if !a.eq_ignore_ascii_case(b) { + return; + } + if !a.eq_ignore_ascii_case(b) { + return; + } + let r = !a.eq_ignore_ascii_case(b); + let r = r || !a.eq_ignore_ascii_case(b); + r && !a.eq_ignore_ascii_case(&b.to_uppercase()); +} + +fn unsupported(a: char, b: char) { + // TODO:: these are rare, and might not be worth supporting + a.to_ascii_lowercase() == char::to_ascii_lowercase(&b); + char::to_ascii_lowercase(&a) == b.to_ascii_lowercase(); + char::to_ascii_lowercase(&a) == char::to_ascii_lowercase(&b); +} + +fn char(a: char, b: char) { + a.eq_ignore_ascii_case(&b); + a.to_ascii_lowercase() == *&b.to_ascii_lowercase(); + *&a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.eq_ignore_ascii_case(&'a'); + 'a'.eq_ignore_ascii_case(&b); +} +fn u8(a: u8, b: u8) { + a.eq_ignore_ascii_case(&b); + a.eq_ignore_ascii_case(&b'a'); + b'a'.eq_ignore_ascii_case(&b); +} +fn ref_str(a: &str, b: &str) { + a.eq_ignore_ascii_case(b); + a.to_uppercase().eq_ignore_ascii_case(b); + a.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(b); +} +fn ref_ref_str(a: &&str, b: &&str) { + a.eq_ignore_ascii_case(b); + a.to_uppercase().eq_ignore_ascii_case(b); + a.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(b); +} +fn string(a: String, b: String) { + a.eq_ignore_ascii_case(&b); + a.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(&b); + &a.to_ascii_lowercase() == &b.to_ascii_lowercase(); + &&a.to_ascii_lowercase() == &&b.to_ascii_lowercase(); + a.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(&b); +} +fn ref_string(a: String, b: &String) { + a.eq_ignore_ascii_case(b); + a.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(b); + + b.eq_ignore_ascii_case(&a); + b.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(&a); +} +fn string_ref_str(a: String, b: &str) { + a.eq_ignore_ascii_case(b); + a.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(b); + + b.eq_ignore_ascii_case(&a); + b.eq_ignore_ascii_case("a"); + "a".eq_ignore_ascii_case(&a); +} +fn ref_u8slice(a: &[u8], b: &[u8]) { + a.eq_ignore_ascii_case(b); +} +fn u8vec(a: Vec<u8>, b: Vec<u8>) { + a.eq_ignore_ascii_case(&b); +} +fn ref_u8vec(a: Vec<u8>, b: &Vec<u8>) { + a.eq_ignore_ascii_case(b); + b.eq_ignore_ascii_case(&a); +} +fn ref_osstr(a: &OsStr, b: &OsStr) { + a.eq_ignore_ascii_case(b); +} +fn osstring(a: OsString, b: OsString) { + a.eq_ignore_ascii_case(b); +} +fn ref_osstring(a: OsString, b: &OsString) { + a.eq_ignore_ascii_case(b); + b.eq_ignore_ascii_case(a); +} diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs new file mode 100644 index 00000000000..2a4d84b30ac --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs @@ -0,0 +1,107 @@ +#![allow(clippy::all)] +#![deny(clippy::manual_ignore_case_cmp)] + +use std::ffi::{OsStr, OsString}; + +fn main() {} + +fn variants(a: &str, b: &str) { + if a.to_ascii_lowercase() == b.to_ascii_lowercase() { + return; + } + if a.to_ascii_uppercase() == b.to_ascii_uppercase() { + return; + } + let r = a.to_ascii_lowercase() == b.to_ascii_lowercase(); + let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase(); + r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase(); + // != + if a.to_ascii_lowercase() != b.to_ascii_lowercase() { + return; + } + if a.to_ascii_uppercase() != b.to_ascii_uppercase() { + return; + } + let r = a.to_ascii_lowercase() != b.to_ascii_lowercase(); + let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase(); + r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase(); +} + +fn unsupported(a: char, b: char) { + // TODO:: these are rare, and might not be worth supporting + a.to_ascii_lowercase() == char::to_ascii_lowercase(&b); + char::to_ascii_lowercase(&a) == b.to_ascii_lowercase(); + char::to_ascii_lowercase(&a) == char::to_ascii_lowercase(&b); +} + +fn char(a: char, b: char) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == *&b.to_ascii_lowercase(); + *&a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == 'a'; + 'a' == b.to_ascii_lowercase(); +} +fn u8(a: u8, b: u8) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == b'a'; + b'a' == b.to_ascii_lowercase(); +} +fn ref_str(a: &str, b: &str) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == "a"; + "a" == b.to_ascii_lowercase(); +} +fn ref_ref_str(a: &&str, b: &&str) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == "a"; + "a" == b.to_ascii_lowercase(); +} +fn string(a: String, b: String) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == "a"; + "a" == b.to_ascii_lowercase(); + &a.to_ascii_lowercase() == &b.to_ascii_lowercase(); + &&a.to_ascii_lowercase() == &&b.to_ascii_lowercase(); + a.to_ascii_lowercase() == "a"; + "a" == b.to_ascii_lowercase(); +} +fn ref_string(a: String, b: &String) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == "a"; + "a" == b.to_ascii_lowercase(); + + b.to_ascii_lowercase() == a.to_ascii_lowercase(); + b.to_ascii_lowercase() == "a"; + "a" == a.to_ascii_lowercase(); +} +fn string_ref_str(a: String, b: &str) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + a.to_ascii_lowercase() == "a"; + "a" == b.to_ascii_lowercase(); + + b.to_ascii_lowercase() == a.to_ascii_lowercase(); + b.to_ascii_lowercase() == "a"; + "a" == a.to_ascii_lowercase(); +} +fn ref_u8slice(a: &[u8], b: &[u8]) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); +} +fn u8vec(a: Vec<u8>, b: Vec<u8>) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); +} +fn ref_u8vec(a: Vec<u8>, b: &Vec<u8>) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + b.to_ascii_lowercase() == a.to_ascii_lowercase(); +} +fn ref_osstr(a: &OsStr, b: &OsStr) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); +} +fn osstring(a: OsString, b: OsString) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); +} +fn ref_osstring(a: OsString, b: &OsString) { + a.to_ascii_lowercase() == b.to_ascii_lowercase(); + b.to_ascii_lowercase() == a.to_ascii_lowercase(); +} diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr new file mode 100644 index 00000000000..11e8b8aebb5 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr @@ -0,0 +1,546 @@ +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:9:8 + | +LL | if a.to_ascii_lowercase() == b.to_ascii_lowercase() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/manual_ignore_case_cmp.rs:2:9 + | +LL | #![deny(clippy::manual_ignore_case_cmp)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | if a.eq_ignore_ascii_case(b) { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:12:8 + | +LL | if a.to_ascii_uppercase() == b.to_ascii_uppercase() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | if a.eq_ignore_ascii_case(b) { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:15:13 + | +LL | let r = a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | let r = a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:16:18 + | +LL | let r = r || a.to_ascii_uppercase() == b.to_ascii_uppercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | let r = r || a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:17:10 + | +LL | r && a.to_ascii_lowercase() == b.to_uppercase().to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | r && a.eq_ignore_ascii_case(&b.to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:19:8 + | +LL | if a.to_ascii_lowercase() != b.to_ascii_lowercase() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | if !a.eq_ignore_ascii_case(b) { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:22:8 + | +LL | if a.to_ascii_uppercase() != b.to_ascii_uppercase() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | if !a.eq_ignore_ascii_case(b) { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:25:13 + | +LL | let r = a.to_ascii_lowercase() != b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | let r = !a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:26:18 + | +LL | let r = r || a.to_ascii_uppercase() != b.to_ascii_uppercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | let r = r || !a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:27:10 + | +LL | r && a.to_ascii_lowercase() != b.to_uppercase().to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | r && !a.eq_ignore_ascii_case(&b.to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:38:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:41:5 + | +LL | a.to_ascii_lowercase() == 'a'; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(&'a'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:42:5 + | +LL | 'a' == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | 'a'.eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:45:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:46:5 + | +LL | a.to_ascii_lowercase() == b'a'; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(&b'a'); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:47:5 + | +LL | b'a' == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b'a'.eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:50:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:51:5 + | +LL | a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.to_uppercase().eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:52:5 + | +LL | a.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:53:5 + | +LL | "a" == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:56:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:57:5 + | +LL | a.to_uppercase().to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.to_uppercase().eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:58:5 + | +LL | a.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:59:5 + | +LL | "a" == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:62:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:63:5 + | +LL | a.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:64:5 + | +LL | "a" == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:67:5 + | +LL | a.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:68:5 + | +LL | "a" == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:71:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:72:5 + | +LL | a.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:73:5 + | +LL | "a" == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:75:5 + | +LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b.eq_ignore_ascii_case(&a); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:76:5 + | +LL | b.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:77:5 + | +LL | "a" == a.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(&a); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:80:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:81:5 + | +LL | a.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:82:5 + | +LL | "a" == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:84:5 + | +LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b.eq_ignore_ascii_case(&a); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:85:5 + | +LL | b.to_ascii_lowercase() == "a"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b.eq_ignore_ascii_case("a"); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:86:5 + | +LL | "a" == a.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | "a".eq_ignore_ascii_case(&a); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:89:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:92:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(&b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:95:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:96:5 + | +LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b.eq_ignore_ascii_case(&a); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:99:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:102:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:105:5 + | +LL | a.to_ascii_lowercase() == b.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | a.eq_ignore_ascii_case(b); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:106:5 + | +LL | b.to_ascii_lowercase() == a.to_ascii_lowercase(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL | b.eq_ignore_ascii_case(a); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 49 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed index 62b372f4b8d..0603b30e346 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.fixed @@ -10,11 +10,15 @@ use proc_macros::external; fn main() { let v_i32 = Vec::<i32>::new(); let s_i32 = v_i32.as_slice(); + let s_i32_ref = &s_i32; + let s_i32_ref_ref = &s_i32_ref; // True positives: let _ = std::mem::size_of_val(s_i32); // WARNING let _ = std::mem::size_of_val(s_i32); // WARNING let _ = std::mem::size_of_val(s_i32) * 5; // WARNING + let _ = std::mem::size_of_val(*s_i32_ref); // WARNING + let _ = std::mem::size_of_val(**s_i32_ref_ref); // WARNING let len = s_i32.len(); let size = size_of::<i32>(); diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs index d59f5fd8b94..14093e653c0 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.rs @@ -10,11 +10,15 @@ use proc_macros::external; fn main() { let v_i32 = Vec::<i32>::new(); let s_i32 = v_i32.as_slice(); + let s_i32_ref = &s_i32; + let s_i32_ref_ref = &s_i32_ref; // True positives: let _ = s_i32.len() * size_of::<i32>(); // WARNING let _ = size_of::<i32>() * s_i32.len(); // WARNING let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING + let _ = size_of::<i32>() * s_i32_ref.len(); // WARNING + let _ = size_of::<i32>() * s_i32_ref_ref.len(); // WARNING let len = s_i32.len(); let size = size_of::<i32>(); diff --git a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr index 4bd8a4fdf17..0397f3a4969 100644 --- a/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_size_calculation.stderr @@ -1,5 +1,5 @@ error: manual slice size calculation - --> tests/ui/manual_slice_size_calculation.rs:15:13 + --> tests/ui/manual_slice_size_calculation.rs:17:13 | LL | let _ = s_i32.len() * size_of::<i32>(); // WARNING | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` @@ -8,40 +8,52 @@ LL | let _ = s_i32.len() * size_of::<i32>(); // WARNING = help: to override `-D warnings` add `#[allow(clippy::manual_slice_size_calculation)]` error: manual slice size calculation - --> tests/ui/manual_slice_size_calculation.rs:16:13 + --> tests/ui/manual_slice_size_calculation.rs:18:13 | LL | let _ = size_of::<i32>() * s_i32.len(); // WARNING | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` error: manual slice size calculation - --> tests/ui/manual_slice_size_calculation.rs:17:13 + --> tests/ui/manual_slice_size_calculation.rs:19:13 | LL | let _ = size_of::<i32>() * s_i32.len() * 5; // WARNING | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:20:13 + | +LL | let _ = size_of::<i32>() * s_i32_ref.len(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(*s_i32_ref)` + +error: manual slice size calculation --> tests/ui/manual_slice_size_calculation.rs:21:13 | +LL | let _ = size_of::<i32>() * s_i32_ref_ref.len(); // WARNING + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(**s_i32_ref_ref)` + +error: manual slice size calculation + --> tests/ui/manual_slice_size_calculation.rs:25:13 + | LL | let _ = len * size_of::<i32>(); // WARNING | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` error: manual slice size calculation - --> tests/ui/manual_slice_size_calculation.rs:22:13 + --> tests/ui/manual_slice_size_calculation.rs:26:13 | LL | let _ = s_i32.len() * size; // WARNING | ^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` error: manual slice size calculation - --> tests/ui/manual_slice_size_calculation.rs:23:13 + --> tests/ui/manual_slice_size_calculation.rs:27:13 | LL | let _ = len * size; // WARNING | ^^^^^^^^^^ help: try: `std::mem::size_of_val(s_i32)` error: manual slice size calculation - --> tests/ui/manual_slice_size_calculation.rs:25:13 + --> tests/ui/manual_slice_size_calculation.rs:29:13 | LL | let _ = external!(&[1u64][..]).len() * size_of::<u64>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::mem::size_of_val(external!(&[1u64][..]))` -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs index b75ef87ab36..71d8ac7a1f0 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.rs +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -3,7 +3,7 @@ #![warn(clippy::module_name_repetitions)] #![allow(dead_code)] -mod foo { +pub mod foo { pub fn foo() {} pub fn foo_bar() {} //~^ ERROR: item name starts with its containing module's name @@ -20,6 +20,22 @@ mod foo { // Should not warn pub struct Foobar; + // #8524 - shouldn't warn when item is declared in a private module... + mod error { + pub struct Error; + pub struct FooError; + } + pub use error::Error; + // ... but should still warn when the item is reexported to create a *public* path with repetition. + pub use error::FooError; + //~^ ERROR: item name starts with its containing module's name + + // FIXME: This should also warn because it creates the public path `foo::FooIter`. + mod iter { + pub struct FooIter; + } + pub use iter::*; + // #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name. pub fn to_foo() {} pub fn into_foo() {} diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr index bffb08f6f87..8fd8b394875 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.stderr +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -31,5 +31,11 @@ error: item name starts with its containing module's name LL | pub struct Foo7Bar; | ^^^^^^^ -error: aborting due to 5 previous errors +error: item name starts with its containing module's name + --> tests/ui/module_name_repetitions.rs:30:20 + | +LL | pub use error::FooError; + | ^^^^^^^^ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/mut_key.stderr b/src/tools/clippy/tests/ui/mut_key.stderr index 5ad9aad2d0a..8698ed4fd67 100644 --- a/src/tools/clippy/tests/ui/mut_key.stderr +++ b/src/tools/clippy/tests/ui/mut_key.stderr @@ -4,6 +4,9 @@ error: mutable key type LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> { | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: ... because it contains `Key`, which has interior mutability + = note: ... because it contains `AtomicUsize`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability = note: `-D clippy::mutable-key-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutable_key_type)]` @@ -12,84 +15,141 @@ error: mutable key type | LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> { | ^^^^^^^^^^^^ + | + = note: ... because it contains `Key`, which has interior mutability + = note: ... because it contains `AtomicUsize`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:35:5 | LL | let _other: HashMap<Key, bool> = HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Key`, which has interior mutability + = note: ... because it contains `AtomicUsize`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:63:22 | LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `(Key, U)`, which has interior mutability + = note: ... because it contains `Key`, which has interior mutability + = note: ... because it contains `AtomicUsize`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:76:5 | LL | let _map = HashMap::<Cell<usize>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:78:5 | LL | let _map = HashMap::<&mut Cell<usize>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `&mut Cell<usize>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:81:5 | LL | let _map = HashMap::<Vec<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Vec<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:83:5 | LL | let _map = HashMap::<BTreeMap<Cell<usize>, ()>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `BTreeMap<Cell<usize>, ()>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:85:5 | LL | let _map = HashMap::<BTreeMap<(), Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `BTreeMap<(), Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:87:5 | LL | let _map = HashMap::<BTreeSet<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `BTreeSet<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:89:5 | LL | let _map = HashMap::<Option<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Option<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:91:5 | LL | let _map = HashMap::<Option<Vec<Cell<usize>>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Option<Vec<Cell<usize>>>`, which has interior mutability + = note: ... because it contains `Vec<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:94:5 | LL | let _map = HashMap::<Box<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Box<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:96:5 | LL | let _map = HashMap::<Rc<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Rc<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: mutable key type --> tests/ui/mut_key.rs:98:5 | LL | let _map = HashMap::<Arc<Cell<usize>>, usize>::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ... because it contains `Arc<Cell<usize>>`, which has interior mutability + = note: ... because it contains `Cell<usize>`, which has interior mutability + = note: ... because it contains `UnsafeCell<usize>`, which has interior mutability error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_raw_string.fixed b/src/tools/clippy/tests/ui/needless_raw_string.fixed index 1a9c601c462..ab061467488 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.fixed +++ b/src/tools/clippy/tests/ui/needless_raw_string.fixed @@ -22,3 +22,12 @@ fn main() { b"no hashes"; c"no hashes"; } + +fn issue_13503() { + println!("SELECT * FROM posts"); + println!("SELECT * FROM posts"); + println!(r##"SELECT * FROM "posts""##); + + // Test arguments as well + println!("{}", "foobar".len()); +} diff --git a/src/tools/clippy/tests/ui/needless_raw_string.rs b/src/tools/clippy/tests/ui/needless_raw_string.rs index 1126ea5aa30..5be8bdeb4ad 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.rs +++ b/src/tools/clippy/tests/ui/needless_raw_string.rs @@ -22,3 +22,12 @@ fn main() { br"no hashes"; cr"no hashes"; } + +fn issue_13503() { + println!(r"SELECT * FROM posts"); + println!(r#"SELECT * FROM posts"#); + println!(r##"SELECT * FROM "posts""##); + + // Test arguments as well + println!("{}", r"foobar".len()); +} diff --git a/src/tools/clippy/tests/ui/needless_raw_string.stderr b/src/tools/clippy/tests/ui/needless_raw_string.stderr index 7d3451a03c7..5169f085573 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string.stderr +++ b/src/tools/clippy/tests/ui/needless_raw_string.stderr @@ -91,5 +91,41 @@ LL - cr"no hashes"; LL + c"no hashes"; | -error: aborting due to 7 previous errors +error: unnecessary raw string literal + --> tests/ui/needless_raw_string.rs:27:14 + | +LL | println!(r"SELECT * FROM posts"); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a plain string literal instead + | +LL - println!(r"SELECT * FROM posts"); +LL + println!("SELECT * FROM posts"); + | + +error: unnecessary raw string literal + --> tests/ui/needless_raw_string.rs:28:14 + | +LL | println!(r#"SELECT * FROM posts"#); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a plain string literal instead + | +LL - println!(r#"SELECT * FROM posts"#); +LL + println!("SELECT * FROM posts"); + | + +error: unnecessary raw string literal + --> tests/ui/needless_raw_string.rs:32:20 + | +LL | println!("{}", r"foobar".len()); + | ^^^^^^^^^ + | +help: use a plain string literal instead + | +LL - println!("{}", r"foobar".len()); +LL + println!("{}", "foobar".len()); + | + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed index b2ad657d6b2..4c113709107 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed +++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.fixed @@ -24,3 +24,13 @@ fn main() { r"rust"; r"hello world"; } + +fn issue_13503() { + println!(r"SELECT * FROM posts"); + println!(r"SELECT * FROM posts"); + println!(r#"SELECT * FROM "posts""#); + println!(r#"SELECT * FROM "posts""#); + + // Test arguments as well + println!("{}", r"foobar".len()); +} diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs index 54d8ed76d47..7b6b4e784ee 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs +++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.rs @@ -24,3 +24,13 @@ fn main() { r###"rust"###; r#"hello world"#; } + +fn issue_13503() { + println!(r"SELECT * FROM posts"); + println!(r#"SELECT * FROM posts"#); + println!(r##"SELECT * FROM "posts""##); + println!(r##"SELECT * FROM "posts""##); + + // Test arguments as well + println!("{}", r"foobar".len()); +} diff --git a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr index 96864f612c0..a213ba3e743 100644 --- a/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr +++ b/src/tools/clippy/tests/ui/needless_raw_string_hashes.stderr @@ -187,5 +187,41 @@ LL - r#"hello world"#; LL + r"hello world"; | -error: aborting due to 15 previous errors +error: unnecessary hashes around raw string literal + --> tests/ui/needless_raw_string_hashes.rs:30:14 + | +LL | println!(r#"SELECT * FROM posts"#); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove all the hashes around the string literal + | +LL - println!(r#"SELECT * FROM posts"#); +LL + println!(r"SELECT * FROM posts"); + | + +error: unnecessary hashes around raw string literal + --> tests/ui/needless_raw_string_hashes.rs:31:14 + | +LL | println!(r##"SELECT * FROM "posts""##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the string literal + | +LL - println!(r##"SELECT * FROM "posts""##); +LL + println!(r#"SELECT * FROM "posts""#); + | + +error: unnecessary hashes around raw string literal + --> tests/ui/needless_raw_string_hashes.rs:32:14 + | +LL | println!(r##"SELECT * FROM "posts""##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the string literal + | +LL - println!(r##"SELECT * FROM "posts""##); +LL + println!(r#"SELECT * FROM "posts""#); + | + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 7452eb77688..625d654dd39 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -341,18 +341,18 @@ fn fn_call_in_nested_expr() { } let opt: Option<i32> = Some(1); - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt.unwrap_or_else(f); // suggest `.unwrap_or_else(f)` // - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt.unwrap_or_else(|| f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` // - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt.unwrap_or_else(|| { let x = f(); x + 1 }); - //~v ERROR: use of `map_or` followed by a function call + //~v ERROR: function call inside of `map_or` let _ = opt.map_or_else(|| f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` // //~v ERROR: use of `unwrap_or` to construct default value @@ -361,7 +361,7 @@ fn fn_call_in_nested_expr() { let opt_foo = Some(Foo { val: String::from("123"), }); - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt_foo.unwrap_or_else(|| Foo { val: String::default() }); } diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index cd6f7bb2070..5b7d8faec7b 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -341,18 +341,18 @@ fn fn_call_in_nested_expr() { } let opt: Option<i32> = Some(1); - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` // - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` // - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt.unwrap_or({ let x = f(); x + 1 }); - //~v ERROR: use of `map_or` followed by a function call + //~v ERROR: function call inside of `map_or` let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` // //~v ERROR: use of `unwrap_or` to construct default value @@ -361,7 +361,7 @@ fn fn_call_in_nested_expr() { let opt_foo = Some(Foo { val: String::from("123"), }); - //~v ERROR: use of `unwrap_or` followed by a function call + //~v ERROR: function call inside of `unwrap_or` let _ = opt_foo.unwrap_or(Foo { val: String::default() }); } diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 06f804fb41e..9f90a830a21 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -1,4 +1,4 @@ -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:52:22 | LL | with_constructor.unwrap_or(make()); @@ -16,19 +16,19 @@ LL | with_new.unwrap_or(Vec::new()); = note: `-D clippy::unwrap-or-default` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:58:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:61:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:64:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); @@ -46,7 +46,7 @@ error: use of `unwrap_or` to construct default value LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:73:18 | LL | self_default.unwrap_or(<FakeDefault>::default()); @@ -64,7 +64,7 @@ error: use of `unwrap_or` to construct default value LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:82:21 | LL | without_default.unwrap_or(Foo::new()); @@ -100,55 +100,55 @@ error: use of `unwrap_or` to construct default value LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `ok_or` followed by a function call +error: function call inside of `ok_or` --> tests/ui/or_fun_call.rs:101:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:105:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:107:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` -error: use of `or` followed by a function call +error: function call inside of `or` --> tests/ui/or_fun_call.rs:131:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:170:14 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:176:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:178:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` -error: use of `map_or` followed by a function call +error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:253:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` -error: use of `map_or` followed by a function call +error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:254:25 | LL | let _ = Some(4).map_or(g(), f); @@ -196,19 +196,19 @@ error: use of `unwrap_or_else` to construct default value LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:345:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:348:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:351:17 | LL | let _ = opt.unwrap_or({ @@ -226,7 +226,7 @@ LL + x + 1 LL ~ }); | -error: use of `map_or` followed by a function call +error: function call inside of `map_or` --> tests/ui/or_fun_call.rs:356:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` @@ -238,7 +238,7 @@ error: use of `unwrap_or` to construct default value LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/or_fun_call.rs:365:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index 4fb6c08bb44..f607a2d50c6 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -5,7 +5,7 @@ clippy::needless_borrow, clippy::needless_borrows_for_generic_args )] -#![warn(clippy::invalid_regex, clippy::trivial_regex)] +#![warn(clippy::invalid_regex, clippy::trivial_regex, clippy::regex_creation_in_loops)] extern crate regex; @@ -118,7 +118,35 @@ fn trivial_regex() { let _ = BRegex::new(r"\b{start}word\b{end}"); } +fn regex_creation_in_loops() { + loop { + static STATIC_REGEX: std::sync::LazyLock<Regex> = std::sync::LazyLock::new(|| Regex::new("a.b").unwrap()); + + let regex = Regex::new("a.b"); + //~^ ERROR: compiling a regex in a loop + let regex = BRegex::new("a.b"); + //~^ ERROR: compiling a regex in a loop + #[allow(clippy::regex_creation_in_loops)] + let allowed_regex = Regex::new("a.b"); + + if true { + let regex = Regex::new("a.b"); + //~^ ERROR: compiling a regex in a loop + } + + for _ in 0..10 { + let nested_regex = Regex::new("a.b"); + //~^ ERROR: compiling a regex in a loop + } + } + + for i in 0..10 { + let dependant_regex = Regex::new(&format!("{i}")); + } +} + fn main() { syntax_error(); trivial_regex(); + regex_creation_in_loops(); } diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr index e936208d8d7..18dd538c68b 100644 --- a/src/tools/clippy/tests/ui/regex.stderr +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -195,5 +195,55 @@ LL | let binary_trivial_empty = BRegex::new("^$"); | = help: consider using `str::is_empty` -error: aborting due to 24 previous errors +error: compiling a regex in a loop + --> tests/ui/regex.rs:125:21 + | +LL | let regex = Regex::new("a.b"); + | ^^^^^^^^^^ + | +help: move the regex construction outside this loop + --> tests/ui/regex.rs:122:5 + | +LL | loop { + | ^^^^ + = note: `-D clippy::regex-creation-in-loops` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::regex_creation_in_loops)]` + +error: compiling a regex in a loop + --> tests/ui/regex.rs:127:21 + | +LL | let regex = BRegex::new("a.b"); + | ^^^^^^^^^^^ + | +help: move the regex construction outside this loop + --> tests/ui/regex.rs:122:5 + | +LL | loop { + | ^^^^ + +error: compiling a regex in a loop + --> tests/ui/regex.rs:133:25 + | +LL | let regex = Regex::new("a.b"); + | ^^^^^^^^^^ + | +help: move the regex construction outside this loop + --> tests/ui/regex.rs:122:5 + | +LL | loop { + | ^^^^ + +error: compiling a regex in a loop + --> tests/ui/regex.rs:138:32 + | +LL | let nested_regex = Regex::new("a.b"); + | ^^^^^^^^^^ + | +help: move the regex construction outside this loop + --> tests/ui/regex.rs:137:9 + | +LL | for _ in 0..10 { + | ^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed index 7e2663d734f..779431303ae 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed @@ -1,5 +1,6 @@ #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] +#![feature(const_trait_impl)] use std::any::Any; @@ -144,6 +145,36 @@ fn f<P: Proj>(obj: &dyn Derived<P>) { Base::<()>::is_base(obj); } +// #13476 +trait Value<const N: usize> {} +fn const_generic<T: Value<0> + Value<1>>() {} + +// #11067 and #9626 +fn assoc_tys_generics<'a, 'b, T, U>() +where + T: IntoIterator<Item = ()> + IntoIterator<Item = i32>, + U: From<&'a str> + From<&'b [u16]>, +{ +} + +// #13476 +#[const_trait] +trait ConstTrait {} +const fn const_trait_bounds_good<T: ConstTrait + ~const ConstTrait>() {} + +const fn const_trait_bounds_bad<T: ~const ConstTrait>() {} +//~^ trait_duplication_in_bounds + +fn projections<T, U, V>() +where + U: ToOwned, + V: ToOwned, + T: IntoIterator<Item = U::Owned>, + //~^ trait_duplication_in_bounds + V: IntoIterator<Item = U::Owned> + IntoIterator<Item = V::Owned>, +{ +} + fn main() { let _x: fn(_) = f::<()>; let _x: fn(_) = f::<i32>; diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs index fede1671a43..3e974dc0a8f 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -1,5 +1,6 @@ #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] +#![feature(const_trait_impl)] use std::any::Any; @@ -144,6 +145,36 @@ fn f<P: Proj>(obj: &dyn Derived<P>) { Base::<()>::is_base(obj); } +// #13476 +trait Value<const N: usize> {} +fn const_generic<T: Value<0> + Value<1>>() {} + +// #11067 and #9626 +fn assoc_tys_generics<'a, 'b, T, U>() +where + T: IntoIterator<Item = ()> + IntoIterator<Item = i32>, + U: From<&'a str> + From<&'b [u16]>, +{ +} + +// #13476 +#[const_trait] +trait ConstTrait {} +const fn const_trait_bounds_good<T: ConstTrait + ~const ConstTrait>() {} + +const fn const_trait_bounds_bad<T: ~const ConstTrait + ~const ConstTrait>() {} +//~^ trait_duplication_in_bounds + +fn projections<T, U, V>() +where + U: ToOwned, + V: ToOwned, + T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>, + //~^ trait_duplication_in_bounds + V: IntoIterator<Item = U::Owned> + IntoIterator<Item = V::Owned>, +{ +} + fn main() { let _x: fn(_) = f::<()>; let _x: fn(_) = f::<i32>; diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr index 78861fc16e8..0dd508e4745 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -1,5 +1,5 @@ error: these bounds contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:6:15 + --> tests/ui/trait_duplication_in_bounds.rs:7:15 | LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` @@ -11,52 +11,64 @@ LL | #![deny(clippy::trait_duplication_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: these where clauses contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:12:8 + --> tests/ui/trait_duplication_in_bounds.rs:13:8 | LL | T: Clone + Clone + Clone + Copy, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:40:26 + --> tests/ui/trait_duplication_in_bounds.rs:41:26 | LL | trait BadSelfTraitBound: Clone + Clone + Clone { | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these where clauses contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:47:15 + --> tests/ui/trait_duplication_in_bounds.rs:48:15 | LL | Self: Clone + Clone + Clone; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these bounds contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:61:24 + --> tests/ui/trait_duplication_in_bounds.rs:62:24 | LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these where clauses contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:68:12 + --> tests/ui/trait_duplication_in_bounds.rs:69:12 | LL | T: Clone + Clone + Clone + Copy, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:101:19 + --> tests/ui/trait_duplication_in_bounds.rs:102:19 | LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>` error: these bounds contain repeated elements - --> tests/ui/trait_duplication_in_bounds.rs:109:22 + --> tests/ui/trait_duplication_in_bounds.rs:110:22 | LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone` error: this trait bound is already specified in trait declaration - --> tests/ui/trait_duplication_in_bounds.rs:117:33 + --> tests/ui/trait_duplication_in_bounds.rs:118:33 | LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) { | ^^^^^^^^^^^^^^^^^ help: try: `Any + Send` -error: aborting due to 9 previous errors +error: these bounds contain repeated elements + --> tests/ui/trait_duplication_in_bounds.rs:165:36 + | +LL | const fn const_trait_bounds_bad<T: ~const ConstTrait + ~const ConstTrait>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `~const ConstTrait` + +error: these where clauses contain repeated elements + --> tests/ui/trait_duplication_in_bounds.rs:172:8 + | +LL | T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `IntoIterator<Item = U::Owned>` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 617d32d1fa7..a4a3ca82e76 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -84,7 +84,10 @@ fn issue_10449() { } // Pointers cannot be cast to integers in const contexts -#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")] +#[allow( + ptr_to_integer_transmute_in_consts, + reason = "This is tested in the compiler test suite" +)] const fn issue_12402<P>(ptr: *const P) { // This test exists even though the compiler lints against it // to test that clippy's transmute lints do not trigger on this. diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs index d68db3c2deb..6aa8e384e26 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -84,7 +84,10 @@ fn issue_10449() { } // Pointers cannot be cast to integers in const contexts -#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")] +#[allow( + ptr_to_integer_transmute_in_consts, + reason = "This is tested in the compiler test suite" +)] const fn issue_12402<P>(ptr: *const P) { // This test exists even though the compiler lints against it // to test that clippy's transmute lints do not trigger on this. diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed b/src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed new file mode 100644 index 00000000000..107e397466d --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_literal_bound.fixed @@ -0,0 +1,65 @@ +#![warn(clippy::unnecessary_literal_bound)] + +struct Struct<'a> { + not_literal: &'a str, +} + +impl Struct<'_> { + // Should warn + fn returns_lit(&self) -> &'static str { + "Hello" + } + + // Should NOT warn + fn returns_non_lit(&self) -> &str { + self.not_literal + } + + // Should warn, does not currently + fn conditionally_returns_lit(&self, cond: bool) -> &str { + if cond { "Literal" } else { "also a literal" } + } + + // Should NOT warn + fn conditionally_returns_non_lit(&self, cond: bool) -> &str { + if cond { "Literal" } else { self.not_literal } + } + + // Should warn + fn contionally_returns_literals_explicit(&self, cond: bool) -> &'static str { + if cond { + return "Literal"; + } + + "also a literal" + } + + // Should NOT warn + fn conditionally_returns_non_lit_explicit(&self, cond: bool) -> &str { + if cond { + return self.not_literal; + } + + "Literal" + } +} + +trait ReturnsStr { + fn trait_method(&self) -> &str; +} + +impl ReturnsStr for u8 { + // Should warn, even though not useful without trait refinement + fn trait_method(&self) -> &'static str { + "Literal" + } +} + +impl ReturnsStr for Struct<'_> { + // Should NOT warn + fn trait_method(&self) -> &str { + self.not_literal + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_bound.rs b/src/tools/clippy/tests/ui/unnecessary_literal_bound.rs new file mode 100644 index 00000000000..b371ff9d3a2 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_literal_bound.rs @@ -0,0 +1,65 @@ +#![warn(clippy::unnecessary_literal_bound)] + +struct Struct<'a> { + not_literal: &'a str, +} + +impl Struct<'_> { + // Should warn + fn returns_lit(&self) -> &str { + "Hello" + } + + // Should NOT warn + fn returns_non_lit(&self) -> &str { + self.not_literal + } + + // Should warn, does not currently + fn conditionally_returns_lit(&self, cond: bool) -> &str { + if cond { "Literal" } else { "also a literal" } + } + + // Should NOT warn + fn conditionally_returns_non_lit(&self, cond: bool) -> &str { + if cond { "Literal" } else { self.not_literal } + } + + // Should warn + fn contionally_returns_literals_explicit(&self, cond: bool) -> &str { + if cond { + return "Literal"; + } + + "also a literal" + } + + // Should NOT warn + fn conditionally_returns_non_lit_explicit(&self, cond: bool) -> &str { + if cond { + return self.not_literal; + } + + "Literal" + } +} + +trait ReturnsStr { + fn trait_method(&self) -> &str; +} + +impl ReturnsStr for u8 { + // Should warn, even though not useful without trait refinement + fn trait_method(&self) -> &str { + "Literal" + } +} + +impl ReturnsStr for Struct<'_> { + // Should NOT warn + fn trait_method(&self) -> &str { + self.not_literal + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr new file mode 100644 index 00000000000..512b2f9a0af --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_literal_bound.stderr @@ -0,0 +1,23 @@ +error: returning a `str` unnecessarily tied to the lifetime of arguments + --> tests/ui/unnecessary_literal_bound.rs:9:30 + | +LL | fn returns_lit(&self) -> &str { + | ^^^^ help: try: `&'static str` + | + = note: `-D clippy::unnecessary-literal-bound` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_literal_bound)]` + +error: returning a `str` unnecessarily tied to the lifetime of arguments + --> tests/ui/unnecessary_literal_bound.rs:29:68 + | +LL | fn contionally_returns_literals_explicit(&self, cond: bool) -> &str { + | ^^^^ help: try: `&'static str` + +error: returning a `str` unnecessarily tied to the lifetime of arguments + --> tests/ui/unnecessary_literal_bound.rs:53:31 + | +LL | fn trait_method(&self) -> &str { + | ^^^^ help: try: `&'static str` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap_or.fixed b/src/tools/clippy/tests/ui/unwrap_or.fixed index e1a47fc7bd9..62bc1966da6 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/unwrap_or.fixed @@ -3,11 +3,11 @@ fn main() { let s = Some(String::from("test string")).unwrap_or_else(|| "Fail".to_string()).len(); - //~^ ERROR: use of `unwrap_or` followed by a function call + //~^ ERROR: function call inside of `unwrap_or` //~| NOTE: `-D clippy::or-fun-call` implied by `-D warnings` } fn new_lines() { let s = Some(String::from("test string")).unwrap_or_else(|| "Fail".to_string()).len(); - //~^ ERROR: use of `unwrap_or` followed by a function call + //~^ ERROR: function call inside of `unwrap_or` } diff --git a/src/tools/clippy/tests/ui/unwrap_or.rs b/src/tools/clippy/tests/ui/unwrap_or.rs index 914bfb939b8..e8e4b6b7168 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.rs +++ b/src/tools/clippy/tests/ui/unwrap_or.rs @@ -3,11 +3,11 @@ fn main() { let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); - //~^ ERROR: use of `unwrap_or` followed by a function call + //~^ ERROR: function call inside of `unwrap_or` //~| NOTE: `-D clippy::or-fun-call` implied by `-D warnings` } fn new_lines() { let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); - //~^ ERROR: use of `unwrap_or` followed by a function call + //~^ ERROR: function call inside of `unwrap_or` } diff --git a/src/tools/clippy/tests/ui/unwrap_or.stderr b/src/tools/clippy/tests/ui/unwrap_or.stderr index 6aa0b9df29b..b712f8cf693 100644 --- a/src/tools/clippy/tests/ui/unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/unwrap_or.stderr @@ -1,4 +1,4 @@ -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/unwrap_or.rs:5:47 | LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); @@ -7,7 +7,7 @@ LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()) = note: `-D clippy::or-fun-call` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]` -error: use of `unwrap_or` followed by a function call +error: function call inside of `unwrap_or` --> tests/ui/unwrap_or.rs:11:47 | LL | let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len(); diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index 68328333937..e29898f068d 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -24,7 +24,6 @@ fn consistent_clippy_crate_versions() { let clippy_version = read_version("Cargo.toml"); let paths = [ - "declare_clippy_lint/Cargo.toml", "clippy_config/Cargo.toml", "clippy_lints/Cargo.toml", "clippy_utils/Cargo.toml", diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index dcf00e4e384..cd9641eedd8 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -35,6 +35,7 @@ users_on_vacation = [ "@Alexendoo", "@dswij", "@Jarcho", + "@blyxyas", "@y21", "@Centri3", ] diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html deleted file mode 100644 index f3d7e504fdf..00000000000 --- a/src/tools/clippy/util/gh-pages/index.html +++ /dev/null @@ -1,330 +0,0 @@ -<!DOCTYPE html> -<!-- -Welcome to a Clippy's lint list, at least the source code of it. If you are -interested in contributing to this website checkout `util/gh-pages/index.html` -inside the rust-clippy repository. - -Otherwise, have a great day =^.^= ---> -<html lang="en"> -<head> - <meta charset="UTF-8"/> - <meta name="viewport" content="width=device-width, initial-scale=1"/> - <meta name="description" content="A collection of lints to catch common mistakes and improve your Rust code."> - - <title>Clippy Lints</title> - - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/> - <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" /> - <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" /> - - <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License --> - <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/> - <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css"> - <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true"> - <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true"> - <link rel="stylesheet" href="style.css"> -</head> -<body ng-app="clippy" ng-controller="lintList"> - <div id="settings-dropdown"> - <div class="settings-icon" tabindex="-1"></div> - <div class="settings-menu" tabindex="-1"> - <div class="setting-radio-name">Theme</div> - <select id="theme-choice" onchange="setTheme(this.value, true)"> - <option value="ayu">Ayu</option> - <option value="coal">Coal</option> - <option value="light">Light</option> - <option value="navy">Navy</option> - <option value="rust">Rust</option> - </select> - <label> - <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> - <span>Disable keyboard shortcuts</span> - </label> - </div> - </div> - - <div class="container"> - <div class="page-header"> - <h1>Clippy Lints</h1> - </div> - - <noscript> - <div class="alert alert-danger" role="alert"> - Sorry, this site only works with JavaScript! :( - </div> - </noscript> - - <div ng-cloak> - - <div class="alert alert-info" role="alert" ng-if="loading"> - Loading… - </div> - <div class="alert alert-danger" role="alert" ng-if="error"> - Error loading lints! - </div> - - <div class="panel panel-default" ng-show="data"> - <div class="panel-body row"> - <div id="upper-filters" class="col-12 col-md-5"> - <div class="btn-group" filter-dropdown> - <button type="button" class="btn btn-default dropdown-toggle"> - Lint levels <span class="badge">{{selectedValuesCount(levels)}}</span> <span class="caret"></span> - </button> - <ul class="dropdown-menu"> - <li class="checkbox"> - <label ng-click="toggleLevels(true)"> - <input type="checkbox" class="invisible" /> - All - </label> - </li> - <li class="checkbox"> - <label ng-click="toggleLevels(false)"> - <input type="checkbox" class="invisible" /> - None - </label> - </li> - <li role="separator" class="divider"></li> - <li class="checkbox" ng-repeat="(level, enabled) in levels"> - <label class="text-capitalize"> - <input type="checkbox" ng-model="levels[level]" /> - {{level}} - </label> - </li> - </ul> - </div> - <div class="btn-group" filter-dropdown> - <button type="button" class="btn btn-default dropdown-toggle"> - Lint groups <span class="badge">{{selectedValuesCount(groups)}}</span> <span class="caret"></span> - </button> - <ul class="dropdown-menu"> - <li class="checkbox"> - <label ng-click="toggleGroups(true)"> - <input type="checkbox" class="invisible" /> - All - </label> - </li> - <li class="checkbox"> - <label ng-click="resetGroupsToDefault()"> - <input type="checkbox" class="invisible" /> - Default - </label> - </li> - <li class="checkbox"> - <label ng-click="toggleGroups(false)"> - <input type="checkbox" class="invisible" /> - None - </label> - </li> - <li role="separator" class="divider"></li> - <li class="checkbox" ng-repeat="(group, enabled) in groups"> - <label class="text-capitalize"> - <input type="checkbox" ng-model="groups[group]" /> - {{group}} - </label> - </li> - </ul> - </div> - <div id="version-filter"> - <div class="btn-group" filter-dropdown> - <button type="button" class="btn btn-default dropdown-toggle"> - Version - <span id="version-filter-count" class="badge"> - {{versionFilterCount(versionFilters)}} - </span> - <span class="caret"></span> - </button> - <ul id="version-filter-selector" class="dropdown-menu"> - <li class="checkbox"> - <label ng-click="clearVersionFilters()"> - <input type="checkbox" class="invisible" /> - Clear filters - </label> - </li> - <li role="separator" class="divider"></li> - <li class="checkbox" ng-repeat="(filter, vars) in versionFilters"> - <label ng-attr-for="filter-{filter}">{{filter}}</label> - <span>1.</span> - <input type="number" - min="29" - ng-attr-id="filter-{filter}" - class="version-filter-input form-control filter-input" - maxlength="2" - ng-model="versionFilters[filter].minorVersion" - ng-model-options="{debounce: 50}" - ng-change="updateVersionFilters()" /> - <span>.0</span> - </li> - </ul> - </div> - </div> - <div class="btn-group" filter-dropdown> - <button type="button" class="btn btn-default dropdown-toggle"> - Applicability <span class="badge">{{selectedValuesCount(applicabilities)}}</span> <span class="caret"></span> - </button> - <ul class="dropdown-menu"> - <li class="checkbox"> - <label ng-click="toggleApplicabilities(true)"> - <input type="checkbox" class="invisible" /> - All - </label> - </li> - <li class="checkbox"> - <label ng-click="toggleApplicabilities(false)"> - <input type="checkbox" class="invisible" /> - None - </label> - </li> - <li role="separator" class="divider"></li> - <li class="checkbox" ng-repeat="(applicability, enabled) in applicabilities"> - <label class="text-capitalize"> - <input type="checkbox" ng-model="applicabilities[applicability]" /> - {{applicability}} - </label> - </li> - </ul> - </div> - </div> - <div class="col-12 col-md-5 search-control"> - <div class="input-group"> - <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label> - <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input" - ng-model="search" ng-blur="updatePath()" ng-keyup="$event.keyCode == 13 && updatePath()" - ng-model-options="{debounce: 50}" /> - <span class="input-group-btn"> - <button class="filter-clear btn" type="button" ng-click="search = ''; updatePath();"> - Clear - </button> - </span> - </div> - </div> - <div class="col-12 col-md-2 btn-group expansion-group"> - <button title="Collapse All" class="btn btn-default expansion-control" type="button" ng-click="toggleExpansion(data, false)"> - <span class="glyphicon glyphicon-collapse-up"></span> - </button> - <button title="Expand All" class="btn btn-default expansion-control" type="button" ng-click="toggleExpansion(data, true)"> - <span class="glyphicon glyphicon-collapse-down"></span> - </button> - </div> - </div> - </div> - <!-- The order of the filters should be from most likely to remove a lint to least likely to improve performance. --> - <article class="panel panel-default" id="{{lint.id}}" ng-repeat="lint in data | filter:bySearch | filter:byGroups | filter:byLevels | filter:byVersion | filter:byApplicabilities"> - <header class="panel-heading" ng-click="open[lint.id] = !open[lint.id]"> - <h2 class="panel-title"> - <div class="panel-title-name"> - <span>{{lint.id}}</span> - <a href="#{{lint.id}}" class="anchor label label-default" - ng-click="openLint(lint); $event.preventDefault(); $event.stopPropagation()">¶</a> - <a href="" id="clipboard-{{lint.id}}" class="anchor label label-default" ng-click="copyToClipboard(lint); $event.stopPropagation()"> - 📋 - </a> - </div> - - <div class="panel-title-addons"> - <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> - - <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> - - - <span class="label label-doc-folding" ng-show="open[lint.id]">−</span> - <span class="label label-doc-folding" ng-hide="open[lint.id]">+</span> - </div> - </h2> - </header> - - <div class="list-group lint-docs" ng-if="open[lint.id]" ng-class="{collapse: true, in: open[lint.id]}"> - <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div> - <div class="lint-additional-info-container"> - <!-- Applicability --> - <div class="lint-additional-info-item"> - <span> Applicability: </span> - <span class="label label-default label-applicability">{{lint.applicability}}</span> - <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> - </div> - <!-- Clippy version --> - <div class="lint-additional-info-item"> - <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span> - <span class="label label-default label-version">{{lint.version}}</span> - </div> - <!-- Open related issues --> - <div class="lint-additional-info-item"> - <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> - </div> - <!-- Jump to source --> - <div class="lint-additional-info-item" ng-if="lint.id_location"> - <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/{{lint.id_location}}">View Source</a> - </div> - </div> - </div> - </article> - </div> - </div> - - <a - aria-label="View source on GitHub" - class="github-corner" - href="https://github.com/rust-lang/rust-clippy" - rel="noopener noreferrer" - target="_blank" - > - <svg - width="80" - height="80" - viewBox="0 0 250 250" - style="position: absolute; top: 0; border: 0; right: 0" - aria-hidden="true" - > - <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="var(--theme-color)"></path> - <path - d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" - fill="currentColor" - style="transform-origin: 130px 106px" - class="octo-arm" - ></path> - <path - d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" - fill="currentColor" - class="octo-body" - ></path> - </svg> - <style> - .github-corner svg { - fill: var(--fg); - color: var(--bg); - } - .github-corner:hover .octo-arm { - animation: octocat-wave 560ms ease-in-out; - } - @keyframes octocat-wave { - 0%, - 100% { - transform: rotate(0); - } - 20%, - 60% { - transform: rotate(-25deg); - } - 40%, - 80% { - transform: rotate(10deg); - } - } - @media (max-width: 500px) { - .github-corner:hover .octo-arm { - animation: none; - } - .github-corner .octo-arm { - animation: octocat-wave 560ms ease-in-out; - } - } - </style> - </a> - - <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script> - <script src="script.js"></script> -</body> -</html> diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html new file mode 100644 index 00000000000..2412f0fd181 --- /dev/null +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -0,0 +1,232 @@ +<!DOCTYPE html> +<!-- +Welcome to a Clippy's lint list, at least the source code of it. If you are +interested in contributing to this website checkout `util/gh-pages/index_template.html` +inside the rust-clippy repository. + +Otherwise, have a great day =^.^= +--> +<html lang="en"> {# #} +<head> {# #} + <meta charset="UTF-8"/> {# #} + <meta name="viewport" content="width=device-width, initial-scale=1"/> {# #} + <meta name="description" content="A collection of lints to catch common mistakes and improve your Rust code."> {# #} + + <title>Clippy Lints</title> {# #} + + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/> {# #} + <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" /> {# #} + <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" /> {# #} + + <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License --> + <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/> {# #} + <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css"> {# #} + <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true"> {# #} + <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true"> {# #} + <link rel="stylesheet" href="style.css"> {# #} +</head> {# #} +<body> {# #} + <script src="theme.js"></script> {# #} + <div id="settings-dropdown"> {# #} + <button class="settings-icon" tabindex="-1"></button> {# #} + <div class="settings-menu" tabindex="-1"> {# #} + <div class="setting-radio-name">Theme</div> {# #} + <select id="theme-choice" onchange="setTheme(this.value, true)"> {# #} + <option value="ayu">Ayu</option> {# #} + <option value="coal">Coal</option> {# #} + <option value="light">Light</option> {# #} + <option value="navy">Navy</option> {# #} + <option value="rust">Rust</option> {# #} + </select> {# #} + <label> {# #} + <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)"> {#+ #} + <span>Disable keyboard shortcuts</span> {# #} + </label> {# #} + </div> {# #} + </div> {# #} + + <div class="container"> {# #} + <div class="page-header"> {# #} + <h1>Clippy Lints</h1> {# #} + </div> {# #} + + <noscript> {# #} + <div class="alert alert-danger" role="alert"> {# #} + Sorry, this site only works with JavaScript! :( {# #} + </div> {# #} + </noscript> {# #} + + <div> {# #} + <div class="panel panel-default"> {# #} + <div class="panel-body row"> {# #} + <div id="upper-filters" class="col-12 col-md-5"> {# #} + <div class="btn-group" id="lint-levels" tabindex="-1"> {# #} + <button type="button" class="btn btn-default dropdown-toggle"> {# #} + Lint levels <span class="badge">4</span> <span class="caret"></span> {# #} + </button> {# #} + <ul class="dropdown-menu" id="lint-levels-selector"> {# #} + <li class="checkbox"> {# #} + <button onclick="toggleElements('levels_filter', true)">All</button> {# #} + </li> {# #} + <li class="checkbox"> {# #} + <button onclick="toggleElements('levels_filter', false)">None</button> {# #} + </li> {# #} + <li role="separator" class="divider"></li> {# #} + </ul> {# #} + </div> {# #} + <div class="btn-group" id="lint-groups" tabindex="-1"> {# #} + <button type="button" class="btn btn-default dropdown-toggle"> {# #} + Lint groups <span class="badge">9</span> <span class="caret"></span> {# #} + </button> {# #} + <ul class="dropdown-menu" id="lint-groups-selector"> {# #} + <li class="checkbox"> {# #} + <button onclick="toggleElements('groups_filter', true)">All</button> {# #} + </li> {# #} + <li class="checkbox"> {# #} + <button onclick="resetGroupsToDefault()">Default</button> {# #} + </li> {# #} + <li class="checkbox"> {# #} + <button onclick="toggleElements('groups_filter', false)">None</button> {# #} + </li> {# #} + <li role="separator" class="divider"></li> {# #} + </ul> {# #} + </div> {# #} + <div class="btn-group" id="version-filter" tabindex="-1"> {# #} + <button type="button" class="btn btn-default dropdown-toggle"> {# #} + Version {#+ #} + <span id="version-filter-count" class="badge">0</span> {#+ #} + <span class="caret"></span> {# #} + </button> {# #} + <ul id="version-filter-selector" class="dropdown-menu"> {# #} + <li class="checkbox"> {# #} + <button onclick="clearVersionFilters()">Clear filters</button> {# #} + </li> {# #} + <li role="separator" class="divider"></li> {# #} + </ul> {# #} + </div> {# #} + <div class="btn-group", id="lint-applicabilities" tabindex="-1"> {# #} + <button type="button" class="btn btn-default dropdown-toggle"> {# #} + Applicability {#+ #} + <span class="badge">4</span> {#+ #} + <span class="caret"></span> {# #} + </button> {# #} + <ul class="dropdown-menu" id="lint-applicabilities-selector"> {# #} + <li class="checkbox"> {# #} + <button onclick="toggleElements('applicabilities_filter', true)">All</button> {# #} + </li> {# #} + <li class="checkbox"> {# #} + <button onclick="toggleElements('applicabilities_filter', false)">None</button> {# #} + </li> {# #} + <li role="separator" class="divider"></li> {# #} + </ul> {# #} + </div> {# #} + </div> {# #} + <div class="col-12 col-md-5 search-control"> {# #} + <div class="input-group"> {# #} + <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label> {# #} + <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input" /> {# #} + <span class="input-group-btn"> {# #} + <button class="filter-clear btn" type="button" onclick="searchState.clearInput(event)"> {# #} + Clear {# #} + </button> {# #} + </span> {# #} + </div> {# #} + </div> {# #} + <div class="col-12 col-md-2 btn-group expansion-group"> {# #} + <button title="Collapse All" class="btn btn-default expansion-control" type="button" onclick="toggleExpansion(false)"> {# #} + <span class="glyphicon glyphicon-collapse-up"></span> {# #} + </button> {# #} + <button title="Expand All" class="btn btn-default expansion-control" type="button" onclick="toggleExpansion(true)"> {# #} + <span class="glyphicon glyphicon-collapse-down"></span> {# #} + </button> {# #} + </div> {# #} + </div> {# #} + </div> + {% for lint in lints %} + <article class="panel panel-default collapsed" id="{{lint.id}}"> {# #} + <header class="panel-heading" onclick="expandLint('{{lint.id}}')"> {# #} + <h2 class="panel-title"> {# #} + <div class="panel-title-name" id="lint-{{lint.id}}"> {# #} + <span>{{lint.id}}</span> {#+ #} + <a href="#{{lint.id}}" class="anchor label label-default" onclick="openLint(event)">¶</a> {#+ #} + <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #} + 📋 {# #} + </a> {# #} + </div> {# #} + + <div class="panel-title-addons"> {# #} + <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #} + + <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #} + + <span class="label label-doc-folding">+</span> {# #} + </div> {# #} + </h2> {# #} + </header> {# #} + + <div class="list-group lint-docs"> {# #} + <div class="list-group-item lint-doc-md">{{Self::markdown(lint.docs)}}</div> {# #} + <div class="lint-additional-info-container"> + {# Applicability #} + <div class="lint-additional-info-item"> {# #} + <span> Applicability: </span> {# #} + <span class="label label-default label-applicability">{{ lint.applicability_str() }}</span> {# #} + <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> {# #} + </div> + {# Clippy version #} + <div class="lint-additional-info-item"> {# #} + <span>{% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: </span> {# #} + <span class="label label-default label-version">{{lint.version}}</span> {# #} + </div> + {# Open related issues #} + <div class="lint-additional-info-item"> {# #} + <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> {# #} + </div> + + {# Jump to source #} + {% if let Some(id_location) = lint.id_location %} + <div class="lint-additional-info-item"> {# #} + <a href="https://github.com/rust-lang/rust-clippy/blob/master/{{id_location}}">View Source</a> {# #} + </div> + {% endif %} + </div> {# #} + </div> {# #} + </article> + {% endfor %} + </div> {# #} + </div> {# #} + + <a {#+ #} + aria-label="View source on GitHub" {#+ #} + class="github-corner" {#+ #} + href="https://github.com/rust-lang/rust-clippy" {#+ #} + rel="noopener noreferrer" {#+ #} + target="_blank" {# #} + > {# #} + <svg {#+ #} + width="80" {#+ #} + height="80" {#+ #} + viewBox="0 0 250 250" {#+ #} + style="position: absolute; top: 0; border: 0; right: 0" {#+ #} + aria-hidden="true" {# #} + > {# #} + <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="var(--theme-color)"></path> {# #} + <path {#+ #} + d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" {#+ #} + fill="currentColor" {#+ #} + style="transform-origin: 130px 106px" {#+ #} + class="octo-arm" {# #} + ></path> {# #} + <path {#+ #} + d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" {#+ #} + fill="currentColor" {#+ #} + class="octo-body" {# #} + ></path> {# #} + </svg> {# #} + </a> {# #} + + <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script> {# #} + <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script> {# #} + <script src="script.js"></script> {# #} +</body> {# #} +</html> {# #} diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 1a5330bc0e5..cc22a39b3d1 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -1,629 +1,567 @@ -(function () { - const md = window.markdownit({ - html: true, - linkify: true, - typographer: true, - highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return '<pre class="hljs"><code>' + - hljs.highlight(str, { language: lang, ignoreIllegals: true }).value + - '</code></pre>'; - } catch (__) {} - } - - return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'; - } - }); - - function scrollToLint(lintId) { - const target = document.getElementById(lintId); - if (!target) { - return; - } - target.scrollIntoView(); - } - - function scrollToLintByURL($scope, $location) { - const removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { - scrollToLint($location.path().substring(1)); - removeListener(); - }); - } - - function selectGroup($scope, selectedGroup) { - const groups = $scope.groups; - for (const group in groups) { - if (groups.hasOwnProperty(group)) { - groups[group] = group === selectedGroup; - } +window.searchState = { + timeout: null, + inputElem: document.getElementById("search-input"), + lastSearch: '', + clearInput: () => { + searchState.inputElem.value = ""; + searchState.filterLints(); + }, + clearInputTimeout: () => { + if (searchState.timeout !== null) { + clearTimeout(searchState.timeout); + searchState.timeout = null } - } - - angular.module("clippy", []) - .filter('markdown', function ($sce) { - return function (text) { - return $sce.trustAsHtml( - md.render(text || '') - // Oh deer, what a hack :O - .replace('<table', '<table class="table"') - ); - }; - }) - .directive('filterDropdown', function ($document) { - return { - restrict: 'A', - link: function ($scope, $element, $attr) { - $element.bind('click', function (event) { - if (event.target.closest('button')) { - $element.toggleClass('open'); - } else { - $element.addClass('open'); - } - $element.addClass('open-recent'); - }); - - $document.bind('click', function () { - if (!$element.hasClass('open-recent')) { - $element.removeClass('open'); - } - $element.removeClass('open-recent'); - }) - } + }, + resetInputTimeout: () => { + searchState.clearInputTimeout(); + setTimeout(searchState.filterLints, 50); + }, + filterLints: () => { + function matchesSearch(lint, terms, searchStr) { + // Search by id + if (lint.elem.id.indexOf(searchStr) !== -1) { + return true; } - }) - .directive('onFinishRender', function ($timeout) { - return { - restrict: 'A', - link: function (scope, element, attr) { - if (scope.$last === true) { - $timeout(function () { - scope.$emit(attr.onFinishRender); - }); - } + // Search the description + // The use of `for`-loops instead of `foreach` enables us to return early + const docsLowerCase = lint.elem.textContent.toLowerCase(); + for (const term of terms) { + // This is more likely and will therefore be checked first + if (docsLowerCase.indexOf(term) !== -1) { + return true; } - }; - }) - .controller("lintList", function ($scope, $http, $location, $timeout) { - // Level filter - const LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; - $scope.levels = { ...LEVEL_FILTERS_DEFAULT }; - $scope.byLevels = function (lint) { - return $scope.levels[lint.level]; - }; - - const GROUPS_FILTER_DEFAULT = { - cargo: true, - complexity: true, - correctness: true, - nursery: true, - pedantic: true, - perf: true, - restriction: true, - style: true, - suspicious: true, - deprecated: false, - } - - $scope.groups = { - ...GROUPS_FILTER_DEFAULT - }; - - $scope.versionFilters = { - "≥": {enabled: false, minorVersion: null }, - "≤": {enabled: false, minorVersion: null }, - "=": {enabled: false, minorVersion: null }, - }; - - // Map the versionFilters to the query parameters in a way that is easier to work with in a URL - const versionFilterKeyMap = { - "≥": "gte", - "≤": "lte", - "=": "eq" - }; - const reverseVersionFilterKeyMap = Object.fromEntries( - Object.entries(versionFilterKeyMap).map(([key, value]) => [value, key]) - ); - - const APPLICABILITIES_FILTER_DEFAULT = { - MachineApplicable: true, - MaybeIncorrect: true, - HasPlaceholders: true, - Unspecified: true, - }; - - $scope.applicabilities = { - ...APPLICABILITIES_FILTER_DEFAULT - } - - // loadFromURLParameters retrieves filter settings from the URL parameters and assigns them - // to corresponding $scope variables. - function loadFromURLParameters() { - // Extract parameters from URL - const urlParameters = $location.search(); - - // Define a helper function that assigns URL parameters to a provided scope variable - const handleParameter = (parameter, scopeVariable, defaultValues) => { - if (urlParameters[parameter]) { - const items = urlParameters[parameter].split(','); - for (const key in scopeVariable) { - if (scopeVariable.hasOwnProperty(key)) { - scopeVariable[key] = items.includes(key); - } - } - } else if (defaultValues) { - for (const key in defaultValues) { - if (scopeVariable.hasOwnProperty(key)) { - scopeVariable[key] = defaultValues[key]; - } - } - } - }; - - handleParameter('levels', $scope.levels, LEVEL_FILTERS_DEFAULT); - handleParameter('groups', $scope.groups, GROUPS_FILTER_DEFAULT); - handleParameter('applicabilities', $scope.applicabilities, APPLICABILITIES_FILTER_DEFAULT); - - // Handle 'versions' parameter separately because it needs additional processing - if (urlParameters.versions) { - const versionFilters = urlParameters.versions.split(','); - for (const versionFilter of versionFilters) { - const [key, minorVersion] = versionFilter.split(':'); - const parsedMinorVersion = parseInt(minorVersion); - // Map the key from the URL parameter to its original form - const originalKey = reverseVersionFilterKeyMap[key]; - - if (originalKey in $scope.versionFilters && !isNaN(parsedMinorVersion)) { - $scope.versionFilters[originalKey].enabled = true; - $scope.versionFilters[originalKey].minorVersion = parsedMinorVersion; - } - } + if (lint.elem.id.indexOf(term) !== -1) { + return true; } - // Load the search parameter from the URL path - const searchParameter = $location.path().substring(1); // Remove the leading slash - if (searchParameter) { - $scope.search = searchParameter; - $scope.open[searchParameter] = true; - scrollToLintByURL($scope, $location); - } + return false; } + return true; + } - // updateURLParameter updates the URL parameter with the given key to the given value - function updateURLParameter(filterObj, urlKey, defaultValue = {}, processFilter = filter => filter) { - const parameter = Object.keys(filterObj) - .filter(filter => filterObj[filter]) - .sort() - .map(processFilter) - .filter(Boolean) // Filters out any falsy values, including null - .join(','); - - const defaultParameter = Object.keys(defaultValue) - .filter(filter => defaultValue[filter]) - .sort() - .map(processFilter) - .filter(Boolean) // Filters out any falsy values, including null - .join(','); - - // if we ended up back at the defaults, just remove it from the URL - if (parameter === defaultParameter) { - $location.search(urlKey, null); - } else { - $location.search(urlKey, parameter || null); - } - } + searchState.clearInputTimeout(); - // updateVersionURLParameter updates the version URL parameter with the given version filters - function updateVersionURLParameter(versionFilters) { - updateURLParameter( - versionFilters, - 'versions', {}, - versionFilter => versionFilters[versionFilter].enabled && versionFilters[versionFilter].minorVersion != null - ? `${versionFilterKeyMap[versionFilter]}:${versionFilters[versionFilter].minorVersion}` - : null - ); + let searchStr = searchState.inputElem.value.trim().toLowerCase(); + if (searchStr.startsWith("clippy::")) { + searchStr = searchStr.slice(8); + } + if (searchState.lastSearch === searchStr) { + return; + } + searchState.lastSearch = searchStr; + const terms = searchStr.split(" "); + const cleanedSearchStr = searchStr.replaceAll("-", "_"); + + for (const lint of filters.getAllLints()) { + lint.searchFilteredOut = !matchesSearch(lint, terms, cleanedSearchStr); + if (lint.filteredOut) { + continue; } - - // updateAllURLParameters updates all the URL parameters with the current filter settings - function updateAllURLParameters() { - updateURLParameter($scope.levels, 'levels', LEVEL_FILTERS_DEFAULT); - updateURLParameter($scope.groups, 'groups', GROUPS_FILTER_DEFAULT); - updateVersionURLParameter($scope.versionFilters); - updateURLParameter($scope.applicabilities, 'applicabilities', APPLICABILITIES_FILTER_DEFAULT); + if (lint.searchFilteredOut) { + lint.elem.style.display = "none"; + } else { + lint.elem.style.display = ""; } + } + if (searchStr.length > 0) { + window.location.hash = `/${searchStr}`; + } else { + window.location.hash = ''; + } + }, +}; - // Add $watches to automatically update URL parameters when the data changes - $scope.$watch('levels', function (newVal, oldVal) { - if (newVal !== oldVal) { - updateURLParameter(newVal, 'levels', LEVEL_FILTERS_DEFAULT); - } - }, true); - - $scope.$watch('groups', function (newVal, oldVal) { - if (newVal !== oldVal) { - updateURLParameter(newVal, 'groups', GROUPS_FILTER_DEFAULT); - } - }, true); - - $scope.$watch('versionFilters', function (newVal, oldVal) { - if (newVal !== oldVal) { - updateVersionURLParameter(newVal); - } - }, true); - - $scope.$watch('applicabilities', function (newVal, oldVal) { - if (newVal !== oldVal) { - updateURLParameter(newVal, 'applicabilities', APPLICABILITIES_FILTER_DEFAULT) - } - }, true); - - // Watch for changes in the URL path and update the search and lint display - $scope.$watch(function () { return $location.path(); }, function (newPath) { - const searchParameter = newPath.substring(1); - if ($scope.search !== searchParameter) { - $scope.search = searchParameter; - $scope.open[searchParameter] = true; - scrollToLintByURL($scope, $location); - } - }); - - let debounceTimeout; - $scope.$watch('search', function (newVal, oldVal) { - if (newVal !== oldVal) { - if (debounceTimeout) { - $timeout.cancel(debounceTimeout); - } - - debounceTimeout = $timeout(function () { - $location.path(newVal); - }, 1000); - } - }); - - $scope.$watch(function () { return $location.search(); }, function (newParameters) { - loadFromURLParameters(); - }, true); - - $scope.updatePath = function () { - if (debounceTimeout) { - $timeout.cancel(debounceTimeout); - } +function handleInputChanged(event) { + if (event.target !== document.activeElement) { + return; + } + searchState.resetInputTimeout(); +} - $location.path($scope.search); - } +function handleShortcut(ev) { + if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { + return; + } - $scope.toggleLevels = function (value) { - const levels = $scope.levels; - for (const key in levels) { - if (levels.hasOwnProperty(key)) { - levels[key] = value; - } - } - }; + if (document.activeElement.tagName === "INPUT") { + if (ev.key === "Escape") { + document.activeElement.blur(); + } + } else { + switch (ev.key) { + case "s": + case "S": + case "/": + ev.preventDefault(); // To prevent the key to be put into the input. + document.getElementById("search-input").focus(); + break; + default: + break; + } + } +} - $scope.toggleGroups = function (value) { - const groups = $scope.groups; - for (const key in groups) { - if (groups.hasOwnProperty(key)) { - groups[key] = value; - } - } - }; +function toggleElements(filter, value) { + let needsUpdate = false; + let count = 0; - $scope.toggleApplicabilities = function (value) { - const applicabilities = $scope.applicabilities; - for (const key in applicabilities) { - if (applicabilities.hasOwnProperty(key)) { - applicabilities[key] = value; - } - } + const element = document.getElementById(filters[filter].id); + onEachLazy( + element.querySelectorAll("ul input"), + el => { + if (el.checked !== value) { + el.checked = value; + filters[filter][el.getAttribute("data-value")] = value; + needsUpdate = true; } + count += 1; + } + ); + element.querySelector(".badge").innerText = value ? count : 0; + if (needsUpdate) { + filters.filterLints(); + } +} - $scope.resetGroupsToDefault = function () { - $scope.groups = { - ...GROUPS_FILTER_DEFAULT - }; - }; +function changeSetting(elem) { + if (elem.id === "disable-shortcuts") { + disableShortcuts = elem.checked; + storeValue(elem.id, elem.checked); + } +} - $scope.selectedValuesCount = function (obj) { - return Object.values(obj).filter(x => x).length; - } +function onEachLazy(lazyArray, func) { + const arr = Array.prototype.slice.call(lazyArray); + for (const el of arr) { + func(el); + } +} - $scope.clearVersionFilters = function () { - for (const filter in $scope.versionFilters) { - $scope.versionFilters[filter] = { enabled: false, minorVersion: null }; - } - } +function highlightIfNeeded(elem) { + onEachLazy(elem.querySelectorAll("pre > code.language-rust:not(.highlighted)"), el => { + hljs.highlightElement(el.parentElement) + el.classList.add("highlighted"); + }); +} - $scope.versionFilterCount = function(obj) { - return Object.values(obj).filter(x => x.enabled).length; - } +function expandLint(lintId) { + const lintElem = document.getElementById(lintId); + const isCollapsed = lintElem.classList.toggle("collapsed"); + lintElem.querySelector(".label-doc-folding").innerText = isCollapsed ? "+" : "−"; + highlightIfNeeded(lintElem); +} - $scope.updateVersionFilters = function() { - for (const filter in $scope.versionFilters) { - const minorVersion = $scope.versionFilters[filter].minorVersion; +// Show details for one lint +function openLint(event) { + event.preventDefault(); + event.stopPropagation(); + expandLint(event.target.getAttribute("href").slice(1)); +} - // 1.29.0 and greater - if (minorVersion && minorVersion > 28) { - $scope.versionFilters[filter].enabled = true; - continue; - } +function copyToClipboard(event) { + event.preventDefault(); + event.stopPropagation(); - $scope.versionFilters[filter].enabled = false; - } - } + const clipboard = event.target; - $scope.byVersion = function(lint) { - const filters = $scope.versionFilters; - for (const filter in filters) { - if (filters[filter].enabled) { - const minorVersion = filters[filter].minorVersion; - - // Strip the "pre " prefix for pre 1.29.0 lints - const lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; - const lintMinorVersion = lintVersion.substring(2, 4); - - switch (filter) { - // "=" gets the highest priority, since all filters are inclusive - case "=": - return (lintMinorVersion == minorVersion); - case "≥": - if (lintMinorVersion < minorVersion) { return false; } - break; - case "≤": - if (lintMinorVersion > minorVersion) { return false; } - break; - default: - return true - } - } - } + let resetClipboardTimeout = null; + const resetClipboardIcon = clipboard.innerHTML; - return true; - } + function resetClipboard() { + resetClipboardTimeout = null; + clipboard.innerHTML = resetClipboardIcon; + } - $scope.byGroups = function (lint) { - return $scope.groups[lint.group]; - }; + navigator.clipboard.writeText("clippy::" + clipboard.parentElement.id.slice(5)); - $scope.bySearch = function (lint, index, array) { - let searchStr = $scope.search; - // It can be `null` I haven't missed this value - if (searchStr == null) { - return true; - } - searchStr = searchStr.toLowerCase(); - if (searchStr.startsWith("clippy::")) { - searchStr = searchStr.slice(8); - } + clipboard.innerHTML = "✓"; + if (resetClipboardTimeout !== null) { + clearTimeout(resetClipboardTimeout); + } + resetClipboardTimeout = setTimeout(resetClipboard, 1000); +} - // Search by id - if (lint.id.indexOf(searchStr.replaceAll("-", "_")) !== -1) { - return true; - } +function handleBlur(event, elementId) { + const parent = document.getElementById(elementId); + if (!parent.contains(document.activeElement) && + !parent.contains(event.relatedTarget) + ) { + parent.classList.remove("open"); + } +} - // Search the description - // The use of `for`-loops instead of `foreach` enables us to return early - const terms = searchStr.split(" "); - const docsLowerCase = lint.docs.toLowerCase(); - for (index = 0; index < terms.length; index++) { - // This is more likely and will therefore be checked first - if (docsLowerCase.indexOf(terms[index]) !== -1) { - continue; - } +function toggleExpansion(expand) { + onEachLazy( + document.querySelectorAll("article"), + expand ? el => { + el.classList.remove("collapsed"); + highlightIfNeeded(el); + } : el => el.classList.add("collapsed"), + ); +} - if (lint.id.indexOf(terms[index]) !== -1) { - continue; - } +// Returns the current URL without any query parameter or hash. +function getNakedUrl() { + return window.location.href.split("?")[0].split("#")[0]; +} - return false; +const GROUPS_FILTER_DEFAULT = { + cargo: true, + complexity: true, + correctness: true, + nursery: true, + pedantic: true, + perf: true, + restriction: true, + style: true, + suspicious: true, + deprecated: false, +}; +const LEVEL_FILTERS_DEFAULT = { + allow: true, + warn: true, + deny: true, + none: true, +}; +const APPLICABILITIES_FILTER_DEFAULT = { + Unspecified: true, + MachineApplicable: true, + MaybeIncorrect: true, + HasPlaceholders: true, +}; +const URL_PARAMS_CORRESPONDANCE = { + "groups_filter": "groups", + "levels_filter": "levels", + "applicabilities_filter": "applicabilities", + "version_filter": "versions", +}; +const VERSIONS_CORRESPONDANCE = { + "lte": "≤", + "gte": "≥", + "eq": "=", +}; + +window.filters = { + groups_filter: { id: "lint-groups", ...GROUPS_FILTER_DEFAULT }, + levels_filter: { id: "lint-levels", ...LEVEL_FILTERS_DEFAULT }, + applicabilities_filter: { id: "lint-applicabilities", ...APPLICABILITIES_FILTER_DEFAULT }, + version_filter: { + "≥": null, + "≤": null, + "=": null, + }, + allLints: null, + getAllLints: () => { + if (filters.allLints === null) { + filters.allLints = Array.prototype.slice.call( + document.getElementsByTagName("article"), + ).map(elem => { + let version = elem.querySelector(".label-version").innerText; + // Strip the "pre " prefix for pre 1.29.0 lints + if (version.startsWith("pre ")) { + version = version.slice(4); } + return { + elem: elem, + group: elem.querySelector(".label-lint-group").innerText, + level: elem.querySelector(".label-lint-level").innerText, + version: parseInt(version.split(".")[1]), + applicability: elem.querySelector(".label-applicability").innerText, + filteredOut: false, + searchFilteredOut: false, + }; + }); + } + return filters.allLints; + }, + regenerateURLparams: () => { + const urlParams = new URLSearchParams(window.location.search); - return true; - } - - $scope.byApplicabilities = function (lint) { - return $scope.applicabilities[lint.applicability]; - }; - - // Show details for one lint - $scope.openLint = function (lint) { - $scope.open[lint.id] = true; - $location.path(lint.id); - }; - - $scope.toggleExpansion = function(lints, isExpanded) { - lints.forEach(lint => { - $scope.open[lint.id] = isExpanded; - }); + function compareObjects(obj1, obj2) { + return (JSON.stringify(obj1) === JSON.stringify({ id: obj1.id, ...obj2 })); + } + function updateIfNeeded(filterName, obj2) { + const obj1 = filters[filterName]; + const name = URL_PARAMS_CORRESPONDANCE[filterName]; + if (!compareObjects(obj1, obj2)) { + urlParams.set( + name, + Object.entries(obj1).filter( + ([key, value]) => value && key !== "id" + ).map( + ([key, _]) => key + ).join(","), + ); + } else { + urlParams.delete(name); } + } - $scope.copyToClipboard = function (lint) { - const clipboard = document.getElementById("clipboard-" + lint.id); - if (clipboard) { - let resetClipboardTimeout = null; - const resetClipboardIcon = clipboard.innerHTML; + updateIfNeeded("groups_filter", GROUPS_FILTER_DEFAULT); + updateIfNeeded("levels_filter", LEVEL_FILTERS_DEFAULT); + updateIfNeeded( + "applicabilities_filter", APPLICABILITIES_FILTER_DEFAULT); - function resetClipboard() { - resetClipboardTimeout = null; - clipboard.innerHTML = resetClipboardIcon; - } + const versions = []; + if (filters.version_filter["="] !== null) { + versions.push(`eq:${filters.version_filter["="]}`); + } + if (filters.version_filter["≥"] !== null) { + versions.push(`gte:${filters.version_filter["≥"]}`); + } + if (filters.version_filter["≤"] !== null) { + versions.push(`lte:${filters.version_filter["≤"]}`); + } + if (versions.length !== 0) { + urlParams.set(URL_PARAMS_CORRESPONDANCE["version_filter"], versions.join(",")); + } else { + urlParams.delete(URL_PARAMS_CORRESPONDANCE["version_filter"]); + } - navigator.clipboard.writeText("clippy::" + lint.id); + let params = urlParams.toString(); + if (params.length !== 0) { + params = `?${params}`; + } - clipboard.innerHTML = "✓"; - if (resetClipboardTimeout !== null) { - clearTimeout(resetClipboardTimeout); - } - resetClipboardTimeout = setTimeout(resetClipboard, 1000); - } + const url = getNakedUrl() + params + window.location.hash + if (!history.state) { + history.pushState(null, "", url); + } else { + history.replaceState(null, "", url); + } + }, + filterLints: () => { + // First we regenerate the URL parameters. + filters.regenerateURLparams(); + for (const lint of filters.getAllLints()) { + lint.filteredOut = (!filters.groups_filter[lint.group] + || !filters.levels_filter[lint.level] + || !filters.applicabilities_filter[lint.applicability] + || !(filters.version_filter["="] === null || lint.version === filters.version_filter["="]) + || !(filters.version_filter["≥"] === null || lint.version > filters.version_filter["≥"]) + || !(filters.version_filter["≤"] === null || lint.version < filters.version_filter["≤"]) + ); + if (lint.filteredOut || lint.searchFilteredOut) { + lint.elem.style.display = "none"; + } else { + lint.elem.style.display = ""; } - - // Get data - $scope.open = {}; - $scope.loading = true; - - // This will be used to jump into the source code of the version that this documentation is for. - $scope.docVersion = window.location.pathname.split('/')[2] || "master"; - - // Set up the filters from the URL parameters before we start loading the data - loadFromURLParameters(); - - $http.get('./lints.json') - .success(function (data) { - $scope.data = data; - $scope.loading = false; - - const selectedGroup = getQueryVariable("sel"); - if (selectedGroup) { - selectGroup($scope, selectedGroup.toLowerCase()); - } - - scrollToLintByURL($scope, $location); - - setTimeout(function () { - const el = document.getElementById('filter-input'); - if (el) { el.focus() } - }, 0); - }) - .error(function (data) { - $scope.error = data; - $scope.loading = false; - }); - }); -})(); - -function getQueryVariable(variable) { - const query = window.location.search.substring(1); - const vars = query.split('&'); - for (const entry of vars) { - const pair = entry.split('='); - if (decodeURIComponent(pair[0]) == variable) { - return decodeURIComponent(pair[1]); + } + }, +}; + +function updateFilter(elem, filter, skipLintsFiltering) { + const value = elem.getAttribute("data-value"); + if (filters[filter][value] !== elem.checked) { + filters[filter][value] = elem.checked; + const counter = document.querySelector(`#${filters[filter].id} .badge`); + counter.innerText = parseInt(counter.innerText) + (elem.checked ? 1 : -1); + if (!skipLintsFiltering) { + filters.filterLints(); } } } -function storeValue(settingName, value) { - try { - localStorage.setItem(`clippy-lint-list-${settingName}`, value); - } catch (e) { } -} - -function loadValue(settingName) { - return localStorage.getItem(`clippy-lint-list-${settingName}`); -} - -function setTheme(theme, store) { - let enableHighlight = false; - let enableNight = false; - let enableAyu = false; - - switch(theme) { - case "ayu": - enableAyu = true; - break; - case "coal": - case "navy": - enableNight = true; - break; - case "rust": - enableHighlight = true; - break; - default: - enableHighlight = true; - theme = "light"; - break; +function updateVersionFilters(elem, skipLintsFiltering) { + let value = elem.value.trim(); + if (value.length === 0) { + value = null; + } else if (/^\d+$/.test(value)) { + value = parseInt(value); + } else { + console.error(`Failed to get version number from "${value}"`); + return; } - document.getElementsByTagName("body")[0].className = theme; + const counter = document.querySelector("#version-filter .badge"); + let count = 0; + onEachLazy(document.querySelectorAll("#version-filter input"), el => { + if (el.value.trim().length !== 0) { + count += 1; + } + }); + counter.innerText = count; - document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight; - document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu; + const comparisonKind = elem.getAttribute("data-value"); + if (filters.version_filter[comparisonKind] !== value) { + filters.version_filter[comparisonKind] = value; + if (!skipLintsFiltering) { + filters.filterLints(); + } + } +} - document.getElementById("styleHighlight").disabled = !enableHighlight; - document.getElementById("styleNight").disabled = !enableNight; - document.getElementById("styleAyu").disabled = !enableAyu; +function clearVersionFilters() { + let needsUpdate = false; - if (store) { - storeValue("theme", theme); - } else { - document.getElementById(`theme-choice`).value = theme; + onEachLazy(document.querySelectorAll("#version-filter input"), el => { + el.value = ""; + const comparisonKind = el.getAttribute("data-value"); + if (filters.version_filter[comparisonKind] !== null) { + needsUpdate = true; + filters.version_filter[comparisonKind] = null; + } + }); + document.querySelector("#version-filter .badge").innerText = 0; + if (needsUpdate) { + filters.filterLints(); } } -function handleShortcut(ev) { - if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) { - return; +function resetGroupsToDefault() { + let needsUpdate = false; + let count = 0; + + onEachLazy(document.querySelectorAll("#lint-groups-selector input"), el => { + const key = el.getAttribute("data-value"); + const value = GROUPS_FILTER_DEFAULT[key]; + if (filters.groups_filter[key] !== value) { + filters.groups_filter[key] = value; + el.checked = value; + needsUpdate = true; + } + if (value) { + count += 1; + } + }); + document.querySelector("#lint-groups .badge").innerText = count; + if (needsUpdate) { + filters.filterLints(); } +} - if (document.activeElement.tagName === "INPUT") { - if (ev.key === "Escape") { - document.activeElement.blur(); - } - } else { - switch (ev.key) { - case "s": - case "S": - case "/": - ev.preventDefault(); // To prevent the key to be put into the input. - document.getElementById("search-input").focus(); - break; - default: - break; +function generateListOfOptions(list, elementId, filter) { + let html = ''; + let nbEnabled = 0; + for (const [key, value] of Object.entries(list)) { + const attr = value ? " checked" : ""; + html += `\ +<li class="checkbox">\ + <label class="text-capitalize">\ + <input type="checkbox" data-value="${key}" \ + onchange="updateFilter(this, '${filter}')"${attr}/>${key}\ + </label>\ +</li>`; + if (value) { + nbEnabled += 1; } } + + const elem = document.getElementById(`${elementId}-selector`); + elem.previousElementSibling.querySelector(".badge").innerText = `${nbEnabled}`; + elem.innerHTML += html; + + setupDropdown(elementId); } -document.addEventListener("keypress", handleShortcut); -document.addEventListener("keydown", handleShortcut); +function setupDropdown(elementId) { + const elem = document.getElementById(elementId); + const button = document.querySelector(`#${elementId} > button`); + button.onclick = () => elem.classList.toggle("open"); + + const setBlur = child => { + child.onblur = event => handleBlur(event, elementId); + }; + onEachLazy(elem.children, setBlur); + onEachLazy(elem.querySelectorAll("select"), setBlur); + onEachLazy(elem.querySelectorAll("input"), setBlur); + onEachLazy(elem.querySelectorAll("ul button"), setBlur); +} -function changeSetting(elem) { - if (elem.id === "disable-shortcuts") { - disableShortcuts = elem.checked; - storeValue(elem.id, elem.checked); +function generateSettings() { + setupDropdown("settings-dropdown"); + + generateListOfOptions(LEVEL_FILTERS_DEFAULT, "lint-levels", "levels_filter"); + generateListOfOptions(GROUPS_FILTER_DEFAULT, "lint-groups", "groups_filter"); + generateListOfOptions( + APPLICABILITIES_FILTER_DEFAULT, "lint-applicabilities", "applicabilities_filter"); + + let html = ''; + for (const kind of ["≥", "≤", "="]) { + html += `\ +<li class="checkbox">\ + <label>${kind}</label>\ + <span>1.</span> \ + <input type="number" \ + min="29" \ + class="version-filter-input form-control filter-input" \ + maxlength="2" \ + data-value="${kind}" \ + onchange="updateVersionFilters(this)" \ + oninput="updateVersionFilters(this)" \ + onkeydown="updateVersionFilters(this)" \ + onkeyup="updateVersionFilters(this)" \ + onpaste="updateVersionFilters(this)" \ + /> + <span>.0</span>\ +</li>`; } + document.getElementById("version-filter-selector").innerHTML += html; + setupDropdown("version-filter"); } -function onEachLazy(lazyArray, func) { - const arr = Array.prototype.slice.call(lazyArray); - for (const el of arr) { - func(el); - } +function generateSearch() { + searchState.inputElem.addEventListener("change", handleInputChanged); + searchState.inputElem.addEventListener("input", handleInputChanged); + searchState.inputElem.addEventListener("keydown", handleInputChanged); + searchState.inputElem.addEventListener("keyup", handleInputChanged); + searchState.inputElem.addEventListener("paste", handleInputChanged); } -function handleBlur(event) { - const parent = document.getElementById("settings-dropdown"); - if (!parent.contains(document.activeElement) && - !parent.contains(event.relatedTarget) - ) { - parent.classList.remove("open"); +function scrollToLint(lintId) { + const target = document.getElementById(lintId); + if (!target) { + return; } + target.scrollIntoView(); + expandLint(lintId); } -function generateSettings() { - const settings = document.getElementById("settings-dropdown"); - const settingsButton = settings.querySelector(".settings-icon") - settingsButton.onclick = () => settings.classList.toggle("open"); - settingsButton.onblur = handleBlur; - const settingsMenu = settings.querySelector(".settings-menu"); - settingsMenu.onblur = handleBlur; - onEachLazy( - settingsMenu.querySelectorAll("input"), - el => el.onblur = handleBlur, - ); +// If the page we arrive on has link to a given lint, we scroll to it. +function scrollToLintByURL() { + const lintId = window.location.hash.substring(2); + if (lintId.length > 0) { + scrollToLint(lintId); + } } -generateSettings(); +function parseURLFilters() { + const urlParams = new URLSearchParams(window.location.search); + + for (const [key, value] of urlParams.entries()) { + for (const [corres_key, corres_value] of Object.entries(URL_PARAMS_CORRESPONDANCE)) { + if (corres_value === key) { + if (key !== "versions") { + const settings = new Set(value.split(",")); + onEachLazy(document.querySelectorAll(`#lint-${key} ul input`), elem => { + elem.checked = settings.has(elem.getAttribute("data-value")); + updateFilter(elem, corres_key, true); + }); + } else { + const settings = value.split(",").map(elem => elem.split(":")); -// loading the theme after the initial load -const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); -const theme = loadValue('theme'); -if (prefersDark.matches && !theme) { - setTheme("coal", false); -} else { - setTheme(theme, false); + for (const [kind, value] of settings) { + const elem = document.querySelector( + `#version-filter input[data-value="${VERSIONS_CORRESPONDANCE[kind]}"]`); + elem.value = value; + updateVersionFilters(elem, true); + } + } + } + } + } } + +document.getElementById(`theme-choice`).value = loadValue("theme"); let disableShortcuts = loadValue('disable-shortcuts') === "true"; document.getElementById("disable-shortcuts").checked = disableShortcuts; + +document.addEventListener("keypress", handleShortcut); +document.addEventListener("keydown", handleShortcut); + +generateSettings(); +generateSearch(); +parseURLFilters(); +scrollToLintByURL(); +filters.filterLints(); diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css index a9485d51104..a68a10b1401 100644 --- a/src/tools/clippy/util/gh-pages/style.css +++ b/src/tools/clippy/util/gh-pages/style.css @@ -272,8 +272,9 @@ L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.649 height: 18px; display: block; filter: invert(0.7); - padding-left: 4px; - padding-top: 3px; + position: absolute; + top: 4px; + left: 5px; } .settings-menu * { @@ -329,6 +330,18 @@ L4.75,12h2.5l0.5393066-2.1572876 c0.2276001-0.1062012,0.4459839-0.2269287,0.649 display: flex; } +ul.dropdown-menu li.checkbox > button { + border: 0; + width: 100%; + background: var(--theme-popup-bg); + color: var(--fg); +} + +ul.dropdown-menu li.checkbox > button:hover { + background: var(--theme-hover); + box-shadow: none; +} + #version-filter { min-width: available; } @@ -396,3 +409,37 @@ body { background: var(--bg); color: var(--fg); } + +article.collapsed .lint-docs { + display: none; +} + +.github-corner svg { + fill: var(--fg); + color: var(--bg); +} +.github-corner:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out; +} +@keyframes octocat-wave { + 0%, + 100% { + transform: rotate(0); + } + 20%, + 60% { + transform: rotate(-25deg); + } + 40%, + 80% { + transform: rotate(10deg); + } +} +@media (max-width: 500px) { + .github-corner:hover .octo-arm { + animation: none; + } + .github-corner .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } +} diff --git a/src/tools/clippy/util/gh-pages/theme.js b/src/tools/clippy/util/gh-pages/theme.js new file mode 100644 index 00000000000..bc296955ddf --- /dev/null +++ b/src/tools/clippy/util/gh-pages/theme.js @@ -0,0 +1,56 @@ +function storeValue(settingName, value) { + try { + localStorage.setItem(`clippy-lint-list-${settingName}`, value); + } catch (e) { } +} + +function loadValue(settingName) { + return localStorage.getItem(`clippy-lint-list-${settingName}`); +} + +function setTheme(theme, store) { + let enableHighlight = false; + let enableNight = false; + let enableAyu = false; + + switch(theme) { + case "ayu": + enableAyu = true; + break; + case "coal": + case "navy": + enableNight = true; + break; + case "rust": + enableHighlight = true; + break; + default: + enableHighlight = true; + theme = "light"; + break; + } + + document.body.className = theme; + + document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight; + document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu; + + document.getElementById("styleHighlight").disabled = !enableHighlight; + document.getElementById("styleNight").disabled = !enableNight; + document.getElementById("styleAyu").disabled = !enableAyu; + + if (store) { + storeValue("theme", theme); + } +} + +(function() { + // loading the theme after the initial load + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); + const theme = loadValue("theme"); + if (prefersDark.matches && !theme) { + setTheme("coal", false); + } else { + setTheme(theme, false); + } +})(); diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 1ee00a3a4e8..ff059940f7c 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -338,8 +338,8 @@ pub struct Config { /// created in `/<build_base>/rustfix_missing_coverage.txt` pub rustfix_coverage: bool, - /// whether to run `tidy` when a rustdoc test fails - pub has_tidy: bool, + /// whether to run `tidy` (html-tidy) when a rustdoc test fails + pub has_html_tidy: bool, /// whether to run `enzyme` autodiff tests pub has_enzyme: bool, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 2e66c084dd7..7d6ede9bcda 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -230,14 +230,14 @@ pub fn parse_config(args: Vec<String>) -> Config { let run_ignored = matches.opt_present("ignored"); let with_debug_assertions = matches.opt_present("with-debug-assertions"); let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode"); - let has_tidy = if mode == Mode::Rustdoc { + let has_html_tidy = if mode == Mode::Rustdoc { Command::new("tidy") .arg("--version") .stdout(Stdio::null()) .status() .map_or(false, |status| status.success()) } else { - // Avoid spawning an external command when we know tidy won't be used. + // Avoid spawning an external command when we know html-tidy won't be used. false }; let has_enzyme = matches.opt_present("has-enzyme"); @@ -336,7 +336,7 @@ pub fn parse_config(args: Vec<String>) -> Config { .opt_str("compare-mode") .map(|s| s.parse().expect("invalid --compare-mode provided")), rustfix_coverage: matches.opt_present("rustfix-coverage"), - has_tidy, + has_html_tidy, has_enzyme, channel: matches.opt_str("channel").unwrap(), git_hash: matches.opt_present("git-hash"), @@ -464,9 +464,7 @@ pub fn run_tests(config: Arc<Config>) { // structure for each test (or each revision of a multi-revision test). let mut tests = Vec::new(); for c in configs { - let mut found_paths = HashSet::new(); - make_tests(c, &mut tests, &mut found_paths); - check_overlapping_tests(&found_paths); + tests.extend(collect_and_make_tests(c)); } tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice())); @@ -545,46 +543,62 @@ pub fn test_opts(config: &Config) -> test::TestOpts { } } +/// Read-only context data used during test collection. +struct TestCollectorCx { + config: Arc<Config>, + cache: HeadersCache, + common_inputs_stamp: Stamp, + modified_tests: Vec<PathBuf>, +} + +/// Mutable state used during test collection. +struct TestCollector { + tests: Vec<test::TestDescAndFn>, + found_path_stems: HashSet<PathBuf>, + poisoned: bool, +} + /// Creates libtest structures for every test/revision in the test suite directory. /// /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests), /// regardless of whether any filters/tests were specified on the command-line, /// because filtering is handled later by libtest. -pub fn make_tests( - config: Arc<Config>, - tests: &mut Vec<test::TestDescAndFn>, - found_paths: &mut HashSet<PathBuf>, -) { +pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> { debug!("making tests from {:?}", config.src_base.display()); - let inputs = common_inputs_stamp(&config); + let common_inputs_stamp = common_inputs_stamp(&config); 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) }); - let cache = HeadersCache::load(&config); - let mut poisoned = false; - collect_tests_from_dir( - config.clone(), - &cache, - &config.src_base, - &PathBuf::new(), - &inputs, - tests, - found_paths, - &modified_tests, - &mut poisoned, - ) - .unwrap_or_else(|reason| { - panic!("Could not read tests from {}: {reason}", config.src_base.display()) - }); + + let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests }; + let mut collector = + TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; + + collect_tests_from_dir(&cx, &mut collector, &cx.config.src_base, &PathBuf::new()) + .unwrap_or_else(|reason| { + panic!("Could not read tests from {}: {reason}", cx.config.src_base.display()) + }); + + let TestCollector { tests, found_path_stems, poisoned } = collector; if poisoned { eprintln!(); panic!("there are errors in tests"); } + + check_for_overlapping_test_paths(&found_path_stems); + + tests } -/// Returns a stamp constructed from input files common to all test cases. +/// Returns the most recent last-modified timestamp from among the input files +/// that are considered relevant to all tests (e.g. the compiler, std, and +/// compiletest itself). +/// +/// (Some of these inputs aren't actually relevant to _all_ tests, but they are +/// common to some subset of tests, and are hopefully unlikely to be modified +/// while working on other tests.) fn common_inputs_stamp(config: &Config) -> Stamp { let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root"); @@ -662,15 +676,10 @@ fn modified_tests(config: &Config, dir: &Path) -> Result<Vec<PathBuf>, String> { /// Recursively scans a directory to find test files and create test structures /// that will be handed over to libtest. fn collect_tests_from_dir( - config: Arc<Config>, - cache: &HeadersCache, + cx: &TestCollectorCx, + collector: &mut TestCollector, dir: &Path, relative_dir_path: &Path, - inputs: &Stamp, - tests: &mut Vec<test::TestDescAndFn>, - found_paths: &mut HashSet<PathBuf>, - modified_tests: &Vec<PathBuf>, - poisoned: &mut bool, ) -> io::Result<()> { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { @@ -679,7 +688,7 @@ fn collect_tests_from_dir( // For run-make tests, a "test file" is actually a directory that contains // an `rmake.rs` or `Makefile`" - if config.mode == Mode::RunMake { + if cx.config.mode == Mode::RunMake { if dir.join("Makefile").exists() && dir.join("rmake.rs").exists() { return Err(io::Error::other( "run-make tests cannot have both `Makefile` and `rmake.rs`", @@ -691,7 +700,7 @@ fn collect_tests_from_dir( file: dir.to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; - tests.extend(make_test(config, cache, &paths, inputs, poisoned)); + make_test(cx, collector, &paths); // This directory is a test, so don't try to find other tests inside it. return Ok(()); } @@ -703,7 +712,7 @@ fn collect_tests_from_dir( // sequential loop because otherwise, if we do it in the // tests themselves, they race for the privilege of // creating the directories and sometimes fail randomly. - let build_dir = output_relative_path(&config, relative_dir_path); + let build_dir = output_relative_path(&cx.config, relative_dir_path); fs::create_dir_all(&build_dir).unwrap(); // Add each `.rs` file as a test, and recurse further on any @@ -715,33 +724,25 @@ fn collect_tests_from_dir( let file_path = file.path(); let file_name = file.file_name(); - if is_test(&file_name) && (!config.only_modified || modified_tests.contains(&file_path)) { + if is_test(&file_name) + && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) + { // We found a test file, so create the corresponding libtest structures. debug!("found test file: {:?}", file_path.display()); // Record the stem of the test file, to check for overlaps later. let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); - found_paths.insert(rel_test_path); + collector.found_path_stems.insert(rel_test_path); let paths = TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; - tests.extend(make_test(config.clone(), cache, &paths, inputs, poisoned)) + make_test(cx, collector, &paths); } else if file_path.is_dir() { // Recurse to find more tests in a subdirectory. 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.clone(), - cache, - &file_path, - &relative_file_path, - inputs, - tests, - found_paths, - modified_tests, - poisoned, - )?; + collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?; } } else { debug!("found other file/directory: {:?}", file_path.display()); @@ -765,17 +766,11 @@ pub fn is_test(file_name: &OsString) -> bool { /// For a single test file, creates one or more test structures (one per revision) /// that can be handed over to libtest to run, possibly in parallel. -fn make_test( - config: Arc<Config>, - cache: &HeadersCache, - testpaths: &TestPaths, - inputs: &Stamp, - poisoned: &mut bool, -) -> Vec<test::TestDescAndFn> { +fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &TestPaths) { // For run-make tests, each "test file" is actually a _directory_ containing // an `rmake.rs` or `Makefile`. But for the purposes of directive parsing, // we want to look at that recipe file, not the directory itself. - let test_path = if config.mode == Mode::RunMake { + let test_path = if cx.config.mode == Mode::RunMake { if testpaths.file.join("rmake.rs").exists() && testpaths.file.join("Makefile").exists() { panic!("run-make tests cannot have both `rmake.rs` and `Makefile`"); } @@ -792,7 +787,7 @@ fn make_test( }; // Scan the test file to discover its revisions, if any. - let early_props = EarlyProps::from_file(&config, &test_path); + let early_props = EarlyProps::from_file(&cx.config, &test_path); // Normally we create one libtest structure per revision, with two exceptions: // - If a test doesn't use revisions, create a dummy revision (None) so that @@ -800,49 +795,49 @@ fn make_test( // - Incremental tests inherently can't run their revisions in parallel, so // we treat them like non-revisioned tests here. Incremental revisions are // handled internally by `runtest::run` instead. - let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental { + let revisions = if early_props.revisions.is_empty() || cx.config.mode == Mode::Incremental { vec![None] } else { early_props.revisions.iter().map(|r| Some(r.as_str())).collect() }; - // For each revision (or the sole dummy revision), create and return a + // For each revision (or the sole dummy revision), create and append a // `test::TestDescAndFn` that can be handed over to libtest. - revisions - .into_iter() - .map(|revision| { - // Create a test name and description to hand over to libtest. - let src_file = - std::fs::File::open(&test_path).expect("open test file to parse ignores"); - let test_name = crate::make_test_name(&config, testpaths, revision); - // Create a libtest description for the test/revision. - // This is where `ignore-*`/`only-*`/`needs-*` directives are handled, - // because they need to set the libtest ignored flag. - let mut desc = make_test_description( - &config, cache, test_name, &test_path, src_file, revision, poisoned, - ); + collector.tests.extend(revisions.into_iter().map(|revision| { + // Create a test name and description to hand over to libtest. + let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); + let test_name = make_test_name(&cx.config, testpaths, revision); + // Create a libtest description for the test/revision. + // This is where `ignore-*`/`only-*`/`needs-*` directives are handled, + // because they need to set the libtest ignored flag. + let mut desc = make_test_description( + &cx.config, + &cx.cache, + test_name, + &test_path, + src_file, + revision, + &mut collector.poisoned, + ); - // If a test's inputs haven't changed since the last time it ran, - // mark it as ignored so that libtest will skip it. - if !config.force_rerun - && is_up_to_date(&config, testpaths, &early_props, revision, inputs) - { - desc.ignore = true; - // Keep this in sync with the "up-to-date" message detected by bootstrap. - desc.ignore_message = Some("up-to-date"); - } + // If a test's inputs haven't changed since the last time it ran, + // mark it as ignored so that libtest will skip it. + if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) { + desc.ignore = true; + // Keep this in sync with the "up-to-date" message detected by bootstrap. + desc.ignore_message = Some("up-to-date"); + } - // Create the callback that will run this test/revision when libtest calls it. - let testfn = make_test_closure(config.clone(), testpaths, revision); + // Create the callback that will run this test/revision when libtest calls it. + let testfn = make_test_closure(Arc::clone(&cx.config), testpaths, revision); - test::TestDescAndFn { desc, testfn } - }) - .collect() + test::TestDescAndFn { desc, testfn } + })); } /// The path of the `stamp` file that gets created or updated whenever a /// particular test completes successfully. -fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { +fn stamp_file_path(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf { output_base_dir(config, testpaths, revision).join("stamp") } @@ -891,21 +886,20 @@ fn files_related_to_test( /// (This is not very reliable in some circumstances, so the `--force-rerun` /// flag can be used to ignore up-to-date checking and always re-run tests.) fn is_up_to_date( - config: &Config, + cx: &TestCollectorCx, testpaths: &TestPaths, props: &EarlyProps, revision: Option<&str>, - inputs: &Stamp, // Last-modified timestamp of the compiler, compiletest etc ) -> bool { - let stamp_name = stamp(config, testpaths, revision); + let stamp_file_path = stamp_file_path(&cx.config, testpaths, revision); // Check the config hash inside the stamp file. - let contents = match fs::read_to_string(&stamp_name) { + let contents = match fs::read_to_string(&stamp_file_path) { Ok(f) => f, Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"), // The test hasn't succeeded yet, so it is not up-to-date. Err(_) => return false, }; - let expected_hash = runtest::compute_stamp_hash(config); + let expected_hash = runtest::compute_stamp_hash(&cx.config); if contents != expected_hash { // Some part of compiletest configuration has changed since the test // last succeeded, so it is not up-to-date. @@ -914,14 +908,14 @@ fn is_up_to_date( // Check the timestamp of the stamp file against the last modified time // of all files known to be relevant to the test. - let mut inputs = inputs.clone(); - for path in files_related_to_test(config, testpaths, props, revision) { - inputs.add_path(&path); + let mut inputs_stamp = cx.common_inputs_stamp.clone(); + for path in files_related_to_test(&cx.config, testpaths, props, revision) { + inputs_stamp.add_path(&path); } // If no relevant files have been modified since the stamp file was last // written, the test is up-to-date. - inputs < Stamp::from_path(&stamp_name) + inputs_stamp < Stamp::from_path(&stamp_file_path) } /// The maximum of a set of file-modified timestamps. @@ -1029,11 +1023,11 @@ fn make_test_closure( /// To avoid problems, we forbid test names from overlapping in this way. /// /// See <https://github.com/rust-lang/rust/pull/109509> for more context. -fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) { +fn check_for_overlapping_test_paths(found_path_stems: &HashSet<PathBuf>) { let mut collisions = Vec::new(); - for path in found_paths { + for path in found_path_stems { for ancestor in path.ancestors().skip(1) { - if found_paths.contains(ancestor) { + if found_path_stems.contains(ancestor) { collisions.push((path, ancestor)); } } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index b0f87593f95..2f0c7d8ddc5 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -18,8 +18,8 @@ fn main() { let config = Arc::new(parse_config(env::args().collect())); - if !config.has_tidy && config.mode == Mode::Rustdoc { - eprintln!("warning: `tidy` is not installed; diffs will not be generated"); + if !config.has_html_tidy && config.mode == Mode::Rustdoc { + eprintln!("warning: `tidy` (html-tidy.org) is not installed; diffs will not be generated"); } if !config.profiler_runtime && config.mode == Mode::CoverageRun { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 69a47fcd0fb..36c5106ddad 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1,5 +1,3 @@ -// ignore-tidy-filelength - use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::ffi::OsString; @@ -29,7 +27,7 @@ use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::read2::{Truncated, read2_abbreviated}; use crate::util::{PathBufExt, add_dylib_path, logv, static_regex}; -use crate::{ColorConfig, json}; +use crate::{ColorConfig, json, stamp_file_path}; mod debugger; @@ -468,7 +466,19 @@ impl<'test> TestCx<'test> { if let Some(revision) = self.revision { let normalized_revision = normalize_revision(revision); - cmd.args(&["--cfg", &normalized_revision]); + let cfg_arg = ["--cfg", &normalized_revision]; + let arg = format!("--cfg={normalized_revision}"); + if self + .props + .compile_flags + .windows(2) + .any(|args| args == cfg_arg || args[0] == arg || args[1] == arg) + { + panic!( + "error: redundant cfg argument `{normalized_revision}` is already created by the revision" + ); + } + cmd.args(cfg_arg); } if !self.props.no_auto_check_cfg { @@ -1885,7 +1895,7 @@ impl<'test> TestCx<'test> { } fn compare_to_default_rustdoc(&mut self, out_dir: &Path) { - if !self.config.has_tidy { + if !self.config.has_html_tidy { return; } println!("info: generating a diff against nightly rustdoc"); @@ -2595,8 +2605,8 @@ impl<'test> TestCx<'test> { } fn create_stamp(&self) { - let stamp = crate::stamp(&self.config, self.testpaths, self.revision); - fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap(); + let stamp_file_path = stamp_file_path(&self.config, self.testpaths, self.revision); + fs::write(&stamp_file_path, compute_stamp_hash(&self.config)).unwrap(); } fn init_incremental_test(&self) { diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d0f9fa7ac42..778061e1310 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -dd5127615ad626741a1116d022cf784637ac05df +54791efd8235805dcfbdad3b8788e08f2142c50b diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index 146f9902f6f..5624c4c479e 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -13,7 +13,7 @@ fn err_sb_ub<'tcx>( msg: String, help: Vec<String>, history: Option<TagHistory>, -) -> InterpError<'tcx> { +) -> InterpErrorKind<'tcx> { err_machine_stop!(TerminationInfo::StackedBorrowsUb { msg, help, history }) } @@ -376,7 +376,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { /// Report a descriptive error when `new` could not be granted from `derived_from`. #[inline(never)] // This is only called on fatal code paths - pub(super) fn grant_error(&self, stack: &Stack) -> InterpError<'tcx> { + pub(super) fn grant_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> { let Operation::Retag(op) = &self.operation else { unreachable!("grant_error should only be called during a retag") }; @@ -402,7 +402,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { /// Report a descriptive error when `access` is not permitted based on `tag`. #[inline(never)] // This is only called on fatal code paths - pub(super) fn access_error(&self, stack: &Stack) -> InterpError<'tcx> { + pub(super) fn access_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> { // Deallocation and retagging also do an access as part of their thing, so handle that here, too. let op = match &self.operation { Operation::Access(op) => op, @@ -424,7 +424,11 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { } #[inline(never)] // This is only called on fatal code paths - pub(super) fn protector_error(&self, item: &Item, kind: ProtectorKind) -> InterpError<'tcx> { + pub(super) fn protector_error( + &self, + item: &Item, + kind: ProtectorKind, + ) -> InterpErrorKind<'tcx> { let protected = match kind { ProtectorKind::WeakProtector => "weakly protected", ProtectorKind::StrongProtector => "strongly protected", @@ -445,7 +449,7 @@ impl<'history, 'ecx, 'tcx> DiagnosticCx<'history, 'ecx, 'tcx> { } #[inline(never)] // This is only called on fatal code paths - pub fn dealloc_error(&self, stack: &Stack) -> InterpError<'tcx> { + pub fn dealloc_error(&self, stack: &Stack) -> InterpErrorKind<'tcx> { let Operation::Dealloc(op) = &self.operation else { unreachable!("dealloc_error should only be called during a deallocation") }; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index cb840f19e3b..b2fd9b2bf05 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -298,7 +298,7 @@ pub(super) struct TbError<'node> { impl TbError<'_> { /// Produce a UB error. - pub fn build<'tcx>(self) -> InterpError<'tcx> { + pub fn build<'tcx>(self) -> InterpErrorKind<'tcx> { use TransitionError::*; let cause = self.access_cause; let accessed = self.accessed_info; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 15cefab1a68..a551b017dfc 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -674,7 +674,7 @@ impl<'tcx> Tree { Ok(()) } }, - |args: ErrHandlerArgs<'_, TransitionError>| -> InterpError<'tcx> { + |args: ErrHandlerArgs<'_, TransitionError>| -> InterpErrorKind<'tcx> { let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args; TbError { conflicting_info, @@ -772,7 +772,7 @@ impl<'tcx> Tree { let err_handler = |perms_range: Range<u64>, access_cause: diagnostics::AccessCause, args: ErrHandlerArgs<'_, TransitionError>| - -> InterpError<'tcx> { + -> InterpErrorKind<'tcx> { let ErrHandlerArgs { error_kind, conflicting_info, accessed_info } = args; TbError { conflicting_info, diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 475139a3b51..f055662891e 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -214,7 +214,7 @@ pub fn report_error<'tcx>( ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, e: InterpErrorInfo<'tcx>, ) -> Option<(i64, bool)> { - use InterpError::*; + use InterpErrorKind::*; use UndefinedBehaviorInfo::*; let mut msg = vec![]; diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index de293495e86..59776a484b5 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -245,17 +245,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let val = match which { Op::MirOp(mir_op) => { // This does NaN adjustments. - let val = this.binary_op(mir_op, &left, &right).map_err(|err| { - match err.kind() { - &InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { + let val = this.binary_op(mir_op, &left, &right).map_err_kind(|kind| { + match kind { + InterpErrorKind::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => { // This resets the interpreter backtrace, but it's not worth avoiding that. let shift_amount = match shift_amount { Either::Left(v) => v.to_string(), Either::Right(v) => v.to_string(), }; - err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}").into() + err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}") } - _ => err + kind => kind } })?; if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 7fce5b63306..f6f91e58969 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -289,11 +289,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_get_alloc_id" => { let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; - let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err(|_e| { + let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}" ))) - .into() })?; this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs index ff995f38196..61410f7c4e0 100644 --- a/src/tools/miri/tests/pass/dyn-upcast.rs +++ b/src/tools/miri/tests/pass/dyn-upcast.rs @@ -9,6 +9,7 @@ fn main() { struct_(); replace_vptr(); vtable_nop_cast(); + drop_principal(); } fn vtable_nop_cast() { @@ -430,3 +431,54 @@ fn replace_vptr() { let s = S(42); invoke_outer(&s); } + +fn drop_principal() { + use std::alloc::Layout; + use std::any::Any; + + const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> { + x + } + + trait Bar: Send + Sync {} + + impl<T: Send + Sync> Bar for T {} + + const fn yeet_principal_2(x: Box<dyn Bar>) -> Box<dyn Send> { + x + } + + struct CallMe<F: FnOnce()>(Option<F>); + + impl<F: FnOnce()> CallMe<F> { + fn new(f: F) -> Self { + CallMe(Some(f)) + } + } + + impl<F: FnOnce()> Drop for CallMe<F> { + fn drop(&mut self) { + (self.0.take().unwrap())(); + } + } + + fn goodbye() { + println!("goodbye"); + } + + let x = Box::new(CallMe::new(goodbye)) as Box<dyn Any + Send>; + let x_layout = Layout::for_value(&*x); + let y = yeet_principal(x); + let y_layout = Layout::for_value(&*y); + assert_eq!(x_layout, y_layout); + println!("before"); + drop(y); + + let x = Box::new(CallMe::new(goodbye)) as Box<dyn Bar>; + let x_layout = Layout::for_value(&*x); + let y = yeet_principal_2(x); + let y_layout = Layout::for_value(&*y); + assert_eq!(x_layout, y_layout); + println!("before"); + drop(y); +} diff --git a/src/tools/miri/tests/pass/dyn-upcast.stdout b/src/tools/miri/tests/pass/dyn-upcast.stdout new file mode 100644 index 00000000000..edd99a114a1 --- /dev/null +++ b/src/tools/miri/tests/pass/dyn-upcast.stdout @@ -0,0 +1,4 @@ +before +goodbye +before +goodbye diff --git a/src/tools/nix-dev-shell/envrc-flake b/src/tools/nix-dev-shell/envrc-flake new file mode 100644 index 00000000000..218d88d8721 --- /dev/null +++ b/src/tools/nix-dev-shell/envrc-flake @@ -0,0 +1,8 @@ +# If you want to use this as an .envrc file to create a shell with necessery components +# to develop rustc, use the following command in the root of the rusr checkout: +# +# ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && echo .envrc >> .git/info/exclude + +if nix flake show path:./src/tools/nix-dev-shell &> /dev/null; then + use flake path:./src/tools/nix-dev-shell +fi diff --git a/src/tools/nix-dev-shell/envrc-shell b/src/tools/nix-dev-shell/envrc-shell new file mode 100644 index 00000000000..fb7231a6c30 --- /dev/null +++ b/src/tools/nix-dev-shell/envrc-shell @@ -0,0 +1,7 @@ +# If you want to use this as an .envrc file to create a shell with necessery components +# to develop rustc, use the following command in the root of the rusr checkout: +# +# ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc && echo .envrc >> .git/info/exclude + +use nix ./src/tools/nix-dev-shell/shell.nix + diff --git a/src/tools/nix-dev-shell/flake.nix b/src/tools/nix-dev-shell/flake.nix new file mode 100644 index 00000000000..8ab5e097427 --- /dev/null +++ b/src/tools/nix-dev-shell/flake.nix @@ -0,0 +1,33 @@ +{ + description = "rustc dev shell"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + x = import ./x { inherit pkgs; }; + in + { + devShells.default = with pkgs; mkShell { + name = "rustc-dev-shell"; + nativeBuildInputs = with pkgs; [ + binutils cmake ninja pkg-config python3 git curl cacert patchelf nix + ]; + buildInputs = with pkgs; [ + openssl glibc.out glibc.static x + ]; + # Avoid creating text files for ICEs. + RUSTC_ICE = "0"; + # Provide `libstdc++.so.6` for the self-contained lld. + LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [ + stdenv.cc.cc.lib + ]}"; + }; + } + ); +} diff --git a/src/tools/nix-dev-shell/shell.nix b/src/tools/nix-dev-shell/shell.nix new file mode 100644 index 00000000000..8a5cbb7c89e --- /dev/null +++ b/src/tools/nix-dev-shell/shell.nix @@ -0,0 +1,19 @@ +{ pkgs ? import <nixpkgs> {} }: +let + x = import ./x { inherit pkgs; }; +in +pkgs.mkShell { + name = "rustc"; + nativeBuildInputs = with pkgs; [ + binutils cmake ninja pkg-config python3 git curl cacert patchelf nix + ]; + buildInputs = with pkgs; [ + openssl glibc.out glibc.static x + ]; + # Avoid creating text files for ICEs. + RUSTC_ICE = "0"; + # Provide `libstdc++.so.6` for the self-contained lld. + LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [ + stdenv.cc.cc.lib + ]}"; +} diff --git a/src/tools/nix-dev-shell/x/default.nix b/src/tools/nix-dev-shell/x/default.nix new file mode 100644 index 00000000000..e6dfbad6f19 --- /dev/null +++ b/src/tools/nix-dev-shell/x/default.nix @@ -0,0 +1,22 @@ +{ + pkgs ? import <nixpkgs> { }, +}: +pkgs.stdenv.mkDerivation { + name = "x"; + + src = ./x.rs; + dontUnpack = true; + + nativeBuildInputs = with pkgs; [ rustc ]; + + buildPhase = '' + PYTHON=${pkgs.lib.getExe pkgs.python3} rustc -Copt-level=3 --crate-name x $src --out-dir $out/bin + ''; + + meta = with pkgs.lib; { + description = "Helper for rust-lang/rust x.py"; + homepage = "https://github.com/rust-lang/rust/blob/master/src/tools/x"; + license = licenses.mit; + mainProgram = "x"; + }; +} diff --git a/src/tools/nix-dev-shell/x/x.rs b/src/tools/nix-dev-shell/x/x.rs new file mode 100644 index 00000000000..9f83b8fd62e --- /dev/null +++ b/src/tools/nix-dev-shell/x/x.rs @@ -0,0 +1,50 @@ +// git clone https://github.com/rust-lang/rust/blob/0ea7ddcc35a2fcaa5da8a7dcfc118c9fb4a63b95/src/tools/x/src/main.rs +// patched to stop doing python probing, stop the probe, please dont, i have a python +//! Run bootstrap from any subdirectory of a rust compiler checkout. +//! +//! We prefer `exec`, to avoid adding an extra process in the process tree. +//! However, since `exec` isn't available on Windows, we indirect through +//! `exec_or_status`, which will call `exec` on unix and `status` on Windows. +//! +//! We use `powershell.exe x.ps1` on Windows, and `sh -c x` on Unix, those are +//! the ones that call `x.py`. We use `sh -c` on Unix, because it is a standard. +//! We also don't use `pwsh` on Windows, because it is not installed by default; + +use std::env; +use std::os::unix::process::CommandExt; +use std::process::{self, Command}; + +fn main() { + match env::args().skip(1).next().as_deref() { + Some("--wrapper-version") => { + println!("0.1.0"); + return; + } + _ => {} + } + let current = match env::current_dir() { + Ok(dir) => dir, + Err(err) => { + eprintln!("Failed to get current directory: {err}"); + process::exit(1); + } + }; + + for dir in current.ancestors() { + let candidate = dir.join("x.py"); + if candidate.exists() { + let mut cmd = Command::new(env!("PYTHON")); + cmd.arg(dir.join("x.py")); + cmd.args(env::args().skip(1)).current_dir(dir); + + let error = cmd.exec(); + eprintln!("Failed to invoke `{:?}`: {}", cmd, error); + } + } + + eprintln!( + "x.py not found. Please run inside of a checkout of `https://github.com/rust-lang/rust`." + ); + + process::exit(1); +} diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 7891edc2447..4a6da47a47d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1188,6 +1188,15 @@ dependencies = [ ] [[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] name = "object" version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1439,9 +1448,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.2.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" dependencies = [ "once_cell", "protobuf-support", @@ -1450,9 +1459,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.2.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" dependencies = [ "thiserror", ] @@ -1488,9 +1497,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.68.0" +version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8cb51bb4534ac3e9c74f1d9bd90e607e60f94f734b1cf1a66f753ad2af6ed7" +checksum = "c6999d098000b98415939f13158dac78cb3eeeb7b0c073847f3e4b623866e27c" dependencies = [ "bitflags 2.6.0", "ra-ap-rustc_index", @@ -1499,9 +1508,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.68.0" +version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b640fba2b7ef4f875459e2e76daeb846ef341d1d376fa758962ac0eba79bce6" +checksum = "ae9fb312d942817dab10790881f555928c1f6a11a85186e8e573ad4a86c7d3be" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1510,9 +1519,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.68.0" +version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faef502419ba5ac9d3079b1a835c6e5b4e605388254bbe55eb5683936f541be9" +checksum = "766e3990eb1066a06deefc561b5a01b32ca5c9211feea31cbf4ed50611519872" dependencies = [ "proc-macro2", "quote", @@ -1521,9 +1530,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.68.0" +version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da7f9d533b8d5be6704558da741ff20b982ad4647b1e9e08632853e4fecf9d5" +checksum = "f4afa98eb7889c137d5a3f1cd189089e16da04d1e4837d358a67aa3dab10ffbe" dependencies = [ "unicode-properties", "unicode-xid", @@ -1531,9 +1540,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.68.0" +version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94389cf81c651b1bda9ac45d3de6a2d851bb6fd4cb893875daa44e419c94205f" +checksum = "d9234c96ffb0565286790407fb7eb7f55ebf69267de4db382fdec0a17f14b0e2" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1541,9 +1550,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.68.0" +version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3679d8dd0114ed6000918309f843782738e51c99d8e4baec0d0f706e4d948819" +checksum = "273d5f72926a58c7eea27aebc898d1d5b32d23d2342f692a94a2cf8746aa4a2f" dependencies = [ "ra-ap-rustc_index", "rustc-hash", @@ -1765,9 +1774,9 @@ dependencies = [ [[package]] name = "scip" -version = "0.3.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5dc1bd66649133af84ab62436ddd2856c2605182b02dec2cd197f684dfe15ef" +checksum = "8dfafd2fa14c6237fa1fc4310f739d02fa915d92977fa069426591f1de046f81" dependencies = [ "protobuf", ] @@ -2093,10 +2102,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", + "time-macros", ] [[package]] @@ -2106,6 +2119,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] name = "tinyvec" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2213,6 +2236,7 @@ checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "sharded-slab", "thread_local", + "time", "tracing-core", "tracing-log", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0b3d6e2a1ef..8c099f324b4 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -74,7 +74,7 @@ proc-macro-srv = { path = "./crates/proc-macro-srv", version = "0.0.0" } proc-macro-srv-cli = { path = "./crates/proc-macro-srv-cli", version = "0.0.0" } profile = { path = "./crates/profile", version = "0.0.0" } project-model = { path = "./crates/project-model", version = "0.0.0" } -salsa = { path = "./crates/salsa", version = "0.0.0" } +ra-salsa = { path = "./crates/ra-salsa", package = "salsa", version = "0.0.0" } span = { path = "./crates/span", version = "0.0.0" } stdx = { path = "./crates/stdx", version = "0.0.0" } syntax = { path = "./crates/syntax", version = "0.0.0" } @@ -85,11 +85,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.68.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.68.0", default-features = false } -ra-ap-rustc_index = { version = "0.68.0", default-features = false } -ra-ap-rustc_abi = { version = "0.68.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.68.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.71.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.71.0", default-features = false } +ra-ap-rustc_index = { version = "0.71.0", default-features = false } +ra-ap-rustc_abi = { version = "0.71.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.71.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } @@ -153,6 +153,9 @@ tracing-tree = "0.3.0" tracing-subscriber = { version = "0.3.18", default-features = false, features = [ "registry", "fmt", + "local-time", + "std", + "time", "tracing-log", ] } triomphe = { version = "0.1.10", default-features = false, features = ["std"] } diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index b17b08a720c..788ceb8857e 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -16,7 +16,7 @@ doctest = false lz4_flex = { version = "0.11", default-features = false } la-arena.workspace = true -salsa.workspace = true +ra-salsa.workspace = true rustc-hash.workspace = true triomphe.workspace = true semver.workspace = true diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs index 4fb6654b612..7e40f5408f1 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -3,8 +3,8 @@ use std::fmt; +use ra_salsa::Durability; use rustc_hash::FxHashMap; -use salsa::Durability; use triomphe::Arc; use vfs::FileId; diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index f5109339ad1..57522d69321 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -288,6 +288,11 @@ pub struct CrateData { /// The cfg options that could be used by the crate pub potential_cfg_options: Option<Arc<CfgOptions>>, pub env: Env, + /// The dependencies of this crate. + /// + /// Note that this may contain more dependencies than the crate actually uses. + /// A common example is the test crate which is included but only actually is active when + /// declared in source via `extern crate test`. pub dependencies: Vec<Dependency>, pub origin: CrateOrigin, pub is_proc_macro: bool, diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 46e258d46f5..0a9e83bc3ba 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -5,8 +5,8 @@ mod input; use std::panic; +use ra_salsa::Durability; use rustc_hash::FxHashMap; -use salsa::Durability; use span::EditionedFileId; use syntax::{ast, Parse, SourceFile, SyntaxError}; use triomphe::Arc; @@ -20,7 +20,7 @@ pub use crate::{ TargetLayoutLoadResult, }, }; -pub use salsa::{self, Cancelled}; +pub use ra_salsa::{self, Cancelled}; pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, VfsPath}; pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; @@ -28,11 +28,11 @@ pub use semver::{BuildMetadata, Prerelease, Version, VersionReq}; #[macro_export] macro_rules! impl_intern_key { ($name:ident) => { - impl $crate::salsa::InternKey for $name { - fn from_intern_id(v: $crate::salsa::InternId) -> Self { + impl $crate::ra_salsa::InternKey for $name { + fn from_intern_id(v: $crate::ra_salsa::InternId) -> Self { $name(v) } - fn as_intern_id(&self) -> $crate::salsa::InternId { + fn as_intern_id(&self) -> $crate::ra_salsa::InternId { self.0 } } @@ -55,30 +55,30 @@ pub trait FileLoader { /// Database which stores all significant input facts: source code and project /// model. Everything else in rust-analyzer is derived from these queries. -#[salsa::query_group(SourceDatabaseStorage)] +#[ra_salsa::query_group(SourceDatabaseStorage)] pub trait SourceDatabase: FileLoader + std::fmt::Debug { - #[salsa::input] + #[ra_salsa::input] fn compressed_file_text(&self, file_id: FileId) -> Arc<[u8]>; /// Text of the file. - #[salsa::lru] + #[ra_salsa::lru] fn file_text(&self, file_id: FileId) -> Arc<str>; /// Parses the file into the syntax tree. - #[salsa::lru] + #[ra_salsa::lru] fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>; /// Returns the set of errors obtained from parsing the file including validation errors. fn parse_errors(&self, file_id: EditionedFileId) -> Option<Arc<[SyntaxError]>>; /// The crate graph. - #[salsa::input] + #[ra_salsa::input] fn crate_graph(&self) -> Arc<CrateGraph>; - #[salsa::input] + #[ra_salsa::input] fn crate_workspace_data(&self) -> Arc<FxHashMap<CrateId, Arc<CrateWorkspaceData>>>; - #[salsa::transparent] + #[ra_salsa::transparent] fn toolchain_channel(&self, krate: CrateId) -> Option<ReleaseChannel>; } @@ -126,14 +126,14 @@ fn file_text(db: &dyn SourceDatabase, file_id: FileId) -> Arc<str> { /// We don't want to give HIR knowledge of source roots, hence we extract these /// methods into a separate DB. -#[salsa::query_group(SourceRootDatabaseStorage)] +#[ra_salsa::query_group(SourceRootDatabaseStorage)] pub trait SourceRootDatabase: SourceDatabase { /// Path to a file, relative to the root of its source root. /// Source root of the file. - #[salsa::input] + #[ra_salsa::input] fn file_source_root(&self, file_id: FileId) -> SourceRootId; /// Contents of the source root. - #[salsa::input] + #[ra_salsa::input] fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; /// Crates whose root fool is in `id`. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index d17ebd7ff92..3ecb57c7567 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -506,14 +506,17 @@ impl ExternCrateDeclData { let crate_id = if name == sym::self_.clone() { Some(krate) } else { - db.crate_def_map(krate) - .extern_prelude() - .find(|&(prelude_name, ..)| *prelude_name == name) - .map(|(_, (root, _))| root.krate()) + db.crate_graph()[krate].dependencies.iter().find_map(|dep| { + if dep.name.symbol() == name.symbol() { + Some(dep.crate_id) + } else { + None + } + }) }; Arc::new(Self { - name: extern_crate.name.clone(), + name, visibility: item_tree[extern_crate.visibility].clone(), alias: extern_crate.alias.clone(), crate_id, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index b1103d35cab..aeda302f35c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -1,5 +1,5 @@ //! Defines database & queries for name resolution. -use base_db::{salsa, CrateId, SourceDatabase, Upcast}; +use base_db::{ra_salsa, CrateId, SourceDatabase, Upcast}; use either::Either; use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId}; use intern::{sym, Interned}; @@ -31,71 +31,71 @@ use crate::{ UseId, UseLoc, VariantId, }; -#[salsa::query_group(InternDatabaseStorage)] +#[ra_salsa::query_group(InternDatabaseStorage)] pub trait InternDatabase: SourceDatabase { // region: items - #[salsa::interned] + #[ra_salsa::interned] fn intern_use(&self, loc: UseLoc) -> UseId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_function(&self, loc: FunctionLoc) -> FunctionId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_struct(&self, loc: StructLoc) -> StructId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_union(&self, loc: UnionLoc) -> UnionId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_enum(&self, loc: EnumLoc) -> EnumId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_const(&self, loc: ConstLoc) -> ConstId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_static(&self, loc: StaticLoc) -> StaticId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_trait(&self, loc: TraitLoc) -> TraitId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_impl(&self, loc: ImplLoc) -> ImplId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id; - #[salsa::interned] + #[ra_salsa::interned] fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId; // endregion: items - #[salsa::interned] + #[ra_salsa::interned] fn intern_block(&self, loc: BlockLoc) -> BlockId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId; } -#[salsa::query_group(DefDatabaseStorage)] +#[ra_salsa::query_group(DefDatabaseStorage)] pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> { /// Whether to expand procedural macros during name resolution. - #[salsa::input] + #[ra_salsa::input] fn expand_proc_attr_macros(&self) -> bool; /// Computes an [`ItemTree`] for the given file or macro expansion. - #[salsa::invoke(ItemTree::file_item_tree_query)] + #[ra_salsa::invoke(ItemTree::file_item_tree_query)] fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>; - #[salsa::invoke(ItemTree::block_item_tree_query)] + #[ra_salsa::invoke(ItemTree::block_item_tree_query)] fn block_item_tree(&self, block_id: BlockId) -> Arc<ItemTree>; - #[salsa::invoke(DefMap::crate_def_map_query)] + #[ra_salsa::invoke(DefMap::crate_def_map_query)] fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>; /// Computes the block-level `DefMap`. - #[salsa::invoke(DefMap::block_def_map_query)] + #[ra_salsa::invoke(DefMap::block_def_map_query)] fn block_def_map(&self, block: BlockId) -> Arc<DefMap>; /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution. @@ -103,139 +103,139 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba // region:data - #[salsa::transparent] - #[salsa::invoke(StructData::struct_data_query)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(StructData::struct_data_query)] fn struct_data(&self, id: StructId) -> Arc<StructData>; - #[salsa::invoke(StructData::struct_data_with_diagnostics_query)] + #[ra_salsa::invoke(StructData::struct_data_with_diagnostics_query)] fn struct_data_with_diagnostics(&self, id: StructId) -> (Arc<StructData>, DefDiagnostics); - #[salsa::transparent] - #[salsa::invoke(StructData::union_data_query)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(StructData::union_data_query)] fn union_data(&self, id: UnionId) -> Arc<StructData>; - #[salsa::invoke(StructData::union_data_with_diagnostics_query)] + #[ra_salsa::invoke(StructData::union_data_with_diagnostics_query)] fn union_data_with_diagnostics(&self, id: UnionId) -> (Arc<StructData>, DefDiagnostics); - #[salsa::invoke(EnumData::enum_data_query)] + #[ra_salsa::invoke(EnumData::enum_data_query)] fn enum_data(&self, e: EnumId) -> Arc<EnumData>; - #[salsa::transparent] - #[salsa::invoke(EnumVariantData::enum_variant_data_query)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(EnumVariantData::enum_variant_data_query)] fn enum_variant_data(&self, id: EnumVariantId) -> Arc<EnumVariantData>; - #[salsa::invoke(EnumVariantData::enum_variant_data_with_diagnostics_query)] + #[ra_salsa::invoke(EnumVariantData::enum_variant_data_with_diagnostics_query)] fn enum_variant_data_with_diagnostics( &self, id: EnumVariantId, ) -> (Arc<EnumVariantData>, DefDiagnostics); - #[salsa::transparent] - #[salsa::invoke(VariantData::variant_data)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(VariantData::variant_data)] fn variant_data(&self, id: VariantId) -> Arc<VariantData>; - #[salsa::transparent] - #[salsa::invoke(ImplData::impl_data_query)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(ImplData::impl_data_query)] fn impl_data(&self, e: ImplId) -> Arc<ImplData>; - #[salsa::invoke(ImplData::impl_data_with_diagnostics_query)] + #[ra_salsa::invoke(ImplData::impl_data_with_diagnostics_query)] fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, DefDiagnostics); - #[salsa::transparent] - #[salsa::invoke(TraitData::trait_data_query)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(TraitData::trait_data_query)] fn trait_data(&self, e: TraitId) -> Arc<TraitData>; - #[salsa::invoke(TraitData::trait_data_with_diagnostics_query)] + #[ra_salsa::invoke(TraitData::trait_data_with_diagnostics_query)] fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitData>, DefDiagnostics); - #[salsa::invoke(TraitAliasData::trait_alias_query)] + #[ra_salsa::invoke(TraitAliasData::trait_alias_query)] fn trait_alias_data(&self, e: TraitAliasId) -> Arc<TraitAliasData>; - #[salsa::invoke(TypeAliasData::type_alias_data_query)] + #[ra_salsa::invoke(TypeAliasData::type_alias_data_query)] fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>; - #[salsa::invoke(FunctionData::fn_data_query)] + #[ra_salsa::invoke(FunctionData::fn_data_query)] fn function_data(&self, func: FunctionId) -> Arc<FunctionData>; - #[salsa::invoke(ConstData::const_data_query)] + #[ra_salsa::invoke(ConstData::const_data_query)] fn const_data(&self, konst: ConstId) -> Arc<ConstData>; - #[salsa::invoke(StaticData::static_data_query)] + #[ra_salsa::invoke(StaticData::static_data_query)] fn static_data(&self, statik: StaticId) -> Arc<StaticData>; - #[salsa::invoke(Macro2Data::macro2_data_query)] + #[ra_salsa::invoke(Macro2Data::macro2_data_query)] fn macro2_data(&self, makro: Macro2Id) -> Arc<Macro2Data>; - #[salsa::invoke(MacroRulesData::macro_rules_data_query)] + #[ra_salsa::invoke(MacroRulesData::macro_rules_data_query)] fn macro_rules_data(&self, makro: MacroRulesId) -> Arc<MacroRulesData>; - #[salsa::invoke(ProcMacroData::proc_macro_data_query)] + #[ra_salsa::invoke(ProcMacroData::proc_macro_data_query)] fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>; - #[salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)] + #[ra_salsa::invoke(ExternCrateDeclData::extern_crate_decl_data_query)] fn extern_crate_decl_data(&self, extern_crate: ExternCrateId) -> Arc<ExternCrateDeclData>; // endregion:data - #[salsa::invoke(Body::body_with_source_map_query)] - #[salsa::lru] + #[ra_salsa::invoke(Body::body_with_source_map_query)] + #[ra_salsa::lru] fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>); - #[salsa::invoke(Body::body_query)] + #[ra_salsa::invoke(Body::body_query)] fn body(&self, def: DefWithBodyId) -> Arc<Body>; - #[salsa::invoke(ExprScopes::expr_scopes_query)] + #[ra_salsa::invoke(ExprScopes::expr_scopes_query)] fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>; - #[salsa::invoke(GenericParams::generic_params_query)] + #[ra_salsa::invoke(GenericParams::generic_params_query)] fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>; // region:attrs - #[salsa::invoke(Attrs::fields_attrs_query)] + #[ra_salsa::invoke(Attrs::fields_attrs_query)] fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>; // should this really be a query? - #[salsa::invoke(crate::attr::fields_attrs_source_map)] + #[ra_salsa::invoke(crate::attr::fields_attrs_source_map)] fn fields_attrs_source_map( &self, def: VariantId, ) -> Arc<ArenaMap<LocalFieldId, AstPtr<Either<ast::TupleField, ast::RecordField>>>>; - #[salsa::invoke(AttrsWithOwner::attrs_query)] + #[ra_salsa::invoke(AttrsWithOwner::attrs_query)] fn attrs(&self, def: AttrDefId) -> Attrs; - #[salsa::transparent] - #[salsa::invoke(lang_item::lang_attr)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(lang_item::lang_attr)] fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>; // endregion:attrs - #[salsa::invoke(LangItems::lang_item_query)] + #[ra_salsa::invoke(LangItems::lang_item_query)] fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>; - #[salsa::invoke(ImportMap::import_map_query)] + #[ra_salsa::invoke(ImportMap::import_map_query)] fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; // region:visibilities - #[salsa::invoke(visibility::field_visibilities_query)] + #[ra_salsa::invoke(visibility::field_visibilities_query)] fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>; // FIXME: unify function_visibility and const_visibility? - #[salsa::invoke(visibility::function_visibility_query)] + #[ra_salsa::invoke(visibility::function_visibility_query)] fn function_visibility(&self, def: FunctionId) -> Visibility; - #[salsa::invoke(visibility::const_visibility_query)] + #[ra_salsa::invoke(visibility::const_visibility_query)] fn const_visibility(&self, def: ConstId) -> Visibility; // endregion:visibilities - #[salsa::invoke(LangItems::crate_lang_items_query)] + #[ra_salsa::invoke(LangItems::crate_lang_items_query)] fn crate_lang_items(&self, krate: CrateId) -> Option<Arc<LangItems>>; - #[salsa::invoke(crate::lang_item::notable_traits_in_deps)] + #[ra_salsa::invoke(crate::lang_item::notable_traits_in_deps)] fn notable_traits_in_deps(&self, krate: CrateId) -> Arc<[Arc<[TraitId]>]>; - #[salsa::invoke(crate::lang_item::crate_notable_traits)] + #[ra_salsa::invoke(crate::lang_item::crate_notable_traits)] fn crate_notable_traits(&self, krate: CrateId) -> Option<Arc<[TraitId]>>; fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 0213bd904b6..157c9ef0805 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -71,7 +71,7 @@ use std::{ use base_db::{ impl_intern_key, - salsa::{self, InternValueTrivial}, + ra_salsa::{self, InternValueTrivial}, CrateId, }; use hir_expand::{ @@ -206,85 +206,85 @@ macro_rules! impl_loc { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct FunctionId(salsa::InternId); +pub struct FunctionId(ra_salsa::InternId); type FunctionLoc = AssocItemLoc<Function>; impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); impl_loc!(FunctionLoc, id: Function, container: ItemContainerId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct StructId(salsa::InternId); +pub struct StructId(ra_salsa::InternId); type StructLoc = ItemLoc<Struct>; impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); impl_loc!(StructLoc, id: Struct, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct UnionId(salsa::InternId); +pub struct UnionId(ra_salsa::InternId); pub type UnionLoc = ItemLoc<Union>; impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union); impl_loc!(UnionLoc, id: Union, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct EnumId(salsa::InternId); +pub struct EnumId(ra_salsa::InternId); pub type EnumLoc = ItemLoc<Enum>; impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); impl_loc!(EnumLoc, id: Enum, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ConstId(salsa::InternId); +pub struct ConstId(ra_salsa::InternId); type ConstLoc = AssocItemLoc<Const>; impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); impl_loc!(ConstLoc, id: Const, container: ItemContainerId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct StaticId(salsa::InternId); +pub struct StaticId(ra_salsa::InternId); pub type StaticLoc = AssocItemLoc<Static>; impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); impl_loc!(StaticLoc, id: Static, container: ItemContainerId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct TraitId(salsa::InternId); +pub struct TraitId(ra_salsa::InternId); pub type TraitLoc = ItemLoc<Trait>; impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); impl_loc!(TraitLoc, id: Trait, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TraitAliasId(salsa::InternId); +pub struct TraitAliasId(ra_salsa::InternId); pub type TraitAliasLoc = ItemLoc<TraitAlias>; impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias); impl_loc!(TraitAliasLoc, id: TraitAlias, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TypeAliasId(salsa::InternId); +pub struct TypeAliasId(ra_salsa::InternId); type TypeAliasLoc = AssocItemLoc<TypeAlias>; impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); impl_loc!(TypeAliasLoc, id: TypeAlias, container: ItemContainerId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct ImplId(salsa::InternId); +pub struct ImplId(ra_salsa::InternId); type ImplLoc = ItemLoc<Impl>; impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); impl_loc!(ImplLoc, id: Impl, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct UseId(salsa::InternId); +pub struct UseId(ra_salsa::InternId); type UseLoc = ItemLoc<Use>; impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use); impl_loc!(UseLoc, id: Use, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct ExternCrateId(salsa::InternId); +pub struct ExternCrateId(ra_salsa::InternId); type ExternCrateLoc = ItemLoc<ExternCrate>; impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate); impl_loc!(ExternCrateLoc, id: ExternCrate, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct ExternBlockId(salsa::InternId); +pub struct ExternBlockId(ra_salsa::InternId); type ExternBlockLoc = ItemLoc<ExternBlock>; impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block); impl_loc!(ExternBlockLoc, id: ExternBlock, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct EnumVariantId(salsa::InternId); +pub struct EnumVariantId(ra_salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariantLoc { @@ -296,7 +296,7 @@ impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_e impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct Macro2Id(salsa::InternId); +pub struct Macro2Id(ra_salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Macro2Loc { pub container: ModuleId, @@ -309,7 +309,7 @@ impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2); impl_loc!(Macro2Loc, id: Macro2, container: ModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct MacroRulesId(salsa::InternId); +pub struct MacroRulesId(ra_salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroRulesLoc { pub container: ModuleId, @@ -338,7 +338,7 @@ pub enum MacroExpander { BuiltInEager(EagerExpander), } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct ProcMacroId(salsa::InternId); +pub struct ProcMacroId(ra_salsa::InternId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ProcMacroLoc { pub container: CrateRootModuleId, @@ -351,7 +351,7 @@ impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_ma impl_loc!(ProcMacroLoc, id: Function, container: CrateRootModuleId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct BlockId(salsa::InternId); +pub struct BlockId(ra_salsa::InternId); #[derive(Debug, Hash, PartialEq, Eq, Clone)] pub struct BlockLoc { ast_id: AstId<ast::BlockExpr>, @@ -363,7 +363,7 @@ impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); /// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and /// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct ConstBlockId(salsa::InternId); +pub struct ConstBlockId(ra_salsa::InternId); impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const); #[derive(Debug, Hash, PartialEq, Eq, Clone)] @@ -803,7 +803,7 @@ impl Clone for Box<dyn OpaqueInternableThing> { /// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These /// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct InTypeConstId(salsa::InternId); +pub struct InTypeConstId(ra_salsa::InternId); impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const); // We would like to set `derive(PartialEq)` diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 7f1d19719da..22b9c2b4e37 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -30,8 +30,8 @@ use crate::{ db::DefDatabase, item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_tree::{ - self, AttrOwner, ExternCrate, FieldsShape, FileItemTreeId, ImportKind, ItemTree, - ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, + self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, + ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, }, macro_call_as_call_id, macro_call_as_call_id_with_eager, nameres::{ @@ -93,6 +93,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI proc_macros, from_glob_import: Default::default(), skip_attrs: Default::default(), + unresolved_extern_crates: Default::default(), is_proc_macro: krate.is_proc_macro, }; if tree_id.is_block() { @@ -126,9 +127,11 @@ impl PartialResolvedImport { } #[derive(Clone, Debug, Eq, PartialEq)] -enum ImportSource { - Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind }, - ExternCrate { id: ExternCrateId }, +struct ImportSource { + use_tree: Idx<ast::UseTree>, + id: UseId, + is_prelude: bool, + kind: ImportKind, } #[derive(Debug, Eq, PartialEq)] @@ -154,25 +157,10 @@ impl Import { path, alias, visibility: visibility.clone(), - source: ImportSource::Use { use_tree: idx, id, is_prelude, kind }, + source: ImportSource { use_tree: idx, id, is_prelude, kind }, }); }); } - - fn from_extern_crate( - tree: &ItemTree, - item_tree_id: ItemTreeId<item_tree::ExternCrate>, - id: ExternCrateId, - ) -> Self { - let it = &tree[item_tree_id.value]; - let visibility = &tree[it.visibility]; - Self { - path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())), - alias: it.alias.clone(), - visibility: visibility.clone(), - source: ImportSource::ExternCrate { id }, - } - } } #[derive(Debug, Eq, PartialEq)] @@ -218,11 +206,16 @@ enum MacroDirectiveKind { struct DefCollector<'a> { db: &'a dyn DefDatabase, def_map: DefMap, + // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap<Name, Dependency>, glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>, unresolved_imports: Vec<ImportDirective>, indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec<MacroDirective>, + // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't + // resolve. When we emit diagnostics for unresolved imports, we only do so if the import + // doesn't start with an unresolved crate's name. + unresolved_extern_crates: FxHashSet<Name>, mod_dirs: FxHashMap<LocalModuleId, ModDir>, cfg_options: &'a CfgOptions, /// List of procedural macros defined by this crate. This is read from the dynamic library @@ -310,6 +303,7 @@ impl DefCollector<'_> { } for (name, dep) in &self.deps { + // Add all if dep.is_prelude() { // This is a bit confusing but the gist is that `no_core` and `no_std` remove the // sysroot dependence on `core` and `std` respectively. Our `CrateGraph` is eagerly @@ -329,6 +323,7 @@ impl DefCollector<'_> { if skip { continue; } + crate_data .extern_prelude .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); @@ -788,61 +783,31 @@ impl DefCollector<'_> { let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db.upcast(), Edition::LATEST)) .entered(); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); - match import.source { - ImportSource::ExternCrate { .. } => { - let name = import - .path - .as_ident() - .expect("extern crate should have been desugared to one-element path"); - - let res = self.resolve_extern_crate(name); - - match res { - Some(res) => PartialResolvedImport::Resolved(PerNs::types( - res.into(), - Visibility::Public, - None, - )), - None => PartialResolvedImport::Unresolved, - } - } - ImportSource::Use { .. } => { - let res = self.def_map.resolve_path_fp_with_macro( - self.db, - ResolveMode::Import, - module_id, - &import.path, - BuiltinShadowMode::Module, - None, // An import may resolve to any kind of macro. - ); - - let def = res.resolved_def; - if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { - return PartialResolvedImport::Unresolved; - } + let res = self.def_map.resolve_path_fp_with_macro( + self.db, + ResolveMode::Import, + module_id, + &import.path, + BuiltinShadowMode::Module, + None, // An import may resolve to any kind of macro. + ); - if res.from_differing_crate { - return PartialResolvedImport::Resolved( - def.filter_visibility(|v| matches!(v, Visibility::Public)), - ); - } + let def = res.resolved_def; + if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { + return PartialResolvedImport::Unresolved; + } - // Check whether all namespaces are resolved. - if def.is_full() { - PartialResolvedImport::Resolved(def) - } else { - PartialResolvedImport::Indeterminate(def) - } - } + if res.from_differing_crate { + return PartialResolvedImport::Resolved( + def.filter_visibility(|v| matches!(v, Visibility::Public)), + ); } - } - fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> { - if *name == sym::self_.clone() { - cov_mark::hit!(extern_crate_self_as); - Some(self.def_map.crate_root()) + // Check whether all namespaces are resolved. + if def.is_full() { + PartialResolvedImport::Resolved(def) } else { - self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id }) + PartialResolvedImport::Indeterminate(def) } } @@ -858,8 +823,12 @@ impl DefCollector<'_> { .unwrap_or(Visibility::Public); match import.source { - ImportSource::ExternCrate { .. } - | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => { + ImportSource { + kind: kind @ (ImportKind::Plain | ImportKind::TypeOnly), + id, + use_tree, + .. + } => { let name = match &import.alias { Some(ImportAlias::Alias(name)) => Some(name), Some(ImportAlias::Underscore) => None, @@ -872,40 +841,20 @@ impl DefCollector<'_> { }, }; - let imp = match import.source { - // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 - ImportSource::ExternCrate { id, .. } => { - if self.def_map.block.is_none() && module_id == DefMap::ROOT { - if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = - (def.take_types(), name) - { - if let Ok(def) = def.try_into() { - Arc::get_mut(&mut self.def_map.data) - .unwrap() - .extern_prelude - .insert(name.clone(), (def, Some(id))); - } - } - } - ImportType::ExternCrate(id) - } - ImportSource::Use { kind, id, use_tree, .. } => { - if kind == ImportKind::TypeOnly { - def.values = None; - def.macros = None; - } - ImportType::Import(ImportId { import: id, idx: use_tree }) - } - }; + if kind == ImportKind::TypeOnly { + def.values = None; + def.macros = None; + } + let imp = ImportType::Import(ImportId { import: id, idx: use_tree }); tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportSource::Use { kind: ImportKind::Glob, id, .. } => { + ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => { tracing::debug!("glob import: {:?}", import); match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { - if let ImportSource::Use { id, is_prelude: true, .. } = import.source { + if is_prelude { // Note: This dodgily overrides the injected prelude. The rustc // implementation seems to work the same though. cov_mark::hit!(std_prelude); @@ -1323,7 +1272,7 @@ impl DefCollector<'_> { _ => return Resolved::No, }; - // Skip #[test]/#[bench] expansion, which would merely result in more memory usage + // Skip #[test]/#[bench]/#[test_case] expansion, which would merely result in more memory usage // due to duplicating functions into macro expansions, but only if `cfg(test)` is active, // otherwise they are expanded to nothing and this can impact e.g. diagnostics (due to things // being cfg'ed out). @@ -1332,7 +1281,7 @@ impl DefCollector<'_> { if matches!( def.kind, MacroDefKind::BuiltInAttr(_, expander) - if expander.is_test() || expander.is_bench() + if expander.is_test() || expander.is_bench() || expander.is_test_case() ) { let test_is_active = self.cfg_options.check_atom(&CfgAtom::Flag(sym::test.clone())); @@ -1560,45 +1509,29 @@ impl DefCollector<'_> { } // Emit diagnostics for all remaining unresolved imports. - - // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't - // resolve. We first emit diagnostics for unresolved extern crates and collect the missing - // crate names. Then we emit diagnostics for unresolved imports, but only if the import - // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a - // heuristic, but it works in practice. - let mut diagnosed_extern_crates = FxHashSet::default(); - for directive in &self.unresolved_imports { - if let ImportSource::ExternCrate { id } = directive.import.source { - let item_tree_id = id.lookup(self.db).id; - let item_tree = item_tree_id.item_tree(self.db); - let extern_crate = &item_tree[item_tree_id.value]; - - diagnosed_extern_crates.insert(extern_crate.name.clone()); - - self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( - directive.module_id, - InFile::new(item_tree_id.file_id(), extern_crate.ast_id), - )); - } - } - - for directive in &self.unresolved_imports { - if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } = - directive.import.source - { - if matches!( - (directive.import.path.segments().first(), &directive.import.path.kind), - (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate) - ) { - continue; - } - let item_tree_id = id.lookup(self.db).id; - self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( - directive.module_id, - item_tree_id, - use_tree, - )); + for import in &self.unresolved_imports { + let &ImportDirective { + module_id, + import: + Import { + ref path, + source: ImportSource { use_tree, id, is_prelude: _, kind: _ }, + .. + }, + .. + } = import; + if matches!( + (path.segments().first(), &path.kind), + (Some(krate), PathKind::Plain | PathKind::Abs) if self.unresolved_extern_crates.contains(krate) + ) { + continue; } + let item_tree_id = id.lookup(self.db).id; + self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( + module_id, + item_tree_id, + use_tree, + )); } self.def_map @@ -1623,7 +1556,8 @@ impl ModCollector<'_, '_> { fn collect(&mut self, items: &[ModItem], container: ItemContainerId) { let krate = self.def_collector.def_map.krate; - let is_crate_root = self.module_id == DefMap::ROOT; + let is_crate_root = + self.module_id == DefMap::ROOT && self.def_collector.def_map.block.is_none(); // Note: don't assert that inserted value is fresh: it's simply not true // for macros. @@ -1632,10 +1566,7 @@ impl ModCollector<'_, '_> { // Prelude module is always considered to be `#[macro_use]`. if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude { // Don't insert macros from the prelude into blocks, as they can be shadowed by other macros. - if prelude_module.krate != krate - && is_crate_root - && self.def_collector.def_map.block.is_none() - { + if prelude_module.krate != krate && is_crate_root { cov_mark::hit!(prelude_is_macro_use); self.def_collector.import_macros_from_extern_crate( prelude_module.krate, @@ -1709,26 +1640,73 @@ impl ModCollector<'_, '_> { id: ItemTreeId::new(self.tree_id, item_tree_id), } .intern(db); - if is_crate_root { - self.process_macro_use_extern_crate( - item_tree_id, - id, - attrs.by_key(&sym::macro_use).attrs(), + def_map.modules[self.module_id].scope.define_extern_crate_decl(id); + + let item_tree::ExternCrate { name, visibility, alias, ast_id } = + &self.item_tree[item_tree_id]; + + let is_self = *name == sym::self_; + let resolved = if is_self { + cov_mark::hit!(extern_crate_self_as); + Some(def_map.crate_root()) + } else { + self.def_collector + .deps + .get(name) + .map(|dep| CrateRootModuleId { krate: dep.crate_id }) + }; + + let name = match alias { + Some(ImportAlias::Alias(name)) => Some(name), + Some(ImportAlias::Underscore) => None, + None => Some(name), + }; + + if let Some(resolved) = resolved { + let vis = resolve_vis(def_map, &self.item_tree[*visibility]); + + if is_crate_root { + // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 + if let Some(name) = name { + Arc::get_mut(&mut def_map.data) + .unwrap() + .extern_prelude + .insert(name.clone(), (resolved, Some(id))); + } + // they also allow `#[macro_use]` + if !is_self { + self.process_macro_use_extern_crate( + id, + attrs.by_key(&sym::macro_use).attrs(), + resolved.krate, + ); + } + } + + self.def_collector.update( + module_id, + &[( + name.cloned(), + PerNs::types( + resolved.into(), + vis, + Some(ImportOrExternCrate::ExternCrate(id)), + ), + )], + vis, + Some(ImportType::ExternCrate(id)), + ); + } else { + if let Some(name) = name { + self.def_collector.unresolved_extern_crates.insert(name.clone()); + } + self.def_collector.def_map.diagnostics.push( + DefDiagnostic::unresolved_extern_crate( + module_id, + InFile::new(self.file_id(), *ast_id), + ), ); } - - self.def_collector.def_map.modules[self.module_id] - .scope - .define_extern_crate_decl(id); - self.def_collector.unresolved_imports.push(ImportDirective { - module_id: self.module_id, - import: Import::from_extern_crate( - self.item_tree, - ItemTreeId::new(self.tree_id, item_tree_id), - id, - ), - status: PartialResolvedImport::Unresolved, - }) } ModItem::ExternBlock(block) => self.collect( &self.item_tree[block].children, @@ -1939,27 +1917,15 @@ impl ModCollector<'_, '_> { fn process_macro_use_extern_crate<'a>( &mut self, - extern_crate: FileItemTreeId<ExternCrate>, extern_crate_id: ExternCrateId, macro_use_attrs: impl Iterator<Item = &'a Attr>, + target_crate: CrateId, ) { - let db = self.def_collector.db; - - let target_crate = - match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) { - Some(m) if m.krate == self.def_collector.def_map.krate => { - cov_mark::hit!(ignore_macro_use_extern_crate_self); - return; - } - Some(m) => m.krate, - None => return, - }; - cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); - let mut single_imports = Vec::new(); for attr in macro_use_attrs { - let Some(paths) = attr.parse_path_comma_token_tree(db.upcast()) else { + let Some(paths) = attr.parse_path_comma_token_tree(self.def_collector.db.upcast()) + else { // `#[macro_use]` (without any paths) found, forget collected names and just import // all visible macros. self.def_collector.import_macros_from_extern_crate( @@ -2523,6 +2489,7 @@ mod tests { from_glob_import: Default::default(), skip_attrs: Default::default(), is_proc_macro: false, + unresolved_extern_crates: Default::default(), }; collector.seed_with_top_level(); collector.collect(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 17e82dc16c4..7b02a89e5de 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -416,7 +416,6 @@ pub struct Arc; #[test] fn macro_use_extern_crate_self() { - cov_mark::check!(ignore_macro_use_extern_crate_self); check( r#" //- /main.rs crate:main diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index df9dec69d46..4db21eb46bd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -3,7 +3,7 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - salsa::{self, Durability}, + ra_salsa::{self, Durability}, AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; use hir_expand::{db::ExpandDatabase, files::FilePosition, InFile}; @@ -18,7 +18,7 @@ use crate::{ LocalModuleId, Lookup, ModuleDefId, ModuleId, }; -#[salsa::database( +#[ra_salsa::database( base_db::SourceRootDatabaseStorage, base_db::SourceDatabaseStorage, hir_expand::db::ExpandDatabaseStorage, @@ -26,8 +26,8 @@ use crate::{ crate::db::DefDatabaseStorage )] pub(crate) struct TestDB { - storage: salsa::Storage<TestDB>, - events: Mutex<Option<Vec<salsa::Event>>>, + storage: ra_salsa::Storage<TestDB>, + events: Mutex<Option<Vec<ra_salsa::Event>>>, } impl Default for TestDB { @@ -51,8 +51,8 @@ impl Upcast<dyn DefDatabase> for TestDB { } } -impl salsa::Database for TestDB { - fn salsa_event(&self, event: salsa::Event) { +impl ra_salsa::Database for TestDB { + fn salsa_event(&self, event: ra_salsa::Event) { let mut events = self.events.lock().unwrap(); if let Some(events) = &mut *events { events.push(event); @@ -215,7 +215,7 @@ impl TestDB { None } - pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> { + pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<ra_salsa::Event> { *self.events.lock().unwrap() = Some(Vec::new()); f(); self.events.lock().unwrap().take().unwrap() @@ -228,7 +228,7 @@ impl TestDB { .filter_map(|e| match e.kind { // This is pretty horrible, but `Debug` is the only way to inspect // QueryDescriptor at the moment. - salsa::EventKind::WillExecute { database_key } => { + ra_salsa::EventKind::WillExecute { database_key } => { Some(format!("{:?}", database_key.debug(self))) } _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 2a8691b461c..74effd2fb16 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -51,6 +51,9 @@ impl BuiltinAttrExpander { pub fn is_bench(self) -> bool { matches!(self, BuiltinAttrExpander::Bench) } + pub fn is_test_case(self) -> bool { + matches!(self, BuiltinAttrExpander::TestCase) + } } register_builtin! { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs index de3a7b9f561..1fdf251ba52 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs @@ -1,7 +1,7 @@ //! Defines a unit of change that can applied to the database to get the next //! state. Changes are transactional. use base_db::{ - salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot, + ra_salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot, SourceRootDatabase, }; use rustc_hash::FxHashMap; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 484a8662eb1..d412bf4eee5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,6 +1,6 @@ //! Defines database & queries for macro expansion. -use base_db::{salsa, CrateId, SourceDatabase}; +use base_db::{ra_salsa, CrateId, SourceDatabase}; use either::Either; use limit::Limit; use mbe::MatchedArmIndex; @@ -53,32 +53,32 @@ pub enum TokenExpander { ProcMacro(CustomProcMacroExpander), } -#[salsa::query_group(ExpandDatabaseStorage)] +#[ra_salsa::query_group(ExpandDatabaseStorage)] pub trait ExpandDatabase: SourceDatabase { /// The proc macros. - #[salsa::input] + #[ra_salsa::input] fn proc_macros(&self) -> Arc<ProcMacros>; fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>; /// Main public API -- parses a hir file, not caring whether it's a real /// file or a macro expansion. - #[salsa::transparent] + #[ra_salsa::transparent] fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; /// Implementation for the macro case. - #[salsa::lru] + #[ra_salsa::lru] fn parse_macro_expansion( &self, macro_file: MacroFileId, ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>; - #[salsa::transparent] - #[salsa::invoke(SpanMap::new)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(SpanMap::new)] fn span_map(&self, file_id: HirFileId) -> SpanMap; - #[salsa::transparent] - #[salsa::invoke(crate::span_map::expansion_span_map)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(crate::span_map::expansion_span_map)] fn expansion_span_map(&self, file_id: MacroFileId) -> Arc<ExpansionSpanMap>; - #[salsa::invoke(crate::span_map::real_span_map)] + #[ra_salsa::invoke(crate::span_map::real_span_map)] fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>; /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the @@ -86,15 +86,15 @@ pub trait ExpandDatabase: SourceDatabase { /// /// We encode macro definitions into ids of macro calls, this what allows us /// to be incremental. - #[salsa::interned] + #[ra_salsa::interned] fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_syntax_context(&self, ctx: SyntaxContextData) -> SyntaxContextId; - #[salsa::transparent] + #[ra_salsa::transparent] fn setup_syntax_context_root(&self) -> (); - #[salsa::transparent] - #[salsa::invoke(crate::hygiene::dump_syntax_contexts)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(crate::hygiene::dump_syntax_contexts)] fn dump_syntax_contexts(&self) -> String; /// Lowers syntactic macro call to a token tree representation. That's a firewall @@ -102,18 +102,18 @@ pub trait ExpandDatabase: SourceDatabase { /// subtree. #[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"] fn macro_arg(&self, id: MacroCallId) -> MacroArgResult; - #[salsa::transparent] + #[ra_salsa::transparent] fn macro_arg_considering_derives( &self, id: MacroCallId, kind: &MacroCallKind, ) -> MacroArgResult; /// Fetches the expander for this macro. - #[salsa::transparent] - #[salsa::invoke(TokenExpander::macro_expander)] + #[ra_salsa::transparent] + #[ra_salsa::invoke(TokenExpander::macro_expander)] fn macro_expander(&self, id: MacroDefId) -> TokenExpander; /// Fetches (and compiles) the expander of this decl macro. - #[salsa::invoke(DeclarativeMacroExpander::expander)] + #[ra_salsa::invoke(DeclarativeMacroExpander::expander)] fn decl_macro_expander( &self, def_crate: CrateId, @@ -135,7 +135,7 @@ pub trait ExpandDatabase: SourceDatabase { &self, macro_call: MacroCallId, ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>; - #[salsa::transparent] + #[ra_salsa::transparent] fn syntax_context(&self, file: HirFileId) -> SyntaxContextId; } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index 5e1448f7950..a8191189157 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -97,7 +97,7 @@ fn apply_mark_internal( call_id: MacroCallId, transparency: Transparency, ) -> SyntaxContextId { - use base_db::salsa; + use base_db::ra_salsa; let call_id = Some(call_id); @@ -107,7 +107,7 @@ fn apply_mark_internal( if transparency >= Transparency::Opaque { let parent = opaque; - opaque = salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert( + opaque = ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert( (parent, call_id, transparency), |new_opaque| SyntaxContextData { outer_expn: call_id, @@ -122,7 +122,7 @@ fn apply_mark_internal( if transparency >= Transparency::SemiTransparent { let parent = opaque_and_semitransparent; opaque_and_semitransparent = - salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert( + ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert( (parent, call_id, transparency), |new_opaque_and_semitransparent| SyntaxContextData { outer_expn: call_id, @@ -200,7 +200,7 @@ pub fn marks_rev( pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String { use crate::db::{InternMacroCallLookupQuery, InternSyntaxContextLookupQuery}; - use base_db::salsa::debug::DebugQueryTable; + use base_db::ra_salsa::debug::DebugQueryTable; let mut s = String::from("Expansions:"); let mut entries = InternMacroCallLookupQuery.in_db(db).entries::<Vec<_>>(); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 56cb5fd375c..5d5f72490d0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -30,7 +30,7 @@ use triomphe::Arc; use std::hash::Hash; -use base_db::{salsa::InternValueTrivial, CrateId}; +use base_db::{ra_salsa::InternValueTrivial, CrateId}; use either::Either; use span::{ Edition, EditionedFileId, ErasedFileAstId, FileAstId, HirFileIdRepr, Span, SpanAnchor, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index d928cafdefc..6ff7831fd81 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -15,11 +15,14 @@ pub fn prettify_macro_expansion( span_map: &ExpansionSpanMap, target_crate_id: CrateId, ) -> SyntaxNode { + // Because `syntax_bridge::prettify_macro_expansion::prettify_macro_expansion()` clones subtree for `syn`, + // that means it will be offsetted to the beginning. + let span_offset = syn.text_range().start(); let crate_graph = db.crate_graph(); let target_crate = &crate_graph[target_crate_id]; let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| { - let ctx = span_map.span_at(dollar_crate.text_range().start()).ctx; + let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx; let replacement = syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { let ctx_data = db.lookup_intern_syntax_context(ctx); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 968a828e9df..e41058aac2a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -1,6 +1,6 @@ //! Constant evaluation details -use base_db::{salsa::Cycle, CrateId}; +use base_db::{ra_salsa::Cycle, CrateId}; use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ body::Body, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 5620d80adb5..3a3a05c369a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -5,7 +5,7 @@ use std::sync; use base_db::{ impl_intern_key, - salsa::{self, InternValueTrivial}, + ra_salsa::{self, InternValueTrivial}, CrateId, Upcast, }; use hir_def::{ @@ -30,22 +30,22 @@ use crate::{ }; use hir_expand::name::Name; -#[salsa::query_group(HirDatabaseStorage)] +#[ra_salsa::query_group(HirDatabaseStorage)] pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { - #[salsa::invoke(crate::infer::infer_query)] + #[ra_salsa::invoke(crate::infer::infer_query)] fn infer(&self, def: DefWithBodyId) -> Arc<InferenceResult>; // region:mir - #[salsa::invoke(crate::mir::mir_body_query)] - #[salsa::cycle(crate::mir::mir_body_recover)] + #[ra_salsa::invoke(crate::mir::mir_body_query)] + #[ra_salsa::cycle(crate::mir::mir_body_recover)] fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>; - #[salsa::invoke(crate::mir::mir_body_for_closure_query)] + #[ra_salsa::invoke(crate::mir::mir_body_for_closure_query)] fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>; - #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] - #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)] + #[ra_salsa::invoke(crate::mir::monomorphized_mir_body_query)] + #[ra_salsa::cycle(crate::mir::monomorphized_mir_body_recover)] fn monomorphized_mir_body( &self, def: DefWithBodyId, @@ -53,7 +53,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { env: Arc<TraitEnvironment>, ) -> Result<Arc<MirBody>, MirLowerError>; - #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] + #[ra_salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] fn monomorphized_mir_body_for_closure( &self, def: ClosureId, @@ -61,12 +61,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { env: Arc<TraitEnvironment>, ) -> Result<Arc<MirBody>, MirLowerError>; - #[salsa::invoke(crate::mir::borrowck_query)] - #[salsa::lru] + #[ra_salsa::invoke(crate::mir::borrowck_query)] + #[ra_salsa::lru] fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>; - #[salsa::invoke(crate::consteval::const_eval_query)] - #[salsa::cycle(crate::consteval::const_eval_recover)] + #[ra_salsa::invoke(crate::consteval::const_eval_query)] + #[ra_salsa::cycle(crate::consteval::const_eval_recover)] fn const_eval( &self, def: GeneralConstId, @@ -74,15 +74,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { trait_env: Option<Arc<TraitEnvironment>>, ) -> Result<Const, ConstEvalError>; - #[salsa::invoke(crate::consteval::const_eval_static_query)] - #[salsa::cycle(crate::consteval::const_eval_static_recover)] + #[ra_salsa::invoke(crate::consteval::const_eval_static_query)] + #[ra_salsa::cycle(crate::consteval::const_eval_static_recover)] fn const_eval_static(&self, def: StaticId) -> Result<Const, ConstEvalError>; - #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] - #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] + #[ra_salsa::invoke(crate::consteval::const_eval_discriminant_variant)] + #[ra_salsa::cycle(crate::consteval::const_eval_discriminant_recover)] fn const_eval_discriminant(&self, def: EnumVariantId) -> Result<i128, ConstEvalError>; - #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] + #[ra_salsa::invoke(crate::method_resolution::lookup_impl_method_query)] fn lookup_impl_method( &self, env: Arc<TraitEnvironment>, @@ -92,8 +92,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { // endregion:mir - #[salsa::invoke(crate::layout::layout_of_adt_query)] - #[salsa::cycle(crate::layout::layout_of_adt_recover)] + #[ra_salsa::invoke(crate::layout::layout_of_adt_query)] + #[ra_salsa::cycle(crate::layout::layout_of_adt_recover)] fn layout_of_adt( &self, def: AdtId, @@ -101,49 +101,49 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { env: Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError>; - #[salsa::invoke(crate::layout::layout_of_ty_query)] - #[salsa::cycle(crate::layout::layout_of_ty_recover)] + #[ra_salsa::invoke(crate::layout::layout_of_ty_query)] + #[ra_salsa::cycle(crate::layout::layout_of_ty_recover)] fn layout_of_ty(&self, ty: Ty, env: Arc<TraitEnvironment>) -> Result<Arc<Layout>, LayoutError>; - #[salsa::invoke(crate::layout::target_data_layout_query)] + #[ra_salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>; - #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] + #[ra_salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>; - #[salsa::invoke(crate::lower::ty_query)] - #[salsa::cycle(crate::lower::ty_recover)] + #[ra_salsa::invoke(crate::lower::ty_query)] + #[ra_salsa::cycle(crate::lower::ty_recover)] fn ty(&self, def: TyDefId) -> Binders<Ty>; /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is /// a `StructId` or `EnumVariantId` with a record constructor. - #[salsa::invoke(crate::lower::value_ty_query)] + #[ra_salsa::invoke(crate::lower::value_ty_query)] fn value_ty(&self, def: ValueTyDefId) -> Option<Binders<Ty>>; - #[salsa::invoke(crate::lower::impl_self_ty_query)] - #[salsa::cycle(crate::lower::impl_self_ty_recover)] + #[ra_salsa::invoke(crate::lower::impl_self_ty_query)] + #[ra_salsa::cycle(crate::lower::impl_self_ty_recover)] fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; - #[salsa::invoke(crate::lower::const_param_ty_query)] + #[ra_salsa::invoke(crate::lower::const_param_ty_query)] fn const_param_ty(&self, def: ConstParamId) -> Ty; - #[salsa::invoke(crate::lower::impl_trait_query)] + #[ra_salsa::invoke(crate::lower::impl_trait_query)] fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; - #[salsa::invoke(crate::lower::field_types_query)] + #[ra_salsa::invoke(crate::lower::field_types_query)] fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>; - #[salsa::invoke(crate::lower::callable_item_sig)] + #[ra_salsa::invoke(crate::lower::callable_item_sig)] fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; - #[salsa::invoke(crate::lower::return_type_impl_traits)] + #[ra_salsa::invoke(crate::lower::return_type_impl_traits)] fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>; - #[salsa::invoke(crate::lower::type_alias_impl_traits)] + #[ra_salsa::invoke(crate::lower::type_alias_impl_traits)] fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option<Arc<Binders<ImplTraits>>>; - #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] - #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] + #[ra_salsa::invoke(crate::lower::generic_predicates_for_param_query)] + #[ra_salsa::cycle(crate::lower::generic_predicates_for_param_recover)] fn generic_predicates_for_param( &self, def: GenericDefId, @@ -151,118 +151,118 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { assoc_name: Option<Name>, ) -> GenericPredicates; - #[salsa::invoke(crate::lower::generic_predicates_query)] + #[ra_salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] + #[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_query)] fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::trait_environment_for_body_query)] - #[salsa::transparent] + #[ra_salsa::invoke(crate::lower::trait_environment_for_body_query)] + #[ra_salsa::transparent] fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>; - #[salsa::invoke(crate::lower::trait_environment_query)] + #[ra_salsa::invoke(crate::lower::trait_environment_query)] fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>; - #[salsa::invoke(crate::lower::generic_defaults_query)] - #[salsa::cycle(crate::lower::generic_defaults_recover)] + #[ra_salsa::invoke(crate::lower::generic_defaults_query)] + #[ra_salsa::cycle(crate::lower::generic_defaults_recover)] fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults; - #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)] + #[ra_salsa::invoke(InherentImpls::inherent_impls_in_crate_query)] fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>; - #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)] + #[ra_salsa::invoke(InherentImpls::inherent_impls_in_block_query)] fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>; /// Collects all crates in the dependency graph that have impls for the /// given fingerprint. This is only used for primitive types and types /// annotated with `rustc_has_incoherent_inherent_impls`; for other types /// we just look at the crate where the type is defined. - #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)] + #[ra_salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)] fn incoherent_inherent_impl_crates( &self, krate: CrateId, fp: TyFingerprint, ) -> SmallVec<[CrateId; 2]>; - #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] + #[ra_salsa::invoke(TraitImpls::trait_impls_in_crate_query)] fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; - #[salsa::invoke(TraitImpls::trait_impls_in_block_query)] + #[ra_salsa::invoke(TraitImpls::trait_impls_in_block_query)] fn trait_impls_in_block(&self, block: BlockId) -> Option<Arc<TraitImpls>>; - #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] + #[ra_salsa::invoke(TraitImpls::trait_impls_in_deps_query)] fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>; // Interned IDs for Chalk integration - #[salsa::interned] + #[ra_salsa::interned] fn intern_callable_def(&self, callable_def: CallableDefId) -> InternedCallableDefId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_type_or_const_param_id( &self, param_id: TypeOrConstParamId, ) -> InternedTypeOrConstParamId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_closure(&self, id: InternedClosure) -> InternedClosureId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId; - #[salsa::invoke(chalk_db::associated_ty_data_query)] + #[ra_salsa::invoke(chalk_db::associated_ty_data_query)] fn associated_ty_data( &self, id: chalk_db::AssocTypeId, ) -> sync::Arc<chalk_db::AssociatedTyDatum>; - #[salsa::invoke(chalk_db::trait_datum_query)] + #[ra_salsa::invoke(chalk_db::trait_datum_query)] fn trait_datum( &self, krate: CrateId, trait_id: chalk_db::TraitId, ) -> sync::Arc<chalk_db::TraitDatum>; - #[salsa::invoke(chalk_db::adt_datum_query)] + #[ra_salsa::invoke(chalk_db::adt_datum_query)] fn adt_datum( &self, krate: CrateId, struct_id: chalk_db::AdtId, ) -> sync::Arc<chalk_db::AdtDatum>; - #[salsa::invoke(chalk_db::impl_datum_query)] + #[ra_salsa::invoke(chalk_db::impl_datum_query)] fn impl_datum( &self, krate: CrateId, impl_id: chalk_db::ImplId, ) -> sync::Arc<chalk_db::ImplDatum>; - #[salsa::invoke(chalk_db::fn_def_datum_query)] + #[ra_salsa::invoke(chalk_db::fn_def_datum_query)] fn fn_def_datum(&self, fn_def_id: FnDefId) -> sync::Arc<chalk_db::FnDefDatum>; - #[salsa::invoke(chalk_db::fn_def_variance_query)] + #[ra_salsa::invoke(chalk_db::fn_def_variance_query)] fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances; - #[salsa::invoke(chalk_db::adt_variance_query)] + #[ra_salsa::invoke(chalk_db::adt_variance_query)] fn adt_variance(&self, adt_id: chalk_db::AdtId) -> chalk_db::Variances; - #[salsa::invoke(chalk_db::associated_ty_value_query)] + #[ra_salsa::invoke(chalk_db::associated_ty_value_query)] fn associated_ty_value( &self, krate: CrateId, id: chalk_db::AssociatedTyValueId, ) -> sync::Arc<chalk_db::AssociatedTyValue>; - #[salsa::invoke(crate::traits::normalize_projection_query)] - #[salsa::transparent] + #[ra_salsa::invoke(crate::traits::normalize_projection_query)] + #[ra_salsa::transparent] fn normalize_projection( &self, projection: crate::ProjectionTy, env: Arc<TraitEnvironment>, ) -> Ty; - #[salsa::invoke(crate::traits::trait_solve_query)] + #[ra_salsa::invoke(crate::traits::trait_solve_query)] fn trait_solve( &self, krate: CrateId, @@ -270,7 +270,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { goal: crate::Canonical<crate::InEnvironment<crate::Goal>>, ) -> Option<crate::Solution>; - #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] + #[ra_salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] fn program_clauses_for_chalk_env( &self, krate: CrateId, @@ -285,23 +285,23 @@ fn hir_database_is_dyn_compatible() { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InternedTypeOrConstParamId(salsa::InternId); +pub struct InternedTypeOrConstParamId(ra_salsa::InternId); impl_intern_key!(InternedTypeOrConstParamId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InternedLifetimeParamId(salsa::InternId); +pub struct InternedLifetimeParamId(ra_salsa::InternId); impl_intern_key!(InternedLifetimeParamId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InternedConstParamId(salsa::InternId); +pub struct InternedConstParamId(ra_salsa::InternId); impl_intern_key!(InternedConstParamId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InternedOpaqueTyId(salsa::InternId); +pub struct InternedOpaqueTyId(ra_salsa::InternId); impl_intern_key!(InternedOpaqueTyId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InternedClosureId(salsa::InternId); +pub struct InternedClosureId(ra_salsa::InternId); impl_intern_key!(InternedClosureId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -310,7 +310,7 @@ pub struct InternedClosure(pub DefWithBodyId, pub ExprId); impl InternValueTrivial for InternedClosure {} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InternedCoroutineId(salsa::InternId); +pub struct InternedCoroutineId(ra_salsa::InternId); impl_intern_key!(InternedCoroutineId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -320,5 +320,5 @@ impl InternValueTrivial for InternedCoroutine {} /// This exists just for Chalk, because Chalk just has a single `FnDefId` where /// we have different IDs for struct and enum variant constructors. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct InternedCallableDefId(salsa::InternId); +pub struct InternedCallableDefId(ra_salsa::InternId); impl_intern_key!(InternedCallableDefId); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 70c03477c4c..10f5bcdad86 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1008,34 +1008,28 @@ impl HirDisplay for Ty { if let Safety::Unsafe = sig.safety { write!(f, "unsafe ")?; } - if !matches!(sig.abi, FnAbi::Rust) { + if !matches!(sig.abi, FnAbi::Rust | FnAbi::RustCall) { f.write_str("extern \"")?; f.write_str(sig.abi.as_str())?; f.write_str("\" ")?; } + write!(f, "fn ")?; + f.start_location_link(def.into()); match def { - CallableDefId::FunctionId(ff) => { - write!(f, "fn ")?; - f.start_location_link(def.into()); - write!( - f, - "{}", - db.function_data(ff).name.display(f.db.upcast(), f.edition()) - )? - } + CallableDefId::FunctionId(ff) => write!( + f, + "{}", + db.function_data(ff).name.display(f.db.upcast(), f.edition()) + )?, CallableDefId::StructId(s) => { - f.start_location_link(def.into()); write!(f, "{}", db.struct_data(s).name.display(f.db.upcast(), f.edition()))? } - CallableDefId::EnumVariantId(e) => { - f.start_location_link(def.into()); - write!( - f, - "{}", - db.enum_variant_data(e).name.display(f.db.upcast(), f.edition()) - )? - } + CallableDefId::EnumVariantId(e) => write!( + f, + "{}", + db.enum_variant_data(e).name.display(f.db.upcast(), f.edition()) + )?, }; f.end_location_link(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 8bc3c50725d..88334b492d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -57,7 +57,7 @@ use crate::{ db::HirDatabase, fold_tys, generics::Generics, - infer::{coerce::CoerceMany, unify::InferenceTable}, + infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable}, lower::ImplTraitLoweringMode, mir::MirSpan, to_assoc_type_id, @@ -1154,6 +1154,7 @@ impl<'a> InferenceContext<'a> { _ = self.infer_expr_coerce( self.body.body_expr, &Expectation::has_type(self.return_ty.clone()), + ExprIsRead::Yes, ) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index caa3960a227..21d0be6ed5f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -5,8 +5,9 @@ use hir_def::{hir::ExprId, AdtId}; use stdx::never; use crate::{ - infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, - PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + infer::{coerce::CoerceNever, unify::InferenceTable}, + Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, + QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, }; #[derive(Debug)] @@ -70,8 +71,9 @@ pub enum CastError { NeedViaThinPtr, NeedViaInt, NonScalar, - UnknownCastPtrKind, - UnknownExprPtrKind, + // We don't want to report errors with unknown types currently. + // UnknownCastPtrKind, + // UnknownExprPtrKind, } impl CastError { @@ -127,7 +129,7 @@ impl CastCheck { return Ok(()); } - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) { + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { apply_adjustments(self.source_expr, adj); set_coercion_cast(self.source_expr); return Ok(()); @@ -153,7 +155,8 @@ impl CastCheck { let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); let sig = table.normalize_associated_types_in(sig); let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) { + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) + { apply_adjustments(self.source_expr, adj); } else { return Err(CastError::IllegalCast); @@ -240,7 +243,8 @@ impl CastCheck { if let TyKind::Array(ety, _) = t_expr.kind(Interner) { // Coerce to a raw pointer so that we generate RawPtr in MIR. let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) { + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes) + { apply_adjustments(self.source_expr, adj); } else { never!( @@ -252,7 +256,7 @@ impl CastCheck { // This is a less strict condition than rustc's `demand_eqtype`, // but false negative is better than false positive - if table.coerce(ety, t_cast).is_ok() { + if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() { return Ok(()); } } @@ -272,9 +276,10 @@ impl CastCheck { match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), - (_, None) => Err(CastError::UnknownCastPtrKind), + // (_, None) => Err(CastError::UnknownCastPtrKind), + // (None, _) => Err(CastError::UnknownExprPtrKind), + (_, None) | (None, _) => Ok(()), (_, Some(PointerKind::Thin)) => Ok(()), - (None, _) => Err(CastError::UnknownExprPtrKind), (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { let principal = |tty: &Binders<QuantifiedWhereClauses>| { @@ -315,7 +320,8 @@ impl CastCheck { expr_ty: &Ty, ) -> Result<(), CastError> { match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { - None => Err(CastError::UnknownExprPtrKind), + // None => Err(CastError::UnknownExprPtrKind), + None => Ok(()), Some(PointerKind::Error) => Ok(()), Some(PointerKind::Thin) => Ok(()), _ => Err(CastError::NeedViaThinPtr), @@ -328,7 +334,8 @@ impl CastCheck { cast_ty: &Ty, ) -> Result<(), CastError> { match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { - None => Err(CastError::UnknownCastPtrKind), + // None => Err(CastError::UnknownCastPtrKind), + None => Ok(()), Some(PointerKind::Error) => Ok(()), Some(PointerKind::Thin) => Ok(()), Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast), @@ -343,7 +350,8 @@ impl CastCheck { cast_ty: &Ty, ) -> Result<(), CastError> { match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { - None => Err(CastError::UnknownCastPtrKind), + // None => Err(CastError::UnknownCastPtrKind), + None => Ok(()), Some(PointerKind::Error) => Ok(()), Some(PointerKind::Thin) => Ok(()), _ => Err(CastError::IllegalCast), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 5cad08b9395..e9825cf0998 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -29,6 +29,7 @@ use crate::{ db::{HirDatabase, InternedClosure}, error_lifetime, from_chalk_trait_id, from_placeholder_idx, generics::Generics, + infer::coerce::CoerceNever, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, to_chalk_trait_id, @@ -65,7 +66,7 @@ impl InferenceContext<'_> { } // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. - let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty); + let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty, CoerceNever::Yes); // Coroutines are not Fn* so return early. if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 7e758c0b517..366c3cb0f17 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -139,8 +139,8 @@ impl CoerceMany { }; if let Some(sig) = sig { let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty); - let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty); + let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty, CoerceNever::Yes); + let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty, CoerceNever::Yes); if let (Ok(result1), Ok(result2)) = (result1, result2) { ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals }); for &e in &self.expressions { @@ -159,9 +159,9 @@ impl CoerceMany { // type is a type variable and the new one is `!`, trying it the other // way around first would mean we make the type variable `!`, instead of // just marking it as possibly diverging. - if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) { + if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) { self.final_ty = Some(res); - } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) { + } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty, CoerceNever::Yes) { self.final_ty = Some(res); } else { match cause { @@ -197,7 +197,7 @@ pub(crate) fn coerce( let vars = table.fresh_subst(tys.binders.as_slice(Interner)); let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); - let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?; + let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars, CoerceNever::Yes)?; // default any type vars that weren't unified back to their original bound vars // (kind of hacky) let find_var = |iv| { @@ -219,6 +219,12 @@ pub(crate) fn coerce( Ok((adjustments, table.resolve_with_fallback(ty, &fallback))) } +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum CoerceNever { + Yes, + No, +} + impl InferenceContext<'_> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. @@ -227,10 +233,16 @@ impl InferenceContext<'_> { expr: Option<ExprId>, from_ty: &Ty, to_ty: &Ty, + // [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89) + // Whether we allow `NeverToAny` coercions. This is unsound if we're + // coercing a place expression without it counting as a read in the MIR. + // This is a side-effect of HIR not really having a great distinction + // between places and values. + coerce_never: CoerceNever, ) -> Result<Ty, TypeError> { let from_ty = self.resolve_ty_shallow(from_ty); let to_ty = self.resolve_ty_shallow(to_ty); - let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty)?; + let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty, coerce_never)?; if let Some(expr) = expr { self.write_expr_adj(expr, adjustments); } @@ -245,10 +257,11 @@ impl InferenceTable<'_> { &mut self, from_ty: &Ty, to_ty: &Ty, + coerce_never: CoerceNever, ) -> Result<(Vec<Adjustment>, Ty), TypeError> { let from_ty = self.resolve_ty_shallow(from_ty); let to_ty = self.resolve_ty_shallow(to_ty); - match self.coerce_inner(from_ty, &to_ty) { + match self.coerce_inner(from_ty, &to_ty, coerce_never) { Ok(InferOk { value: (adjustments, ty), goals }) => { self.register_infer_ok(InferOk { value: (), goals }); Ok((adjustments, ty)) @@ -260,19 +273,23 @@ impl InferenceTable<'_> { } } - fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult { + fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult { if from_ty.is_never() { - // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound - // type variable, we want `?T` to fallback to `!` if not - // otherwise constrained. An example where this arises: - // - // let _: Option<?T> = Some({ return; }); - // - // here, we would coerce from `!` to `?T`. if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { self.set_diverging(*tv, true); } - return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); + if coerce_never == CoerceNever::Yes { + // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound + // type variable, we want `?T` to fallback to `!` if not + // otherwise constrained. An example where this arises: + // + // let _: Option<?T> = Some({ return; }); + // + // here, we would coerce from `!` to `?T`. + return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); + } else { + return self.unify_and(&from_ty, to_ty, identity); + } } // If we are coercing into a TAIT, coerce into its proxy inference var, instead. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index a04e7b17ae6..657e4d77966 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -10,10 +10,11 @@ use either::Either; use hir_def::{ hir::{ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId, - Literal, Statement, UnaryOp, + Literal, Pat, PatId, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, + resolver::ValueNs, BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, }; use hir_expand::name::Name; @@ -28,7 +29,7 @@ use crate::{ error_lifetime, generics::{generics, Generics}, infer::{ - coerce::{CoerceMany, CoercionCause}, + coerce::{CoerceMany, CoerceNever, CoercionCause}, find_continuable, pat::contains_explicit_ref_binding, BreakableKind, @@ -52,9 +53,20 @@ use super::{ Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, }; +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum ExprIsRead { + Yes, + No, +} + impl InferenceContext<'_> { - pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { - let ty = self.infer_expr_inner(tgt_expr, expected); + pub(crate) fn infer_expr( + &mut self, + tgt_expr: ExprId, + expected: &Expectation, + is_read: ExprIsRead, + ) -> Ty { + let ty = self.infer_expr_inner(tgt_expr, expected, is_read); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { let could_unify = self.unify(&ty, &expected_ty); if !could_unify { @@ -67,16 +79,26 @@ impl InferenceContext<'_> { ty } - pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId) -> Ty { - self.infer_expr_inner(tgt_expr, &Expectation::None) + pub(crate) fn infer_expr_no_expect(&mut self, tgt_expr: ExprId, is_read: ExprIsRead) -> Ty { + self.infer_expr_inner(tgt_expr, &Expectation::None, is_read) } /// Infer type of expression with possibly implicit coerce to the expected type. /// Return the type after possible coercion. - pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { - let ty = self.infer_expr_inner(expr, expected); + pub(super) fn infer_expr_coerce( + &mut self, + expr: ExprId, + expected: &Expectation, + is_read: ExprIsRead, + ) -> Ty { + let ty = self.infer_expr_inner(expr, expected, is_read); if let Some(target) = expected.only_has_type(&mut self.table) { - match self.coerce(Some(expr), &ty, &target) { + let coerce_never = if self.expr_guaranteed_to_constitute_read_for_never(expr, is_read) { + CoerceNever::Yes + } else { + CoerceNever::No + }; + match self.coerce(Some(expr), &ty, &target, coerce_never) { Ok(res) => res, Err(_) => { self.result.type_mismatches.insert( @@ -91,8 +113,137 @@ impl InferenceContext<'_> { } } - fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty { - let ty = self.infer_expr_inner(expr, expected); + /// Whether this expression constitutes a read of value of the type that + /// it evaluates to. + /// + /// This is used to determine if we should consider the block to diverge + /// if the expression evaluates to `!`, and if we should insert a `NeverToAny` + /// coercion for values of type `!`. + /// + /// This function generally returns `false` if the expression is a place + /// expression and the *parent* expression is the scrutinee of a match or + /// the pointee of an `&` addr-of expression, since both of those parent + /// expressions take a *place* and not a value. + pub(super) fn expr_guaranteed_to_constitute_read_for_never( + &mut self, + expr: ExprId, + is_read: ExprIsRead, + ) -> bool { + // rustc does the place expr check first, but since we are feeding + // readness of the `expr` as a given value, we just can short-circuit + // the place expr check if it's true(see codes and comments below) + if is_read == ExprIsRead::Yes { + return true; + } + + // We only care about place exprs. Anything else returns an immediate + // which would constitute a read. We don't care about distinguishing + // "syntactic" place exprs since if the base of a field projection is + // not a place then it would've been UB to read from it anyways since + // that constitutes a read. + if !self.is_syntactic_place_expr(expr) { + return true; + } + + // rustc queries parent hir node of `expr` here and determine whether + // the current `expr` is read of value per its parent. + // But since we don't have hir node, we cannot follow such "bottom-up" + // method. + // So, we pass down such readness from the parent expression through the + // recursive `infer_expr*` calls in a "top-down" manner. + is_read == ExprIsRead::Yes + } + + /// Whether this pattern constitutes a read of value of the scrutinee that + /// it is matching against. This is used to determine whether we should + /// perform `NeverToAny` coercions. + fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool { + match &self.body[pat] { + // Does not constitute a read. + Pat::Wild => false, + + // This is unnecessarily restrictive when the pattern that doesn't + // constitute a read is unreachable. + // + // For example `match *never_ptr { value => {}, _ => {} }` or + // `match *never_ptr { _ if false => {}, value => {} }`. + // + // It is however fine to be restrictive here; only returning `true` + // can lead to unsoundness. + Pat::Or(subpats) => { + subpats.iter().all(|pat| self.pat_guaranteed_to_constitute_read_for_never(*pat)) + } + + // All of these constitute a read, or match on something that isn't `!`, + // which would require a `NeverToAny` coercion. + Pat::Bind { .. } + | Pat::TupleStruct { .. } + | Pat::Path(_) + | Pat::Tuple { .. } + | Pat::Box { .. } + | Pat::Ref { .. } + | Pat::Lit(_) + | Pat::Range { .. } + | Pat::Slice { .. } + | Pat::ConstBlock(_) + | Pat::Record { .. } + | Pat::Missing => true, + } + } + + fn is_syntactic_place_expr(&self, expr: ExprId) -> bool { + match &self.body[expr] { + // Lang item paths cannot currently be local variables or statics. + Expr::Path(Path::LangItem(_, _)) => false, + Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false, + Expr::Path(path) => self + .resolver + .resolve_path_in_value_ns_fully(self.db.upcast(), path) + .map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), + Expr::Underscore => true, + Expr::UnaryOp { op: UnaryOp::Deref, .. } => true, + Expr::Field { .. } | Expr::Index { .. } => true, + Expr::Call { .. } + | Expr::MethodCall { .. } + | Expr::Tuple { .. } + | Expr::If { .. } + | Expr::Match { .. } + | Expr::Closure { .. } + | Expr::Block { .. } + | Expr::Array(..) + | Expr::Break { .. } + | Expr::Continue { .. } + | Expr::Return { .. } + | Expr::Become { .. } + | Expr::Let { .. } + | Expr::Loop { .. } + | Expr::InlineAsm(..) + | Expr::OffsetOf(..) + | Expr::Literal(..) + | Expr::Const(..) + | Expr::UnaryOp { .. } + | Expr::BinaryOp { .. } + | Expr::Yield { .. } + | Expr::Cast { .. } + | Expr::Async { .. } + | Expr::Unsafe { .. } + | Expr::Await { .. } + | Expr::Ref { .. } + | Expr::Range { .. } + | Expr::Box { .. } + | Expr::RecordLit { .. } + | Expr::Yeet { .. } + | Expr::Missing => false, + } + } + + fn infer_expr_coerce_never( + &mut self, + expr: ExprId, + expected: &Expectation, + is_read: ExprIsRead, + ) -> Ty { + let ty = self.infer_expr_inner(expr, expected, is_read); // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from `!` to `expected`. if ty.is_never() { @@ -105,7 +256,7 @@ impl InferenceContext<'_> { } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(Some(expr), &ty, &target) + self.coerce(Some(expr), &ty, &target, CoerceNever::Yes) .expect("never-to-any coercion should always succeed") } else { ty @@ -124,7 +275,12 @@ impl InferenceContext<'_> { } } - fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { + fn infer_expr_inner( + &mut self, + tgt_expr: ExprId, + expected: &Expectation, + is_read: ExprIsRead, + ) -> Ty { self.db.unwind_if_cancelled(); let ty = match &self.body[tgt_expr] { @@ -134,17 +290,18 @@ impl InferenceContext<'_> { self.infer_expr_coerce_never( condition, &Expectation::HasType(self.result.standard_types.bool_.clone()), + ExprIsRead::Yes, ); let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let then_ty = self.infer_expr_inner(then_branch, expected); + let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes); let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table)); coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch)); match else_branch { Some(else_branch) => { - let else_ty = self.infer_expr_inner(else_branch, expected); + let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); coerce.coerce( self, @@ -163,7 +320,12 @@ impl InferenceContext<'_> { coerce.complete(self) } &Expr::Let { pat, expr } => { - let input_ty = self.infer_expr(expr, &Expectation::none()); + let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) { + ExprIsRead::Yes + } else { + ExprIsRead::No + }; + let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); self.infer_top_pat(pat, &input_ty); self.result.standard_types.bool_.clone() } @@ -176,7 +338,7 @@ impl InferenceContext<'_> { Expr::Const(id) => { self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { let loc = this.db.lookup_intern_anonymous_const(*id); - this.infer_expr(loc.root, expected) + this.infer_expr(loc.root, expected, ExprIsRead::Yes) }) .1 } @@ -189,7 +351,11 @@ impl InferenceContext<'_> { let ty = self.table.new_type_var(); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { - this.infer_expr(body, &Expectation::HasType(TyBuilder::unit())); + this.infer_expr( + body, + &Expectation::HasType(TyBuilder::unit()), + ExprIsRead::Yes, + ); }); match breaks { @@ -312,7 +478,7 @@ impl InferenceContext<'_> { ty } Expr::Call { callee, args, .. } => { - let callee_ty = self.infer_expr(*callee, &Expectation::none()); + let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes); let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { @@ -393,7 +559,12 @@ impl InferenceContext<'_> { expected, ), Expr::Match { expr, arms } => { - let input_ty = self.infer_expr(*expr, &Expectation::none()); + let scrutinee_is_read = arms + .iter() + .all(|arm| self.pat_guaranteed_to_constitute_read_for_never(arm.pat)); + let scrutinee_is_read = + if scrutinee_is_read { ExprIsRead::Yes } else { ExprIsRead::No }; + let input_ty = self.infer_expr(*expr, &Expectation::none(), scrutinee_is_read); if arms.is_empty() { self.diverges = Diverges::Always; @@ -423,11 +594,12 @@ impl InferenceContext<'_> { self.infer_expr_coerce_never( guard_expr, &Expectation::HasType(self.result.standard_types.bool_.clone()), + ExprIsRead::Yes, ); } self.diverges = Diverges::Maybe; - let arm_ty = self.infer_expr_inner(arm.expr, &expected); + let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes); all_arms_diverge &= self.diverges; coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr)); } @@ -480,7 +652,11 @@ impl InferenceContext<'_> { }, None => self.err_ty(), }; - self.infer_expr_inner(expr, &Expectation::HasType(opt_coerce_to)) + self.infer_expr_inner( + expr, + &Expectation::HasType(opt_coerce_to), + ExprIsRead::Yes, + ) } else { TyBuilder::unit() }; @@ -517,10 +693,14 @@ impl InferenceContext<'_> { Expr::Yield { expr } => { if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() { if let Some(expr) = expr { - self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty)); + self.infer_expr_coerce( + *expr, + &Expectation::has_type(yield_ty), + ExprIsRead::Yes, + ); } else { let unit = self.result.standard_types.unit.clone(); - let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty); + let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes); } resume_ty } else { @@ -530,7 +710,7 @@ impl InferenceContext<'_> { } Expr::Yeet { expr } => { if let &Some(expr) = expr { - self.infer_expr_no_expect(expr); + self.infer_expr_no_expect(expr, ExprIsRead::Yes); } self.result.standard_types.never.clone() } @@ -589,28 +769,37 @@ impl InferenceContext<'_> { // Field type might have some unknown types // FIXME: we may want to emit a single type variable for all instance of type fields? let field_ty = self.insert_type_vars(field_ty); - self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); + self.infer_expr_coerce( + field.expr, + &Expectation::has_type(field_ty), + ExprIsRead::Yes, + ); } } None => { for field in fields.iter() { - self.infer_expr_coerce(field.expr, &Expectation::None); + // Field projections don't constitute reads. + self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No); } } } if let Some(expr) = spread { - self.infer_expr(*expr, &Expectation::has_type(ty.clone())); + self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); } ty } Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), Expr::Await { expr } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); + let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) } Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); - let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone())); + let expr_ty = self.infer_expr( + *expr, + &Expectation::Castable(cast_ty.clone()), + ExprIsRead::Yes, + ); self.deferred_cast_checks.push(CastCheck::new( tgt_expr, *expr, @@ -638,7 +827,7 @@ impl InferenceContext<'_> { } else { Expectation::none() }; - let inner_ty = self.infer_expr_inner(*expr, &expectation); + let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes); match rawness { Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), Rawness::Ref => { @@ -650,7 +839,7 @@ impl InferenceContext<'_> { } &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); + let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); let inner_ty = self.resolve_ty_shallow(&inner_ty); // FIXME: Note down method resolution her match op { @@ -720,19 +909,32 @@ impl InferenceContext<'_> { // cannot happen in destructuring assignments because of how // they are desugared. if is_ordinary { - let lhs_ty = self.infer_expr(lhs, &Expectation::none()); - self.infer_expr_coerce(*rhs, &Expectation::has_type(lhs_ty)); + // LHS of assignment doesn't constitute reads. + let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No); + self.infer_expr_coerce( + *rhs, + &Expectation::has_type(lhs_ty), + ExprIsRead::No, + ); } else { - let rhs_ty = self.infer_expr(*rhs, &Expectation::none()); + let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes); self.infer_assignee_expr(lhs, &rhs_ty); } self.result.standard_types.unit.clone() } Some(BinaryOp::LogicOp(_)) => { let bool_ty = self.result.standard_types.bool_.clone(); - self.infer_expr_coerce(*lhs, &Expectation::HasType(bool_ty.clone())); + self.infer_expr_coerce( + *lhs, + &Expectation::HasType(bool_ty.clone()), + ExprIsRead::Yes, + ); let lhs_diverges = self.diverges; - self.infer_expr_coerce(*rhs, &Expectation::HasType(bool_ty.clone())); + self.infer_expr_coerce( + *rhs, + &Expectation::HasType(bool_ty.clone()), + ExprIsRead::Yes, + ); // Depending on the LHS' value, the RHS can never execute. self.diverges = lhs_diverges; bool_ty @@ -741,11 +943,12 @@ impl InferenceContext<'_> { _ => self.err_ty(), }, Expr::Range { lhs, rhs, range_type } => { - let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none())); + let lhs_ty = + lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes)); let rhs_expect = lhs_ty .as_ref() .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); - let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect)); + let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect, ExprIsRead::Yes)); match (range_type, lhs_ty, rhs_ty) { (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { Some(adt) => TyBuilder::adt(self.db, adt).build(), @@ -779,8 +982,8 @@ impl InferenceContext<'_> { } } Expr::Index { base, index, is_assignee_expr } => { - let base_ty = self.infer_expr_inner(*base, &Expectation::none()); - let index_ty = self.infer_expr(*index, &Expectation::none()); + let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes); + let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { let canonicalized = self.canonicalize(base_ty.clone()); @@ -851,7 +1054,11 @@ impl InferenceContext<'_> { }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { - *ty = self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); + *ty = self.infer_expr_coerce( + *expr, + &Expectation::has_type(ty.clone()), + ExprIsRead::Yes, + ); } TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner) @@ -958,7 +1165,7 @@ impl InferenceContext<'_> { Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), Expr::InlineAsm(asm) => { let mut check_expr_asm_operand = |expr, is_input: bool| { - let ty = self.infer_expr_no_expect(expr); + let ty = self.infer_expr_no_expect(expr, ExprIsRead::Yes); // If this is an input value, we require its type to be fully resolved // at this point. This allows us to provide helpful coercions which help @@ -975,11 +1182,11 @@ impl InferenceContext<'_> { CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(), ) .intern(Interner); - _ = self.coerce(Some(expr), &ty, &fnptr_ty); + _ = self.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes); } TyKind::Ref(mutbl, _, base_ty) => { let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); - _ = self.coerce(Some(expr), &ty, &ptr_ty); + _ = self.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes); } _ => {} } @@ -1016,7 +1223,9 @@ impl InferenceContext<'_> { // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); self.write_expr_ty(tgt_expr, ty.clone()); - if self.resolve_ty_shallow(&ty).is_never() { + if self.resolve_ty_shallow(&ty).is_never() + && self.expr_guaranteed_to_constitute_read_for_never(tgt_expr, is_read) + { // Any expression that produces a value of type `!` must have diverged self.diverges = Diverges::Always; } @@ -1041,7 +1250,7 @@ impl InferenceContext<'_> { let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected); if let Some(target) = expected.only_has_type(&mut this.table) { - match this.coerce(Some(tgt_expr), &ty, &target) { + match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) { Ok(res) => res, Err(_) => { this.result.type_mismatches.insert( @@ -1153,7 +1362,7 @@ impl InferenceContext<'_> { Array::ElementList { elements, .. } => { let mut coerce = CoerceMany::new(elem_ty); for &expr in elements.iter() { - let cur_elem_ty = self.infer_expr_inner(expr, &expected); + let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr)); } ( @@ -1162,13 +1371,17 @@ impl InferenceContext<'_> { ) } &Array::Repeat { initializer, repeat } => { - self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone())); + self.infer_expr_coerce( + initializer, + &Expectation::has_type(elem_ty.clone()), + ExprIsRead::Yes, + ); let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner); match self.body[repeat] { Expr::Underscore => { self.write_expr_ty(repeat, usize); } - _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)), + _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes), } ( @@ -1193,7 +1406,8 @@ impl InferenceContext<'_> { .as_mut() .expect("infer_return called outside function body") .expected_ty(); - let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty)); + let return_expr_ty = + self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr)); self.return_coercion = Some(coerce_many); @@ -1213,7 +1427,7 @@ impl InferenceContext<'_> { None => { // FIXME: diagnose return outside of function if let Some(expr) = expr { - self.infer_expr_no_expect(expr); + self.infer_expr_no_expect(expr, ExprIsRead::Yes); } } } @@ -1225,8 +1439,11 @@ impl InferenceContext<'_> { Some(return_coercion) => { let ret_ty = return_coercion.expected_ty(); - let call_expr_ty = - self.infer_expr_inner(expr, &Expectation::HasType(ret_ty.clone())); + let call_expr_ty = self.infer_expr_inner( + expr, + &Expectation::HasType(ret_ty.clone()), + ExprIsRead::Yes, + ); // NB: this should *not* coerce. // tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`). @@ -1234,7 +1451,7 @@ impl InferenceContext<'_> { } None => { // FIXME: diagnose `become` outside of functions - self.infer_expr_no_expect(expr); + self.infer_expr_no_expect(expr, ExprIsRead::Yes); } } @@ -1255,7 +1472,7 @@ impl InferenceContext<'_> { }) .unwrap_or_else(Expectation::none); - let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp); + let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes); TyBuilder::adt(self.db, box_id) .push(inner_ty) .fill_with_defaults(self.db, || self.table.new_type_var()) @@ -1333,12 +1550,13 @@ impl InferenceContext<'_> { Expr::Underscore => rhs_ty.clone(), _ => { // `lhs` is a place expression, a unit struct, or an enum variant. - let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none()); + // LHS of assignment doesn't constitute reads. + let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No); // This is the only branch where this function may coerce any type. // We are returning early to avoid the unifiability check below. let lhs_ty = self.insert_type_vars_shallow(lhs_ty); - let ty = match self.coerce(None, &rhs_ty, &lhs_ty) { + let ty = match self.coerce(None, &rhs_ty, &lhs_ty, CoerceNever::Yes) { Ok(ty) => ty, Err(_) => { self.result.type_mismatches.insert( @@ -1373,7 +1591,12 @@ impl InferenceContext<'_> { tgt_expr: ExprId, ) -> Ty { let lhs_expectation = Expectation::none(); - let lhs_ty = self.infer_expr(lhs, &lhs_expectation); + let is_read = if matches!(op, BinaryOp::Assignment { .. }) { + ExprIsRead::Yes + } else { + ExprIsRead::No + }; + let lhs_ty = self.infer_expr(lhs, &lhs_expectation, is_read); let rhs_ty = self.table.new_type_var(); let trait_func = lang_items_for_bin_op(op).and_then(|(name, lang_item)| { @@ -1396,7 +1619,7 @@ impl InferenceContext<'_> { self.err_ty() }; - self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty)); + self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty), ExprIsRead::Yes); return ret_ty; } @@ -1415,7 +1638,7 @@ impl InferenceContext<'_> { let method_ty = self.db.value_ty(func.into()).unwrap().substitute(Interner, &subst); self.register_obligations_for_call(&method_ty); - self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone())); + self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone()), ExprIsRead::Yes); let ret_ty = match method_ty.callable_sig(self.db) { Some(sig) => { @@ -1487,12 +1710,25 @@ impl InferenceContext<'_> { .unwrap_or_else(|| this.table.new_type_var()); let ty = if let Some(expr) = initializer { + // If we have a subpattern that performs a read, we want to consider this + // to diverge for compatibility to support something like `let x: () = *never_ptr;`. + let target_is_read = + if this.pat_guaranteed_to_constitute_read_for_never(*pat) { + ExprIsRead::Yes + } else { + ExprIsRead::No + }; let ty = if contains_explicit_ref_binding(this.body, *pat) { - this.infer_expr(*expr, &Expectation::has_type(decl_ty.clone())) + this.infer_expr( + *expr, + &Expectation::has_type(decl_ty.clone()), + target_is_read, + ) } else { this.infer_expr_coerce( *expr, &Expectation::has_type(decl_ty.clone()), + target_is_read, ) }; if type_ref.is_some() { @@ -1512,17 +1748,19 @@ impl InferenceContext<'_> { this.infer_expr_coerce( *expr, &Expectation::HasType(this.result.standard_types.never.clone()), + ExprIsRead::Yes, ); this.diverges = previous_diverges; } } &Statement::Expr { expr, has_semi } => { if has_semi { - this.infer_expr(expr, &Expectation::none()); + this.infer_expr(expr, &Expectation::none(), ExprIsRead::Yes); } else { this.infer_expr_coerce( expr, &Expectation::HasType(this.result.standard_types.unit.clone()), + ExprIsRead::Yes, ); } } @@ -1532,7 +1770,7 @@ impl InferenceContext<'_> { // FIXME: This should make use of the breakable CoerceMany if let Some(expr) = tail { - this.infer_expr_coerce(expr, expected) + this.infer_expr_coerce(expr, expected, ExprIsRead::Yes) } else { // Citing rustc: if there is no explicit tail expression, // that is typically equivalent to a tail expression @@ -1545,8 +1783,20 @@ impl InferenceContext<'_> { // we don't even make an attempt at coercion this.table.new_maybe_never_var() } else if let Some(t) = expected.only_has_type(&mut this.table) { + let coerce_never = if this + .expr_guaranteed_to_constitute_read_for_never(expr, ExprIsRead::Yes) + { + CoerceNever::Yes + } else { + CoerceNever::No + }; if this - .coerce(Some(expr), &this.result.standard_types.unit.clone(), &t) + .coerce( + Some(expr), + &this.result.standard_types.unit.clone(), + &t, + coerce_never, + ) .is_err() { this.result.type_mismatches.insert( @@ -1658,7 +1908,8 @@ impl InferenceContext<'_> { name: &Name, expected: &Expectation, ) -> Ty { - let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none()); + // Field projections don't constitute reads. + let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No); if name.is_missing() { // Bail out early, don't even try to look up field. Also, we don't issue an unresolved @@ -1730,7 +1981,7 @@ impl InferenceContext<'_> { generic_args: Option<&GenericArgs>, expected: &Expectation, ) -> Ty { - let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none()); + let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); let resolved = method_resolution::lookup_method( @@ -1917,7 +2168,7 @@ impl InferenceContext<'_> { let expected_ty = self.normalize_associated_types_in(expected_ty); let expected = Expectation::rvalue_hint(self, expected_ty); // infer with the expected type we have... - let ty = self.infer_expr_inner(arg, &expected); + let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes); // then coerce to either the expected type or just the formal parameter type let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) { @@ -1931,7 +2182,20 @@ impl InferenceContext<'_> { // The function signature may contain some unknown types, so we need to insert // type vars here to avoid type mismatch false positive. let coercion_target = self.insert_type_vars(coercion_target); - if self.coerce(Some(arg), &ty, &coercion_target).is_err() && !arg_count_mismatch { + + // Any expression that produces a value of type `!` must have diverged, + // unless it's a place expression that isn't being read from, in which case + // diverging would be unsound since we may never actually read the `!`. + // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. + let coerce_never = + if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) { + CoerceNever::Yes + } else { + CoerceNever::No + }; + if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err() + && !arg_count_mismatch + { self.result.type_mismatches.insert( arg.into(), TypeMismatch { expected: coercion_target, actual: ty.clone() }, @@ -2106,7 +2370,7 @@ impl InferenceContext<'_> { } let _ty = arg.data(Interner).ty.clone(); let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly - self.infer_expr(args[arg_idx as usize], &expected); + self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes); // FIXME: evaluate and unify with the const } let mut indices = legacy_const_generics_indices.as_ref().clone(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 8e52725e536..6a0daee6ea9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -12,12 +12,11 @@ use hir_expand::name::Name; use intern::sym; use crate::{ - infer::Expectation, lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, Interner, - OverloadedDeref, TyBuilder, TyKind, + infer::{expr::ExprIsRead, Expectation, InferenceContext}, + lower::lower_to_chalk_mutability, + Adjust, Adjustment, AutoBorrow, Interner, OverloadedDeref, TyBuilder, TyKind, }; -use super::InferenceContext; - impl InferenceContext<'_> { pub(crate) fn infer_mut_body(&mut self) { self.infer_mut_expr(self.body.body_expr, Mutability::Not); @@ -164,7 +163,11 @@ impl InferenceContext<'_> { if let Some(ty) = self.result.type_of_expr.get(index) { ty.clone() } else { - self.infer_expr(index, &Expectation::none()) + self.infer_expr( + index, + &Expectation::none(), + ExprIsRead::Yes, + ) }; let trait_ref = TyBuilder::trait_ref(self.db, index_trait) .push(base_ty) @@ -180,6 +183,7 @@ impl InferenceContext<'_> { self.infer_mut_expr(index, Mutability::Not); } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + let mut mutability = mutability; if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { if mutability == Mutability::Mut { if let Some(deref_trait) = self @@ -187,7 +191,17 @@ impl InferenceContext<'_> { .lang_item(self.table.trait_env.krate, LangItem::DerefMut) .and_then(|l| l.as_trait()) { - if let Some(deref_fn) = self + let ty = self.result.type_of_expr.get(*expr); + let is_mut_ptr = ty.is_some_and(|ty| { + let ty = self.table.resolve_ty_shallow(ty); + matches!( + ty.kind(Interner), + chalk_ir::TyKind::Raw(Mutability::Mut, _) + ) + }); + if is_mut_ptr { + mutability = Mutability::Not; + } else if let Some(deref_fn) = self .db .trait_data(deref_trait) .method_by_name(&Name::new_symbol_root(sym::deref_mut.clone())) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 50542b2acd4..fee6755408e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -12,7 +12,7 @@ use stdx::TupleExt; use crate::{ consteval::{try_const_usize, usize_const}, - infer::{BindingMode, Expectation, InferenceContext, TypeMismatch}, + infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch}, lower::lower_to_chalk_mutability, primitive::UintTy, static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, @@ -361,7 +361,7 @@ impl InferenceContext<'_> { None => self.err_ty(), }, Pat::ConstBlock(expr) => { - self.infer_expr(*expr, &Expectation::has_type(expected.clone())) + self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes) } Pat::Missing => self.err_ty(), }; @@ -497,7 +497,7 @@ impl InferenceContext<'_> { } } - self.infer_expr(expr, &Expectation::has_type(expected.clone())) + self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes) } fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs index 7f994783c11..3dbefc5cec8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs @@ -8,7 +8,7 @@ use crate::{ ProgramClauseData, ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, Ty, TyData, TyKind, VariableKind, VariableKinds, }; -use base_db::salsa::InternId; +use base_db::ra_salsa::InternId; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; use hir_def::TypeAliasId; use intern::{impl_internable, Interned}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 4cdc0db46a1..2c68d50013e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -2,7 +2,7 @@ use std::fmt; -use base_db::salsa::Cycle; +use base_db::ra_salsa::Cycle; use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ @@ -72,6 +72,8 @@ pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx #[derive(Debug, PartialEq, Eq, Clone)] pub enum LayoutError { + // FIXME: Remove variants that duplicate LayoutCalculatorError's variants after sync + BadCalc(LayoutCalculatorError<()>), EmptyUnion, HasErrorConst, HasErrorType, @@ -90,6 +92,7 @@ impl std::error::Error for LayoutError {} impl fmt::Display for LayoutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + LayoutError::BadCalc(err) => err.fallback_fmt(f), LayoutError::EmptyUnion => write!(f, "type is an union with no fields"), LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"), LayoutError::HasErrorType => write!(f, "type contains an error"), @@ -114,11 +117,7 @@ impl fmt::Display for LayoutError { impl<F> From<LayoutCalculatorError<F>> for LayoutError { fn from(err: LayoutCalculatorError<F>) -> Self { - match err { - LayoutCalculatorError::EmptyUnion => LayoutError::EmptyUnion, - LayoutCalculatorError::UnexpectedUnsized(_) => LayoutError::UnexpectedUnsized, - LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow, - } + LayoutError::BadCalc(err.without_payload()) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index a060ebfe6be..c1a67fcc407 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -2,7 +2,7 @@ use std::{cmp, ops::Bound}; -use base_db::salsa::Cycle; +use base_db::ra_salsa::Cycle; use hir_def::{ data::adt::VariantData, layout::{Integer, ReprOptions, TargetDataLayout}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index ef570a20556..649cf88bb8d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -56,7 +56,7 @@ use std::{ hash::{BuildHasherDefault, Hash}, }; -use base_db::salsa::InternValueTrivial; +use base_db::ra_salsa::InternValueTrivial; use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index c6c2108e34a..c7ed68448bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -11,7 +11,7 @@ use std::{ ops::{self, Not as _}, }; -use base_db::{salsa::Cycle, CrateId}; +use base_db::{ra_salsa::Cycle, CrateId}; use chalk_ir::{ cast::Cast, fold::{Shift, TypeFoldable}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index c61d8277142..2f38e8fa14c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -5,7 +5,7 @@ use chalk_solve::rust_ir; -use base_db::salsa::{self, InternKey}; +use base_db::ra_salsa::{self, InternKey}; use hir_def::{LifetimeParamId, TraitId, TypeAliasId, TypeOrConstParamId}; use crate::{ @@ -116,24 +116,24 @@ impl From<crate::db::InternedCoroutineId> for chalk_ir::CoroutineId<Interner> { } pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { - chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id)) + chalk_ir::ForeignDefId(ra_salsa::InternKey::as_intern_id(&id)) } pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId { - salsa::InternKey::from_intern_id(id.0) + ra_salsa::InternKey::from_intern_id(id.0) } pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId { - chalk_ir::AssocTypeId(salsa::InternKey::as_intern_id(&id)) + chalk_ir::AssocTypeId(ra_salsa::InternKey::as_intern_id(&id)) } pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { - salsa::InternKey::from_intern_id(id.0) + ra_salsa::InternKey::from_intern_id(id.0) } pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); - let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); + let interned_id = ra_salsa::InternKey::from_intern_id(ra_salsa::InternId::from(idx.idx)); db.lookup_intern_type_or_const_param_id(interned_id) } @@ -141,13 +141,13 @@ pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Place let interned_id = db.intern_type_or_const_param_id(id); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, - idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), + idx: ra_salsa::InternKey::as_intern_id(&interned_id).as_usize(), } } pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); - let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); + let interned_id = ra_salsa::InternKey::from_intern_id(ra_salsa::InternId::from(idx.idx)); db.lookup_intern_lifetime_param_id(interned_id) } @@ -155,14 +155,14 @@ pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> Place let interned_id = db.intern_lifetime_param_id(id); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, - idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), + idx: ra_salsa::InternKey::as_intern_id(&interned_id).as_usize(), } } pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { - chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) + chalk_ir::TraitId(ra_salsa::InternKey::as_intern_id(&id)) } pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId { - salsa::InternKey::from_intern_id(id.0) + ra_salsa::InternKey::from_intern_id(id.0) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index a2cb122c543..16994cdd0c6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2,7 +2,7 @@ use std::{fmt::Write, iter, mem}; -use base_db::salsa::Cycle; +use base_db::ra_salsa::Cycle; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ body::Body, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index 8f6582b7f80..4c6bc376e2b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -9,7 +9,7 @@ use std::mem; -use base_db::salsa::Cycle; +use base_db::ra_salsa::Cycle; use chalk_ir::{ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, ConstData, DebruijnIndex, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 0efb9c52fb6..f37dd91d8e9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -3,7 +3,7 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - salsa::{self, Durability}, + ra_salsa::{self, Durability}, AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; use hir_def::{db::DefDatabase, ModuleId}; @@ -14,7 +14,7 @@ use syntax::TextRange; use test_utils::extract_annotations; use triomphe::Arc; -#[salsa::database( +#[ra_salsa::database( base_db::SourceRootDatabaseStorage, base_db::SourceDatabaseStorage, hir_expand::db::ExpandDatabaseStorage, @@ -23,8 +23,8 @@ use triomphe::Arc; crate::db::HirDatabaseStorage )] pub(crate) struct TestDB { - storage: salsa::Storage<TestDB>, - events: Mutex<Option<Vec<salsa::Event>>>, + storage: ra_salsa::Storage<TestDB>, + events: Mutex<Option<Vec<ra_salsa::Event>>>, } impl Default for TestDB { @@ -54,8 +54,8 @@ impl Upcast<dyn DefDatabase> for TestDB { } } -impl salsa::Database for TestDB { - fn salsa_event(&self, event: salsa::Event) { +impl ra_salsa::Database for TestDB { + fn salsa_event(&self, event: ra_salsa::Event) { let mut events = self.events.lock().unwrap(); if let Some(events) = &mut *events { events.push(event); @@ -63,9 +63,9 @@ impl salsa::Database for TestDB { } } -impl salsa::ParallelDatabase for TestDB { - fn snapshot(&self) -> salsa::Snapshot<TestDB> { - salsa::Snapshot::new(TestDB { +impl ra_salsa::ParallelDatabase for TestDB { + fn snapshot(&self) -> ra_salsa::Snapshot<TestDB> { + ra_salsa::Snapshot::new(TestDB { storage: self.storage.snapshot(), events: Default::default(), }) @@ -128,7 +128,7 @@ impl TestDB { } impl TestDB { - pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> { + pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<ra_salsa::Event> { *self.events.lock().unwrap() = Some(Vec::new()); f(); self.events.lock().unwrap().take().unwrap() @@ -141,7 +141,7 @@ impl TestDB { .filter_map(|e| match e.kind { // This is pretty horrible, but `Debug` is the only way to inspect // QueryDescriptor at the moment. - salsa::EventKind::WillExecute { database_key } => { + ra_salsa::EventKind::WillExecute { database_key } => { Some(format!("{:?}", database_key.debug(self))) } _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 22cef3505bf..b63d632dd26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -1,4 +1,4 @@ -use base_db::salsa::InternKey; +use base_db::ra_salsa::InternKey; use expect_test::{expect, Expect}; use hir_def::db::DefDatabase; use hir_expand::files::InFileWrapper; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 53b69c12f05..446f0b21a2a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -64,7 +64,7 @@ fn infer_macros_expanded() { "#, expect![[r#" !0..17 '{Foo(v...,2,])}': Foo - !1..4 'Foo': extern "rust-call" Foo({unknown}) -> Foo + !1..4 'Foo': fn Foo({unknown}) -> Foo !1..16 'Foo(vec![1,2,])': Foo !5..15 'vec![1,2,]': {unknown} 155..181 '{ ...,2); }': () @@ -97,7 +97,7 @@ fn infer_legacy_textual_scoped_macros_expanded() { "#, expect![[r#" !0..17 '{Foo(v...,2,])}': Foo - !1..4 'Foo': extern "rust-call" Foo({unknown}) -> Foo + !1..4 'Foo': fn Foo({unknown}) -> Foo !1..16 'Foo(vec![1,2,])': Foo !5..15 'vec![1,2,]': {unknown} 194..250 '{ ...,2); }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 5c63cd00f97..1ca4c9b2ad5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -539,3 +539,249 @@ fn test() { "#, ); } + +#[test] +fn diverging_place_match1() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn not_a_read() -> ! { + unsafe { + let x: *const ! = 0 as _; + let _: ! = *x; + } +} +"#, + expect![[r#" + 21..100 '{ ... } }': ! + 27..98 'unsafe... }': ! + 48..49 'x': *const ! + 62..63 '0': i32 + 62..68 '0 as _': *const ! + 82..83 '_': ! + 89..91 '*x': ! + 90..91 'x': *const ! + 27..98: expected !, got () + "#]], + ) +} + +#[test] +fn diverging_place_match2() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn not_a_read_implicit() -> ! { + unsafe { + let x: *const ! = 0 as _; + let _ = *x; + } +} +"#, + expect![[r#" + 30..106 '{ ... } }': ! + 36..104 'unsafe... }': ! + 57..58 'x': *const ! + 71..72 '0': i32 + 71..77 '0 as _': *const ! + 91..92 '_': ! + 95..97 '*x': ! + 96..97 'x': *const ! + 36..104: expected !, got () + "#]], + ) +} + +#[test] +fn diverging_place_match3() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn not_a_read_guide_coercion() -> ! { + unsafe { + let x: *const ! = 0 as _; + let _: () = *x; + } +} +"#, + expect![[r#" + 36..116 '{ ... } }': ! + 42..114 'unsafe... }': ! + 63..64 'x': *const ! + 77..78 '0': i32 + 77..83 '0 as _': *const ! + 97..98 '_': () + 105..107 '*x': ! + 106..107 'x': *const ! + 42..114: expected !, got () + 105..107: expected (), got ! + "#]], + ) +} + +#[test] +fn diverging_place_match4() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn empty_match() -> ! { + unsafe { + let x: *const ! = 0 as _; + match *x { _ => {} }; + } +} +"#, + expect![[r#" + 22..108 '{ ... } }': ! + 28..106 'unsafe... }': ! + 49..50 'x': *const ! + 63..64 '0': i32 + 63..69 '0 as _': *const ! + 79..99 'match ...> {} }': () + 85..87 '*x': ! + 86..87 'x': *const ! + 90..91 '_': ! + 95..97 '{}': () + 28..106: expected !, got () + "#]], + ) +} + +#[test] +fn diverging_place_match5() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn field_projection() -> ! { + unsafe { + let x: *const (!, ()) = 0 as _; + let _ = (*x).0; + } +} +"#, + expect![[r#" + 27..113 '{ ... } }': ! + 33..111 'unsafe... }': ! + 54..55 'x': *const (!, ()) + 74..75 '0': i32 + 74..80 '0 as _': *const (!, ()) + 94..95 '_': ! + 98..104 '(*x).0': ! + 99..101 '*x': (!, ()) + 100..101 'x': *const (!, ()) + 33..111: expected !, got () + "#]], + ) +} + +#[test] +fn diverging_place_match6() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn covered_arm() -> ! { + unsafe { + let x: *const ! = 0 as _; + let (_ | 1i32) = *x; + } +} +"#, + expect![[r#" + 22..107 '{ ... } }': ! + 28..105 'unsafe... }': ! + 49..50 'x': *const ! + 63..64 '0': i32 + 63..69 '0 as _': *const ! + 84..85 '_': ! + 84..92 '_ | 1i32': ! + 88..92 '1i32': i32 + 88..92 '1i32': i32 + 96..98 '*x': ! + 97..98 'x': *const ! + 28..105: expected !, got () + 88..92: expected !, got i32 + "#]], + ) +} + +#[test] +fn diverging_place_match7() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn uncovered_arm() -> ! { + unsafe { + let x: *const ! = 0 as _; + let (1i32 | _) = *x; + } +} +"#, + expect![[r#" + 24..109 '{ ... } }': ! + 30..107 'unsafe... }': ! + 51..52 'x': *const ! + 65..66 '0': i32 + 65..71 '0 as _': *const ! + 86..90 '1i32': i32 + 86..90 '1i32': i32 + 86..94 '1i32 | _': ! + 93..94 '_': ! + 98..100 '*x': ! + 99..100 'x': *const ! + 30..107: expected !, got () + 86..90: expected !, got i32 + "#]], + ) +} + +#[test] +fn diverging_place_match8() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn coerce_ref_binding() -> ! { + unsafe { + let x: *const ! = 0 as _; + let ref _x: () = *x; + } +} +"#, + expect![[r#" + 29..114 '{ ... } }': ! + 35..112 'unsafe... }': ! + 56..57 'x': *const ! + 70..71 '0': i32 + 70..76 '0 as _': *const ! + 90..96 'ref _x': &'? () + 103..105 '*x': ! + 104..105 'x': *const ! + 103..105: expected (), got ! + "#]], + ) +} + +#[test] +fn never_place_isnt_diverging() { + check_infer_with_mismatches( + r#" +//- minicore: sized +fn make_up_a_pointer<T>() -> *const T { + unsafe { + let x: *const ! = 0 as _; + &raw const *x + } +} +"#, + expect![[r#" + 38..116 '{ ... } }': *const T + 44..114 'unsafe... }': *const T + 65..66 'x': *const ! + 79..80 '0': i32 + 79..85 '0 as _': *const ! + 95..108 '&raw const *x': *const ! + 106..108 '*x': ! + 107..108 'x': *const ! + 95..108: expected *const T, got *const ! + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 51c27f8714a..4949d4016bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -227,13 +227,13 @@ fn infer_pattern_match_ergonomics() { 37..41 'A(n)': A<i32> 39..40 'n': &'? i32 44..49 '&A(1)': &'? A<i32> - 45..46 'A': extern "rust-call" A<i32>(i32) -> A<i32> + 45..46 'A': fn A<i32>(i32) -> A<i32> 45..49 'A(1)': A<i32> 47..48 '1': i32 59..63 'A(n)': A<i32> 61..62 'n': &'? mut i32 66..75 '&mut A(1)': &'? mut A<i32> - 71..72 'A': extern "rust-call" A<i32>(i32) -> A<i32> + 71..72 'A': fn A<i32>(i32) -> A<i32> 71..75 'A(1)': A<i32> 73..74 '1': i32 "#]], @@ -548,18 +548,18 @@ impl Foo { 56..64 'Self(s,)': Foo 61..62 's': &'? usize 67..75 '&Foo(0,)': &'? Foo - 68..71 'Foo': extern "rust-call" Foo(usize) -> Foo + 68..71 'Foo': fn Foo(usize) -> Foo 68..75 'Foo(0,)': Foo 72..73 '0': usize 89..97 'Self(s,)': Foo 94..95 's': &'? mut usize 100..112 '&mut Foo(0,)': &'? mut Foo - 105..108 'Foo': extern "rust-call" Foo(usize) -> Foo + 105..108 'Foo': fn Foo(usize) -> Foo 105..112 'Foo(0,)': Foo 109..110 '0': usize 126..134 'Self(s,)': Foo 131..132 's': usize - 137..140 'Foo': extern "rust-call" Foo(usize) -> Foo + 137..140 'Foo': fn Foo(usize) -> Foo 137..144 'Foo(0,)': Foo 141..142 '0': usize "#]], @@ -933,7 +933,7 @@ fn foo(foo: Foo) { 48..51 'foo': Foo 62..84 'const ... 32) }': Foo 68..84 '{ Foo(... 32) }': Foo - 70..73 'Foo': extern "rust-call" Foo(usize) -> Foo + 70..73 'Foo': fn Foo(usize) -> Foo 70..82 'Foo(15 + 32)': Foo 74..76 '15': usize 74..81 '15 + 32': usize diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index a3cf12d8a16..c4822a90f9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -645,7 +645,7 @@ fn issue_4953() { "#, expect![[r#" 58..72 '{ Self(0i64) }': Foo - 60..64 'Self': extern "rust-call" Foo(i64) -> Foo + 60..64 'Self': fn Foo(i64) -> Foo 60..70 'Self(0i64)': Foo 65..69 '0i64': i64 "#]], @@ -659,7 +659,7 @@ fn issue_4953() { "#, expect![[r#" 64..78 '{ Self(0i64) }': Foo<i64> - 66..70 'Self': extern "rust-call" Foo<i64>(i64) -> Foo<i64> + 66..70 'Self': fn Foo<i64>(i64) -> Foo<i64> 66..76 'Self(0i64)': Foo<i64> 71..75 '0i64': i64 "#]], @@ -859,7 +859,7 @@ fn main() { 94..96 '{}': () 109..160 '{ ...10); }': () 119..120 's': S<i32> - 123..124 'S': extern "rust-call" S<i32>() -> S<i32> + 123..124 'S': fn S<i32>() -> S<i32> 123..126 'S()': S<i32> 132..133 's': S<i32> 132..144 's.g(|_x| {})': () @@ -1616,7 +1616,7 @@ fn main() { 37..48 'S(.., a, b)': S 43..44 'a': usize 46..47 'b': {unknown} - 51..52 'S': extern "rust-call" S(usize) -> S + 51..52 'S': fn S(usize) -> S 51..55 'S(1)': S 53..54 '1': usize 65..75 '(.., a, b)': (i32, {unknown}) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 0473ee02fab..a8170b60606 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -236,14 +236,14 @@ fn test() { expect![[r#" 71..153 '{ ...a.c; }': () 81..82 'c': C - 85..86 'C': extern "rust-call" C(usize) -> C + 85..86 'C': fn C(usize) -> C 85..89 'C(1)': C 87..88 '1': usize 95..96 'B': B 106..107 'a': A 113..132 'A { b:...C(1) }': A 120..121 'B': B - 126..127 'C': extern "rust-call" C(usize) -> C + 126..127 'C': fn C(usize) -> C 126..130 'C(1)': C 128..129 '1': usize 138..139 'a': A @@ -629,12 +629,12 @@ impl E { 86..107 '{ ... }': () 96..100 'Self': S1 134..158 '{ ... }': () - 144..148 'Self': extern "rust-call" S2(isize) -> S2 + 144..148 'Self': fn S2(isize) -> S2 144..151 'Self(1)': S2 149..150 '1': isize 184..230 '{ ... }': () 194..202 'Self::V1': E - 212..220 'Self::V2': extern "rust-call" V2(u32) -> E + 212..220 'Self::V2': fn V2(u32) -> E 212..223 'Self::V2(1)': E 221..222 '1': u32 "#]], @@ -860,11 +860,11 @@ fn test() { 256..277 'A::foo...42))))': &'? i32 263..276 '&&B(B(A(42)))': &'? &'? B<B<A<i32>>> 264..276 '&B(B(A(42)))': &'? B<B<A<i32>>> - 265..266 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> + 265..266 'B': fn B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 265..276 'B(B(A(42)))': B<B<A<i32>>> - 267..268 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>> + 267..268 'B': fn B<A<i32>>(A<i32>) -> B<A<i32>> 267..275 'B(A(42))': B<A<i32>> - 269..270 'A': extern "rust-call" A<i32>(i32) -> A<i32> + 269..270 'A': fn A<i32>(i32) -> A<i32> 269..274 'A(42)': A<i32> 271..273 '42': i32 "#]], @@ -914,16 +914,16 @@ fn test(a: A<i32>) { 253..254 'a': A<i32> 264..310 '{ ...))); }': () 274..275 't': &'? i32 - 278..279 'A': extern "rust-call" A<i32>(*mut i32) -> A<i32> + 278..279 'A': fn A<i32>(*mut i32) -> A<i32> 278..292 'A(0 as *mut _)': A<i32> 278..307 'A(0 as...B(a)))': &'? i32 280..281 '0': usize 280..291 '0 as *mut _': *mut i32 297..306 '&&B(B(a))': &'? &'? B<B<A<i32>>> 298..306 '&B(B(a))': &'? B<B<A<i32>>> - 299..300 'B': extern "rust-call" B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> + 299..300 'B': fn B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 299..306 'B(B(a))': B<B<A<i32>>> - 301..302 'B': extern "rust-call" B<A<i32>>(A<i32>) -> B<A<i32>> + 301..302 'B': fn B<A<i32>>(A<i32>) -> B<A<i32>> 301..305 'B(a)': B<A<i32>> 303..304 'a': A<i32> "#]], @@ -1277,16 +1277,16 @@ fn infer_tuple_struct_generics() { "#, expect![[r#" 75..183 '{ ...one; }': () - 81..82 'A': extern "rust-call" A<i32>(i32) -> A<i32> + 81..82 'A': fn A<i32>(i32) -> A<i32> 81..86 'A(42)': A<i32> 83..85 '42': i32 - 92..93 'A': extern "rust-call" A<u128>(u128) -> A<u128> + 92..93 'A': fn A<u128>(u128) -> A<u128> 92..101 'A(42u128)': A<u128> 94..100 '42u128': u128 - 107..111 'Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str> + 107..111 'Some': fn Some<&'static str>(&'static str) -> Option<&'static str> 107..116 'Some("x")': Option<&'static str> 112..115 '"x"': &'static str - 122..134 'Option::Some': extern "rust-call" Some<&'static str>(&'static str) -> Option<&'static str> + 122..134 'Option::Some': fn Some<&'static str>(&'static str) -> Option<&'static str> 122..139 'Option...e("x")': Option<&'static str> 135..138 '"x"': &'static str 145..149 'None': Option<{unknown}> @@ -1572,7 +1572,7 @@ fn infer_type_alias() { 204..207 'z.y': i8 298..362 '{ ... &e; }': () 308..309 'e': Enum - 312..325 'm::Alias::Foo': extern "rust-call" Foo(u8) -> Enum + 312..325 'm::Alias::Foo': fn Foo(u8) -> Enum 312..328 'm::Ali...Foo(0)': Enum 326..327 '0': u8 338..354 'm::Ali...Foo(x)': Enum @@ -2191,10 +2191,10 @@ fn main() { 103..231 '{ ... }); }': () 109..161 'async ... }': impl Future<Output = Result<(), ()>> 125..139 'return Err(())': ! - 132..135 'Err': extern "rust-call" Err<(), ()>(()) -> Result<(), ()> + 132..135 'Err': fn Err<(), ()>(()) -> Result<(), ()> 132..139 'Err(())': Result<(), ()> 136..138 '()': () - 149..151 'Ok': extern "rust-call" Ok<(), ()>(()) -> Result<(), ()> + 149..151 'Ok': fn Ok<(), ()>(()) -> Result<(), ()> 149..155 'Ok(())': Result<(), ()> 152..154 '()': () 167..171 'test': fn test<(), (), impl FnMut() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl FnMut() -> impl Future<Output = Result<(), ()>>) @@ -2202,10 +2202,10 @@ fn main() { 172..227 '|| asy... }': impl FnMut() -> impl Future<Output = Result<(), ()>> 175..227 'async ... }': impl Future<Output = Result<(), ()>> 191..205 'return Err(())': ! - 198..201 'Err': extern "rust-call" Err<(), ()>(()) -> Result<(), ()> + 198..201 'Err': fn Err<(), ()>(()) -> Result<(), ()> 198..205 'Err(())': Result<(), ()> 202..204 '()': () - 215..217 'Ok': extern "rust-call" Ok<(), ()>(()) -> Result<(), ()> + 215..217 'Ok': fn Ok<(), ()>(()) -> Result<(), ()> 215..221 'Ok(())': Result<(), ()> 218..220 '()': () "#]], @@ -2234,7 +2234,7 @@ fn infer_generic_from_later_assignment() { 94..127 '{ ... }': () 104..107 'end': Option<bool> 104..120 'end = ...(true)': () - 110..114 'Some': extern "rust-call" Some<bool>(bool) -> Option<bool> + 110..114 'Some': fn Some<bool>(bool) -> Option<bool> 110..120 'Some(true)': Option<bool> 115..119 'true': bool "#]], @@ -2269,7 +2269,7 @@ fn infer_loop_break_with_val() { 111..121 'break None': ! 117..121 'None': Option<bool> 142..158 'break ...(true)': ! - 148..152 'Some': extern "rust-call" Some<bool>(bool) -> Option<bool> + 148..152 'Some': fn Some<bool>(bool) -> Option<bool> 148..158 'Some(true)': Option<bool> 153..157 'true': bool "#]], @@ -2516,7 +2516,7 @@ fn generic_default_in_struct_literal() { 254..281 'OtherT...1i32 }': OtherThing<i32> 275..279 '1i32': i32 291..292 'b': OtherThing<i32> - 295..310 'OtherThing::Two': extern "rust-call" Two<i32>(i32) -> OtherThing<i32> + 295..310 'OtherThing::Two': fn Two<i32>(i32) -> OtherThing<i32> 295..316 'OtherT...(1i32)': OtherThing<i32> 311..315 '1i32': i32 "#]], @@ -3028,7 +3028,7 @@ fn f() { expect![[r#" 72..166 '{ ... } }': () 78..164 'match ... }': () - 84..92 'Foo::Bar': extern "rust-call" Bar(i32) -> Foo + 84..92 'Foo::Bar': fn Bar(i32) -> Foo 84..95 'Foo::Bar(3)': Foo 93..94 '3': i32 106..119 'Qux::Bar(bar)': Foo @@ -3087,9 +3087,9 @@ fn main() { 322..324 '{}': Foo<T> 338..559 '{ ...r(); }': () 348..353 'boxed': Box<Foo<i32>> - 356..359 'Box': extern "rust-call" Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>> + 356..359 'Box': fn Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>> 356..371 'Box(Foo(0_i32))': Box<Foo<i32>> - 360..363 'Foo': extern "rust-call" Foo<i32>(i32) -> Foo<i32> + 360..363 'Foo': fn Foo<i32>(i32) -> Foo<i32> 360..370 'Foo(0_i32)': Foo<i32> 364..369 '0_i32': i32 382..386 'bad1': &'? i32 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 0b2d6bdd259..9b982a124e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -523,7 +523,7 @@ fn test() -> u64 { expect![[r#" 37..86 '{ ... a.1 }': u64 47..48 'a': S - 51..52 'S': extern "rust-call" S(i32, u64) -> S + 51..52 'S': fn S(i32, u64) -> S 51..58 'S(4, 6)': S 53..54 '4': i32 56..57 '6': u64 @@ -549,7 +549,7 @@ fn test() -> u64 { expect![[r#" 43..108 '{ ...0(2) }': u64 53..54 'a': S - 57..58 'S': extern "rust-call" S(fn(u32) -> u64) -> S + 57..58 'S': fn S(fn(u32) -> u64) -> S 57..74 'S(|i| ...s u64)': S 59..73 '|i| 2*i as u64': impl Fn(u32) -> u64 60..61 'i': u32 @@ -1027,7 +1027,7 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { 201..202 'x': impl Trait<u64> 208..209 'y': &'? impl Trait<u32> 219..220 'z': S<u16> - 223..224 'S': extern "rust-call" S<u16>(u16) -> S<u16> + 223..224 'S': fn S<u16>(u16) -> S<u16> 223..227 'S(1)': S<u16> 225..226 '1': u16 233..236 'bar': fn bar(S<u16>) @@ -1269,10 +1269,10 @@ fn bar() { 226..229 'foo': fn foo<i32>([R<(), i32>; 2]) -> i32 226..250 'foo([R...B(7)])': i32 230..249 '[R::A(...:B(7)]': [R<(), i32>; 2] - 231..235 'R::A': extern "rust-call" A<(), i32>(()) -> R<(), i32> + 231..235 'R::A': fn A<(), i32>(()) -> R<(), i32> 231..239 'R::A(())': R<(), i32> 236..238 '()': () - 241..245 'R::B': extern "rust-call" B<(), i32>(i32) -> R<(), i32> + 241..245 'R::B': fn B<(), i32>(i32) -> R<(), i32> 241..248 'R::B(7)': R<(), i32> 246..247 '7': i32 "#]], @@ -1421,7 +1421,7 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) { 142..147 'input': &'? str 149..150 't': T 152..154 '{}': () - 156..159 'Bar': extern "rust-call" Bar<u8>(u8) -> Bar<u8> + 156..159 'Bar': fn Bar<u8>(u8) -> Bar<u8> 156..162 'Bar(C)': Bar<u8> 160..161 'C': u8 "#]], @@ -2046,7 +2046,7 @@ fn test() { 118..120 '{}': () 136..255 '{ ... 1); }': () 146..147 'x': Option<u32> - 150..162 'Option::Some': extern "rust-call" Some<u32>(u32) -> Option<u32> + 150..162 'Option::Some': fn Some<u32>(u32) -> Option<u32> 150..168 'Option...(1u32)': Option<u32> 163..167 '1u32': u32 174..175 'x': Option<u32> @@ -2602,7 +2602,7 @@ fn test() -> impl Trait<i32> { 178..180 '{}': () 213..309 '{ ...t()) }': S<i32> 223..225 's1': S<u32> - 228..229 'S': extern "rust-call" S<u32>(u32) -> S<u32> + 228..229 'S': fn S<u32>(u32) -> S<u32> 228..240 'S(default())': S<u32> 230..237 'default': fn default<u32>() -> u32 230..239 'default()': u32 @@ -2612,11 +2612,11 @@ fn test() -> impl Trait<i32> { 263..264 'x': i32 272..275 'bar': fn bar<i32>(S<i32>) -> i32 272..289 'bar(S(...lt()))': i32 - 276..277 'S': extern "rust-call" S<i32>(i32) -> S<i32> + 276..277 'S': fn S<i32>(i32) -> S<i32> 276..288 'S(default())': S<i32> 278..285 'default': fn default<i32>() -> i32 278..287 'default()': i32 - 295..296 'S': extern "rust-call" S<i32>(i32) -> S<i32> + 295..296 'S': fn S<i32>(i32) -> S<i32> 295..307 'S(default())': S<i32> 297..304 'default': fn default<i32>() -> i32 297..306 'default()': i32 @@ -2846,7 +2846,7 @@ fn main() { 1036..1041 'x > 0': bool 1040..1041 '0': i32 1042..1060 '{ Some...u32) }': Option<u32> - 1044..1048 'Some': extern "rust-call" Some<u32>(u32) -> Option<u32> + 1044..1048 'Some': fn Some<u32>(u32) -> Option<u32> 1044..1058 'Some(x as u32)': Option<u32> 1049..1050 'x': i32 1049..1057 'x as u32': u32 @@ -2982,9 +2982,9 @@ fn test() { 175..185 'foo.test()': bool 191..194 'bar': fn bar<{unknown}>({unknown}) -> {unknown} 191..201 'bar.test()': bool - 207..213 'Struct': extern "rust-call" Struct(usize) -> Struct + 207..213 'Struct': fn Struct(usize) -> Struct 207..220 'Struct.test()': bool - 226..239 'Enum::Variant': extern "rust-call" Variant(usize) -> Enum + 226..239 'Enum::Variant': fn Variant(usize) -> Enum 226..246 'Enum::...test()': bool "#]], ); @@ -3563,12 +3563,12 @@ fn main(){ 95..99 'self': Wrapper 101..104 'rhs': u32 122..150 '{ ... }': Wrapper - 132..139 'Wrapper': extern "rust-call" Wrapper(u32) -> Wrapper + 132..139 'Wrapper': fn Wrapper(u32) -> Wrapper 132..144 'Wrapper(rhs)': Wrapper 140..143 'rhs': u32 162..248 '{ ...um; }': () 172..179 'wrapped': Wrapper - 182..189 'Wrapper': extern "rust-call" Wrapper(u32) -> Wrapper + 182..189 'Wrapper': fn Wrapper(u32) -> Wrapper 182..193 'Wrapper(10)': Wrapper 190..192 '10': u32 203..206 'num': u32 diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index 74c8fc96d4a..7474d7bc54d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -1,7 +1,7 @@ //! Applies changes to the IDE state transactionally. use base_db::{ - salsa::{ + ra_salsa::{ debug::{DebugQueryTable, TableEntry}, Database, Durability, Query, QueryTable, }, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index a45ff9a9545..aed093f0ebf 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -48,7 +48,7 @@ pub use hir::ChangeWithProcMacros; use std::{fmt, mem::ManuallyDrop}; use base_db::{ - salsa::{self, Durability}, + ra_salsa::{self, Durability}, AnchoredPath, CrateId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, DEFAULT_FILE_TEXT_LRU_CAP, }; @@ -74,7 +74,7 @@ pub type FxIndexMap<K, V> = pub type FilePosition = FilePositionWrapper<FileId>; pub type FileRange = FileRangeWrapper<FileId>; -#[salsa::database( +#[ra_salsa::database( base_db::SourceRootDatabaseStorage, base_db::SourceDatabaseStorage, hir::db::ExpandDatabaseStorage, @@ -89,7 +89,7 @@ pub struct RootDatabase { // `&RootDatabase -> &dyn OtherDatabase` cast will instantiate its drop glue in the vtable, // which duplicates `Weak::drop` and `Arc::drop` tens of thousands of times, which makes // compile times of all `ide_*` and downstream crates suffer greatly. - storage: ManuallyDrop<salsa::Storage<RootDatabase>>, + storage: ManuallyDrop<ra_salsa::Storage<RootDatabase>>, } impl Drop for RootDatabase { @@ -134,7 +134,7 @@ impl FileLoader for RootDatabase { } } -impl salsa::Database for RootDatabase {} +impl ra_salsa::Database for RootDatabase {} impl Default for RootDatabase { fn default() -> RootDatabase { @@ -144,7 +144,7 @@ impl Default for RootDatabase { impl RootDatabase { pub fn new(lru_capacity: Option<u16>) -> RootDatabase { - let mut db = RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()) }; + let mut db = RootDatabase { storage: ManuallyDrop::new(ra_salsa::Storage::default()) }; db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); db.set_proc_macros_with_durability(Default::default(), Durability::HIGH); db.set_local_roots_with_durability(Default::default(), Durability::HIGH); @@ -195,13 +195,15 @@ impl RootDatabase { } } -impl salsa::ParallelDatabase for RootDatabase { - fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { - salsa::Snapshot::new(RootDatabase { storage: ManuallyDrop::new(self.storage.snapshot()) }) +impl ra_salsa::ParallelDatabase for RootDatabase { + fn snapshot(&self) -> ra_salsa::Snapshot<RootDatabase> { + ra_salsa::Snapshot::new(RootDatabase { + storage: ManuallyDrop::new(self.storage.snapshot()), + }) } } -#[salsa::query_group(LineIndexDatabaseStorage)] +#[ra_salsa::query_group(LineIndexDatabaseStorage)] pub trait LineIndexDatabase: base_db::SourceDatabase { fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 19d8a15422e..0002fda0ba7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -10,7 +10,7 @@ use hir::db::DefDatabase; use crate::{ base_db::{ - salsa::{Database, ParallelDatabase, Snapshot}, + ra_salsa::{Database, ParallelDatabase, Snapshot}, Cancelled, CrateId, SourceDatabase, SourceRootDatabase, }, symbol_index::SymbolsDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 852ee595be4..c5215eb3e63 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -7,7 +7,7 @@ use std::mem; use std::{cell::LazyCell, cmp::Reverse}; -use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase}; +use base_db::{ra_salsa::Database, SourceDatabase, SourceRootDatabase}; use either::Either; use hir::{ sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 209b1477bac..94d354d28e5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -28,7 +28,7 @@ use std::{ }; use base_db::{ - salsa::{self, ParallelDatabase}, + ra_salsa::{self, ParallelDatabase}, SourceRootDatabase, SourceRootId, Upcast, }; use fst::{raw::IndexedValue, Automaton, Streamer}; @@ -99,7 +99,7 @@ impl Query { } } -#[salsa::query_group(SymbolsDatabaseStorage)] +#[ra_salsa::query_group(SymbolsDatabaseStorage)] pub trait SymbolsDatabase: HirDatabase + SourceRootDatabase + Upcast<dyn HirDatabase> { /// The symbol index for a given module. These modules should only be in source roots that /// are inside local_roots. @@ -108,18 +108,18 @@ pub trait SymbolsDatabase: HirDatabase + SourceRootDatabase + Upcast<dyn HirData /// The symbol index for a given source root within library_roots. fn library_symbols(&self, source_root_id: SourceRootId) -> Arc<SymbolIndex>; - #[salsa::transparent] + #[ra_salsa::transparent] /// The symbol indices of modules that make up a given crate. fn crate_symbols(&self, krate: Crate) -> Box<[Arc<SymbolIndex>]>; /// The set of "local" (that is, from the current workspace) roots. /// Files in local roots are assumed to change frequently. - #[salsa::input] + #[ra_salsa::input] fn local_roots(&self) -> Arc<FxHashSet<SourceRootId>>; /// The set of roots for crates.io libraries. /// Files in libraries are assumed to never change. - #[salsa::input] + #[ra_salsa::input] fn library_roots(&self) -> Arc<FxHashSet<SourceRootId>>; } @@ -155,13 +155,13 @@ pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolI /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap<DB>(DB); -impl<DB: ParallelDatabase> Snap<salsa::Snapshot<DB>> { +impl<DB: ParallelDatabase> Snap<ra_salsa::Snapshot<DB>> { fn new(db: &DB) -> Self { Self(db.snapshot()) } } -impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> { - fn clone(&self) -> Snap<salsa::Snapshot<DB>> { +impl<DB: ParallelDatabase> Clone for Snap<ra_salsa::Snapshot<DB>> { + fn clone(&self) -> Snap<ra_salsa::Snapshot<DB>> { Snap(self.0.snapshot()) } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index ad4baf5e3a4..4bd29b8c79b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -95,10 +95,10 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) - DiagnosticCode::RustcHardError("E0605"), format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty), ), - CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => ( - DiagnosticCode::RustcHardError("E0641"), - "cannot cast to a pointer of an unknown kind".to_owned(), - ), + // CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => ( + // DiagnosticCode::RustcHardError("E0641"), + // "cannot cast to a pointer of an unknown kind".to_owned(), + // ), }; Diagnostic::new(code, message, display_range) } @@ -457,20 +457,20 @@ fn foo<T: ?Sized>() { ); } - #[test] - fn order_dependent_cast_inference() { - check_diagnostics( - r#" -//- minicore: sized -fn main() { - let x = &"hello"; - let mut y = 0 as *const _; - //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind - y = x as *const _; -} -"#, - ); - } + // #[test] + // fn order_dependent_cast_inference() { + // check_diagnostics( + // r#" + // //- minicore: sized + // fn main() { + // let x = &"hello"; + // let mut y = 0 as *const _; + // //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind + // y = x as *const _; + // } + // "#, + // ); + // } #[test] fn ptr_to_ptr_different_regions() { @@ -1111,4 +1111,22 @@ fn foo() { "#, ); } + + #[test] + fn cast_isize_to_infer_pointer() { + check_diagnostics( + r#" +//- minicore: coerce_unsized +struct Foo {} + +struct Wrap<'a>(&'a mut Foo); + +fn main() { + let lparam: isize = 0; + + let _wrap = Wrap(unsafe { &mut *(lparam as *mut _) }); +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 95542793915..6fa0e7a5a89 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1258,4 +1258,29 @@ pub unsafe fn foo(a: *mut A) { "#, ); } + + #[test] + fn regression_15799() { + check_diagnostics( + r#" +//- minicore: deref_mut +struct WrapPtr(*mut u32); + +impl core::ops::Deref for WrapPtr { + type Target = *mut u32; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn main() { + let mut x = 0u32; + let wrap = WrapPtr(&mut x); + unsafe { + **wrap = 6; + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 5cce7c4aed5..90f88d6705b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -130,6 +130,7 @@ fn add_missing_ok_or_some( if d.actual.is_unit() { if let Expr::BlockExpr(block) = &expr { if block.tail_expr().is_none() { + // Fix for forms like `fn foo() -> Result<(), String> {}` let mut builder = TextEdit::builder(); let block_indent = block.indent_level(); @@ -156,6 +157,20 @@ fn add_missing_ok_or_some( acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range)); } return Some(()); + } else if let Expr::ReturnExpr(ret_expr) = &expr { + // Fix for forms like `fn foo() -> Result<(), String> { return; }` + if ret_expr.expr().is_none() { + let mut builder = TextEdit::builder(); + builder + .insert(ret_expr.syntax().text_range().end(), format!(" {variant_name}(())")); + let source_change = SourceChange::from_text_edit( + expr_ptr.file_id.original_file(ctx.sema.db), + builder.finish(), + ); + let name = format!("Insert {variant_name}(()) as the return value"); + acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range)); + } + return Some(()); } } @@ -604,6 +619,29 @@ fn foo() -> Result<(), ()> { } #[test] + fn test_wrapped_unit_as_return_expr() { + check_fix( + r#" +//- minicore: result +fn foo(b: bool) -> Result<(), String> { + if b { + return$0; + } + + Err("oh dear".to_owned()) +}"#, + r#" +fn foo(b: bool) -> Result<(), String> { + if b { + return Ok(()); + } + + Err("oh dear".to_owned()) +}"#, + ); + } + + #[test] fn test_in_const_and_static() { check_fix( r#" diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 42930889d75..d783e195252 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -1,7 +1,7 @@ use expect_test::{expect, Expect}; use hir::{FilePosition, FileRange}; use ide_db::{ - base_db::{salsa::Durability, SourceDatabase}, + base_db::{ra_salsa::Durability, SourceDatabase}, EditionedFileId, FxHashSet, }; use test_utils::RangeOrOffset; diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 155259a1380..1b82c00d1dc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -19,6 +19,12 @@ pub struct CallItem { pub ranges: Vec<FileRange>, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CallHierarchyConfig { + /// Whether to exclude tests from the call hierarchy + pub exclude_tests: bool, +} + pub(crate) fn call_hierarchy( db: &RootDatabase, position: FilePosition, @@ -28,6 +34,7 @@ pub(crate) fn call_hierarchy( pub(crate) fn incoming_calls( db: &RootDatabase, + CallHierarchyConfig { exclude_tests }: CallHierarchyConfig, FilePosition { file_id, offset }: FilePosition, ) -> Option<Vec<CallItem>> { let sema = &Semantics::new(db); @@ -56,11 +63,18 @@ pub(crate) fn incoming_calls( references.iter().filter_map(|FileReference { name, .. }| name.as_name_ref()); for name in references { // This target is the containing function - let nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| { + let def_nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| { let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?; - def.try_to_nav(sema.db) + // We should return def before check if it is a test, so that we + // will not continue to search for outer fn in nested fns + def.try_to_nav(sema.db).map(|nav| (def, nav)) }); - if let Some(nav) = nav { + + if let Some((def, nav)) = def_nav { + if exclude_tests && def.is_test(db) { + continue; + } + let range = sema.original_range(name.syntax()); calls.add(nav.call_site, range.into()); if let Some(other) = nav.def_site { @@ -75,6 +89,7 @@ pub(crate) fn incoming_calls( pub(crate) fn outgoing_calls( db: &RootDatabase, + CallHierarchyConfig { exclude_tests }: CallHierarchyConfig, FilePosition { file_id, offset }: FilePosition, ) -> Option<Vec<CallItem>> { let sema = Semantics::new(db); @@ -103,7 +118,12 @@ pub(crate) fn outgoing_calls( let expr = call.expr()?; let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?; match callable.kind() { - hir::CallableKind::Function(it) => it.try_to_nav(db), + hir::CallableKind::Function(it) => { + if exclude_tests && it.is_test(db) { + return None; + } + it.try_to_nav(db) + } hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db), hir::CallableKind::TupleStruct(it) => it.try_to_nav(db), _ => None, @@ -112,6 +132,9 @@ pub(crate) fn outgoing_calls( } ast::CallableExpr::MethodCall(expr) => { let function = sema.resolve_method_call(&expr)?; + if exclude_tests && function.is_test(db) { + return None; + } function .try_to_nav(db) .zip(Some(sema.original_range(expr.name_ref()?.syntax()))) @@ -149,6 +172,7 @@ mod tests { use crate::fixture; fn check_hierarchy( + exclude_tests: bool, ra_fixture: &str, expected_nav: Expect, expected_incoming: Expect, @@ -172,18 +196,21 @@ mod tests { let nav = navs.pop().unwrap(); expected_nav.assert_eq(&nav.debug_render()); + let config = crate::CallHierarchyConfig { exclude_tests }; + let item_pos = FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() }; - let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap(); + let incoming_calls = analysis.incoming_calls(config, item_pos).unwrap().unwrap(); expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n")); - let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap(); + let outgoing_calls = analysis.outgoing_calls(config, item_pos).unwrap().unwrap(); expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n")); } #[test] fn test_call_hierarchy_on_ref() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -200,6 +227,7 @@ fn caller() { #[test] fn test_call_hierarchy_on_def() { check_hierarchy( + false, r#" //- /lib.rs fn call$0ee() {} @@ -216,6 +244,7 @@ fn caller() { #[test] fn test_call_hierarchy_in_same_fn() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -233,6 +262,7 @@ fn caller() { #[test] fn test_call_hierarchy_in_different_fn() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -255,6 +285,7 @@ fn caller2() { #[test] fn test_call_hierarchy_in_tests_mod() { check_hierarchy( + false, r#" //- /lib.rs cfg:test fn callee() {} @@ -283,6 +314,7 @@ mod tests { #[test] fn test_call_hierarchy_in_different_files() { check_hierarchy( + false, r#" //- /lib.rs mod foo; @@ -304,6 +336,7 @@ pub fn callee() {} #[test] fn test_call_hierarchy_outgoing() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -321,6 +354,7 @@ fn call$0er() { #[test] fn test_call_hierarchy_outgoing_in_different_files() { check_hierarchy( + false, r#" //- /lib.rs mod foo; @@ -342,6 +376,7 @@ pub fn callee() {} #[test] fn test_call_hierarchy_incoming_outgoing() { check_hierarchy( + false, r#" //- /lib.rs fn caller1() { @@ -365,6 +400,7 @@ fn caller3() { #[test] fn test_call_hierarchy_issue_5103() { check_hierarchy( + false, r#" fn a() { b() @@ -382,6 +418,7 @@ fn main() { ); check_hierarchy( + false, r#" fn a() { b$0() @@ -402,6 +439,7 @@ fn main() { #[test] fn test_call_hierarchy_in_macros_incoming() { check_hierarchy( + false, r#" macro_rules! define { ($ident:ident) => { @@ -423,6 +461,7 @@ fn caller() { expect![[]], ); check_hierarchy( + false, r#" macro_rules! define { ($ident:ident) => { @@ -448,6 +487,7 @@ fn caller() { #[test] fn test_call_hierarchy_in_macros_outgoing() { check_hierarchy( + false, r#" macro_rules! define { ($ident:ident) => { @@ -473,6 +513,7 @@ fn caller$0() { #[test] fn test_call_hierarchy_in_macros_incoming_different_files() { check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -498,6 +539,7 @@ macro_rules! call { expect![[]], ); check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -523,6 +565,7 @@ macro_rules! call { expect![[]], ); check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -558,6 +601,7 @@ macro_rules! call { #[test] fn test_call_hierarchy_in_macros_outgoing_different_files() { check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -585,6 +629,7 @@ macro_rules! call { expect![[]], ); check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -616,6 +661,7 @@ macro_rules! call { #[test] fn test_trait_method_call_hierarchy() { check_hierarchy( + false, r#" trait T1 { fn call$0ee(); @@ -636,4 +682,64 @@ fn caller() { expect![[]], ); } + + #[test] + fn test_call_hierarchy_excluding_tests() { + check_hierarchy( + false, + r#" +fn main() { + f1(); +} + +fn f1$0() { + f2(); f3(); +} + +fn f2() { + f1(); f3(); +} + +#[test] +fn f3() { + f1(); f2(); +} +"#, + expect!["f1 Function FileId(0) 25..52 28..30"], + expect![[r#" + main Function FileId(0) 0..23 3..7 : FileId(0):16..18 + f2 Function FileId(0) 54..81 57..59 : FileId(0):68..70 + f3 Function FileId(0) 83..118 94..96 : FileId(0):105..107"#]], + expect![[r#" + f2 Function FileId(0) 54..81 57..59 : FileId(0):39..41 + f3 Function FileId(0) 83..118 94..96 : FileId(0):45..47"#]], + ); + + check_hierarchy( + true, + r#" +fn main() { + f1(); +} + +fn f1$0() { + f2(); f3(); +} + +fn f2() { + f1(); f3(); +} + +#[test] +fn f3() { + f1(); f2(); +} +"#, + expect!["f1 Function FileId(0) 25..52 28..30"], + expect![[r#" + main Function FileId(0) 0..23 3..7 : FileId(0):16..18 + f2 Function FileId(0) 54..81 57..59 : FileId(0):68..70"#]], + expect!["f2 Function FileId(0) 54..81 57..59 : FileId(0):39..41"], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 4c8e3fc3040..fc29ba06dad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -281,99 +281,95 @@ fn highlight_references( } } -// If `file_id` is None, -pub(crate) fn highlight_exit_points( +fn hl_exit_points( sema: &Semantics<'_, RootDatabase>, - token: SyntaxToken, -) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> { - fn hl( - sema: &Semantics<'_, RootDatabase>, - def_token: Option<SyntaxToken>, - body: ast::Expr, - ) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> { - let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default(); + def_token: Option<SyntaxToken>, + body: ast::Expr, +) -> Option<FxHashMap<EditionedFileId, FxHashSet<HighlightedRange>>> { + let mut highlights: FxHashMap<EditionedFileId, FxHashSet<_>> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().insert(hrange); + } + }; - let mut push_to_highlights = |file_id, range| { - if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { - let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; - highlights.entry(file_id).or_default().insert(hrange); + if let Some(tok) = def_token { + let file_id = sema.hir_file_for(&tok.parent()?); + let range = Some(tok.text_range()); + push_to_highlights(file_id, range); + } + + WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| { + let file_id = sema.hir_file_for(expr.syntax()); + + let range = match &expr { + ast::Expr::TryExpr(try_) => try_.question_mark_token().map(|token| token.text_range()), + ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) + if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) => + { + Some(expr.syntax().text_range()) } + _ => None, }; - if let Some(tok) = def_token { - let file_id = sema.hir_file_for(&tok.parent()?); - let range = Some(tok.text_range()); - push_to_highlights(file_id, range); - } + push_to_highlights(file_id, range); + }); - WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| { + // We should handle `return` separately, because when it is used in a `try` block, + // it will exit the outside function instead of the block itself. + WalkExpandedExprCtx::new(sema) + .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure) + .walk(&body, &mut |_, expr| { let file_id = sema.hir_file_for(expr.syntax()); let range = match &expr { - ast::Expr::TryExpr(try_) => { - try_.question_mark_token().map(|token| token.text_range()) - } - ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) - if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) => - { - Some(expr.syntax().text_range()) - } + ast::Expr::ReturnExpr(expr) => expr.return_token().map(|token| token.text_range()), _ => None, }; push_to_highlights(file_id, range); }); - // We should handle `return` separately, because when it is used in a `try` block, - // it will exit the outside function instead of the block itself. - WalkExpandedExprCtx::new(sema) - .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure) - .walk(&body, &mut |_, expr| { - let file_id = sema.hir_file_for(expr.syntax()); - - let range = match &expr { - ast::Expr::ReturnExpr(expr) => { - expr.return_token().map(|token| token.text_range()) - } - _ => None, - }; - - push_to_highlights(file_id, range); - }); - - let tail = match body { - ast::Expr::BlockExpr(b) => b.tail_expr(), - e => Some(e), - }; + let tail = match body { + ast::Expr::BlockExpr(b) => b.tail_expr(), + e => Some(e), + }; - if let Some(tail) = tail { - for_each_tail_expr(&tail, &mut |tail| { - let file_id = sema.hir_file_for(tail.syntax()); - let range = match tail { - ast::Expr::BreakExpr(b) => b - .break_token() - .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()), - _ => tail.syntax().text_range(), - }; - push_to_highlights(file_id, Some(range)); - }); - } - Some(highlights) + if let Some(tail) = tail { + for_each_tail_expr(&tail, &mut |tail| { + let file_id = sema.hir_file_for(tail.syntax()); + let range = match tail { + ast::Expr::BreakExpr(b) => b + .break_token() + .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()), + _ => tail.syntax().text_range(), + }; + push_to_highlights(file_id, Some(range)); + }); } + Some(highlights) +} +// If `file_id` is None, +pub(crate) fn highlight_exit_points( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, +) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> { let mut res = FxHashMap::default(); for def in goto_definition::find_fn_or_blocks(sema, &token) { let new_map = match_ast! { match def { - ast::Fn(fn_) => fn_.body().and_then(|body| hl(sema, fn_.fn_token(), body.into())), + ast::Fn(fn_) => fn_.body().and_then(|body| hl_exit_points(sema, fn_.fn_token(), body.into())), ast::ClosureExpr(closure) => { let pipe_tok = closure.param_list().and_then(|p| p.pipe_token()); - closure.body().and_then(|body| hl(sema, pipe_tok, body)) + closure.body().and_then(|body| hl_exit_points(sema, pipe_tok, body)) }, ast::BlockExpr(blk) => match blk.modifier() { - Some(ast::BlockModifier::Async(t)) => hl(sema, Some(t), blk.into()), + Some(ast::BlockModifier::Async(t)) => hl_exit_points(sema, Some(t), blk.into()), Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => { - hl(sema, Some(t), blk.into()) + hl_exit_points(sema, Some(t), blk.into()) }, _ => continue, }, @@ -517,10 +513,23 @@ pub(crate) fn highlight_yield_points( match anc { ast::Fn(fn_) => hl(sema, fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)), ast::BlockExpr(block_expr) => { - if block_expr.async_token().is_none() { + let Some(async_token) = block_expr.async_token() else { continue; + }; + + // Async blocks act similar to closures. So we want to + // highlight their exit points too, but only if we are on + // the async token. + if async_token == token { + let exit_points = hl_exit_points( + sema, + Some(async_token.clone()), + block_expr.clone().into(), + ); + merge_map(&mut res, exit_points); } - hl(sema, block_expr.async_token(), Some(block_expr.into())) + + hl(sema, Some(async_token), Some(block_expr.into())) }, ast::ClosureExpr(closure) => hl(sema, closure.async_token(), closure.body()), _ => continue, @@ -877,6 +886,27 @@ pub async$0 fn foo() { } #[test] + fn test_hl_exit_points_of_async_blocks() { + check( + r#" +pub fn foo() { + let x = async$0 { + // ^^^^^ + 0.await; + // ^^^^^ + 0?; + // ^ + return 0; + // ^^^^^^ + 0 + // ^ + }; +} +"#, + ); + } + + #[test] fn test_hl_let_else_yield_points() { check( r#" @@ -925,11 +955,9 @@ async fn foo() { async fn foo() { (async { // ^^^^^ - (async { - 0.await - }).await$0 } - // ^^^^^ - ).await; + (async { 0.await }).await$0 + // ^^^^^ + }).await; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index e60be577f79..81397b07855 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -8988,3 +8988,33 @@ mod m { "#]], ); } + +#[test] +fn regression_18238() { + check( + r#" +macro_rules! foo { + ($name:ident) => { + pub static $name = Foo::new(|| { + $crate; + }); + }; +} + +foo!(BAR_$0); +"#, + expect![[r#" + *BAR_* + + ```rust + test + ``` + + ```rust + pub static BAR_: {error} = Foo::new(||{ + crate; + }) + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index c46c4c8ce94..d7163d57d22 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -64,7 +64,7 @@ use fetch_crates::CrateInfo; use hir::{sym, ChangeWithProcMacros}; use ide_db::{ base_db::{ - salsa::{self, ParallelDatabase}, + ra_salsa::{self, ParallelDatabase}, CrateOrigin, CrateWorkspaceData, Env, FileLoader, FileSet, SourceDatabase, SourceRootDatabase, VfsPath, }, @@ -79,7 +79,7 @@ use crate::navigation_target::ToNav; pub use crate::{ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, - call_hierarchy::CallItem, + call_hierarchy::{CallHierarchyConfig, CallItem}, expand_macro::ExpandedMacro, file_structure::{StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, @@ -218,7 +218,7 @@ impl Default for AnalysisHost { /// `Analysis` are canceled (most method return `Err(Canceled)`). #[derive(Debug)] pub struct Analysis { - db: salsa::Snapshot<RootDatabase>, + db: ra_salsa::Snapshot<RootDatabase>, } // As a general design guideline, `Analysis` API are intended to be independent @@ -564,13 +564,21 @@ impl Analysis { } /// Computes incoming calls for the given file position. - pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> { - self.with_db(|db| call_hierarchy::incoming_calls(db, position)) + pub fn incoming_calls( + &self, + config: CallHierarchyConfig, + position: FilePosition, + ) -> Cancellable<Option<Vec<CallItem>>> { + self.with_db(|db| call_hierarchy::incoming_calls(db, config, position)) } /// Computes outgoing calls for the given file position. - pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> { - self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) + pub fn outgoing_calls( + &self, + config: CallHierarchyConfig, + position: FilePosition, + ) -> Cancellable<Option<Vec<CallItem>>> { + self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position)) } /// Returns a `mod name;` declaration which created the current module. diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 9bc7bf411f0..9259243db85 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -792,6 +792,7 @@ pub(crate) fn orig_range_with_focus_r( .definition_range(db) }; + // FIXME: Also make use of the syntax context to determine which site we are at? let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db); let ((call_site_range, call_site_focus), def_site) = match InFile::new(hir_file, name).original_node_file_range_opt(db) { diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index e46cb5a781f..f17c1fa5c62 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -421,19 +421,28 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt None } - let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?; - let type_name = target_type_name(&impl_def)?; - - let mut replacement_text = String::from(new_name); - replacement_text.push_str(": "); - match (self_param.amp_token(), self_param.mut_token()) { - (Some(_), None) => replacement_text.push('&'), - (Some(_), Some(_)) => replacement_text.push_str("&mut "), - (_, _) => (), - }; - replacement_text.push_str(type_name.as_str()); + match self_param.syntax().ancestors().find_map(ast::Impl::cast) { + Some(impl_def) => { + let type_name = target_type_name(&impl_def)?; + + let mut replacement_text = String::from(new_name); + replacement_text.push_str(": "); + match (self_param.amp_token(), self_param.mut_token()) { + (Some(_), None) => replacement_text.push('&'), + (Some(_), Some(_)) => replacement_text.push_str("&mut "), + (_, _) => (), + }; + replacement_text.push_str(type_name.as_str()); - Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) + Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) + } + None => { + cov_mark::hit!(rename_self_outside_of_methods); + let mut replacement_text = String::from(new_name); + replacement_text.push_str(": _"); + Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) + } + } } #[cfg(test)] @@ -1978,6 +1987,26 @@ impl Foo { } #[test] + fn test_self_outside_of_methods() { + cov_mark::check!(rename_self_outside_of_methods); + check( + "foo", + r#" +fn f($0self) -> i32 { + use self as _; + self.i +} +"#, + r#" +fn f(foo: _) -> i32 { + use self as _; + foo.i +} +"#, + ); + } + + #[test] fn test_self_in_path_to_parameter() { check( "foo", diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs index 41cc9c067d3..6def28e0b74 100644 --- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs +++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs @@ -59,7 +59,7 @@ mod tests { use expect_test::expect; use ide_assists::{Assist, AssistResolveStrategy}; use ide_db::{ - base_db::salsa::Durability, symbol_index::SymbolsDatabase, FileRange, FxHashSet, + base_db::ra_salsa::Durability, symbol_index::SymbolsDatabase, FileRange, FxHashSet, RootDatabase, }; use test_fixture::WithFixture; diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index 67d6932da96..9e823daa2be 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -6,7 +6,7 @@ use hir::{ }; use ide_db::{ base_db::{ - salsa::{ + ra_salsa::{ debug::{DebugQueryTable, TableEntry}, Query, QueryTable, }, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index 129b287e52f..7820e4e5a5f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -45,7 +45,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> -<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span> +<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root public">self</span> <span class="keyword">as</span> <span class="module crate_root declaration">this</span><span class="semicolon">;</span> +<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span> <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration">abc</span><span class="semicolon">;</span> <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="module crate_root declaration">definitely_unresolved</span><span class="semicolon">;</span> +<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="punctuation">_</span><span class="semicolon">;</span> +<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">test</span> <span class="keyword">as</span> <span class="module crate_root declaration">opt_in_crate</span><span class="semicolon">;</span> +<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">test</span> <span class="keyword">as</span> <span class="punctuation">_</span><span class="semicolon">;</span> +<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">proc_macro</span><span class="semicolon">;</span> </code></pre> \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 94cee4ef43b..a20147add36 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -874,14 +874,23 @@ pub fn block_comments2() {} fn test_extern_crate() { check_highlighting( r#" -//- /main.rs crate:main deps:std,alloc +//- /main.rs crate:main deps:std,alloc,test,proc_macro extern-prelude:std,alloc +extern crate self as this; extern crate std; extern crate alloc as abc; extern crate unresolved as definitely_unresolved; +extern crate unresolved as _; +extern crate test as opt_in_crate; +extern crate test as _; +extern crate proc_macro; //- /std/lib.rs crate:std pub struct S; //- /alloc/lib.rs crate:alloc -pub struct A +pub struct A; +//- /test/lib.rs crate:test +pub struct T; +//- /proc_macro/lib.rs crate:proc_macro +pub struct ProcMacro; "#, expect_file!["./test_data/highlight_extern_crate.html"], false, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index e0fa753fa70..ecfabca092c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -145,7 +145,7 @@ fn type_bound(p: &mut Parser<'_>) -> bool { T![for] => types::for_type(p, false), // test precise_capturing // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {} - T![use] => { + T![use] if p.nth_at(1, T![<]) => { p.bump_any(); generic_param_list(p) } diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 7ea23b4f752..5322463a713 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -188,10 +188,11 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::RawIdent => IDENT, - rustc_lexer::TokenKind::GuardedStrPrefix => { + rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => { err = "Invalid string literal (reserved syntax)"; ERROR - }, + } + rustc_lexer::TokenKind::GuardedStrPrefix => POUND, rustc_lexer::TokenKind::Literal { kind, .. } => { self.extend_literal(token_text.len(), kind); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast new file mode 100644 index 00000000000..751f007df94 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rast @@ -0,0 +1,26 @@ +SOURCE_FILE + IMPL + IMPL_KW "impl" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + COLON ":" + WHITESPACE "\n" + TYPE_BOUND_LIST + ERROR + USE_KW "use" + WHITESPACE " " + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "std" + SEMICOLON ";" + WHITESPACE "\n" +error 8: expected R_ANGLE +error 8: expected type +error 11: expected `{` +error 15: expected BANG +error 15: expected `{`, `[`, `(` diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs new file mode 100644 index 00000000000..571552bda84 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0055_impl_use.rs @@ -0,0 +1,2 @@ +impl<T: +use std; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 71b9b61e205..d1ee579c0d8 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -553,7 +553,7 @@ impl ProjectWorkspace { ProjectWorkspaceKind::Json(project) => project .crates() .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone())) - .map(AbsPathBuf::assert) + .map(|build_file| self.workspace_root().join(build_file)) .collect(), _ => vec![], } diff --git a/src/tools/rust-analyzer/crates/salsa/Cargo.toml b/src/tools/rust-analyzer/crates/ra-salsa/Cargo.toml index 0d3e1197b5c..b81e780337f 100644 --- a/src/tools/rust-analyzer/crates/salsa/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ra-salsa/Cargo.toml @@ -10,7 +10,7 @@ description = "A generic framework for on-demand, incrementalized computation (e rust-version.workspace = true [lib] -name = "salsa" +name = "ra_salsa" [dependencies] indexmap = "2.1.0" @@ -23,7 +23,7 @@ oorandom = "11" triomphe = "0.1.11" itertools.workspace = true -salsa-macros = { version = "0.0.0", path = "salsa-macros" } +ra-salsa-macros = { version = "0.0.0", path = "ra-salsa-macros", package = "salsa-macros" } [dev-dependencies] linked-hash-map = "0.5.6" diff --git a/src/tools/rust-analyzer/crates/salsa/FAQ.md b/src/tools/rust-analyzer/crates/ra-salsa/FAQ.md index 9c9f6f92da9..9c9f6f92da9 100644 --- a/src/tools/rust-analyzer/crates/salsa/FAQ.md +++ b/src/tools/rust-analyzer/crates/ra-salsa/FAQ.md diff --git a/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-APACHE index 1b5ec8b78e2..1b5ec8b78e2 100644 --- a/src/tools/rust-analyzer/crates/salsa/LICENSE-APACHE +++ b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-APACHE diff --git a/src/tools/rust-analyzer/crates/salsa/LICENSE-MIT b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-MIT index 31aa79387f2..31aa79387f2 100644 --- a/src/tools/rust-analyzer/crates/salsa/LICENSE-MIT +++ b/src/tools/rust-analyzer/crates/ra-salsa/LICENSE-MIT diff --git a/src/tools/rust-analyzer/crates/salsa/README.md b/src/tools/rust-analyzer/crates/ra-salsa/README.md index 4a8d9f8c731..4a8d9f8c731 100644 --- a/src/tools/rust-analyzer/crates/salsa/README.md +++ b/src/tools/rust-analyzer/crates/ra-salsa/README.md diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/Cargo.toml b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/Cargo.toml index 791d2f6e9f5..5613d75c752 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/Cargo.toml @@ -11,7 +11,7 @@ rust-version.workspace = true [lib] proc-macro = true -name = "salsa_macros" +name = "ra_salsa_macros" [dependencies] heck = "0.4" diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-APACHE b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-APACHE index 0bf2cad6488..0bf2cad6488 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-APACHE +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-APACHE diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-MIT b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-MIT index d99cce5f720..d99cce5f720 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/LICENSE-MIT +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/LICENSE-MIT diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/README.md b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/README.md index 94389aee61a..94389aee61a 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/README.md +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/README.md diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/database_storage.rs index f16d814b9f0..63ab84a621e 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/database_storage.rs @@ -1,4 +1,4 @@ -//! Implementation for `[salsa::database]` decorator. +//! Implementation for `[ra_salsa::database]` decorator. use heck::ToSnakeCase; use proc_macro::TokenStream; @@ -32,7 +32,7 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream { .iter() .map(|QueryGroup { group_path }| { quote! { - <#group_path as salsa::plumbing::QueryGroup>::GroupStorage + <#group_path as ra_salsa::plumbing::QueryGroup>::GroupStorage } }) .collect(); @@ -64,12 +64,12 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream { // ANCHOR:HasQueryGroup has_group_impls.extend(quote! { - impl salsa::plumbing::HasQueryGroup<#group_path> for #database_name { + impl ra_salsa::plumbing::HasQueryGroup<#group_path> for #database_name { fn group_storage(&self) -> &#group_storage { &self.#db_storage_field.query_store().#group_name_snake } - fn group_storage_mut(&mut self) -> (&#group_storage, &mut salsa::Runtime) { + fn group_storage_mut(&mut self) -> (&#group_storage, &mut ra_salsa::Runtime) { let (query_store_mut, runtime) = self.#db_storage_field.query_store_mut(); (&query_store_mut.#group_name_snake, runtime) } @@ -98,13 +98,13 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream { let mut database_data = vec![]; for QueryGroup { group_path } in query_groups { database_data.push(quote! { - <#group_path as salsa::plumbing::QueryGroup>::GroupData + <#group_path as ra_salsa::plumbing::QueryGroup>::GroupData }); } // ANCHOR:DatabaseStorageTypes output.extend(quote! { - impl salsa::plumbing::DatabaseStorageTypes for #database_name { + impl ra_salsa::plumbing::DatabaseStorageTypes for #database_name { type DatabaseStorage = __SalsaDatabaseStorage; } }); @@ -121,81 +121,81 @@ pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream { fmt_ops.extend(quote! { #group_index => { let storage: &#group_storage = - <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); + <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); storage.fmt_index(self, input, fmt) } }); maybe_changed_ops.extend(quote! { #group_index => { let storage: &#group_storage = - <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); + <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); storage.maybe_changed_after(self, input, revision) } }); cycle_recovery_strategy_ops.extend(quote! { #group_index => { let storage: &#group_storage = - <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); + <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); storage.cycle_recovery_strategy(self, input) } }); for_each_ops.extend(quote! { let storage: &#group_storage = - <Self as salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); + <Self as ra_salsa::plumbing::HasQueryGroup<#group_path>>::group_storage(self); storage.for_each_query(runtime, &mut op); }); } output.extend(quote! { - impl salsa::plumbing::DatabaseOps for #database_name { - fn ops_database(&self) -> &dyn salsa::Database { + impl ra_salsa::plumbing::DatabaseOps for #database_name { + fn ops_database(&self) -> &dyn ra_salsa::Database { self } - fn ops_salsa_runtime(&self) -> &salsa::Runtime { + fn ops_salsa_runtime(&self) -> &ra_salsa::Runtime { self.#db_storage_field.salsa_runtime() } - fn synthetic_write(&mut self, durability: salsa::Durability) { + fn synthetic_write(&mut self, durability: ra_salsa::Durability) { self.#db_storage_field.salsa_runtime_mut().synthetic_write(durability) } fn fmt_index( &self, - input: salsa::DatabaseKeyIndex, + input: ra_salsa::DatabaseKeyIndex, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { match input.group_index() { #fmt_ops - i => panic!("salsa: invalid group index {}", i) + i => panic!("ra_salsa: invalid group index {}", i) } } fn maybe_changed_after( &self, - input: salsa::DatabaseKeyIndex, - revision: salsa::Revision + input: ra_salsa::DatabaseKeyIndex, + revision: ra_salsa::Revision ) -> bool { match input.group_index() { #maybe_changed_ops - i => panic!("salsa: invalid group index {}", i) + i => panic!("ra_salsa: invalid group index {}", i) } } fn cycle_recovery_strategy( &self, - input: salsa::DatabaseKeyIndex, - ) -> salsa::plumbing::CycleRecoveryStrategy { + input: ra_salsa::DatabaseKeyIndex, + ) -> ra_salsa::plumbing::CycleRecoveryStrategy { match input.group_index() { #cycle_recovery_strategy_ops - i => panic!("salsa: invalid group index {}", i) + i => panic!("ra_salsa: invalid group index {}", i) } } fn for_each_query( &self, - mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps), + mut op: &mut dyn FnMut(&dyn ra_salsa::plumbing::QueryStorageMassOps), ) { - let runtime = salsa::Database::salsa_runtime(self); + let runtime = ra_salsa::Database::salsa_runtime(self); #for_each_ops } } diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/lib.rs index d3e17c5ebf1..d3e17c5ebf1 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/lib.rs diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/parenthesized.rs index 5ecd1b8a058..5ecd1b8a058 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/parenthesized.rs diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs index eeaf008a15c..88db6093ee0 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs @@ -1,4 +1,4 @@ -//! Implementation for `[salsa::query_group]` decorator. +//! Implementation for `[ra_salsa::query_group]` decorator. use crate::parenthesized::Parenthesized; use heck::ToUpperCamelCase; @@ -10,7 +10,7 @@ use syn::{ ReturnType, TraitItem, Type, }; -/// Implementation for `[salsa::query_group]` decorator. +/// Implementation for `[ra_salsa::query_group]` decorator. pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream { let group_struct = parse_macro_input!(args as Ident); let input: ItemTrait = parse_macro_input!(input as ItemTrait); @@ -82,7 +82,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream num_storages += 1; } _ => { - return Error::new(span, format!("unknown salsa attribute `{name}`")) + return Error::new(span, format!("unknown ra_salsa attribute `{name}`")) .to_compile_error() .into(); } @@ -100,7 +100,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream Some(invoke) if storage == QueryStorage::Input => { return Error::new( invoke.span(), - "#[salsa::invoke] cannot be set on #[salsa::input] queries", + "#[ra_salsa::invoke] cannot be set on #[ra_salsa::input] queries", ) .to_compile_error() .into(); @@ -155,7 +155,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream } }; - // For `#[salsa::interned]` keys, we create a "lookup key" automatically. + // For `#[ra_salsa::interned]` keys, we create a "lookup key" automatically. // // For a query like: // @@ -257,7 +257,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream // difference in total compilation time in rust-analyzer, though // it's not totally obvious why that should be. fn __shim(db: &(dyn #trait_name + '_), #(#key_names: #keys),*) -> #value { - salsa::plumbing::get_query_table::<#qt>(db).get((#(#key_names),*)) + ra_salsa::plumbing::get_query_table::<#qt>(db).get((#(#key_names),*)) } __shim(self, #(#key_names),*) @@ -302,20 +302,20 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream # [doc = #set_constant_fn_docs] - fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability); + fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: ra_salsa::Durability); }); query_fn_definitions.extend(quote! { fn #set_fn_name(&mut self, #(#key_names: #keys,)* value__: #value) { fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value) { - salsa::plumbing::get_query_table_mut::<#qt>(db).set((#(#key_names),*), value__) + ra_salsa::plumbing::get_query_table_mut::<#qt>(db).set((#(#key_names),*), value__) } __shim(self, #(#key_names,)* value__) } - fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) { - fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) { - salsa::plumbing::get_query_table_mut::<#qt>(db).set_with_durability((#(#key_names),*), value__, durability__) + fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: ra_salsa::Durability) { + fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value, durability__: ra_salsa::Durability) { + ra_salsa::plumbing::get_query_table_mut::<#qt>(db).set_with_durability((#(#key_names),*), value__, durability__) } __shim(self, #(#key_names,)* value__ ,durability__) } @@ -324,7 +324,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream // A field for the storage struct storage_fields.extend(quote! { - #fn_name: std::sync::Arc<<#qt as salsa::Query>::Storage>, + #fn_name: std::sync::Arc<<#qt as ra_salsa::Query>::Storage>, }); } @@ -334,8 +334,8 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream quote! { #(#trait_attrs)* #trait_vis trait #trait_name : - salsa::Database + - salsa::plumbing::HasQueryGroup<#group_struct> + + ra_salsa::Database + + ra_salsa::plumbing::HasQueryGroup<#group_struct> + #bounds { #query_fn_declarations @@ -348,7 +348,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream /// Representative struct for the query group. #trait_vis struct #group_struct { } - impl salsa::plumbing::QueryGroup for #group_struct + impl ra_salsa::plumbing::QueryGroup for #group_struct { type DynDb = #dyn_db; type GroupStorage = #group_storage; @@ -362,8 +362,8 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream impl<DB> #trait_name for DB where DB: #bounds, - DB: salsa::Database, - DB: salsa::plumbing::HasQueryGroup<#group_struct>, + DB: ra_salsa::Database, + DB: ra_salsa::plumbing::HasQueryGroup<#group_struct>, { #query_fn_definitions } @@ -379,18 +379,18 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream let qt = &query.query_type; let storage = match &query.storage { - QueryStorage::Memoized => quote!(salsa::plumbing::MemoizedStorage<Self>), - QueryStorage::LruMemoized => quote!(salsa::plumbing::LruMemoizedStorage<Self>), + QueryStorage::Memoized => quote!(ra_salsa::plumbing::MemoizedStorage<Self>), + QueryStorage::LruMemoized => quote!(ra_salsa::plumbing::LruMemoizedStorage<Self>), QueryStorage::LruDependencies => { - quote!(salsa::plumbing::LruDependencyStorage<Self>) + quote!(ra_salsa::plumbing::LruDependencyStorage<Self>) } QueryStorage::Input if query.keys.is_empty() => { - quote!(salsa::plumbing::UnitInputStorage<Self>) + quote!(ra_salsa::plumbing::UnitInputStorage<Self>) } - QueryStorage::Input => quote!(salsa::plumbing::InputStorage<Self>), - QueryStorage::Interned => quote!(salsa::plumbing::InternedStorage<Self>), + QueryStorage::Input => quote!(ra_salsa::plumbing::InputStorage<Self>), + QueryStorage::Interned => quote!(ra_salsa::plumbing::InternedStorage<Self>), QueryStorage::InternedLookup { intern_query_type } => { - quote!(salsa::plumbing::LookupInternedStorage<Self, #intern_query_type>) + quote!(ra_salsa::plumbing::LookupInternedStorage<Self, #intern_query_type>) } QueryStorage::Transparent => panic!("should have been filtered"), }; @@ -408,9 +408,9 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream impl #qt { /// Get access to extra methods pertaining to this query. /// You can also use it to invoke this query. - #trait_vis fn in_db(self, db: &#dyn_db) -> salsa::QueryTable<'_, Self> + #trait_vis fn in_db(self, db: &#dyn_db) -> ra_salsa::QueryTable<'_, Self> { - salsa::plumbing::get_query_table::<#qt>(db) + ra_salsa::plumbing::get_query_table::<#qt>(db) } } }); @@ -439,7 +439,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream /// also set a cancellation flag. This will cause any query /// invocations in other threads to unwind with a `Cancelled` /// sentinel value and eventually let the `set` succeed once all - /// threads have unwound past the salsa invocation. + /// threads have unwound past the ra_salsa invocation. /// /// If your query implementations are performing expensive /// operations without invoking another query, you can also use @@ -448,13 +448,13 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream /// thus allowing the `set` to succeed. Otherwise, long-running /// computations may lead to "starvation", meaning that the /// thread attempting to `set` has to wait a long, long time. =) - #trait_vis fn in_db_mut(self, db: &mut #dyn_db) -> salsa::QueryTableMut<'_, Self> + #trait_vis fn in_db_mut(self, db: &mut #dyn_db) -> ra_salsa::QueryTableMut<'_, Self> { - salsa::plumbing::get_query_table_mut::<#qt>(db) + ra_salsa::plumbing::get_query_table_mut::<#qt>(db) } } - impl<'d> salsa::QueryDb<'d> for #qt + impl<'d> ra_salsa::QueryDb<'d> for #qt { type DynDb = #dyn_db + 'd; type Group = #group_struct; @@ -462,7 +462,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream } // ANCHOR:Query_impl - impl salsa::Query for #qt + impl ra_salsa::Query for #qt { type Key = (#(#keys),*); type Value = #value; @@ -473,13 +473,13 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream const QUERY_NAME: &'static str = #query_name; fn query_storage<'a>( - group_storage: &'a <Self as salsa::QueryDb<'_>>::GroupStorage, + group_storage: &'a <Self as ra_salsa::QueryDb<'_>>::GroupStorage, ) -> &'a std::sync::Arc<Self::Storage> { &group_storage.#fn_name } fn query_storage_mut<'a>( - group_storage: &'a <Self as salsa::QueryDb<'_>>::GroupStorage, + group_storage: &'a <Self as ra_salsa::QueryDb<'_>>::GroupStorage, ) -> &'a std::sync::Arc<Self::Storage> { &group_storage.#fn_name } @@ -501,10 +501,10 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream let recover = if let Some(cycle_recovery_fn) = &query.cycle { quote! { - const CYCLE_STRATEGY: salsa::plumbing::CycleRecoveryStrategy = - salsa::plumbing::CycleRecoveryStrategy::Fallback; - fn cycle_fallback(db: &<Self as salsa::QueryDb<'_>>::DynDb, cycle: &salsa::Cycle, #key_pattern: &<Self as salsa::Query>::Key) - -> <Self as salsa::Query>::Value { + const CYCLE_STRATEGY: ra_salsa::plumbing::CycleRecoveryStrategy = + ra_salsa::plumbing::CycleRecoveryStrategy::Fallback; + fn cycle_fallback(db: &<Self as ra_salsa::QueryDb<'_>>::DynDb, cycle: &ra_salsa::Cycle, #key_pattern: &<Self as ra_salsa::Query>::Key) + -> <Self as ra_salsa::Query>::Value { #cycle_recovery_fn( db, cycle, @@ -514,17 +514,17 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream } } else { quote! { - const CYCLE_STRATEGY: salsa::plumbing::CycleRecoveryStrategy = - salsa::plumbing::CycleRecoveryStrategy::Panic; + const CYCLE_STRATEGY: ra_salsa::plumbing::CycleRecoveryStrategy = + ra_salsa::plumbing::CycleRecoveryStrategy::Panic; } }; output.extend(quote_spanned! {span=> // ANCHOR:QueryFunction_impl - impl salsa::plumbing::QueryFunction for #qt + impl ra_salsa::plumbing::QueryFunction for #qt { - fn execute(db: &<Self as salsa::QueryDb<'_>>::DynDb, #key_pattern: <Self as salsa::Query>::Key) - -> <Self as salsa::Query>::Value { + fn execute(db: &<Self as ra_salsa::QueryDb<'_>>::DynDb, #key_pattern: <Self as ra_salsa::Query>::Key) + -> <Self as ra_salsa::Query>::Value { #invoke(db, #(#key_names),*) } @@ -539,7 +539,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { fmt_ops.extend(quote! { #query_index => { - salsa::plumbing::QueryStorageOps::fmt_index( + ra_salsa::plumbing::QueryStorageOps::fmt_index( &*self.#fn_name, db, input.key_index(), fmt, ) } @@ -550,7 +550,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { maybe_changed_ops.extend(quote! { #query_index => { - salsa::plumbing::QueryStorageOps::maybe_changed_after( + ra_salsa::plumbing::QueryStorageOps::maybe_changed_after( &*self.#fn_name, db, input.key_index(), revision ) } @@ -561,7 +561,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { cycle_recovery_strategy_ops.extend(quote! { #query_index => { - salsa::plumbing::QueryStorageOps::cycle_recovery_strategy( + ra_salsa::plumbing::QueryStorageOps::cycle_recovery_strategy( &*self.#fn_name ) } @@ -587,7 +587,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream #group_storage { #( #queries_with_storage: - std::sync::Arc::new(salsa::plumbing::QueryStorageOps::new(group_index)), + std::sync::Arc::new(ra_salsa::plumbing::QueryStorageOps::new(group_index)), )* } } @@ -599,42 +599,42 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream #trait_vis fn fmt_index( &self, db: &(#dyn_db + '_), - input: salsa::DatabaseKeyIndex, + input: ra_salsa::DatabaseKeyIndex, fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { match input.query_index() { #fmt_ops - i => panic!("salsa: impossible query index {}", i), + i => panic!("ra_salsa: impossible query index {}", i), } } #trait_vis fn maybe_changed_after( &self, db: &(#dyn_db + '_), - input: salsa::DatabaseKeyIndex, - revision: salsa::Revision, + input: ra_salsa::DatabaseKeyIndex, + revision: ra_salsa::Revision, ) -> bool { match input.query_index() { #maybe_changed_ops - i => panic!("salsa: impossible query index {}", i), + i => panic!("ra_salsa: impossible query index {}", i), } } #trait_vis fn cycle_recovery_strategy( &self, db: &(#dyn_db + '_), - input: salsa::DatabaseKeyIndex, - ) -> salsa::plumbing::CycleRecoveryStrategy { + input: ra_salsa::DatabaseKeyIndex, + ) -> ra_salsa::plumbing::CycleRecoveryStrategy { match input.query_index() { #cycle_recovery_strategy_ops - i => panic!("salsa: impossible query index {}", i), + i => panic!("ra_salsa: impossible query index {}", i), } } #trait_vis fn for_each_query( &self, - _runtime: &salsa::Runtime, - mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps), + _runtime: &ra_salsa::Runtime, + mut op: &mut dyn FnMut(&dyn ra_salsa::plumbing::QueryStorageMassOps), ) { #for_each_ops } @@ -684,23 +684,23 @@ impl TryFrom<syn::Attribute> for SalsaAttr { } fn is_not_salsa_attr_path(path: &syn::Path) -> bool { - path.segments.first().map(|s| s.ident != "salsa").unwrap_or(true) || path.segments.len() != 2 + path.segments.first().map(|s| s.ident != "ra_salsa").unwrap_or(true) || path.segments.len() != 2 } fn filter_attrs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<SalsaAttr>) { let mut other = vec![]; - let mut salsa = vec![]; - // Leave non-salsa attributes untouched. These are - // attributes that don't start with `salsa::` or don't have + let mut ra_salsa = vec![]; + // Leave non-ra_salsa attributes untouched. These are + // attributes that don't start with `ra_salsa::` or don't have // exactly two segments in their path. - // Keep the salsa attributes around. + // Keep the ra_salsa attributes around. for attr in attrs { match SalsaAttr::try_from(attr) { - Ok(it) => salsa.push(it), + Ok(it) => ra_salsa.push(it), Err(it) => other.push(it), } } - (other, salsa) + (other, ra_salsa) } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/salsa/src/debug.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/debug.rs index 5f113541f04..5f113541f04 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/debug.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/debug.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived.rs index 8b2fdd6b19c..8b2fdd6b19c 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/derived.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs index de7a3976074..de7a3976074 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru.rs index bdb448e2412..bdb448e2412 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/derived_lru.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs index d0e4b5422b5..d0e4b5422b5 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/derived_lru/slot.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/durability.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/durability.rs index 7b8e6840fc9..7b8e6840fc9 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/durability.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/durability.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/hash.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/hash.rs index 3b2d7df3fbe..3b2d7df3fbe 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/hash.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/hash.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/input.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs index f04f48e3bab..f04f48e3bab 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/input.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/intern_id.rs index 8e74c100aca..35b495998e1 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/intern_id.rs @@ -12,7 +12,7 @@ use std::num::NonZeroU32; /// which are implemented for `u32` and `usize`: /// /// ``` -/// # use salsa::InternId; +/// # use ra_salsa::InternId; /// let intern_id1 = InternId::from(22_u32); /// let intern_id2 = InternId::from(22_usize); /// assert_eq!(intern_id1, intern_id2); @@ -25,7 +25,7 @@ use std::num::NonZeroU32; /// `usize` using the `as_u32` or `as_usize` methods or the `From` impls. /// /// ``` -/// # use salsa::InternId; +/// # use ra_salsa::InternId;; /// let intern_id = InternId::from(22_u32); /// let value = u32::from(intern_id); /// assert_eq!(value, 22); @@ -41,7 +41,7 @@ use std::num::NonZeroU32; /// word. /// /// ```should_panic -/// # use salsa::InternId; +/// # use ra_salsa::InternId;; /// InternId::from(InternId::MAX); /// ``` /// @@ -70,7 +70,7 @@ impl InternId { /// Convert this raw-id into a u32 value. /// /// ``` - /// # use salsa::InternId; + /// # use ra_salsa::InternId; /// let intern_id = InternId::from(22_u32); /// let value = intern_id.as_usize(); /// assert_eq!(value, 22); @@ -82,7 +82,7 @@ impl InternId { /// Convert this raw-id into a usize value. /// /// ``` - /// # use salsa::InternId; + /// # use ra_salsa::InternId; /// let intern_id = InternId::from(22_u32); /// let value = intern_id.as_usize(); /// assert_eq!(value, 22); diff --git a/src/tools/rust-analyzer/crates/salsa/src/interned.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/interned.rs index 359662ec6b2..359662ec6b2 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/interned.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/interned.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs index 48d6dc2e387..1b327773ec6 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs @@ -745,6 +745,6 @@ impl Cycle { // Re-export the procedural macros. #[allow(unused_imports)] #[macro_use] -extern crate salsa_macros; +extern crate ra_salsa_macros; use plumbing::HasQueryGroup; -pub use salsa_macros::*; +pub use ra_salsa_macros::*; diff --git a/src/tools/rust-analyzer/crates/salsa/src/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs index a6f96beeab1..a6f96beeab1 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/lru.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/plumbing.rs index e96b9daa979..e96b9daa979 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/plumbing.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/revision.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/revision.rs index 7f4c333fb19..7f4c333fb19 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/revision.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/revision.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs index 5fe5f4b46d3..5fe5f4b46d3 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/dependency_graph.rs index ed1d499f637..ed1d499f637 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/dependency_graph.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs index 73869671886..73869671886 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs diff --git a/src/tools/rust-analyzer/crates/salsa/src/storage.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/storage.rs index e0acf44041b..e0acf44041b 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/storage.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/src/storage.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/cycles.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/cycles.rs index e9bddfc630e..81136626551 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/cycles.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/cycles.rs @@ -1,7 +1,7 @@ use std::panic::UnwindSafe; use expect_test::expect; -use salsa::{Durability, ParallelDatabase, Snapshot}; +use ra_salsa::{Durability, ParallelDatabase, Snapshot}; // Axes: // @@ -49,13 +49,13 @@ struct Error { cycle: Vec<String>, } -#[salsa::database(GroupStruct)] +#[ra_salsa::database(GroupStruct)] #[derive(Default)] struct DatabaseImpl { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for DatabaseImpl {} +impl ra_salsa::Database for DatabaseImpl {} impl ParallelDatabase for DatabaseImpl { fn snapshot(&self) -> Snapshot<Self> { @@ -75,37 +75,37 @@ enum CycleQuery { AthenC, } -#[salsa::query_group(GroupStruct)] -trait Database: salsa::Database { +#[ra_salsa::query_group(GroupStruct)] +trait Database: ra_salsa::Database { // `a` and `b` depend on each other and form a cycle fn memoized_a(&self) -> (); fn memoized_b(&self) -> (); fn volatile_a(&self) -> (); fn volatile_b(&self) -> (); - #[salsa::input] + #[ra_salsa::input] fn a_invokes(&self) -> CycleQuery; - #[salsa::input] + #[ra_salsa::input] fn b_invokes(&self) -> CycleQuery; - #[salsa::input] + #[ra_salsa::input] fn c_invokes(&self) -> CycleQuery; - #[salsa::cycle(recover_a)] + #[ra_salsa::cycle(recover_a)] fn cycle_a(&self) -> Result<(), Error>; - #[salsa::cycle(recover_b)] + #[ra_salsa::cycle(recover_b)] fn cycle_b(&self) -> Result<(), Error>; fn cycle_c(&self) -> Result<(), Error>; } -fn recover_a(db: &dyn Database, cycle: &salsa::Cycle) -> Result<(), Error> { +fn recover_a(db: &dyn Database, cycle: &ra_salsa::Cycle) -> Result<(), Error> { Err(Error { cycle: cycle.all_participants(db) }) } -fn recover_b(db: &dyn Database, cycle: &salsa::Cycle) -> Result<(), Error> { +fn recover_b(db: &dyn Database, cycle: &ra_salsa::Cycle) -> Result<(), Error> { Err(Error { cycle: cycle.all_participants(db) }) } @@ -155,10 +155,10 @@ fn cycle_c(db: &dyn Database) -> Result<(), Error> { } #[track_caller] -fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> salsa::Cycle { +fn extract_cycle(f: impl FnOnce() + UnwindSafe) -> ra_salsa::Cycle { let v = std::panic::catch_unwind(f); if let Err(d) = &v { - if let Some(cycle) = d.downcast_ref::<salsa::Cycle>() { + if let Some(cycle) = d.downcast_ref::<ra_salsa::Cycle>() { return cycle.clone(); } } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/dyn_trait.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/dyn_trait.rs index 09ebc5c4ce4..6075ae5c11e 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/dyn_trait.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/dyn_trait.rs @@ -1,16 +1,16 @@ //! Test that you can implement a query using a `dyn Trait` setup. -#[salsa::database(DynTraitStorage)] +#[ra_salsa::database(DynTraitStorage)] #[derive(Default)] struct DynTraitDatabase { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for DynTraitDatabase {} +impl ra_salsa::Database for DynTraitDatabase {} -#[salsa::query_group(DynTraitStorage)] +#[ra_salsa::query_group(DynTraitStorage)] trait DynTrait { - #[salsa::input] + #[ra_salsa::input] fn input(&self, x: u32) -> u32; fn output(&self, x: u32) -> u32; diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/constants.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/constants.rs index 32bfbc4564b..6e51545b60a 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/constants.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/constants.rs @@ -1,10 +1,10 @@ use crate::implementation::{TestContext, TestContextImpl}; -use salsa::debug::DebugQueryTable; -use salsa::Durability; +use ra_salsa::debug::DebugQueryTable; +use ra_salsa::Durability; -#[salsa::query_group(Constants)] +#[ra_salsa::query_group(Constants)] pub(crate) trait ConstantsDatabase: TestContext { - #[salsa::input] + #[ra_salsa::input] fn input(&self, key: char) -> usize; fn add(&self, key1: char, key2: char) -> usize; diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/counter.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/counter.rs index c04857e24c9..c04857e24c9 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/counter.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/counter.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/implementation.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/implementation.rs index 84349134415..e9a59c46304 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/implementation.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/implementation.rs @@ -5,12 +5,12 @@ use crate::memoized_dep_inputs; use crate::memoized_inputs; use crate::memoized_volatile; -pub(crate) trait TestContext: salsa::Database { +pub(crate) trait TestContext: ra_salsa::Database { fn clock(&self) -> &Counter; fn log(&self) -> &Log; } -#[salsa::database( +#[ra_salsa::database( constants::Constants, memoized_dep_inputs::MemoizedDepInputs, memoized_inputs::MemoizedInputs, @@ -18,7 +18,7 @@ pub(crate) trait TestContext: salsa::Database { )] #[derive(Default)] pub(crate) struct TestContextImpl { - storage: salsa::Storage<TestContextImpl>, + storage: ra_salsa::Storage<TestContextImpl>, clock: Counter, log: Log, } @@ -56,4 +56,4 @@ impl TestContext for TestContextImpl { } } -impl salsa::Database for TestContextImpl {} +impl ra_salsa::Database for TestContextImpl {} diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/log.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/log.rs index 1ee57fe667d..1ee57fe667d 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/log.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/log.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/main.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/main.rs index bcd13c75f71..bcd13c75f71 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/main.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/main.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_dep_inputs.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_dep_inputs.rs index 4ea33e0c1a0..0043bb45745 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_dep_inputs.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_dep_inputs.rs @@ -1,14 +1,14 @@ use crate::implementation::{TestContext, TestContextImpl}; -#[salsa::query_group(MemoizedDepInputs)] +#[ra_salsa::query_group(MemoizedDepInputs)] pub(crate) trait MemoizedDepInputsContext: TestContext { fn dep_memoized2(&self) -> usize; fn dep_memoized1(&self) -> usize; - #[salsa::dependencies] + #[ra_salsa::dependencies] fn dep_derived1(&self) -> usize; - #[salsa::input] + #[ra_salsa::input] fn dep_input1(&self) -> usize; - #[salsa::input] + #[ra_salsa::input] fn dep_input2(&self) -> usize; } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_inputs.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_inputs.rs index 53d2ace8871..007dc3db95a 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_inputs.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_inputs.rs @@ -1,11 +1,11 @@ use crate::implementation::{TestContext, TestContextImpl}; -#[salsa::query_group(MemoizedInputs)] +#[ra_salsa::query_group(MemoizedInputs)] pub(crate) trait MemoizedInputsContext: TestContext { fn max(&self) -> usize; - #[salsa::input] + #[ra_salsa::input] fn input1(&self) -> usize; - #[salsa::input] + #[ra_salsa::input] fn input2(&self) -> usize; } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_volatile.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_volatile.rs index 3dcc32eece3..cd00cc2e6cc 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/incremental/memoized_volatile.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/incremental/memoized_volatile.rs @@ -1,7 +1,7 @@ use crate::implementation::{TestContext, TestContextImpl}; -use salsa::{Database, Durability}; +use ra_salsa::{Database, Durability}; -#[salsa::query_group(MemoizedVolatile)] +#[ra_salsa::query_group(MemoizedVolatile)] pub(crate) trait MemoizedVolatileContext: TestContext { // Queries for testing a "volatile" value wrapped by // memoization. diff --git a/src/tools/rust-analyzer/crates/salsa/tests/interned.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/interned.rs index d097e41cfd6..108b129fa3f 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/interned.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/interned.rs @@ -1,37 +1,37 @@ //! Test that you can implement a query using a `dyn Trait` setup. -use salsa::InternId; +use ra_salsa::InternId; -#[salsa::database(InternStorage)] +#[ra_salsa::database(InternStorage)] #[derive(Default)] struct Database { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for Database {} +impl ra_salsa::Database for Database {} -impl salsa::ParallelDatabase for Database { - fn snapshot(&self) -> salsa::Snapshot<Self> { - salsa::Snapshot::new(Database { storage: self.storage.snapshot() }) +impl ra_salsa::ParallelDatabase for Database { + fn snapshot(&self) -> ra_salsa::Snapshot<Self> { + ra_salsa::Snapshot::new(Database { storage: self.storage.snapshot() }) } } -#[salsa::query_group(InternStorage)] +#[ra_salsa::query_group(InternStorage)] trait Intern { - #[salsa::interned] + #[ra_salsa::interned] fn intern1(&self, x: String) -> InternId; - #[salsa::interned] + #[ra_salsa::interned] fn intern2(&self, x: String, y: String) -> InternId; - #[salsa::interned] + #[ra_salsa::interned] fn intern_key(&self, x: String) -> InternKey; } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct InternKey(InternId); -impl salsa::InternKey for InternKey { +impl ra_salsa::InternKey for InternKey { fn from_intern_id(v: InternId) -> Self { InternKey(v) } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/lru.rs index ef98a2c32b4..f351f242468 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/lru.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/lru.rs @@ -22,11 +22,11 @@ impl Drop for HotPotato { } } -#[salsa::query_group(QueryGroupStorage)] -trait QueryGroup: salsa::Database { - #[salsa::lru] +#[ra_salsa::query_group(QueryGroupStorage)] +trait QueryGroup: ra_salsa::Database { + #[ra_salsa::lru] fn get(&self, x: u32) -> Arc<HotPotato>; - #[salsa::lru] + #[ra_salsa::lru] fn get_volatile(&self, x: u32) -> usize; } @@ -40,13 +40,13 @@ fn get_volatile(db: &dyn QueryGroup, _x: u32) -> usize { COUNTER.fetch_add(1, Ordering::SeqCst) } -#[salsa::database(QueryGroupStorage)] +#[ra_salsa::database(QueryGroupStorage)] #[derive(Default)] struct Database { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for Database {} +impl ra_salsa::Database for Database {} #[test] fn lru_works() { diff --git a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/macros.rs index 9b07740e7de..7bb6369b500 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/macros.rs @@ -1,6 +1,6 @@ -#[salsa::query_group(MyStruct)] -trait MyDatabase: salsa::Database { - #[salsa::invoke(another_module::another_name)] +#[ra_salsa::query_group(MyStruct)] +trait MyDatabase: ra_salsa::Database { + #[ra_salsa::invoke(another_module::another_name)] fn my_query(&self, key: ()) -> (); } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/no_send_sync.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/no_send_sync.rs index 2a25c437c3e..56bd3f4a7ed 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/no_send_sync.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/no_send_sync.rs @@ -1,7 +1,7 @@ use std::rc::Rc; -#[salsa::query_group(NoSendSyncStorage)] -trait NoSendSyncDatabase: salsa::Database { +#[ra_salsa::query_group(NoSendSyncStorage)] +trait NoSendSyncDatabase: ra_salsa::Database { fn no_send_sync_value(&self, key: bool) -> Rc<bool>; fn no_send_sync_key(&self, key: Rc<bool>) -> bool; } @@ -14,13 +14,13 @@ fn no_send_sync_key(_db: &dyn NoSendSyncDatabase, key: Rc<bool>) -> bool { *key } -#[salsa::database(NoSendSyncStorage)] +#[ra_salsa::database(NoSendSyncStorage)] #[derive(Default)] struct DatabaseImpl { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for DatabaseImpl {} +impl ra_salsa::Database for DatabaseImpl {} #[test] fn no_send_sync() { diff --git a/src/tools/rust-analyzer/crates/salsa/tests/on_demand_inputs.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/on_demand_inputs.rs index cad594f536f..4d7832f9ba0 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/on_demand_inputs.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/on_demand_inputs.rs @@ -8,10 +8,10 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use salsa::{Database as _, Durability, EventKind}; +use ra_salsa::{Database as _, Durability, EventKind}; -#[salsa::query_group(QueryGroupStorage)] -trait QueryGroup: salsa::Database + AsRef<HashMap<u32, u32>> { +#[ra_salsa::query_group(QueryGroupStorage)] +trait QueryGroup: ra_salsa::Database + AsRef<HashMap<u32, u32>> { fn a(&self, x: u32) -> u32; fn b(&self, x: u32) -> u32; fn c(&self, x: u32) -> u32; @@ -32,16 +32,16 @@ fn c(db: &dyn QueryGroup, x: u32) -> u32 { db.b(x) } -#[salsa::database(QueryGroupStorage)] +#[ra_salsa::database(QueryGroupStorage)] #[derive(Default)] struct Database { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, external_state: HashMap<u32, u32>, - on_event: Option<Box<dyn Fn(&Database, salsa::Event)>>, + on_event: Option<Box<dyn Fn(&Database, ra_salsa::Event)>>, } -impl salsa::Database for Database { - fn salsa_event(&self, event: salsa::Event) { +impl ra_salsa::Database for Database { + fn salsa_event(&self, event: ra_salsa::Event) { if let Some(cb) = &self.on_event { cb(self, event) } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/panic_safely.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/panic_safely.rs index c11ae9c2144..047a50eb4b2 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/panic_safely.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/panic_safely.rs @@ -1,10 +1,10 @@ -use salsa::{Database, ParallelDatabase, Snapshot}; +use ra_salsa::{Database, ParallelDatabase, Snapshot}; use std::panic::{self, AssertUnwindSafe}; use std::sync::atomic::{AtomicU32, Ordering::SeqCst}; -#[salsa::query_group(PanicSafelyStruct)] -trait PanicSafelyDatabase: salsa::Database { - #[salsa::input] +#[ra_salsa::query_group(PanicSafelyStruct)] +trait PanicSafelyDatabase: ra_salsa::Database { + #[ra_salsa::input] fn one(&self) -> usize; fn panic_safely(&self) -> (); @@ -23,15 +23,15 @@ fn outer(db: &dyn PanicSafelyDatabase) { db.panic_safely(); } -#[salsa::database(PanicSafelyStruct)] +#[ra_salsa::database(PanicSafelyStruct)] #[derive(Default)] struct DatabaseStruct { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for DatabaseStruct {} +impl ra_salsa::Database for DatabaseStruct {} -impl salsa::ParallelDatabase for DatabaseStruct { +impl ra_salsa::ParallelDatabase for DatabaseStruct { fn snapshot(&self) -> Snapshot<Self> { Snapshot::new(DatabaseStruct { storage: self.storage.snapshot() }) } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/cancellation.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/cancellation.rs index 9a92e5cc1ff..e47a8ef9aa8 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/cancellation.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/cancellation.rs @@ -1,5 +1,5 @@ use crate::setup::{CancellationFlag, Knobs, ParDatabase, ParDatabaseImpl, WithValue}; -use salsa::{Cancelled, ParallelDatabase}; +use ra_salsa::{Cancelled, ParallelDatabase}; macro_rules! assert_cancelled { ($thread:expr) => { @@ -96,7 +96,7 @@ fn in_par_get_set_cancellation_transitive() { assert_eq!(thread2.join().unwrap(), 111); } -/// https://github.com/salsa-rs/salsa/issues/66 +/// https://github.com/ra_salsa-rs/ra_salsa/issues/66 #[test] fn no_back_dating_in_cancellation() { let mut db = ParDatabaseImpl::default(); diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/frozen.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/frozen.rs index 5359a8820e2..9e42e261517 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/frozen.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/frozen.rs @@ -1,6 +1,6 @@ use crate::setup::{ParDatabase, ParDatabaseImpl}; use crate::signal::Signal; -use salsa::{Database, ParallelDatabase}; +use ra_salsa::{Database, ParallelDatabase}; use std::{ panic::{catch_unwind, AssertUnwindSafe}, sync::Arc, diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/independent.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/independent.rs index bd6ba3bf931..cbbac0608d1 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/independent.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/independent.rs @@ -1,5 +1,5 @@ use crate::setup::{ParDatabase, ParDatabaseImpl}; -use salsa::ParallelDatabase; +use ra_salsa::ParallelDatabase; /// Test two `sum` queries (on distinct keys) executing in different /// threads. Really just a test that `snapshot` etc compiles. diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/main.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/main.rs index 31c0da18375..31c0da18375 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/main.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/main.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_all_recover.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_all_recover.rs index a13ae3418f2..dabdb3babc0 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_all_recover.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_all_recover.rs @@ -3,7 +3,7 @@ //! both intra and cross thread. use crate::setup::{Knobs, ParDatabaseImpl}; -use salsa::ParallelDatabase; +use ra_salsa::ParallelDatabase; // Recover cycle test: // @@ -46,37 +46,37 @@ fn parallel_cycle_all_recover() { assert_eq!(thread_b.join().unwrap(), 21); } -#[salsa::query_group(ParallelCycleAllRecover)] +#[ra_salsa::query_group(ParallelCycleAllRecover)] pub(crate) trait TestDatabase: Knobs { - #[salsa::cycle(recover_a1)] + #[ra_salsa::cycle(recover_a1)] fn a1(&self, key: i32) -> i32; - #[salsa::cycle(recover_a2)] + #[ra_salsa::cycle(recover_a2)] fn a2(&self, key: i32) -> i32; - #[salsa::cycle(recover_b1)] + #[ra_salsa::cycle(recover_b1)] fn b1(&self, key: i32) -> i32; - #[salsa::cycle(recover_b2)] + #[ra_salsa::cycle(recover_b2)] fn b2(&self, key: i32) -> i32; } -fn recover_a1(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover_a1(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover_a1"); key * 10 + 1 } -fn recover_a2(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover_a2(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover_a2"); key * 10 + 2 } -fn recover_b1(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover_b1(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover_b1"); key * 20 + 1 } -fn recover_b2(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover_b2(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover_b2"); key * 20 + 2 } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_mid_recover.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_mid_recover.rs index 971fe7ab120..20c508e0b8b 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_mid_recover.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_mid_recover.rs @@ -3,7 +3,7 @@ //! both intra and cross thread. use crate::setup::{Knobs, ParDatabaseImpl}; -use salsa::ParallelDatabase; +use ra_salsa::ParallelDatabase; // Recover cycle test: // @@ -47,27 +47,27 @@ fn parallel_cycle_mid_recovers() { assert_eq!(thread_b.join().unwrap(), 22); } -#[salsa::query_group(ParallelCycleMidRecovers)] +#[ra_salsa::query_group(ParallelCycleMidRecovers)] pub(crate) trait TestDatabase: Knobs { fn a1(&self, key: i32) -> i32; fn a2(&self, key: i32) -> i32; - #[salsa::cycle(recover_b1)] + #[ra_salsa::cycle(recover_b1)] fn b1(&self, key: i32) -> i32; fn b2(&self, key: i32) -> i32; - #[salsa::cycle(recover_b3)] + #[ra_salsa::cycle(recover_b3)] fn b3(&self, key: i32) -> i32; } -fn recover_b1(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover_b1(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover_b1"); key * 20 + 2 } -fn recover_b3(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover_b3(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover_b1"); key * 200 + 2 } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_none_recover.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs index 3c73852eafb..88d5fee0a22 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_none_recover.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_none_recover.rs @@ -4,7 +4,7 @@ use crate::setup::{Knobs, ParDatabaseImpl}; use expect_test::expect; -use salsa::ParallelDatabase; +use ra_salsa::ParallelDatabase; #[test] fn parallel_cycle_none_recover() { @@ -24,7 +24,7 @@ fn parallel_cycle_none_recover() { // We expect B to panic because it detects a cycle (it is the one that calls A, ultimately). // Right now, it panics with a string. let err_b = thread_b.join().unwrap_err(); - if let Some(c) = err_b.downcast_ref::<salsa::Cycle>() { + if let Some(c) = err_b.downcast_ref::<ra_salsa::Cycle>() { expect![[r#" [ "parallel::parallel_cycle_none_recover::AQuery::a(-1)", @@ -38,10 +38,10 @@ fn parallel_cycle_none_recover() { // We expect A to propagate a panic, which causes us to use the sentinel // type `Canceled`. - assert!(thread_a.join().unwrap_err().downcast_ref::<salsa::Cycle>().is_some()); + assert!(thread_a.join().unwrap_err().downcast_ref::<ra_salsa::Cycle>().is_some()); } -#[salsa::query_group(ParallelCycleNoneRecover)] +#[ra_salsa::query_group(ParallelCycleNoneRecover)] pub(crate) trait TestDatabase: Knobs { fn a(&self, key: i32) -> i32; fn b(&self, key: i32) -> i32; diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_one_recovers.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_one_recovers.rs index 025fbf37477..074ed1bd349 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/parallel_cycle_one_recovers.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/parallel_cycle_one_recovers.rs @@ -3,7 +3,7 @@ //! both intra and cross thread. use crate::setup::{Knobs, ParDatabaseImpl}; -use salsa::ParallelDatabase; +use ra_salsa::ParallelDatabase; // Recover cycle test: // @@ -49,11 +49,11 @@ fn parallel_cycle_one_recovers() { assert_eq!(thread_b.join().unwrap(), 22); } -#[salsa::query_group(ParallelCycleOneRecovers)] +#[ra_salsa::query_group(ParallelCycleOneRecovers)] pub(crate) trait TestDatabase: Knobs { fn a1(&self, key: i32) -> i32; - #[salsa::cycle(recover)] + #[ra_salsa::cycle(recover)] fn a2(&self, key: i32) -> i32; fn b1(&self, key: i32) -> i32; @@ -61,7 +61,7 @@ pub(crate) trait TestDatabase: Knobs { fn b2(&self, key: i32) -> i32; } -fn recover(_db: &dyn TestDatabase, _cycle: &salsa::Cycle, key: &i32) -> i32 { +fn recover(_db: &dyn TestDatabase, _cycle: &ra_salsa::Cycle, key: &i32) -> i32 { tracing::debug!("recover"); key * 20 + 2 } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/race.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/race.rs index c53d4b464ea..7aa6d4530b4 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/race.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/race.rs @@ -1,7 +1,7 @@ use std::panic::AssertUnwindSafe; use crate::setup::{ParDatabase, ParDatabaseImpl}; -use salsa::{Cancelled, ParallelDatabase}; +use ra_salsa::{Cancelled, ParallelDatabase}; /// Test where a read and a set are racing with one another. /// Should be atomic. diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/setup.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/setup.rs index 0a35902b435..fd1f51326e3 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/setup.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/setup.rs @@ -1,16 +1,16 @@ use crate::signal::Signal; -use salsa::Database; -use salsa::ParallelDatabase; -use salsa::Snapshot; +use ra_salsa::Database; +use ra_salsa::ParallelDatabase; +use ra_salsa::Snapshot; use std::sync::Arc; use std::{ cell::Cell, panic::{catch_unwind, resume_unwind, AssertUnwindSafe}, }; -#[salsa::query_group(Par)] +#[ra_salsa::query_group(Par)] pub(crate) trait ParDatabase: Knobs { - #[salsa::input] + #[ra_salsa::input] fn input(&self, key: char) -> usize; fn sum(&self, key: &'static str) -> usize; @@ -152,7 +152,7 @@ fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize { db.sum2_drop_sum(key) } -#[salsa::database( +#[ra_salsa::database( Par, crate::parallel_cycle_all_recover::ParallelCycleAllRecover, crate::parallel_cycle_none_recover::ParallelCycleNoneRecover, @@ -161,13 +161,13 @@ fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize { )] #[derive(Default)] pub(crate) struct ParDatabaseImpl { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, knobs: KnobsStruct, } impl Database for ParDatabaseImpl { - fn salsa_event(&self, event: salsa::Event) { - if let salsa::EventKind::WillBlockOn { .. } = event.kind { + fn salsa_event(&self, event: ra_salsa::Event) { + if let ra_salsa::EventKind::WillBlockOn { .. } = event.kind { self.signal(self.knobs().signal_on_will_block.get()); } } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/signal.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/signal.rs index 0af7b66e482..0af7b66e482 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/signal.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/signal.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/stress.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/stress.rs index 2fa317b2b90..f3a435b47f1 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/stress.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/stress.rs @@ -1,17 +1,17 @@ use rand::seq::SliceRandom; use rand::Rng; -use salsa::ParallelDatabase; -use salsa::Snapshot; -use salsa::{Cancelled, Database}; +use ra_salsa::ParallelDatabase; +use ra_salsa::Snapshot; +use ra_salsa::{Cancelled, Database}; // Number of operations a reader performs const N_MUTATOR_OPS: usize = 100; const N_READER_OPS: usize = 100; -#[salsa::query_group(Stress)] -trait StressDatabase: salsa::Database { - #[salsa::input] +#[ra_salsa::query_group(Stress)] +trait StressDatabase: ra_salsa::Database { + #[ra_salsa::input] fn a(&self, key: usize) -> usize; fn b(&self, key: usize) -> usize; @@ -28,15 +28,15 @@ fn c(db: &dyn StressDatabase, key: usize) -> usize { db.b(key) } -#[salsa::database(Stress)] +#[ra_salsa::database(Stress)] #[derive(Default)] struct StressDatabaseImpl { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for StressDatabaseImpl {} +impl ra_salsa::Database for StressDatabaseImpl {} -impl salsa::ParallelDatabase for StressDatabaseImpl { +impl ra_salsa::ParallelDatabase for StressDatabaseImpl { fn snapshot(&self) -> Snapshot<StressDatabaseImpl> { Snapshot::new(StressDatabaseImpl { storage: self.storage.snapshot() }) } diff --git a/src/tools/rust-analyzer/crates/salsa/tests/parallel/true_parallel.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/true_parallel.rs index d0e58efd1ac..44db17bd852 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/parallel/true_parallel.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/parallel/true_parallel.rs @@ -1,5 +1,5 @@ use crate::setup::{Knobs, ParDatabase, ParDatabaseImpl, WithValue}; -use salsa::ParallelDatabase; +use ra_salsa::ParallelDatabase; use std::panic::{self, AssertUnwindSafe}; /// Test where two threads are executing sum. We show that they can diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/implementation.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/implementation.rs index 2843660f154..39b2befd15b 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/implementation.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/implementation.rs @@ -1,10 +1,10 @@ use crate::queries; use std::cell::Cell; -#[salsa::database(queries::GroupStruct)] +#[ra_salsa::database(queries::GroupStruct)] #[derive(Default)] pub(crate) struct DatabaseImpl { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, counter: Cell<usize>, } @@ -16,4 +16,4 @@ impl queries::Counter for DatabaseImpl { } } -impl salsa::Database for DatabaseImpl {} +impl ra_salsa::Database for DatabaseImpl {} diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/main.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/main.rs index e92c61740e0..e92c61740e0 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/main.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/main.rs diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/queries.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/queries.rs index 0847fadefb0..bc9b10ae7bb 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/queries.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/queries.rs @@ -1,8 +1,8 @@ -pub(crate) trait Counter: salsa::Database { +pub(crate) trait Counter: ra_salsa::Database { fn increment(&self) -> usize; } -#[salsa::query_group(GroupStruct)] +#[ra_salsa::query_group(GroupStruct)] pub(crate) trait Database: Counter { fn memoized(&self) -> usize; fn volatile(&self) -> usize; diff --git a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/tests.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/tests.rs index 8e2f9b03cb9..7c33bbfc747 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/storage_varieties/tests.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/storage_varieties/tests.rs @@ -2,8 +2,8 @@ use crate::implementation::DatabaseImpl; use crate::queries::Database; -use salsa::Database as _Database; -use salsa::Durability; +use ra_salsa::Database as _Database; +use ra_salsa::Durability; #[test] fn memoized_twice() { diff --git a/src/tools/rust-analyzer/crates/salsa/tests/transparent.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/transparent.rs index 2e6dd4267b2..886f4641065 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/transparent.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/transparent.rs @@ -1,10 +1,10 @@ //! Test that transparent (uncached) queries work -#[salsa::query_group(QueryGroupStorage)] +#[ra_salsa::query_group(QueryGroupStorage)] trait QueryGroup { - #[salsa::input] + #[ra_salsa::input] fn input(&self, x: u32) -> u32; - #[salsa::transparent] + #[ra_salsa::transparent] fn wrap(&self, x: u32) -> u32; fn get(&self, x: u32) -> u32; } @@ -17,13 +17,13 @@ fn get(db: &dyn QueryGroup, x: u32) -> u32 { db.wrap(x) } -#[salsa::database(QueryGroupStorage)] +#[ra_salsa::database(QueryGroupStorage)] #[derive(Default)] struct Database { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for Database {} +impl ra_salsa::Database for Database {} #[test] fn transparent_queries_work() { diff --git a/src/tools/rust-analyzer/crates/salsa/tests/variadic.rs b/src/tools/rust-analyzer/crates/ra-salsa/tests/variadic.rs index cb857844eb7..11a6d13ebe2 100644 --- a/src/tools/rust-analyzer/crates/salsa/tests/variadic.rs +++ b/src/tools/rust-analyzer/crates/ra-salsa/tests/variadic.rs @@ -1,6 +1,6 @@ -#[salsa::query_group(HelloWorld)] -trait HelloWorldDatabase: salsa::Database { - #[salsa::input] +#[ra_salsa::query_group(HelloWorld)] +trait HelloWorldDatabase: ra_salsa::Database { + #[ra_salsa::input] fn input(&self, a: u32, b: u32) -> u32; fn none(&self) -> u32; @@ -28,13 +28,13 @@ fn trailing(_db: &dyn HelloWorldDatabase, a: u32, b: u32) -> u32 { a - b } -#[salsa::database(HelloWorld)] +#[ra_salsa::database(HelloWorld)] #[derive(Default)] struct DatabaseStruct { - storage: salsa::Storage<Self>, + storage: ra_salsa::Storage<Self>, } -impl salsa::Database for DatabaseStruct {} +impl ra_salsa::Database for DatabaseStruct {} #[test] fn execute() { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index eb95f42d755..2dd2f2242a0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -25,7 +25,7 @@ crossbeam-channel.workspace = true dirs = "5.0.1" dissimilar.workspace = true itertools.workspace = true -scip = "0.3.3" +scip = "0.5.1" lsp-types = { version = "=0.95.0", features = ["proposed"] } parking_lot = "0.12.1" xflags = "0.3.0" diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 4844c514ae9..c2164614274 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -22,7 +22,7 @@ use ide::{ }; use ide_db::{ base_db::{ - salsa::{self, debug::DebugQueryTable, ParallelDatabase}, + ra_salsa::{self, debug::DebugQueryTable, ParallelDatabase}, SourceDatabase, SourceRootDatabase, }, EditionedFileId, LineIndexDatabase, SnippetCap, @@ -46,8 +46,8 @@ use crate::cli::{ /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap<DB>(DB); -impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> { - fn clone(&self) -> Snap<salsa::Snapshot<DB>> { +impl<DB: ParallelDatabase> Clone for Snap<ra_salsa::Snapshot<DB>> { + fn clone(&self) -> Snap<ra_salsa::Snapshot<DB>> { Snap(self.0.snapshot()) } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index ef2e542cf22..518b588cb7d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -12,10 +12,11 @@ use std::{ use cfg::{CfgAtom, CfgDiff}; use hir::Symbol; use ide::{ - AssistConfig, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig, - ExprFillDefaultMode, GenericParameterHints, HighlightConfig, HighlightRelatedConfig, - HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, - MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId, + AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig, + CompletionFieldsToResolve, DiagnosticsConfig, ExprFillDefaultMode, GenericParameterHints, + HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, + InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + Snippet, SnippetScope, SourceRootId, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -262,7 +263,7 @@ config_data! { /// Exclude imports from find-all-references. references_excludeImports: bool = false, - /// Exclude tests from find-all-references. + /// Exclude tests from find-all-references and call-hierarchy. references_excludeTests: bool = false, /// Inject additional highlighting into doc comments. @@ -1392,6 +1393,10 @@ impl Config { } } + pub fn call_hierarchy(&self) -> CallHierarchyConfig { + CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() } + } + pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig { let client_capability_fields = self.completion_resolve_support_properties(); CompletionConfig { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index c3142c9cfca..7fbeaa4e3ea 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -51,6 +51,11 @@ pub(crate) struct FetchWorkspaceResponse { pub(crate) force_crate_graph_reload: bool, } +pub(crate) struct FetchBuildDataResponse { + pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, + pub(crate) build_scripts: Vec<anyhow::Result<WorkspaceBuildScripts>>, +} + // Enforces drop order pub(crate) struct Handle<H, C> { pub(crate) handle: H, @@ -152,8 +157,7 @@ pub(crate) struct GlobalState { // op queues pub(crate) fetch_workspaces_queue: OpQueue<FetchWorkspaceRequest, FetchWorkspaceResponse>, - pub(crate) fetch_build_data_queue: - OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>, + pub(crate) fetch_build_data_queue: OpQueue<(), FetchBuildDataResponse>, pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>, pub(crate) prime_caches_queue: OpQueue, pub(crate) discover_workspace_queue: OpQueue, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 9773d8dbce0..a9f8ac3a80a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1708,7 +1708,8 @@ pub(crate) fn handle_call_hierarchy_incoming( let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let call_items = match snap.analysis.incoming_calls(fpos)? { + let config = snap.config.call_hierarchy(); + let call_items = match snap.analysis.incoming_calls(config, fpos)? { None => return Ok(None), Some(it) => it, }; @@ -1745,7 +1746,8 @@ pub(crate) fn handle_call_hierarchy_outgoing( let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let call_items = match snap.analysis.outgoing_calls(fpos)? { + let config = snap.config.call_hierarchy(); + let call_items = match snap.analysis.outgoing_calls(config, fpos)? { None => return Ok(None), Some(it) => it, }; @@ -2201,14 +2203,14 @@ fn run_rustfmt( let cmd = Utf8PathBuf::from(&command); let target_spec = TargetSpec::for_file(snap, file_id)?; let mut cmd = match target_spec { - Some(TargetSpec::Cargo(spec)) => { - // approach: if the command name contains a path separator, join it with the workspace root. + Some(TargetSpec::Cargo(_)) => { + // approach: if the command name contains a path separator, join it with the project root. // however, if the path is absolute, joining will result in the absolute path being preserved. // as a fallback, rely on $PATH-based discovery. let cmd_path = if command.contains(std::path::MAIN_SEPARATOR) || (cfg!(windows) && command.contains('/')) { - spec.workspace_root.join(cmd).into() + snap.config.root_path().join(cmd).into() } else { cmd }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 375b7428c2d..d654dc3e7f4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -280,14 +280,14 @@ fn completion_item( let mut something_to_resolve = false; let filter_text = if fields_to_resolve.resolve_filter_text { - something_to_resolve = !item.lookup().is_empty(); + something_to_resolve |= !item.lookup().is_empty(); None } else { Some(item.lookup().to_owned()) }; let text_edit = if fields_to_resolve.resolve_text_edit { - something_to_resolve = true; + something_to_resolve |= true; None } else { // LSP does not allow arbitrary edits in completion, so we have to do a @@ -319,14 +319,14 @@ fn completion_item( let insert_text_format = item.is_snippet.then_some(lsp_types::InsertTextFormat::SNIPPET); let tags = if fields_to_resolve.resolve_tags { - something_to_resolve = item.deprecated; + something_to_resolve |= item.deprecated; None } else { item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]) }; let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints { if fields_to_resolve.resolve_command { - something_to_resolve = true; + something_to_resolve |= true; None } else { Some(command::trigger_parameter_hints()) @@ -336,14 +336,14 @@ fn completion_item( }; let detail = if fields_to_resolve.resolve_detail { - something_to_resolve = item.detail.is_some(); + something_to_resolve |= item.detail.is_some(); None } else { - item.detail + item.detail.clone() }; let documentation = if fields_to_resolve.resolve_documentation { - something_to_resolve = item.documentation.is_some(); + something_to_resolve |= item.documentation.is_some(); None } else { item.documentation.map(documentation) @@ -366,11 +366,11 @@ fn completion_item( if config.completion_label_details_support() { if fields_to_resolve.resolve_label_details { - something_to_resolve = true; + something_to_resolve |= true; } else { lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails { detail: item.label_detail.as_ref().map(ToString::to_string), - description: lsp_item.detail.clone(), + description: item.detail, }); } } else if let Some(label_detail) = item.label_detail { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index ef289720568..20be38a9e4b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -23,7 +23,8 @@ use crate::{ discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, flycheck::{self, FlycheckMessage}, global_state::{ - file_id_to_url, url_to_file_id, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState, + file_id_to_url, url_to_file_id, FetchBuildDataResponse, FetchWorkspaceRequest, + FetchWorkspaceResponse, GlobalState, }, hack_recover_crate_name, handlers::dispatch::{NotificationDispatcher, RequestDispatcher}, @@ -738,8 +739,10 @@ impl GlobalState { let (state, msg) = match progress { BuildDataProgress::Begin => (Some(Progress::Begin), None), BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)), - BuildDataProgress::End(build_data_result) => { - self.fetch_build_data_queue.op_completed(build_data_result); + BuildDataProgress::End((workspaces, build_scripts)) => { + let resp = FetchBuildDataResponse { workspaces, build_scripts }; + self.fetch_build_data_queue.op_completed(resp); + if let Err(e) = self.fetch_build_data_error() { error!("FetchBuildDataError: {e}"); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 60ee0295a3a..0b24833358d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -18,7 +18,7 @@ use std::{iter, mem}; use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder}; use ide::CrateId; use ide_db::{ - base_db::{salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths}, + base_db::{ra_salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths}, FxHashMap, }; use itertools::Itertools; @@ -33,7 +33,9 @@ use vfs::{AbsPath, AbsPathBuf, ChangeKind}; use crate::{ config::{Config, FilesWatcher, LinkedProject}, flycheck::{FlycheckConfig, FlycheckHandle}, - global_state::{FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState}, + global_state::{ + FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState, + }, lsp_ext, main_loop::{DiscoverProjectParam, Task}, op_queue::Cause, @@ -475,7 +477,9 @@ impl GlobalState { if same_workspaces { let (workspaces, build_scripts) = match self.fetch_build_data_queue.last_op_result() { - Some((workspaces, build_scripts)) => (workspaces.clone(), build_scripts.as_slice()), + Some(FetchBuildDataResponse { workspaces, build_scripts }) => { + (workspaces.clone(), build_scripts.as_slice()) + } None => (Default::default(), Default::default()), }; @@ -769,12 +773,14 @@ impl GlobalState { pub(super) fn fetch_build_data_error(&self) -> Result<(), String> { let mut buf = String::new(); - let Some((_, ws)) = &self.fetch_build_data_queue.last_op_result() else { + let Some(FetchBuildDataResponse { build_scripts, .. }) = + &self.fetch_build_data_queue.last_op_result() + else { return Ok(()); }; - for ws in ws { - match ws { + for script in build_scripts { + match script { Ok(data) => { if let Some(stderr) = data.error() { stdx::format_to!(buf, "{:#}\n", stderr) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs index b73f6e77514..5ab2dc2b67a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs @@ -7,7 +7,7 @@ use anyhow::Context; use tracing::level_filters::LevelFilter; use tracing_subscriber::{ filter::{filter_fn, Targets}, - fmt::MakeWriter, + fmt::{time, MakeWriter}, layer::SubscriberExt, Layer, Registry, }; @@ -58,6 +58,10 @@ where let writer = self.writer; let ra_fmt_layer = tracing_subscriber::fmt::layer() + .with_timer( + time::OffsetTime::local_rfc_3339() + .expect("Could not get local offset, make sure you're on the main thread"), + ) .with_target(false) .with_ansi(false) .with_writer(writer) diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index 3381dac0b42..569da8082a8 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -12,7 +12,7 @@ authors.workspace = true [dependencies] la-arena.workspace = true -salsa.workspace = true +ra-salsa.workspace = true rustc-hash.workspace = true hashbrown.workspace = true text-size.workspace = true diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 3863b3e809c..67d7bb9a0de 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -21,7 +21,7 @@ //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. use std::fmt; -use salsa::{InternId, InternValue}; +use ra_salsa::{InternId, InternValue}; use crate::MacroCallId; @@ -39,11 +39,11 @@ impl fmt::Debug for SyntaxContextId { } } -impl salsa::InternKey for SyntaxContextId { - fn from_intern_id(v: salsa::InternId) -> Self { +impl ra_salsa::InternKey for SyntaxContextId { + fn from_intern_id(v: ra_salsa::InternId) -> Self { SyntaxContextId(v) } - fn as_intern_id(&self) -> salsa::InternId { + fn as_intern_id(&self) -> ra_salsa::InternId { self.0 } } diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index 61e4c98128a..bd270bfe2b1 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -1,7 +1,7 @@ //! File and span related types. use std::fmt::{self, Write}; -use salsa::InternId; +use ra_salsa::InternId; mod ast_id; mod hygiene; @@ -261,13 +261,13 @@ pub struct MacroFileId { /// `MacroCallId` identifies a particular macro invocation, like /// `println!("Hello, {}", world)`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct MacroCallId(salsa::InternId); +pub struct MacroCallId(ra_salsa::InternId); -impl salsa::InternKey for MacroCallId { - fn from_intern_id(v: salsa::InternId) -> Self { +impl ra_salsa::InternKey for MacroCallId { + fn from_intern_id(v: ra_salsa::InternId) -> Self { MacroCallId(v) } - fn as_intern_id(&self) -> salsa::InternId { + fn as_intern_id(&self) -> ra_salsa::InternId { self.0 } } diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index 708fc2b7891..babeb4272be 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -852,7 +852,7 @@ Exclude imports from find-all-references. [[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`):: + -- -Exclude tests from find-all-references. +Exclude tests from find-all-references and call-hierarchy. -- [[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`):: + diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index a823e5bb96c..a52b3d1ec5c 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2333,7 +2333,7 @@ "title": "references", "properties": { "rust-analyzer.references.excludeTests": { - "markdownDescription": "Exclude tests from find-all-references.", + "markdownDescription": "Exclude tests from find-all-references and call-hierarchy.", "default": false, "type": "boolean" } diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts index 35867f710d5..8fc9f09324a 100644 --- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts +++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts @@ -1,9 +1,9 @@ import * as vscode from "vscode"; import * as os from "os"; import type { Config } from "./config"; -import { type Env, log } from "./util"; +import { type Env, log, spawnAsync } from "./util"; import type { PersistentState } from "./persistent_state"; -import { exec, spawnSync } from "child_process"; +import { exec } from "child_process"; import { TextDecoder } from "node:util"; export async function bootstrap( @@ -61,13 +61,12 @@ async function getServer( // if so, use the rust-analyzer component const toolchainUri = vscode.Uri.joinPath(workspaceFolder.uri, "rust-toolchain.toml"); if (await hasToolchainFileWithRaDeclared(toolchainUri)) { - const res = spawnSync("rustup", ["which", "rust-analyzer"], { - encoding: "utf8", + const res = await spawnAsync("rustup", ["which", "rust-analyzer"], { env: { ...process.env }, cwd: workspaceFolder.uri.fsPath, }); if (!res.error && res.status === 0) { - toolchainServerPath = earliestToolchainPath( + toolchainServerPath = await earliestToolchainPath( toolchainServerPath, res.stdout.trim(), raVersionResolver, @@ -114,10 +113,8 @@ async function getServer( } // Given a path to a rust-analyzer executable, resolve its version and return it. -function raVersionResolver(path: string): string | undefined { - const res = spawnSync(path, ["--version"], { - encoding: "utf8", - }); +async function raVersionResolver(path: string): Promise<string | undefined> { + const res = await spawnAsync(path, ["--version"]); if (!res.error && res.status === 0) { return res.stdout; } else { @@ -126,13 +123,16 @@ function raVersionResolver(path: string): string | undefined { } // Given a path to two rust-analyzer executables, return the earliest one by date. -function earliestToolchainPath( +async function earliestToolchainPath( path0: string | undefined, path1: string, - raVersionResolver: (path: string) => string | undefined, -): string { + raVersionResolver: (path: string) => Promise<string | undefined>, +): Promise<string> { if (path0) { - if (orderFromPath(path0, raVersionResolver) < orderFromPath(path1, raVersionResolver)) { + if ( + (await orderFromPath(path0, raVersionResolver)) < + (await orderFromPath(path1, raVersionResolver)) + ) { return path0; } else { return path1; @@ -150,11 +150,11 @@ function earliestToolchainPath( // nightly - /Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer // versioned - /Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer // stable - /Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer -function orderFromPath( +async function orderFromPath( path: string, - raVersionResolver: (path: string) => string | undefined, -): string { - const raVersion = raVersionResolver(path); + raVersionResolver: (path: string) => Promise<string | undefined>, +): Promise<string> { + const raVersion = await raVersionResolver(path); const raDate = raVersion?.match(/^rust-analyzer .*\(.* (\d{4}-\d{2}-\d{2})\)$/); if (raDate?.length === 2) { const precedence = path.includes("nightly-") ? "0" : "1"; @@ -184,11 +184,10 @@ async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean> } } -export function isValidExecutable(path: string, extraEnv: Env): boolean { +export async function isValidExecutable(path: string, extraEnv: Env): Promise<boolean> { log.debug("Checking availability of a binary at", path); - const res = spawnSync(path, ["--version"], { - encoding: "utf8", + const res = await spawnAsync(path, ["--version"], { env: { ...process.env, ...extraEnv }, }); diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index fb7e340e517..77ab44f24ce 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -6,6 +6,7 @@ import type * as ra from "./lsp_ext"; import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { createTaskFromRunnable, prepareEnv } from "./run"; +import { execSync } from "node:child_process"; import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util"; import type { Config } from "./config"; @@ -105,9 +106,11 @@ async function getDebugConfiguration( const commandCCpp: string = createCommandLink("ms-vscode.cpptools"); const commandCodeLLDB: string = createCommandLink("vadimcn.vscode-lldb"); const commandNativeDebug: string = createCommandLink("webfreak.debug"); + const commandLLDBDap: string = createCommandLink("llvm-vs-code-extensions.lldb-dap"); await vscode.window.showErrorMessage( `Install [CodeLLDB](command:${commandCodeLLDB} "Open CodeLLDB")` + + `, [lldb-dap](command:${commandLLDBDap} "Open lldb-dap")` + `, [C/C++](command:${commandCCpp} "Open C/C++") ` + `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`, ); @@ -220,10 +223,30 @@ type DebugConfigProvider<Type extends string, DebugConfig extends BaseDebugConfi type KnownEnginesType = (typeof knownEngines)[keyof typeof knownEngines]; const knownEngines: { + "llvm-vs-code-extensions.lldb-dap": DebugConfigProvider<"lldb-dap", LldbDapDebugConfig>; "vadimcn.vscode-lldb": DebugConfigProvider<"lldb", CodeLldbDebugConfig>; "ms-vscode.cpptools": DebugConfigProvider<"cppvsdbg" | "cppdbg", CCppDebugConfig>; "webfreak.debug": DebugConfigProvider<"gdb", NativeDebugConfig>; } = { + "llvm-vs-code-extensions.lldb-dap": { + type: "lldb-dap", + executableProperty: "program", + environmentProperty: (env) => ["env", Object.entries(env).map(([k, v]) => `${k}=${v}`)], + runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ + "args", + runnableArgs.executableArgs, + ], + additional: { + sourceMap: [ + [ + `/rustc/${/commit-hash:\s(.*)$/m.exec( + execSync("rustc -V -v", {}).toString(), + )?.[1]}/library`, + "${config:rust-analyzer.cargo.sysroot}/lib/rustlib/src/rust/library", + ], + ], + }, + }, "vadimcn.vscode-lldb": { type: "lldb", executableProperty: "program", @@ -336,6 +359,13 @@ type CCppDebugConfig = { }; } & BaseDebugConfig<"cppvsdbg" | "cppdbg">; +type LldbDapDebugConfig = { + program: string; + args: string[]; + env: string[]; + sourceMap: [string, string][]; +} & BaseDebugConfig<"lldb-dap">; + type CodeLldbDebugConfig = { program: string; args: string[]; diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts index db64a013fda..d7ca6b3557d 100644 --- a/src/tools/rust-analyzer/editors/code/src/util.ts +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode"; import { strict as nativeAssert } from "assert"; -import { exec, type ExecOptions } from "child_process"; +import { exec, spawn, type SpawnOptionsWithoutStdio, type ExecOptions } from "child_process"; import { inspect } from "util"; import type { CargoRunnableArgs, ShellRunnableArgs } from "./lsp_ext"; @@ -233,3 +233,55 @@ export function expectNotUndefined<T>(input: Undefinable<T>, msg: string): NotUn export function unwrapUndefinable<T>(input: Undefinable<T>): NotUndefined<T> { return expectNotUndefined(input, `unwrapping \`undefined\``); } + +interface SpawnAsyncReturns { + stdout: string; + stderr: string; + status: number | null; + error?: Error | undefined; +} + +export async function spawnAsync( + path: string, + args?: ReadonlyArray<string>, + options?: SpawnOptionsWithoutStdio, +): Promise<SpawnAsyncReturns> { + const child = spawn(path, args, options); + const stdout: Array<Buffer> = []; + const stderr: Array<Buffer> = []; + try { + const res = await new Promise<{ stdout: string; stderr: string; status: number | null }>( + (resolve, reject) => { + child.stdout.on("data", (chunk) => stdout.push(Buffer.from(chunk))); + child.stderr.on("data", (chunk) => stderr.push(Buffer.from(chunk))); + child.on("error", (error) => + reject({ + stdout: Buffer.concat(stdout).toString("utf8"), + stderr: Buffer.concat(stderr).toString("utf8"), + error, + }), + ); + child.on("close", (status) => + resolve({ + stdout: Buffer.concat(stdout).toString("utf8"), + stderr: Buffer.concat(stderr).toString("utf8"), + status, + }), + ); + }, + ); + + return { + stdout: res.stdout, + stderr: res.stderr, + status: res.status, + }; + } catch (e: any) { + return { + stdout: e.stdout, + stderr: e.stderr, + status: e.status, + error: e.error, + }; + } +} diff --git a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts index 8aeb72180a0..baabf4f8977 100644 --- a/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts +++ b/src/tools/rust-analyzer/editors/code/tests/unit/bootstrap.test.ts @@ -6,9 +6,9 @@ export async function getTests(ctx: Context) { await ctx.suite("Bootstrap/Select toolchain RA", (suite) => { suite.addTest("Order of nightly RA", async () => { assert.deepStrictEqual( - _private.orderFromPath( + await _private.orderFromPath( "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", - function (path: string) { + async function (path: string) { assert.deepStrictEqual( path, "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", @@ -22,9 +22,9 @@ export async function getTests(ctx: Context) { suite.addTest("Order of versioned RA", async () => { assert.deepStrictEqual( - _private.orderFromPath( + await _private.orderFromPath( "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer", - function (path: string) { + async function (path: string) { assert.deepStrictEqual( path, "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer", @@ -38,9 +38,9 @@ export async function getTests(ctx: Context) { suite.addTest("Order of versioned RA when unable to obtain version date", async () => { assert.deepStrictEqual( - _private.orderFromPath( + await _private.orderFromPath( "/Users/myuser/.rustup/toolchains/1.72.1-aarch64-apple-darwin/bin/rust-analyzer", - function () { + async function () { return "rust-analyzer 1.72.1"; }, ), @@ -50,9 +50,9 @@ export async function getTests(ctx: Context) { suite.addTest("Order of stable RA", async () => { assert.deepStrictEqual( - _private.orderFromPath( + await _private.orderFromPath( "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", - function (path: string) { + async function (path: string) { assert.deepStrictEqual( path, "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", @@ -66,7 +66,7 @@ export async function getTests(ctx: Context) { suite.addTest("Order with invalid path to RA", async () => { assert.deepStrictEqual( - _private.orderFromPath("some-weird-path", function () { + await _private.orderFromPath("some-weird-path", async function () { return undefined; }), "2", @@ -75,10 +75,10 @@ export async function getTests(ctx: Context) { suite.addTest("Earliest RA between nightly and stable", async () => { assert.deepStrictEqual( - _private.earliestToolchainPath( + await _private.earliestToolchainPath( "/Users/myuser/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rust-analyzer", "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer", - function (path: string) { + async function (path: string) { if ( path === "/Users/myuser/.rustup/toolchains/nightly-2022-11-22-aarch64-apple-darwin/bin/rust-analyzer" diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index f217c6a19cb..d0f9fa7ac42 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -cf24c73141a77db730f4b7fda69dcd7e8b113b51 +dd5127615ad626741a1116d022cf784637ac05df diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs index ea51d33ed9c..0268e2473c0 100644 --- a/src/tools/rust-analyzer/xtask/src/tidy.rs +++ b/src/tools/rust-analyzer/xtask/src/tidy.rs @@ -223,7 +223,7 @@ struct TidyDocs { impl TidyDocs { fn visit(&mut self, path: &Path, text: &str) { // Tests and diagnostic fixes don't need module level comments. - if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "salsa"]) { + if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "ra-salsa"]) { return; } diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 22126674c15..97c42752c12 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2758,7 +2758,6 @@ ui/lint/issue-63364.rs ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs ui/lint/issue-79546-fuel-ice.rs ui/lint/issue-79744.rs -ui/lint/issue-80988.rs ui/lint/issue-81218.rs ui/lint/issue-83477.rs ui/lint/issue-87274-paren-parent.rs diff --git a/src/tools/unicode-table-generator/Cargo.toml b/src/tools/unicode-table-generator/Cargo.toml index ef01877c0b9..f8a500922d0 100644 --- a/src/tools/unicode-table-generator/Cargo.toml +++ b/src/tools/unicode-table-generator/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "unicode-bdd" +name = "unicode-table-generator" version = "0.1.0" edition = "2021" diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs index 3a5b869f72f..221e5d75ef5 100644 --- a/src/tools/unicode-table-generator/src/range_search.rs +++ b/src/tools/unicode-table-generator/src/range_search.rs @@ -16,16 +16,14 @@ const fn bitset_search< let bucket_idx = (needle / 64) as usize; let chunk_map_idx = bucket_idx / CHUNK_SIZE; let chunk_piece = bucket_idx % CHUNK_SIZE; - // FIXME: const-hack: Revert to `slice::get` after `const_slice_index` - // feature stabilizes. + // FIXME(const-hack): Revert to `slice::get` when slice indexing becomes possible in const. let chunk_idx = if chunk_map_idx < chunk_idx_map.len() { chunk_idx_map[chunk_map_idx] } else { return false; }; let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize; - // FIXME: const-hack: Revert to `slice::get` after `const_slice_index` - // feature stabilizes. + // FIXME(const-hack): Revert to `slice::get` when slice indexing becomes possible in const. let word = if idx < bitset_canonical.len() { bitset_canonical[idx] } else { diff --git a/tests/assembly/x86-return-float.rs b/tests/assembly/x86-return-float.rs index f9ebf53dddc..30c5e869633 100644 --- a/tests/assembly/x86-return-float.rs +++ b/tests/assembly/x86-return-float.rs @@ -315,10 +315,10 @@ pub fn return_f16(x: f16) -> f16 { #[no_mangle] pub fn return_f128(x: f128) -> f128 { // CHECK: movl [[#%d,OFFSET:]](%ebp), %[[PTR:.*]] - // CHECK-NEXT: movl [[#%d,OFFSET+16]](%ebp), %[[VAL4:.*]] // CHECK-NEXT: movl [[#%d,OFFSET+4]](%ebp), %[[VAL1:.*]] // CHECK-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] // CHECK-NEXT: movl [[#%d,OFFSET+12]](%ebp), %[[VAL3:.*]] + // CHECK-NEXT: movl [[#%d,OFFSET+16]](%ebp), %[[VAL4:.*]] // CHECK-NEXT: movl %[[VAL4:.*]] 12(%[[PTR]]) // CHECK-NEXT: movl %[[VAL3:.*]] 8(%[[PTR]]) // CHECK-NEXT: movl %[[VAL2:.*]] 4(%[[PTR]]) diff --git a/tests/codegen/float/f128.rs b/tests/codegen/float/f128.rs index 80b572fbbc9..4af264101de 100644 --- a/tests/codegen/float/f128.rs +++ b/tests/codegen/float/f128.rs @@ -1,7 +1,11 @@ // 32-bit x86 returns `f32` and `f64` differently to avoid the x87 stack. -//@ revisions: x86 other +// 32-bit systems will return 128bit values using a return area pointer. +//@ revisions: x86 bit32 bit64 //@[x86] only-x86 -//@[other] ignore-x86 +//@[bit32] ignore-x86 +//@[bit32] only-32bit +//@[bit64] ignore-x86 +//@[bit64] only-64bit // Verify that our intrinsics generate the correct LLVM calls for f128 @@ -52,42 +56,54 @@ pub fn f128_le(a: f128, b: f128) -> bool { a <= b } -// CHECK-LABEL: fp128 @f128_neg( +// x86-LABEL: void @f128_neg({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_neg({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_neg( #[no_mangle] pub fn f128_neg(a: f128) -> f128 { // CHECK: fneg fp128 -a } -// CHECK-LABEL: fp128 @f128_add( +// x86-LABEL: void @f128_add({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_add({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_add( #[no_mangle] pub fn f128_add(a: f128, b: f128) -> f128 { // CHECK: fadd fp128 %{{.+}}, %{{.+}} a + b } -// CHECK-LABEL: fp128 @f128_sub( +// x86-LABEL: void @f128_sub({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_sub({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_sub( #[no_mangle] pub fn f128_sub(a: f128, b: f128) -> f128 { // CHECK: fsub fp128 %{{.+}}, %{{.+}} a - b } -// CHECK-LABEL: fp128 @f128_mul( +// x86-LABEL: void @f128_mul({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_mul({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_mul( #[no_mangle] pub fn f128_mul(a: f128, b: f128) -> f128 { // CHECK: fmul fp128 %{{.+}}, %{{.+}} a * b } -// CHECK-LABEL: fp128 @f128_div( +// x86-LABEL: void @f128_div({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_div({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_div( #[no_mangle] pub fn f128_div(a: f128, b: f128) -> f128 { // CHECK: fdiv fp128 %{{.+}}, %{{.+}} a / b } -// CHECK-LABEL: fp128 @f128_rem( +// x86-LABEL: void @f128_rem({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_rem({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_rem( #[no_mangle] pub fn f128_rem(a: f128, b: f128) -> f128 { // CHECK: frem fp128 %{{.+}}, %{{.+}} @@ -143,44 +159,56 @@ pub fn f128_as_f16(a: f128) -> f16 { a as f16 } -// other-LABEL: float @f128_as_f32( // x86-LABEL: i32 @f128_as_f32( +// bit32-LABEL: float @f128_as_f32( +// bit64-LABEL: float @f128_as_f32( #[no_mangle] pub fn f128_as_f32(a: f128) -> f32 { // CHECK: fptrunc fp128 %{{.+}} to float a as f32 } -// other-LABEL: double @f128_as_f64( // x86-LABEL: void @f128_as_f64( +// bit32-LABEL: double @f128_as_f64( +// bit64-LABEL: double @f128_as_f64( #[no_mangle] pub fn f128_as_f64(a: f128) -> f64 { // CHECK: fptrunc fp128 %{{.+}} to double a as f64 } -// CHECK-LABEL: fp128 @f128_as_self( +// x86-LABEL: void @f128_as_self({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_as_self({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f128_as_self( #[no_mangle] pub fn f128_as_self(a: f128) -> f128 { - // CHECK: ret fp128 %{{.+}} + // x86: store fp128 %a, ptr %_0, align 16 + // bit32: store fp128 %a, ptr %_0, align 16 + // bit64: ret fp128 %{{.+}} a as f128 } -// CHECK-LABEL: fp128 @f16_as_f128( +// x86-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f16_as_f128( #[no_mangle] pub fn f16_as_f128(a: f16) -> f128 { // CHECK: fpext half %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @f32_as_f128( +// x86-LABEL: void @f32_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f32_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f32_as_f128( #[no_mangle] pub fn f32_as_f128(a: f32) -> f128 { // CHECK: fpext float %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @f64_as_f128( +// x86-LABEL: void @f64_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f64_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f64_as_f128( #[no_mangle] pub fn f64_as_f128(a: f64) -> f128 { // CHECK: fpext double %{{.+}} to fp128 @@ -216,7 +244,9 @@ pub fn f128_as_u64(a: f128) -> u64 { a as u64 } -// CHECK-LABEL: i128 @f128_as_u128( +// x86-LABEL: void @f128_as_u128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_as_u128({{.*}}sret([16 x i8]) +// bit64-LABEL: i128 @f128_as_u128( #[no_mangle] pub fn f128_as_u128(a: f128) -> u128 { // CHECK: call i128 @llvm.fptoui.sat.i128.f128(fp128 %{{.+}}) @@ -250,7 +280,9 @@ pub fn f128_as_i64(a: f128) -> i64 { a as i64 } -// CHECK-LABEL: i128 @f128_as_i128( +// x86-LABEL: void @f128_as_i128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f128_as_i128({{.*}}sret([16 x i8]) +// bit64-LABEL: i128 @f128_as_i128( #[no_mangle] pub fn f128_as_i128(a: f128) -> i128 { // CHECK: call i128 @llvm.fptosi.sat.i128.f128(fp128 %{{.+}}) @@ -259,70 +291,90 @@ pub fn f128_as_i128(a: f128) -> i128 { /* int to float conversions */ -// CHECK-LABEL: fp128 @u8_as_f128( +// x86-LABEL: void @u8_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @u8_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @u8_as_f128( #[no_mangle] pub fn u8_as_f128(a: u8) -> f128 { // CHECK: uitofp i8 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @u16_as_f128( +// x86-LABEL: void @u16_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @u16_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @u16_as_f128( #[no_mangle] pub fn u16_as_f128(a: u16) -> f128 { // CHECK: uitofp i16 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @u32_as_f128( +// x86-LABEL: void @u32_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @u32_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @u32_as_f128( #[no_mangle] pub fn u32_as_f128(a: u32) -> f128 { // CHECK: uitofp i32 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @u64_as_f128( +// x86-LABEL: void @u64_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @u64_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @u64_as_f128( #[no_mangle] pub fn u64_as_f128(a: u64) -> f128 { // CHECK: uitofp i64 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @u128_as_f128( +// x86-LABEL: void @u128_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @u128_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @u128_as_f128( #[no_mangle] pub fn u128_as_f128(a: u128) -> f128 { // CHECK: uitofp i128 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @i8_as_f128( +// x86-LABEL: void @i8_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @i8_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @i8_as_f128( #[no_mangle] pub fn i8_as_f128(a: i8) -> f128 { // CHECK: sitofp i8 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @i16_as_f128( +// x86-LABEL: void @i16_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @i16_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @i16_as_f128( #[no_mangle] pub fn i16_as_f128(a: i16) -> f128 { // CHECK: sitofp i16 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @i32_as_f128( +// x86-LABEL: void @i32_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @i32_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @i32_as_f128( #[no_mangle] pub fn i32_as_f128(a: i32) -> f128 { // CHECK: sitofp i32 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @i64_as_f128( +// x86-LABEL: void @i64_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @i64_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @i64_as_f128( #[no_mangle] pub fn i64_as_f128(a: i64) -> f128 { // CHECK: sitofp i64 %{{.+}} to fp128 a as f128 } -// CHECK-LABEL: fp128 @i128_as_f128( +// x86-LABEL: void @i128_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @i128_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @i128_as_f128( #[no_mangle] pub fn i128_as_f128(a: i128) -> f128 { // CHECK: sitofp i128 %{{.+}} to fp128 diff --git a/tests/codegen/float/f16.rs b/tests/codegen/float/f16.rs index 2910d7d3e92..80931051f18 100644 --- a/tests/codegen/float/f16.rs +++ b/tests/codegen/float/f16.rs @@ -1,7 +1,11 @@ // 32-bit x86 returns `f32` and `f64` differently to avoid the x87 stack. -//@ revisions: x86 other +// 32-bit systems will return 128bit values using a return area pointer. +//@ revisions: x86 bit32 bit64 //@[x86] only-x86 -//@[other] ignore-x86 +//@[bit32] ignore-x86 +//@[bit32] only-32bit +//@[bit64] ignore-x86 +//@[bit64] only-64bit // Verify that our intrinsics generate the correct LLVM calls for f16 @@ -145,23 +149,27 @@ pub fn f16_as_self(a: f16) -> f16 { a as f16 } -// other-LABEL: float @f16_as_f32( // x86-LABEL: i32 @f16_as_f32( +// bit32-LABEL: float @f16_as_f32( +// bit64-LABEL: float @f16_as_f32( #[no_mangle] pub fn f16_as_f32(a: f16) -> f32 { // CHECK: fpext half %{{.+}} to float a as f32 } -// other-LABEL: double @f16_as_f64( // x86-LABEL: void @f16_as_f64( +// bit32-LABEL: double @f16_as_f64( +// bit64-LABEL: double @f16_as_f64( #[no_mangle] pub fn f16_as_f64(a: f16) -> f64 { // CHECK: fpext half %{{.+}} to double a as f64 } -// CHECK-LABEL: fp128 @f16_as_f128( +// x86-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f16_as_f128({{.*}}sret([16 x i8]) +// bit64-LABEL: fp128 @f16_as_f128( #[no_mangle] pub fn f16_as_f128(a: f16) -> f128 { // CHECK: fpext half %{{.+}} to fp128 @@ -218,7 +226,9 @@ pub fn f16_as_u64(a: f16) -> u64 { a as u64 } -// CHECK-LABEL: i128 @f16_as_u128( +// x86-LABEL: void @f16_as_u128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f16_as_u128({{.*}}sret([16 x i8]) +// bit64-LABEL: i128 @f16_as_u128( #[no_mangle] pub fn f16_as_u128(a: f16) -> u128 { // CHECK: call i128 @llvm.fptoui.sat.i128.f16(half %{{.+}}) @@ -252,7 +262,9 @@ pub fn f16_as_i64(a: f16) -> i64 { a as i64 } -// CHECK-LABEL: i128 @f16_as_i128( +// x86-LABEL: void @f16_as_i128({{.*}}sret([16 x i8]) +// bit32-LABEL: void @f16_as_i128({{.*}}sret([16 x i8]) +// bit64-LABEL: i128 @f16_as_i128( #[no_mangle] pub fn f16_as_i128(a: f16) -> i128 { // CHECK: call i128 @llvm.fptosi.sat.i128.f16(half %{{.+}}) diff --git a/tests/codegen/i128-x86-align.rs b/tests/codegen/i128-x86-align.rs index 3e6ed2b8e16..6b1da445c40 100644 --- a/tests/codegen/i128-x86-align.rs +++ b/tests/codegen/i128-x86-align.rs @@ -19,13 +19,15 @@ pub struct ScalarPair { #[no_mangle] pub fn load(x: &ScalarPair) -> ScalarPair { // CHECK-LABEL: @load( + // CHECK-SAME: sret([32 x i8]) align 16 dereferenceable(32) %_0, // CHECK-SAME: align 16 dereferenceable(32) %x // CHECK: [[A:%.*]] = load i32, ptr %x, align 16 // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr %x, i64 16 // CHECK-NEXT: [[B:%.*]] = load i128, ptr [[GEP]], align 16 - // CHECK-NEXT: [[IV1:%.*]] = insertvalue { i32, i128 } poison, i32 [[A]], 0 - // CHECK-NEXT: [[IV2:%.*]] = insertvalue { i32, i128 } [[IV1]], i128 [[B]], 1 - // CHECK-NEXT: ret { i32, i128 } [[IV2]] + // CHECK-NEXT: store i32 [[A]], ptr %_0, align 16 + // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr %_0, i64 16 + // CHECK-NEXT: store i128 [[B]], ptr [[GEP]], align 16 + // CHECK-NEXT: ret void *x } @@ -53,29 +55,23 @@ pub fn alloca() { #[no_mangle] pub fn load_volatile(x: &ScalarPair) -> ScalarPair { // CHECK-LABEL: @load_volatile( + // CHECK-SAME: sret([32 x i8]) align 16 dereferenceable(32) %_0, // CHECK-SAME: align 16 dereferenceable(32) %x - // CHECK: [[TMP:%.*]] = alloca [32 x i8], align 16 // CHECK: [[LOAD:%.*]] = load volatile %ScalarPair, ptr %x, align 16 - // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr [[TMP]], align 16 - // CHECK-NEXT: [[A:%.*]] = load i32, ptr [[TMP]], align 16 - // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP]], i64 16 - // CHECK-NEXT: [[B:%.*]] = load i128, ptr [[GEP]], align 16 + // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr %_0, align 16 + // CHECK-NEXT: ret void unsafe { std::intrinsics::volatile_load(x) } } #[no_mangle] pub fn transmute(x: ScalarPair) -> (std::mem::MaybeUninit<i128>, i128) { - // CHECK-LABEL: define { i128, i128 } @transmute(i32 noundef %x.0, i128 noundef %x.1) - // CHECK: [[TMP:%.*]] = alloca [32 x i8], align 16 - // CHECK-NEXT: store i32 %x.0, ptr [[TMP]], align 16 - // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[TMP]], i64 16 + // CHECK-LABEL: @transmute( + // CHECK-SAME: sret([32 x i8]) align 16 dereferenceable(32) %_0, + // CHECK-SAME: i32 noundef %x.0, i128 noundef %x.1 + // CHECK: store i32 %x.0, ptr %_0, align 16 + // CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr %_0, i64 16 // CHECK-NEXT: store i128 %x.1, ptr [[GEP]], align 16 - // CHECK-NEXT: [[LOAD1:%.*]] = load i128, ptr %_0, align 16 - // CHECK-NEXT: [[GEP2:%.*]] = getelementptr inbounds i8, ptr [[TMP]], i64 16 - // CHECK-NEXT: [[LOAD2:%.*]] = load i128, ptr [[GEP2]], align 16 - // CHECK-NEXT: [[IV1:%.*]] = insertvalue { i128, i128 } poison, i128 [[LOAD1]], 0 - // CHECK-NEXT: [[IV2:%.*]] = insertvalue { i128, i128 } [[IV1]], i128 [[LOAD2]], 1 - // CHECK-NEXT: ret { i128, i128 } [[IV2]] + // CHECK-NEXT: ret void unsafe { std::mem::transmute(x) } } diff --git a/tests/codegen/issues/issue-101082.rs b/tests/codegen/issues/issue-101082.rs index 550d267a98f..4be1b6cb168 100644 --- a/tests/codegen/issues/issue-101082.rs +++ b/tests/codegen/issues/issue-101082.rs @@ -1,4 +1,9 @@ //@ compile-flags: -O +//@ revisions: host x86-64-v3 + +// This particular CPU regressed in #131563 +//@[x86-64-v3] only-x86_64 +//@[x86-64-v3] compile-flags: -Ctarget-cpu=x86-64-v3 #![crate_type = "lib"] diff --git a/tests/codegen/issues/issue-108395-branchy-bool-match.rs b/tests/codegen/issues/issue-108395-branchy-bool-match.rs new file mode 100644 index 00000000000..24f5c0f6635 --- /dev/null +++ b/tests/codegen/issues/issue-108395-branchy-bool-match.rs @@ -0,0 +1,27 @@ +//@ compile-flags: -O -Zmerge-functions=disabled +//! Test for <https://github.com/rust-lang/rust/issues/108395>. Check that +//! matching on two bools with wildcards does not produce branches. +#![crate_type = "lib"] + +// CHECK-LABEL: @wildcard( +#[no_mangle] +pub fn wildcard(a: u16, b: u16, v: u16) -> u16 { + // CHECK-NOT: br + match (a == v, b == v) { + (true, false) => 0, + (false, true) => u16::MAX, + _ => 1 << 15, // half + } +} + +// CHECK-LABEL: @exhaustive( +#[no_mangle] +pub fn exhaustive(a: u16, b: u16, v: u16) -> u16 { + // CHECK-NOT: br + match (a == v, b == v) { + (true, false) => 0, + (false, true) => u16::MAX, + (true, true) => 1 << 15, + (false, false) => 1 << 15, + } +} diff --git a/tests/codegen/iter-repeat-n-trivial-drop.rs b/tests/codegen/iter-repeat-n-trivial-drop.rs index 7de224b92d8..4dab499a8a5 100644 --- a/tests/codegen/iter-repeat-n-trivial-drop.rs +++ b/tests/codegen/iter-repeat-n-trivial-drop.rs @@ -47,7 +47,7 @@ pub fn iter_repeat_n_next(it: &mut std::iter::RepeatN<NotCopy>) -> Option<NotCop #[no_mangle] // CHECK-LABEL: @vec_extend_via_iter_repeat_n pub fn vec_extend_via_iter_repeat_n() -> Vec<u8> { - // CHECK: %[[ADDR:.+]] = tail call {{(noalias )?}}noundef dereferenceable_or_null(1234) ptr @__rust_alloc(i64 noundef 1234, i64 noundef 1) + // CHECK: %[[ADDR:.+]] = tail call {{(noalias )?}}noundef dereferenceable_or_null(1234) ptr @__rust_alloc(i64 noundef {{(range\(i64 1, 0\) )?}}1234, i64 noundef {{(range\(i64 1, -9223372036854775807\) )?}}1) // CHECK: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1234) %[[ADDR]], i8 42, i64 1234, let n = 1234_usize; diff --git a/tests/codegen/mir-aggregate-no-alloca.rs b/tests/codegen/mir-aggregate-no-alloca.rs index c0e7e1a05e3..04ffb075538 100644 --- a/tests/codegen/mir-aggregate-no-alloca.rs +++ b/tests/codegen/mir-aggregate-no-alloca.rs @@ -1,3 +1,7 @@ +// 32-bit systems will return 128bit values using a return area pointer. +//@ revisions: bit32 bit64 +//@[bit32] only-32bit +//@[bit64] only-64bit //@ compile-flags: -O -C no-prepopulate-passes -Z randomize-layout=no #![crate_type = "lib"] @@ -98,26 +102,36 @@ pub fn make_struct_1(a: i32) -> Struct1 { pub struct Struct2Asc(i16, i64); -// CHECK-LABEL: { i64, i16 } @make_struct_2_asc(i16 noundef %a, i64 noundef %b) +// bit32-LABEL: void @make_struct_2_asc({{.*}} sret({{[^,]*}}) {{.*}} %s, +// bit64-LABEL: { i64, i16 } @make_struct_2_asc( +// CHECK-SAME: i16 noundef %a, i64 noundef %b) #[no_mangle] pub fn make_struct_2_asc(a: i16, b: i64) -> Struct2Asc { // CHECK-NOT: alloca - // CHECK: %[[TEMP0:.+]] = insertvalue { i64, i16 } poison, i64 %b, 0 - // CHECK: %[[TEMP1:.+]] = insertvalue { i64, i16 } %[[TEMP0]], i16 %a, 1 - // CHECK: ret { i64, i16 } %[[TEMP1]] + // bit32: %[[GEP:.+]] = getelementptr inbounds i8, ptr %s, i32 8 + // bit32: store i16 %a, ptr %[[GEP]] + // bit32: store i64 %b, ptr %s + // bit64: %[[TEMP0:.+]] = insertvalue { i64, i16 } poison, i64 %b, 0 + // bit64: %[[TEMP1:.+]] = insertvalue { i64, i16 } %[[TEMP0]], i16 %a, 1 + // bit64: ret { i64, i16 } %[[TEMP1]] let s = Struct2Asc(a, b); s } pub struct Struct2Desc(i64, i16); -// CHECK-LABEL: { i64, i16 } @make_struct_2_desc(i64 noundef %a, i16 noundef %b) +// bit32-LABEL: void @make_struct_2_desc({{.*}} sret({{[^,]*}}) {{.*}} %s, +// bit64-LABEL: { i64, i16 } @make_struct_2_desc( +// CHECK-SAME: i64 noundef %a, i16 noundef %b) #[no_mangle] pub fn make_struct_2_desc(a: i64, b: i16) -> Struct2Desc { // CHECK-NOT: alloca - // CHECK: %[[TEMP0:.+]] = insertvalue { i64, i16 } poison, i64 %a, 0 - // CHECK: %[[TEMP1:.+]] = insertvalue { i64, i16 } %[[TEMP0]], i16 %b, 1 - // CHECK: ret { i64, i16 } %[[TEMP1]] + // bit32: store i64 %a, ptr %s + // bit32: %[[GEP:.+]] = getelementptr inbounds i8, ptr %s, i32 8 + // bit32: store i16 %b, ptr %[[GEP]] + // bit64: %[[TEMP0:.+]] = insertvalue { i64, i16 } poison, i64 %a, 0 + // bit64: %[[TEMP1:.+]] = insertvalue { i64, i16 } %[[TEMP0]], i16 %b, 1 + // bit64: ret { i64, i16 } %[[TEMP1]] let s = Struct2Desc(a, b); s } diff --git a/tests/codegen/range-attribute.rs b/tests/codegen/range-attribute.rs index bb19bec0fb9..8972fc76ca2 100644 --- a/tests/codegen/range-attribute.rs +++ b/tests/codegen/range-attribute.rs @@ -1,6 +1,10 @@ // Checks that range metadata gets emitted on functions result and arguments // with scalar value. +// 32-bit systems will return 128bit values using a return area pointer. +//@ revisions: bit32 bit64 +//@[bit32] only-32bit +//@[bit64] only-64bit //@ compile-flags: -O -C no-prepopulate-passes //@ min-llvm-version: 19 @@ -13,7 +17,8 @@ use std::num::NonZero; #[no_mangle] pub fn helper(_: usize) {} -// CHECK: noundef range(i128 1, 0) i128 @nonzero_int(i128 noundef range(i128 1, 0) %x) +// bit32: void @nonzero_int({{.*}} sret([16 x i8]) {{.*}}, i128 noundef range(i128 1, 0) %x) +// bit64: noundef range(i128 1, 0) i128 @nonzero_int(i128 noundef range(i128 1, 0) %x) #[no_mangle] pub fn nonzero_int(x: NonZero<u128>) -> NonZero<u128> { x @@ -43,7 +48,9 @@ pub enum Enum1 { C(u64), } -// CHECK: { [[ENUM1_TYP:i[0-9]+]], i64 } @enum1_value([[ENUM1_TYP]] noundef range([[ENUM1_TYP]] 0, 3) %x.0, i64 noundef %x.1) +// bit32: void @enum1_value({{.*}} sret({{[^,]*}}) {{[^,]*}}, [[ENUM1_TYP:i[0-9]+]] +// bit64: { [[ENUM1_TYP:i[0-9]+]], i64 } @enum1_value([[ENUM1_TYP]] +// CHECK-SAME: noundef range([[ENUM1_TYP]] 0, 3) %x.0, i64 noundef %x.1) #[no_mangle] pub fn enum1_value(x: Enum1) -> Enum1 { x diff --git a/tests/codegen/tuple-layout-opt.rs b/tests/codegen/tuple-layout-opt.rs index 601563bc061..5b2f65e7aa7 100644 --- a/tests/codegen/tuple-layout-opt.rs +++ b/tests/codegen/tuple-layout-opt.rs @@ -1,3 +1,7 @@ +// 32-bit systems will return 128bit values using a return area pointer. +//@ revisions: bit32 bit64 +//@[bit32] only-32bit +//@[bit64] only-64bit //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 // Test that tuples get optimized layout, in particular with a ZST in the last field (#63244) @@ -5,42 +9,48 @@ #![crate_type = "lib"] type ScalarZstLast = (u128, ()); -// CHECK: define {{(dso_local )?}}i128 @test_ScalarZstLast(i128 %_1) +// bit32: define {{(dso_local )?}}void @test_ScalarZstLast({{.*}} sret([16 x i8]) {{.*}}, i128 %_1) +// bit64: define {{(dso_local )?}}i128 @test_ScalarZstLast(i128 %_1) #[no_mangle] pub fn test_ScalarZstLast(_: ScalarZstLast) -> ScalarZstLast { loop {} } type ScalarZstFirst = ((), u128); -// CHECK: define {{(dso_local )?}}i128 @test_ScalarZstFirst(i128 %_1) +// bit32: define {{(dso_local )?}}void @test_ScalarZstFirst({{.*}} sret([16 x i8]) {{.*}}, i128 %_1) +// bit64: define {{(dso_local )?}}i128 @test_ScalarZstFirst(i128 %_1) #[no_mangle] pub fn test_ScalarZstFirst(_: ScalarZstFirst) -> ScalarZstFirst { loop {} } type ScalarPairZstLast = (u8, u128, ()); -// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairZstLast(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}void @test_ScalarPairZstLast(ptr sret({{[^,]*}}) +// CHECK-SAME: %_0, i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairZstLast(_: ScalarPairZstLast) -> ScalarPairZstLast { loop {} } type ScalarPairZstFirst = ((), u8, u128); -// CHECK: define {{(dso_local )?}}{ i8, i128 } @test_ScalarPairZstFirst(i8 %_1.0, i128 %_1.1) +// CHECK: define {{(dso_local )?}}void @test_ScalarPairZstFirst(ptr sret({{[^,]*}}) +// CHECK-SAME: %_0, i8 %_1.0, i128 %_1.1) #[no_mangle] pub fn test_ScalarPairZstFirst(_: ScalarPairZstFirst) -> ScalarPairZstFirst { loop {} } type ScalarPairLotsOfZsts = ((), u8, (), u128, ()); -// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLotsOfZsts(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}void @test_ScalarPairLotsOfZsts(ptr sret({{[^,]*}}) +// CHECK-SAME: %_0, i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairLotsOfZsts(_: ScalarPairLotsOfZsts) -> ScalarPairLotsOfZsts { loop {} } type ScalarPairLottaNesting = (((), ((), u8, (), u128, ())), ()); -// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLottaNesting(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}void @test_ScalarPairLottaNesting(ptr sret({{[^,]*}}) +// CHECK-SAME: %_0, i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairLottaNesting(_: ScalarPairLottaNesting) -> ScalarPairLottaNesting { loop {} diff --git a/tests/codegen/union-abi.rs b/tests/codegen/union-abi.rs index a1c081d7d61..b3c67a59730 100644 --- a/tests/codegen/union-abi.rs +++ b/tests/codegen/union-abi.rs @@ -1,9 +1,13 @@ //@ ignore-emscripten vectors passed directly //@ compile-flags: -O -C no-prepopulate-passes // 32-bit x86 returns `f32` differently to avoid the x87 stack. -//@ revisions: x86 other +// 32-bit systems will return 128bit values using a return area pointer. +//@ revisions: x86 bit32 bit64 //@[x86] only-x86 -//@[other] ignore-x86 +//@[bit32] ignore-x86 +//@[bit32] only-32bit +//@[bit64] ignore-x86 +//@[bit64] only-64bit // This test that using union forward the abi of the inner type, as // discussed in #54668 @@ -71,8 +75,9 @@ pub union UnionF32 { a: f32, } -// other: define {{(dso_local )?}}float @test_UnionF32(float %_1) // x86: define {{(dso_local )?}}i32 @test_UnionF32(float %_1) +// bit32: define {{(dso_local )?}}float @test_UnionF32(float %_1) +// bit64: define {{(dso_local )?}}float @test_UnionF32(float %_1) #[no_mangle] pub fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} @@ -83,8 +88,9 @@ pub union UnionF32F32 { b: f32, } -// other: define {{(dso_local )?}}float @test_UnionF32F32(float %_1) // x86: define {{(dso_local )?}}i32 @test_UnionF32F32(float %_1) +// bit32: define {{(dso_local )?}}float @test_UnionF32F32(float %_1) +// bit64: define {{(dso_local )?}}float @test_UnionF32F32(float %_1) #[no_mangle] pub fn test_UnionF32F32(_: UnionF32F32) -> UnionF32F32 { loop {} @@ -104,7 +110,9 @@ pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { pub union UnionU128 { a: u128, } -// CHECK: define {{(dso_local )?}}i128 @test_UnionU128(i128 %_1) +// x86: define {{(dso_local )?}}void @test_UnionU128({{.*}}sret([16 x i8]){{.*}}, i128 %_1) +// bit32: define {{(dso_local )?}}void @test_UnionU128({{.*}}sret([16 x i8]){{.*}}, i128 %_1) +// bit64: define {{(dso_local )?}}i128 @test_UnionU128(i128 %_1) #[no_mangle] pub fn test_UnionU128(_: UnionU128) -> UnionU128 { loop {} diff --git a/tests/coverage/async_closure.cov-map b/tests/coverage/async_closure.cov-map new file mode 100644 index 00000000000..4d00f0d9b33 --- /dev/null +++ b/tests/coverage/async_closure.cov-map @@ -0,0 +1,56 @@ +Function name: async_closure::call_once::<async_closure::main::{closure#0}> +Raw bytes (9): 0x[01, 01, 00, 01, 01, 07, 01, 00, 2c] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 7, 1) to (start + 0, 44) +Highest counter ID seen: c0 + +Function name: async_closure::call_once::<async_closure::main::{closure#0}>::{closure#0} +Raw bytes (14): 0x[01, 01, 00, 02, 01, 07, 2c, 01, 0e, 05, 02, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 7, 44) to (start + 1, 14) +- Code(Counter(1)) at (prev + 2, 1) to (start + 0, 2) +Highest counter ID seen: c1 + +Function name: async_closure::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 0b, 01, 01, 16, 01, 02, 05, 02, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 11, 1) to (start + 1, 22) +- Code(Counter(0)) at (prev + 2, 5) to (start + 2, 2) +Highest counter ID seen: c0 + +Function name: async_closure::main::{closure#0} +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 23, 00, 24] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 12, 35) to (start + 0, 36) +Highest counter ID seen: c0 + +Function name: async_closure::main::{closure#0}::{closure#0}::<i16> +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 22, 00, 24] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 12, 34) to (start + 0, 36) +Highest counter ID seen: c0 + +Function name: async_closure::main::{closure#0}::{closure#1}::<i32> +Raw bytes (9): 0x[01, 01, 00, 01, 01, 0c, 23, 00, 24] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Counter(0)) at (prev + 12, 35) to (start + 0, 36) +Highest counter ID seen: c0 + diff --git a/tests/coverage/async_closure.coverage b/tests/coverage/async_closure.coverage new file mode 100644 index 00000000000..fd6edf7c29e --- /dev/null +++ b/tests/coverage/async_closure.coverage @@ -0,0 +1,24 @@ + LL| |#![feature(async_closure)] + LL| |//@ edition: 2021 + LL| | + LL| |//@ aux-build: executor.rs + LL| |extern crate executor; + LL| | + LL| 1|async fn call_once(f: impl async FnOnce()) { + LL| 1| f().await; + LL| 1|} + LL| | + LL| 1|pub fn main() { + LL| 2| let async_closure = async || {}; + ^1 + ------------------ + | async_closure::main::{closure#0}: + | LL| 1| let async_closure = async || {}; + ------------------ + | async_closure::main::{closure#0}::{closure#1}::<i32>: + | LL| 1| let async_closure = async || {}; + ------------------ + LL| 1| executor::block_on(async_closure()); + LL| 1| executor::block_on(call_once(async_closure)); + LL| 1|} + diff --git a/tests/coverage/async_closure.rs b/tests/coverage/async_closure.rs new file mode 100644 index 00000000000..c076d03eef4 --- /dev/null +++ b/tests/coverage/async_closure.rs @@ -0,0 +1,15 @@ +#![feature(async_closure)] +//@ edition: 2021 + +//@ aux-build: executor.rs +extern crate executor; + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +pub fn main() { + let async_closure = async || {}; + executor::block_on(async_closure()); + executor::block_on(call_once(async_closure)); +} diff --git a/tests/crashes/131190.rs b/tests/crashes/131190.rs deleted file mode 100644 index 3a0e64c69d5..00000000000 --- a/tests/crashes/131190.rs +++ /dev/null @@ -1,19 +0,0 @@ -//@ known-bug: #131190 -//@ compile-flags: -Cinstrument-coverage --edition=2018 - -use std::future::Future; - -pub fn block_on<T>(fut: impl Future<Output = T>) -> T {} - -async fn call_once(f: impl async FnOnce(DropMe)) { - f(DropMe("world")).await; -} - -struct DropMe(&'static str); - -pub fn main() { - block_on(async { - let async_closure = async move |a: DropMe| {}; - call_once(async_closure).await; - }); -} diff --git a/tests/crashes/131637.rs b/tests/crashes/131637.rs new file mode 100644 index 00000000000..7d328384a74 --- /dev/null +++ b/tests/crashes/131637.rs @@ -0,0 +1,7 @@ +//@ known-bug: #121637 +#![feature(non_lifetime_binders)] +trait Trait<Type> { + type Type; + + fn method(&self) -> impl for<T> Trait<impl Trait<T>>; +} diff --git a/tests/crashes/131648.rs b/tests/crashes/131648.rs new file mode 100644 index 00000000000..68046ce2a1f --- /dev/null +++ b/tests/crashes/131648.rs @@ -0,0 +1,7 @@ +//@ known-bug: #131648 +#![feature(return_type_notation)] + +trait IntFactory { + fn stream(self) -> impl IntFactory<stream(..): Send>; +} +fn main() {} diff --git a/tests/crashes/131668.rs b/tests/crashes/131668.rs new file mode 100644 index 00000000000..90aa4494425 --- /dev/null +++ b/tests/crashes/131668.rs @@ -0,0 +1,12 @@ +//@ known-bug: #131668 + +#![feature(generic_associated_types_extended)] +trait B { + type Y<const N: i16>; +} + +struct Erase<T: B>(T); + +fn make_static() { + Erase::<dyn for<'c> B<&'c ()>>(()); +} diff --git a/tests/crashes/131758.rs b/tests/crashes/131758.rs new file mode 100644 index 00000000000..942c5fd7a50 --- /dev/null +++ b/tests/crashes/131758.rs @@ -0,0 +1,11 @@ +//@ known-bug: #131758 +#![feature(unboxed_closures)] +trait Foo {} + +impl<T: Fn<(i32,)>> Foo for T {} + +fn baz<T: Foo>(_: T) {} + +fn main() { + baz(|x| ()); +} diff --git a/tests/crashes/131762.rs b/tests/crashes/131762.rs new file mode 100644 index 00000000000..85cb9c8f20a --- /dev/null +++ b/tests/crashes/131762.rs @@ -0,0 +1,9 @@ +//@ known-bug: #131762 +// ignore-tidy-linelength + +#![feature(generic_assert)] +struct FloatWrapper(f64); + +fn main() { + assert!((0.0 / 0.0 >= 0.0) == (FloatWrapper(0.0 / 0.0) >= FloatWrapper(size_of::<u8>, size_of::<u16>, size_of::<usize> as fn() -> usize))) +} diff --git a/tests/crashes/131787.rs b/tests/crashes/131787.rs new file mode 100644 index 00000000000..5c24ff8c143 --- /dev/null +++ b/tests/crashes/131787.rs @@ -0,0 +1,5 @@ +//@ known-bug: #131787 +#[track_caller] +static no_mangle: u32 = { + unimplemented!(); +}; diff --git a/tests/crashes/131886.rs b/tests/crashes/131886.rs new file mode 100644 index 00000000000..c31c2d6aa8b --- /dev/null +++ b/tests/crashes/131886.rs @@ -0,0 +1,12 @@ +//@ known-bug: #131886 +//@ compile-flags: -Zvalidate-mir --crate-type=lib +#![feature(trait_upcasting, type_alias_impl_trait)] + +type Tait = impl Sized; + +trait Foo<'a>: Bar<'a, 'a, Tait> {} +trait Bar<'a, 'b, T> {} + +fn test_correct3<'a>(x: &dyn Foo<'a>, _: Tait) { + let _ = x as &dyn Bar<'_, '_, ()>; +} diff --git a/tests/crashes/131915.rs b/tests/crashes/131915.rs new file mode 100644 index 00000000000..58d45adcb3b --- /dev/null +++ b/tests/crashes/131915.rs @@ -0,0 +1,13 @@ +//@ known-bug: #131915 + +macro_rules! y { + ( $($matcher:tt)*) => { + x + }; +} + +const _: A< + { + y! { test.tou8 } + }, +>; diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index ae445ad9b91..5fc77f95eaf 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,36 +1,36 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] -| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] -| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] -| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] -| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] +| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] +| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send +| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] +| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] +| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] +| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] +| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] +| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send +| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] +| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] +| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] +| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] +| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] +| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send +| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] +| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] +| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] +| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | fn address_of_reborrow() -> () { let mut _0: (); diff --git a/tests/mir-opt/asm_unwind_panic_abort.main.AbortUnwindingCalls.after.mir b/tests/mir-opt/asm_unwind_panic_abort.main.AbortUnwindingCalls.after.mir index 005b3ee3b24..0ea9937e2a2 100644 --- a/tests/mir-opt/asm_unwind_panic_abort.main.AbortUnwindingCalls.after.mir +++ b/tests/mir-opt/asm_unwind_panic_abort.main.AbortUnwindingCalls.after.mir @@ -7,7 +7,7 @@ fn main() -> () { bb0: { StorageLive(_1); _1 = const (); - asm!("", options(MAY_UNWIND)) -> [return: bb1, unwind terminate(abi)]; + asm!("", options(MAY_UNWIND)) -> [return: bb1, unwind: bb2]; } bb1: { @@ -15,4 +15,8 @@ fn main() -> () { _0 = const (); return; } + + bb2 (cleanup): { + terminate(abi); + } } diff --git a/tests/mir-opt/asm_unwind_panic_abort.rs b/tests/mir-opt/asm_unwind_panic_abort.rs index fff60942124..4ae76cbd16f 100644 --- a/tests/mir-opt/asm_unwind_panic_abort.rs +++ b/tests/mir-opt/asm_unwind_panic_abort.rs @@ -10,7 +10,9 @@ fn main() { // CHECK-LABEL: fn main( // CHECK: asm!( - // CHECK-SAME: unwind terminate(abi) + // CHECK-SAME: unwind: [[unwind:bb.*]]] + // CHECK: [[unwind]] (cleanup) + // CHECK-NEXT: terminate(abi) unsafe { std::arch::asm!("", options(may_unwind)); } diff --git a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir index d4f0363e443..b9d26c67538 100644 --- a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> -| 1: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> +| 0: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> +| 1: user_ty: Canonical { value: Ty(std::option::Option<std::boxed::Box<u32>>), max_universe: U0, variables: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option<std::boxed::Box<u32>> | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/issue_101867.main.built.after.mir b/tests/mir-opt/building/issue_101867.main.built.after.mir index 34e5bedf4ce..dd1d093c4db 100644 --- a/tests/mir-opt/building/issue_101867.main.built.after.mir +++ b/tests/mir-opt/building/issue_101867.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> -| 1: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> +| 0: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> +| 1: user_ty: Canonical { value: Ty(std::option::Option<u8>), max_universe: U0, variables: [] }, span: $DIR/issue_101867.rs:4:12: 4:22, inferred_ty: std::option::Option<u8> | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir index be972b62cbd..6e349a2a24f 100644 --- a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir +++ b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir @@ -1,10 +1,10 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test -| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test -| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test -| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }], defining_opaque_types: [] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test +| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test +| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:15:14: 15:23, inferred_ty: *mut Test +| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test +| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }, CanonicalVarInfo { kind: Region(U0) }] }, span: $DIR/receiver_ptr_mutability.rs:19:18: 19:31, inferred_ty: &&&&*mut Test | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/c_unwind_terminate.rs b/tests/mir-opt/c_unwind_terminate.rs new file mode 100644 index 00000000000..64524e74d28 --- /dev/null +++ b/tests/mir-opt/c_unwind_terminate.rs @@ -0,0 +1,25 @@ +//@ needs-unwind + +struct Noise; +impl Drop for Noise { + fn drop(&mut self) { + eprintln!("Noisy Drop"); + } +} + +fn panic() { + panic!(); +} + +// EMIT_MIR c_unwind_terminate.test.AbortUnwindingCalls.after.mir +extern "C" fn test() { + // CHECK-LABEL: fn test( + // CHECK: drop + // CHECK-SAME: unwind: [[unwind:bb.*]]] + // CHECK: [[unwind]] (cleanup) + // CHECK-NEXT: terminate(abi) + let _val = Noise; + panic(); +} + +fn main() {} diff --git a/tests/mir-opt/c_unwind_terminate.test.AbortUnwindingCalls.after.mir b/tests/mir-opt/c_unwind_terminate.test.AbortUnwindingCalls.after.mir new file mode 100644 index 00000000000..dd792d743cc --- /dev/null +++ b/tests/mir-opt/c_unwind_terminate.test.AbortUnwindingCalls.after.mir @@ -0,0 +1,36 @@ +// MIR for `test` after AbortUnwindingCalls + +fn test() -> () { + let mut _0: (); + let _1: Noise; + let _2: (); + scope 1 { + debug _val => _1; + } + + bb0: { + StorageLive(_1); + _1 = Noise; + StorageLive(_2); + _2 = panic() -> [return: bb1, unwind: bb3]; + } + + bb1: { + StorageDead(_2); + _0 = const (); + drop(_1) -> [return: bb2, unwind: bb4]; + } + + bb2: { + StorageDead(_1); + return; + } + + bb3 (cleanup): { + drop(_1) -> [return: bb4, unwind terminate(cleanup)]; + } + + bb4 (cleanup): { + terminate(abi); + } +} diff --git a/tests/mir-opt/issue_72181_1.main.built.after.mir b/tests/mir-opt/issue_72181_1.main.built.after.mir index 293aa37944d..79eaf966833 100644 --- a/tests/mir-opt/issue_72181_1.main.built.after.mir +++ b/tests/mir-opt/issue_72181_1.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void -| 1: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void +| 0: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void +| 1: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 161c73529f5..48a399eb39c 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 161c73529f5..48a399eb39c 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [], defining_opaque_types: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/run-make/avr-rjmp-offset/avr-rjmp-offsets.rs b/tests/run-make/avr-rjmp-offset/avr-rjmp-offsets.rs new file mode 100644 index 00000000000..2f97fc1ed95 --- /dev/null +++ b/tests/run-make/avr-rjmp-offset/avr-rjmp-offsets.rs @@ -0,0 +1,47 @@ +//! This test case is a `#![no_core]`-version of the MVCE presented in #129301. +//! +//! The function [`delay()`] is removed, as it is not necessary to trigger the +//! wrong behavior and would require some additional lang items. +#![feature(no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![no_main] +#![allow(internal_features)] + +use minicore::ptr; + +#[no_mangle] +pub fn main() -> ! { + let port_b = 0x25 as *mut u8; // the I/O-address of PORTB + + // a simple loop with some trivial instructions within. This loop label has + // to be placed correctly before the `ptr::write_volatile()` (some LLVM ver- + // sions did place it after the first loop instruction, causing unsoundness) + loop { + unsafe { ptr::write_volatile(port_b, 1) }; + unsafe { ptr::write_volatile(port_b, 2) }; + } +} + +// FIXME: replace with proper minicore once available (#130693) +mod minicore { + #[lang = "sized"] + pub trait Sized {} + + #[lang = "copy"] + pub trait Copy {} + impl Copy for u32 {} + impl Copy for &u32 {} + impl<T: ?Sized> Copy for *mut T {} + + pub mod ptr { + #[inline] + #[rustc_diagnostic_item = "ptr_write_volatile"] + pub unsafe fn write_volatile<T>(dst: *mut T, src: T) { + extern "rust-intrinsic" { + #[rustc_nounwind] + pub fn volatile_store<T>(dst: *mut T, val: T); + } + unsafe { volatile_store(dst, src) }; + } + } +} diff --git a/tests/run-make/avr-rjmp-offset/rmake.rs b/tests/run-make/avr-rjmp-offset/rmake.rs new file mode 100644 index 00000000000..89cbca309be --- /dev/null +++ b/tests/run-make/avr-rjmp-offset/rmake.rs @@ -0,0 +1,60 @@ +//@ needs-llvm-components: avr +//@ needs-rust-lld +//! Regression test for #129301/llvm-project#106722 within `rustc`. +//! +//! Some LLVM-versions had wrong offsets in the local labels, causing the first +//! loop instruction to be missed. This test therefore contains a simple loop +//! with trivial instructions in it, to see, where the label is placed. +//! +//! This must be a `rmake`-test and cannot be a `tests/assembly`-test, since the +//! wrong output is only produced with direct assembly generation, but not when +//! "emit-asm" is used, as described in the issue description of #129301: +//! https://github.com/rust-lang/rust/issues/129301#issue-2475070770 +use run_make_support::{llvm_objdump, rustc}; + +fn main() { + rustc() + .input("avr-rjmp-offsets.rs") + .opt_level("s") + .panic("abort") + .target("avr-unknown-gnu-atmega328") + // normally one links with `avr-gcc`, but this is not available in CI, + // hence this test diverges from the default behavior to enable linking + // at all, which is necessary for the test (to resolve the labels). To + // not depend on a special linker script, the main-function is marked as + // the entry function, causing the linker to not remove it. + .linker("rust-lld") + .link_arg("--entry=main") + .output("compiled") + .run(); + + let disassembly = llvm_objdump().disassemble().input("compiled").run().stdout_utf8(); + + // search for the following instruction sequence: + // ```disassembly + // 00000080 <main>: + // 80: 81 e0 ldi r24, 0x1 + // 82: 92 e0 ldi r25, 0x2 + // 84: 85 b9 out 0x5, r24 + // 86: 95 b9 out 0x5, r25 + // 88: fd cf rjmp .-6 + // ``` + // This matches on all instructions, since the size of the instructions be- + // fore the relative jump has an impact on the label offset. Old versions + // of the Rust compiler did produce a label `rjmp .-4` (misses the first + // instruction in the loop). + assert!(disassembly.contains("<main>"), "no main function in output"); + disassembly + .trim() + .lines() + .skip_while(|&line| !line.contains("<main>")) + .inspect(|line| println!("{line}")) + .skip(1) + .zip(["ldi\t", "ldi\t", "out\t", "out\t", "rjmp\t.-6"]) + .for_each(|(line, expected_instruction)| { + assert!( + line.contains(expected_instruction), + "expected instruction `{expected_instruction}`, got `{line}`" + ); + }); +} diff --git a/tests/run-make/longjmp-across-rust/main.rs b/tests/run-make/longjmp-across-rust/main.rs index cc1d5b126dd..0ebf11ac03c 100644 --- a/tests/run-make/longjmp-across-rust/main.rs +++ b/tests/run-make/longjmp-across-rust/main.rs @@ -10,19 +10,11 @@ fn main() { } } -struct A; - -impl Drop for A { - fn drop(&mut self) {} -} - extern "C" fn test_middle() { - let _a = A; foo(); } fn foo() { - let _a = A; unsafe { test_end(); } diff --git a/tests/rustdoc-json/traits/is_dyn_compatible.rs b/tests/rustdoc-json/traits/is_dyn_compatible.rs new file mode 100644 index 00000000000..bccf94d17d6 --- /dev/null +++ b/tests/rustdoc-json/traits/is_dyn_compatible.rs @@ -0,0 +1,19 @@ +#![no_std] + +//@ has "$.index[*][?(@.name=='FooDynIncompatible')]" +//@ is "$.index[*][?(@.name=='FooDynIncompatible')].inner.trait.is_dyn_compatible" false +pub trait FooDynIncompatible { + fn foo() -> Self; +} + +//@ has "$.index[*][?(@.name=='BarDynIncompatible')]" +//@ is "$.index[*][?(@.name=='BarDynIncompatible')].inner.trait.is_dyn_compatible" false +pub trait BarDynIncompatible<T> { + fn foo(i: T); +} + +//@ has "$.index[*][?(@.name=='FooDynCompatible')]" +//@ is "$.index[*][?(@.name=='FooDynCompatible')].inner.trait.is_dyn_compatible" true +pub trait FooDynCompatible { + fn foo(&self); +} diff --git a/tests/rustdoc-json/traits/is_object_safe.rs b/tests/rustdoc-json/traits/is_object_safe.rs deleted file mode 100644 index 35c4e4eb847..00000000000 --- a/tests/rustdoc-json/traits/is_object_safe.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![no_std] - -//@ has "$.index[*][?(@.name=='FooUnsafe')]" -//@ is "$.index[*][?(@.name=='FooUnsafe')].inner.trait.is_object_safe" false -pub trait FooUnsafe { - fn foo() -> Self; -} - -//@ has "$.index[*][?(@.name=='BarUnsafe')]" -//@ is "$.index[*][?(@.name=='BarUnsafe')].inner.trait.is_object_safe" false -pub trait BarUnsafe<T> { - fn foo(i: T); -} - -//@ has "$.index[*][?(@.name=='FooSafe')]" -//@ is "$.index[*][?(@.name=='FooSafe')].inner.trait.is_object_safe" true -pub trait FooSafe { - fn foo(&self); -} diff --git a/tests/ui/associated-types/impl-trait-return-missing-constraint.stderr b/tests/ui/associated-types/impl-trait-return-missing-constraint.stderr index 24409b32ad3..2c6c8ee5d19 100644 --- a/tests/ui/associated-types/impl-trait-return-missing-constraint.stderr +++ b/tests/ui/associated-types/impl-trait-return-missing-constraint.stderr @@ -2,18 +2,16 @@ error[E0271]: type mismatch resolving `<impl Bar as Foo>::Item == i32` --> $DIR/impl-trait-return-missing-constraint.rs:25:13 | LL | fn bar() -> impl Bar { - | -------- the expected opaque type + | -------- the found opaque type ... LL | fn baz() -> impl Bar<Item = i32> { - | ^^^^^^^^^^^^^^^^^^^^ expected associated type, found `i32` + | ^^^^^^^^^^^^^^^^^^^^ expected `i32`, found associated type LL | LL | bar() | ----- return type was inferred to be `impl Bar` here | - = note: expected associated type `<impl Bar as Foo>::Item` - found type `i32` - = help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32` or calling a method that returns `<impl Bar as Foo>::Item` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + = note: expected type `i32` + found associated type `<impl Bar as Foo>::Item` help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32` | LL | fn bar() -> impl Bar<Item = i32> { diff --git a/tests/ui/coherence/coherence-cow.re_a.stderr b/tests/ui/coherence/coherence-cow.re_a.stderr index 0bc017817b6..67de3629491 100644 --- a/tests/ui/coherence/coherence-cow.re_a.stderr +++ b/tests/ui/coherence/coherence-cow.re_a.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T> Remote for Pair<T,Cover<T>> { } | ^^^^^^^^^^^^^^^^^^^---------------- - | | | - | | `Pair` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Pair` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-cow.re_b.stderr b/tests/ui/coherence/coherence-cow.re_b.stderr index 9bdb49dcc04..360900cc7ff 100644 --- a/tests/ui/coherence/coherence-cow.re_b.stderr +++ b/tests/ui/coherence/coherence-cow.re_b.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T> Remote for Pair<Cover<T>,T> { } | ^^^^^^^^^^^^^^^^^^^---------------- - | | | - | | `Pair` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Pair` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-cow.re_c.stderr b/tests/ui/coherence/coherence-cow.re_c.stderr index dfff2667ebb..73f2aa196fd 100644 --- a/tests/ui/coherence/coherence-cow.re_c.stderr +++ b/tests/ui/coherence/coherence-cow.re_c.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T,U> Remote for Pair<Cover<T>,U> { } | ^^^^^^^^^^^^^^^^^^^^^---------------- - | | | - | | `Pair` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Pair` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-fundamental-trait-objects.stderr b/tests/ui/coherence/coherence-fundamental-trait-objects.stderr index db6a9474804..ca43d70e0b1 100644 --- a/tests/ui/coherence/coherence-fundamental-trait-objects.stderr +++ b/tests/ui/coherence/coherence-fundamental-trait-objects.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Misc for dyn Fundamental<Local> {} | ^^^^^^^^^^^^^^---------------------- - | | | - | | `dyn Fundamental<Local>` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `dyn Fundamental<Local>` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr index ea38afc40ce..77d1bdee5ac 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr @@ -39,10 +39,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl !Send for dyn Marker2 {} | ^^^^^^^^^^^^^^^----------- - | | | - | | `dyn Marker2` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `dyn Marker2` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` diff --git a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr index 2a8713bc327..f38aeaed0aa 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr @@ -39,10 +39,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | unsafe impl Send for dyn Marker2 {} | ^^^^^^^^^^^^^^^^^^^^^----------- - | | | - | | `dyn Marker2` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `dyn Marker2` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `(dyn Object + 'static)` diff --git a/tests/ui/coherence/coherence-impls-copy.stderr b/tests/ui/coherence/coherence-impls-copy.stderr index f529a056b0f..79fe044c4cf 100644 --- a/tests/ui/coherence/coherence-impls-copy.stderr +++ b/tests/ui/coherence/coherence-impls-copy.stderr @@ -13,10 +13,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Copy for &'static [NotSync] {} | ^^^^^^^^^^^^^^------------------ - | | | - | | this is not defined in the current crate because slices are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because slices are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for primitive types @@ -24,10 +25,11 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Copy for i32 {} | ^^^^^^^^^^^^^^--- - | | | - | | `i32` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `i32` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0206]: the trait `Copy` cannot be implemented for this type @@ -41,10 +43,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Copy for (MyType, MyType) {} | ^^^^^^^^^^^^^^---------------- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0206]: the trait `Copy` cannot be implemented for this type @@ -58,10 +61,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Copy for [MyType] {} | ^^^^^^^^^^^^^^-------- - | | | - | | this is not defined in the current crate because slices are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because slices are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0206]: the trait `Copy` cannot be implemented for this type diff --git a/tests/ui/coherence/coherence-impls-send.stderr b/tests/ui/coherence/coherence-impls-send.stderr index e1071846e14..78e89eb5540 100644 --- a/tests/ui/coherence/coherence-impls-send.stderr +++ b/tests/ui/coherence/coherence-impls-send.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | unsafe impl Send for &'static [NotSync] {} | ^^^^^^^^^^^^^^^^^^^^^------------------ - | | | - | | this is not defined in the current crate because slices are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because slices are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -14,10 +15,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | unsafe impl Send for (MyType, MyType) {} | ^^^^^^^^^^^^^^^^^^^^^---------------- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `Send`, can only be implemented for a struct/enum type, not `&'static NotSync` @@ -31,10 +33,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | unsafe impl Send for [MyType] {} | ^^^^^^^^^^^^^^^^^^^^^-------- - | | | - | | this is not defined in the current crate because slices are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because slices are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 4 previous errors diff --git a/tests/ui/coherence/coherence-impls-sized.stderr b/tests/ui/coherence/coherence-impls-sized.stderr index 17a7544521d..3201f1b25de 100644 --- a/tests/ui/coherence/coherence-impls-sized.stderr +++ b/tests/ui/coherence/coherence-impls-sized.stderr @@ -21,10 +21,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Sized for (MyType, MyType) {} | ^^^^^^^^^^^^^^^---------------- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0322]: explicit impls for the `Sized` trait are not permitted @@ -44,10 +45,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Sized for [MyType] {} | ^^^^^^^^^^^^^^^-------- - | | | - | | this is not defined in the current crate because slices are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because slices are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0322]: explicit impls for the `Sized` trait are not permitted @@ -61,10 +63,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Sized for &'static [NotSync] {} | ^^^^^^^^^^^^^^^------------------ - | | | - | | this is not defined in the current crate because slices are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because slices are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 9 previous errors diff --git a/tests/ui/coherence/coherence-negative-impls-copy-bad.stderr b/tests/ui/coherence/coherence-negative-impls-copy-bad.stderr index 2295d6315d1..074cd87ba92 100644 --- a/tests/ui/coherence/coherence-negative-impls-copy-bad.stderr +++ b/tests/ui/coherence/coherence-negative-impls-copy-bad.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl !Copy for str {} | ^^^^^^^^^^^^^^^--- - | | | - | | `str` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `str` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -14,10 +15,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl !Copy for fn() {} | ^^^^^^^^^^^^^^^---- - | | | - | | `fn()` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `fn()` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -25,10 +27,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl !Copy for () {} | ^^^^^^^^^^^^^^^-- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 3 previous errors diff --git a/tests/ui/coherence/coherence-orphan.stderr b/tests/ui/coherence/coherence-orphan.stderr index f6ffae34290..dcf423e24ee 100644 --- a/tests/ui/coherence/coherence-orphan.stderr +++ b/tests/ui/coherence/coherence-orphan.stderr @@ -3,11 +3,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl TheTrait<usize> for isize {} | ^^^^^---------------^^^^^----- - | | | | - | | | `isize` is not defined in the current crate - | | `usize` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `isize` is not defined in the current crate + | `usize` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate @@ -15,10 +16,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl !Send for Vec<isize> {} | ^^^^^^^^^^^^^^^---------- - | | | - | | `Vec` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Vec` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/coherence-overlapping-pairs.stderr b/tests/ui/coherence/coherence-overlapping-pairs.stderr index 4d0a9c6ee14..6e7a90ac369 100644 --- a/tests/ui/coherence/coherence-overlapping-pairs.stderr +++ b/tests/ui/coherence/coherence-overlapping-pairs.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T> Remote for lib::Pair<T,Foo> { } | ^^^^^^^^^^^^^^^^^^^---------------- - | | | - | | `Pair` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Pair` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-pair-covered-uncovered-1.stderr b/tests/ui/coherence/coherence-pair-covered-uncovered-1.stderr index 15cd66e9d09..d7890d156ca 100644 --- a/tests/ui/coherence/coherence-pair-covered-uncovered-1.stderr +++ b/tests/ui/coherence/coherence-pair-covered-uncovered-1.stderr @@ -3,11 +3,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl<T, U> Remote1<Pair<T, Local<U>>> for i32 { } | ^^^^^^^^^^^--------------------------^^^^^--- - | | | | - | | | `i32` is not defined in the current crate - | | `Pair` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `i32` is not defined in the current crate + | `Pair` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-pair-covered-uncovered.stderr b/tests/ui/coherence/coherence-pair-covered-uncovered.stderr index 359dbe8509d..fa3d170f81d 100644 --- a/tests/ui/coherence/coherence-pair-covered-uncovered.stderr +++ b/tests/ui/coherence/coherence-pair-covered-uncovered.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T,U> Remote for Pair<T,Local<U>> { } | ^^^^^^^^^^^^^^^^^^^^^---------------- - | | | - | | `Pair` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Pair` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-vec-local-2.stderr b/tests/ui/coherence/coherence-vec-local-2.stderr index e4249710d00..cb12275cf01 100644 --- a/tests/ui/coherence/coherence-vec-local-2.stderr +++ b/tests/ui/coherence/coherence-vec-local-2.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T> Remote for Vec<Local<T>> { } | ^^^^^^^^^^^^^^^^^^^------------- - | | | - | | `Vec` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Vec` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence-vec-local.stderr b/tests/ui/coherence/coherence-vec-local.stderr index c465fb1966e..9278b9458d5 100644 --- a/tests/ui/coherence/coherence-vec-local.stderr +++ b/tests/ui/coherence/coherence-vec-local.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl Remote for Vec<Local> { } | ^^^^^^^^^^^^^^^^---------- - | | | - | | `Vec` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Vec` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence_local_err_struct.stderr b/tests/ui/coherence/coherence_local_err_struct.stderr index 96572b5a716..280dd57bd42 100644 --- a/tests/ui/coherence/coherence_local_err_struct.stderr +++ b/tests/ui/coherence/coherence_local_err_struct.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl lib::MyCopy for lib::MyStruct<MyType> { } | ^^^^^^^^^^^^^^^^^^^^^--------------------- - | | | - | | `MyStruct` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `MyStruct` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/coherence_local_err_tuple.stderr b/tests/ui/coherence/coherence_local_err_tuple.stderr index 85a063bb34a..d07adab0014 100644 --- a/tests/ui/coherence/coherence_local_err_tuple.stderr +++ b/tests/ui/coherence/coherence_local_err_tuple.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl lib::MyCopy for (MyType,) { } | ^^^^^^^^^^^^^^^^^^^^^--------- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/impl-foreign-for-foreign.stderr b/tests/ui/coherence/impl-foreign-for-foreign.stderr index 6c74b47a1c4..4ff965290c8 100644 --- a/tests/ui/coherence/impl-foreign-for-foreign.stderr +++ b/tests/ui/coherence/impl-foreign-for-foreign.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Remote for i32 { | ^^^^^^^^^^^^^^^^--- - | | | - | | `i32` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `i32` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/impl-foreign-for-foreign[foreign].stderr b/tests/ui/coherence/impl-foreign-for-foreign[foreign].stderr index e24537bce22..ce5376f98cb 100644 --- a/tests/ui/coherence/impl-foreign-for-foreign[foreign].stderr +++ b/tests/ui/coherence/impl-foreign-for-foreign[foreign].stderr @@ -3,11 +3,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Remote1<Rc<i32>> for i32 { | ^^^^^----------------^^^^^--- - | | | | - | | | `i32` is not defined in the current crate - | | `Rc` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `i32` is not defined in the current crate + | `Rc` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for primitive types @@ -15,11 +16,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Remote1<Rc<Local>> for f64 { | ^^^^^------------------^^^^^--- - | | | | - | | | `f64` is not defined in the current crate - | | `Rc` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `f64` is not defined in the current crate + | `Rc` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for primitive types @@ -27,11 +29,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl<T> Remote1<Rc<T>> for f32 { | ^^^^^^^^--------------^^^^^--- - | | | | - | | | `f32` is not defined in the current crate - | | `Rc` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `f32` is not defined in the current crate + | `Rc` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 3 previous errors diff --git a/tests/ui/coherence/impl-foreign-for-fundamental[foreign].stderr b/tests/ui/coherence/impl-foreign-for-fundamental[foreign].stderr index 55ea4409e6f..596f8436567 100644 --- a/tests/ui/coherence/impl-foreign-for-fundamental[foreign].stderr +++ b/tests/ui/coherence/impl-foreign-for-fundamental[foreign].stderr @@ -3,11 +3,12 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl Remote for Box<i32> { | ^^^^^------^^^^^-------- - | | | | - | | | `i32` is not defined in the current crate - | | `std::alloc::Global` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `i32` is not defined in the current crate + | `std::alloc::Global` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate @@ -15,11 +16,12 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T> Remote for Box<Rc<T>> { | ^^^^^^^^------^^^^^---------- - | | | | - | | | `Rc` is not defined in the current crate - | | `std::alloc::Global` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `Rc` is not defined in the current crate + | `std::alloc::Global` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/impl-foreign[foreign]-for-foreign.stderr b/tests/ui/coherence/impl-foreign[foreign]-for-foreign.stderr index fe8a34b78cf..d9dd2b8a8c6 100644 --- a/tests/ui/coherence/impl-foreign[foreign]-for-foreign.stderr +++ b/tests/ui/coherence/impl-foreign[foreign]-for-foreign.stderr @@ -3,11 +3,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Remote1<u32> for f64 { | ^^^^^------------^^^^^--- - | | | | - | | | `f64` is not defined in the current crate - | | `u32` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `f64` is not defined in the current crate + | `u32` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr b/tests/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr index 8e77c13e111..91f1886142c 100644 --- a/tests/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr +++ b/tests/ui/coherence/impl-foreign[fundemental[foreign]]-for-foreign.stderr @@ -3,12 +3,13 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Remote1<Box<String>> for i32 { | ^^^^^--------------------^^^^^--- - | | | | - | | | `i32` is not defined in the current crate - | | `String` is not defined in the current crate - | | `std::alloc::Global` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `i32` is not defined in the current crate + | `String` is not defined in the current crate + | `std::alloc::Global` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for primitive types @@ -16,12 +17,13 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Remote1<Box<Rc<i32>>> for f64 { | ^^^^^---------------------^^^^^--- - | | | | - | | | `f64` is not defined in the current crate - | | `Rc` is not defined in the current crate - | | `std::alloc::Global` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `f64` is not defined in the current crate + | `Rc` is not defined in the current crate + | `std::alloc::Global` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for primitive types @@ -29,12 +31,13 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl<T> Remote1<Box<Rc<T>>> for f32 { | ^^^^^^^^-------------------^^^^^--- - | | | | - | | | `f32` is not defined in the current crate - | | `Rc` is not defined in the current crate - | | `std::alloc::Global` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `f32` is not defined in the current crate + | `Rc` is not defined in the current crate + | `std::alloc::Global` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 3 previous errors diff --git a/tests/ui/coherence/impl[t]-foreign-for-foreign[t].stderr b/tests/ui/coherence/impl[t]-foreign-for-foreign[t].stderr index 92346c29198..306a5d1610d 100644 --- a/tests/ui/coherence/impl[t]-foreign-for-foreign[t].stderr +++ b/tests/ui/coherence/impl[t]-foreign-for-foreign[t].stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl Remote for Rc<Local> { | ^^^^^^^^^^^^^^^^--------- - | | | - | | `Rc` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Rc` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate @@ -14,10 +15,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl<T> Remote for Arc<T> { | ^^^^^^^^^^^^^^^^^^^------ - | | | - | | `Arc` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Arc` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 2 previous errors diff --git a/tests/ui/command/command-exec.rs b/tests/ui/command/command-exec.rs index c97b8561410..d2545b0b472 100644 --- a/tests/ui/command/command-exec.rs +++ b/tests/ui/command/command-exec.rs @@ -26,23 +26,23 @@ fn main() { } "exec-test2" => { - Command::new("/path/to/nowhere").exec(); + let _ = Command::new("/path/to/nowhere").exec(); println!("passed"); } "exec-test3" => { - Command::new(&me).arg("bad\0").exec(); + let _ = Command::new(&me).arg("bad\0").exec(); println!("passed"); } "exec-test4" => { - Command::new(&me).current_dir("/path/to/nowhere").exec(); + let _ = Command::new(&me).current_dir("/path/to/nowhere").exec(); println!("passed"); } "exec-test5" => { env::set_var("VARIABLE", "ABC"); - Command::new("definitely-not-a-real-binary").env("VARIABLE", "XYZ").exec(); + let _ = Command::new("definitely-not-a-real-binary").env("VARIABLE", "XYZ").exec(); assert_eq!(env::var("VARIABLE").unwrap(), "ABC"); println!("passed"); } diff --git a/tests/ui/coroutine/type-mismatch-signature-deduction.stderr b/tests/ui/coroutine/type-mismatch-signature-deduction.stderr index 08927196037..64dc3a8b1f8 100644 --- a/tests/ui/coroutine/type-mismatch-signature-deduction.stderr +++ b/tests/ui/coroutine/type-mismatch-signature-deduction.stderr @@ -22,10 +22,10 @@ error[E0271]: type mismatch resolving `<{coroutine@$DIR/type-mismatch-signature- --> $DIR/type-mismatch-signature-deduction.rs:5:13 | LL | fn foo() -> impl Coroutine<Return = i32> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<{integer}, _>`, found `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `Result<{integer}, _>` | - = note: expected enum `Result<{integer}, _>` - found type `i32` + = note: expected type `i32` + found enum `Result<{integer}, _>` error: aborting due to 2 previous errors diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index a686b913c55..8c01b61191e 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -29,10 +29,7 @@ error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Tex --> $DIR/as_expression.rs:57:5 | LL | SelectInt.check("bar"); - | ^^^^^^^^^^^^^^^^^^^^^^ expected `Integer`, found `Text` - | - = note: expected struct `Integer` - found struct `Text` + | ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer` error: aborting due to 3 previous errors diff --git a/tests/ui/dropck/drop-on-non-struct.stderr b/tests/ui/dropck/drop-on-non-struct.stderr index e8fbe5e9726..9495642e45e 100644 --- a/tests/ui/dropck/drop-on-non-struct.stderr +++ b/tests/ui/dropck/drop-on-non-struct.stderr @@ -9,10 +9,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl<'a> Drop for &'a mut isize { | ^^^^^^^^^^^^^^^^^^------------- - | | | - | | `isize` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `isize` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions diff --git a/tests/ui/error-codes/E0117.stderr b/tests/ui/error-codes/E0117.stderr index f144aa9f72c..f6e80e59304 100644 --- a/tests/ui/error-codes/E0117.stderr +++ b/tests/ui/error-codes/E0117.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl Drop for u32 {} | ^^^^^^^^^^^^^^--- - | | | - | | `u32` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `u32` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions diff --git a/tests/ui/error-codes/e0119/complex-impl.stderr b/tests/ui/error-codes/e0119/complex-impl.stderr index c0519c60e42..b7e434c4afe 100644 --- a/tests/ui/error-codes/e0119/complex-impl.stderr +++ b/tests/ui/error-codes/e0119/complex-impl.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl<R> External for (Q, R) {} | ^^^^^^^^^^^^^^^^^^^^^------ - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/errors/issue-99572-impl-trait-on-pointer.rs b/tests/ui/errors/issue-99572-impl-trait-on-pointer.rs index 272c6bd3fb7..61f11a88c61 100644 --- a/tests/ui/errors/issue-99572-impl-trait-on-pointer.rs +++ b/tests/ui/errors/issue-99572-impl-trait-on-pointer.rs @@ -5,21 +5,23 @@ use std::{fmt, marker}; struct LocalType; impl fmt::Display for *mut LocalType { -//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types -//~| NOTE impl doesn't use only types from inside the current crate -//~| NOTE `*mut LocalType` is not defined in the current crate because raw pointers are always foreign -//~| NOTE define and implement a trait or new type instead -//~| HELP consider introducing a new wrapper type + //~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + //~| NOTE impl doesn't have any local type before any uncovered type parameters + //~| NOTE for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + //~| NOTE `*mut LocalType` is not defined in the current crate because raw pointers are always foreign + //~| NOTE define and implement a trait or new type instead + //~| HELP consider introducing a new wrapper type fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "This not compile") } } impl<T> marker::Copy for *mut T { -//~^ ERROR only traits defined in the current crate can be implemented for arbitrary types -//~| NOTE impl doesn't use only types from inside the current crate -//~| NOTE `*mut T` is not defined in the current crate because raw pointers are always foreign -//~| NOTE define and implement a trait or new type instead + //~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + //~| NOTE impl doesn't have any local type before any uncovered type parameters + //~| NOTE for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + //~| NOTE `*mut T` is not defined in the current crate because raw pointers are always foreign + //~| NOTE define and implement a trait or new type instead } fn main() {} diff --git a/tests/ui/errors/issue-99572-impl-trait-on-pointer.stderr b/tests/ui/errors/issue-99572-impl-trait-on-pointer.stderr index 78d7a47deaa..bd40b059e58 100644 --- a/tests/ui/errors/issue-99572-impl-trait-on-pointer.stderr +++ b/tests/ui/errors/issue-99572-impl-trait-on-pointer.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl fmt::Display for *mut LocalType { | ^^^^^^^^^^^^^^^^^^^^^^-------------- - | | | - | | `*mut LocalType` is not defined in the current crate because raw pointers are always foreign - | impl doesn't use only types from inside the current crate + | | + | `*mut LocalType` is not defined in the current crate because raw pointers are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead help: consider introducing a new wrapper type | @@ -16,14 +17,15 @@ LL ~ impl fmt::Display for WrapperType { | error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/issue-99572-impl-trait-on-pointer.rs:18:1 + --> $DIR/issue-99572-impl-trait-on-pointer.rs:19:1 | LL | impl<T> marker::Copy for *mut T { | ^^^^^^^^^^^^^^^^^^^^^^^^^------ - | | | - | | `*mut T` is not defined in the current crate because raw pointers are always foreign - | impl doesn't use only types from inside the current crate + | | + | `*mut T` is not defined in the current crate because raw pointers are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/bound-normalization-fail.stderr b/tests/ui/impl-trait/bound-normalization-fail.stderr index fcac9ac34db..fc124bd1171 100644 --- a/tests/ui/impl-trait/bound-normalization-fail.stderr +++ b/tests/ui/impl-trait/bound-normalization-fail.stderr @@ -7,13 +7,13 @@ LL | LL | Foo(()) | ------- return type was inferred to be `Foo<()>` here | -note: expected this to be `()` +note: expected this to be `<T as impl_trait::Trait>::Assoc` --> $DIR/bound-normalization-fail.rs:14:19 | LL | type Output = T; | ^ - = note: expected unit type `()` - found associated type `<T as impl_trait::Trait>::Assoc` + = note: expected associated type `<T as impl_trait::Trait>::Assoc` + found unit type `()` help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc` to `()` | LL | fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> { @@ -28,13 +28,13 @@ LL | LL | Foo(()) | ------- return type was inferred to be `Foo<()>` here | -note: expected this to be `()` +note: expected this to be `<T as lifetimes::Trait<'a>>::Assoc` --> $DIR/bound-normalization-fail.rs:14:19 | LL | type Output = T; | ^ - = note: expected unit type `()` - found associated type `<T as lifetimes::Trait<'a>>::Assoc` + = note: expected associated type `<T as lifetimes::Trait<'a>>::Assoc` + found unit type `()` help: consider constraining the associated type `<T as lifetimes::Trait<'a>>::Assoc` to `()` | LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output = T::Assoc> { diff --git a/tests/ui/impl-trait/in-trait/default-body-type-err.stderr b/tests/ui/impl-trait/in-trait/default-body-type-err.stderr index 6f1ac4bce43..3c737f095ce 100644 --- a/tests/ui/impl-trait/in-trait/default-body-type-err.stderr +++ b/tests/ui/impl-trait/in-trait/default-body-type-err.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `<&i32 as Deref>::Target == String` --> $DIR/default-body-type-err.rs:4:22 | LL | fn lol(&self) -> impl Deref<Target = String> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `String` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `String`, found `i32` LL | LL | &1i32 | ----- return type was inferred to be `&i32` here diff --git a/tests/ui/impl-trait/issues/issue-78722-2.stderr b/tests/ui/impl-trait/issues/issue-78722-2.stderr index 2cf6b94dd9d..27b4b712830 100644 --- a/tests/ui/impl-trait/issues/issue-78722-2.stderr +++ b/tests/ui/impl-trait/issues/issue-78722-2.stderr @@ -16,7 +16,7 @@ error[E0271]: expected `{async block@$DIR/issue-78722-2.rs:13:13: 13:18}` to be --> $DIR/issue-78722-2.rs:11:30 | LL | fn concrete_use() -> F { - | ^ expected `()`, found `u8` + | ^ expected `u8`, found `()` error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/issues/issue-78722.stderr b/tests/ui/impl-trait/issues/issue-78722.stderr index 3642000597f..109bda0c5cd 100644 --- a/tests/ui/impl-trait/issues/issue-78722.stderr +++ b/tests/ui/impl-trait/issues/issue-78722.stderr @@ -12,7 +12,7 @@ error[E0271]: expected `{async block@$DIR/issue-78722.rs:10:13: 10:18}` to be a --> $DIR/issue-78722.rs:8:30 | LL | fn concrete_use() -> F { - | ^ expected `()`, found `u8` + | ^ expected `u8`, found `()` error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/capturing-implicit.rs b/tests/ui/impl-trait/precise-capturing/capturing-implicit.rs new file mode 100644 index 00000000000..5ef8542d862 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capturing-implicit.rs @@ -0,0 +1,15 @@ +//@ edition: 2024 +//@ compile-flags: -Zunstable-options + +#![feature(rustc_attrs)] +#![feature(type_alias_impl_trait)] +#![rustc_variance_of_opaques] + +fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> { + //~^ ERROR ['_: o] + //~| ERROR ['_: o] + //~| ERROR `impl Trait` captures lifetime parameter + [*x] +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/capturing-implicit.stderr b/tests/ui/impl-trait/precise-capturing/capturing-implicit.stderr new file mode 100644 index 00000000000..b14ed20bd36 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capturing-implicit.stderr @@ -0,0 +1,22 @@ +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/capturing-implicit.rs:8:11 + | +LL | fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> { + | ^ -------------------------------------------- lifetime captured due to being mentioned in the bounds of the `impl Trait` + | | + | this lifetime parameter is captured + +error: ['_: o] + --> $DIR/capturing-implicit.rs:8:19 + | +LL | fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ['_: o] + --> $DIR/capturing-implicit.rs:8:44 + | +LL | fn foo(x: &()) -> impl IntoIterator<Item = impl Sized> + use<> { + | ^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr b/tests/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr index c4ea4474066..fa71adc6380 100644 --- a/tests/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr +++ b/tests/ui/impl-trait/projection-mismatch-in-impl-where-clause.stderr @@ -4,7 +4,7 @@ error[E0271]: type mismatch resolving `<() as Super>::Assoc == ()` LL | fn test() -> impl Test { | ^^^^^^^^^ type mismatch resolving `<() as Super>::Assoc == ()` | -note: expected this to be `u8` +note: expected this to be `()` --> $DIR/projection-mismatch-in-impl-where-clause.rs:6:18 | LL | type Assoc = u8; diff --git a/tests/ui/impl-trait/unsized_coercion5.next.stderr b/tests/ui/impl-trait/unsized_coercion5.next.stderr deleted file mode 100644 index 5644ac7ab04..00000000000 --- a/tests/ui/impl-trait/unsized_coercion5.next.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/unsized_coercion5.rs:16:32 - | -LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>; - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `Send`, found trait `Trait + Send` - | | - | expected due to this - | - = note: expected struct `Box<dyn Send>` - found struct `Box<dyn Trait + Send>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/unsized_coercion5.old.stderr b/tests/ui/impl-trait/unsized_coercion5.old.stderr index 06ad54b1f1d..e56c026b037 100644 --- a/tests/ui/impl-trait/unsized_coercion5.old.stderr +++ b/tests/ui/impl-trait/unsized_coercion5.old.stderr @@ -1,16 +1,5 @@ -error[E0308]: mismatched types - --> $DIR/unsized_coercion5.rs:16:32 - | -LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>; - | ------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `Send`, found trait `Trait + Send` - | | - | expected due to this - | - = note: expected struct `Box<dyn Send>` - found struct `Box<dyn Trait + Send>` - error[E0277]: the size for values of type `impl Trait + ?Sized` cannot be known at compilation time - --> $DIR/unsized_coercion5.rs:16:32 + --> $DIR/unsized_coercion5.rs:17:32 | LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>; | ^ doesn't have a size known at compile-time @@ -18,7 +7,6 @@ LL | let y: Box<dyn Send> = x as Box<dyn Trait + Send>; = help: the trait `Sized` is not implemented for `impl Trait + ?Sized` = note: required for the cast from `Box<impl Trait + ?Sized>` to `Box<dyn Trait + Send>` -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion5.rs b/tests/ui/impl-trait/unsized_coercion5.rs index 85d313caa13..81f8a6afe9a 100644 --- a/tests/ui/impl-trait/unsized_coercion5.rs +++ b/tests/ui/impl-trait/unsized_coercion5.rs @@ -3,6 +3,7 @@ //@ revisions: next old //@[next] compile-flags: -Znext-solver +//@[next] check-pass #![feature(trait_upcasting)] @@ -15,7 +16,6 @@ fn hello() -> Box<impl Trait + ?Sized> { let x = hello(); let y: Box<dyn Send> = x as Box<dyn Trait + Send>; //[old]~^ ERROR: the size for values of type `impl Trait + ?Sized` cannot be know - //~^^ ERROR: mismatched types } Box::new(1u32) } diff --git a/tests/ui/issues/issue-33941.stderr b/tests/ui/issues/issue-33941.stderr index f1b6b6ba17e..9535ea57430 100644 --- a/tests/ui/issues/issue-33941.stderr +++ b/tests/ui/issues/issue-33941.stderr @@ -20,10 +20,10 @@ error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but --> $DIR/issue-33941.rs:6:14 | LL | for _ in HashMap::new().iter().cloned() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(&_, &_)`, found `&_` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&_`, found `(&_, &_)` | - = note: expected tuple `(&_, &_)` - found reference `&_` + = note: expected reference `&_` + found tuple `(&_, &_)` = note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `Iterator` = note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `IntoIterator` diff --git a/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr b/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr index 1ea2d48b474..9164e4696eb 100644 --- a/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr +++ b/tests/ui/issues/issue-67039-unsound-pin-partialeq.stderr @@ -2,10 +2,10 @@ error[E0271]: type mismatch resolving `<Rc<Apple> as Deref>::Target == Rc<Apple> --> $DIR/issue-67039-unsound-pin-partialeq.rs:25:29 | LL | let _ = Pin::new(Apple) == Rc::pin(Apple); - | ^^ expected `Apple`, found `Rc<Apple>` + | ^^ expected `Rc<Apple>`, found `Apple` | - = note: expected struct `Apple` - found struct `Rc<Apple>` + = note: expected struct `Rc<Apple>` + found struct `Apple` = note: required for `Pin<Apple>` to implement `PartialEq<Pin<Rc<Apple>>>` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-67535.stderr b/tests/ui/issues/issue-67535.stderr index 4d7a02a5096..2afa2199a6a 100644 --- a/tests/ui/issues/issue-67535.stderr +++ b/tests/ui/issues/issue-67535.stderr @@ -3,11 +3,12 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl std::ops::AddAssign for () { | ^^^^^-------------------^^^^^-- - | | | | - | | | this is not defined in the current crate because tuples are always foreign - | | this is not defined in the current crate because this is a foreign trait - | impl doesn't use only types from inside the current crate + | | | + | | this is not defined in the current crate because tuples are always foreign + | this is not defined in the current crate because this is a foreign trait | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -15,11 +16,12 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl std::ops::AddAssign for [(); 1] { | ^^^^^-------------------^^^^^------- - | | | | - | | | this is not defined in the current crate because arrays are always foreign - | | this is not defined in the current crate because this is a foreign trait - | impl doesn't use only types from inside the current crate + | | | + | | this is not defined in the current crate because arrays are always foreign + | this is not defined in the current crate because this is a foreign trait | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -27,11 +29,12 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl std::ops::AddAssign for &[u8] { | ^^^^^-------------------^^^^^----- - | | | | - | | | this is not defined in the current crate because slices are always foreign - | | this is not defined in the current crate because this is a foreign trait - | impl doesn't use only types from inside the current crate + | | | + | | this is not defined in the current crate because slices are always foreign + | this is not defined in the current crate because this is a foreign trait | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 3 previous errors diff --git a/tests/crashes/126966.rs b/tests/ui/layout/thaw-transmute-invalid-enum.rs index 2c9f1a70f4f..835dcc04996 100644 --- a/tests/crashes/126966.rs +++ b/tests/ui/layout/thaw-transmute-invalid-enum.rs @@ -1,10 +1,14 @@ -//@ known-bug: rust-lang/rust#126966 +#![crate_type = "lib"] + mod assert { use std::mem::{Assume, TransmuteFrom}; + //~^ ERROR: use of unstable library feature 'transmutability' + //~| ERROR: use of unstable library feature 'transmutability' pub fn is_transmutable<Src, Dst>() where Dst: TransmuteFrom<Src>, + //~^ ERROR: use of unstable library feature 'transmutability' { } } @@ -15,6 +19,7 @@ enum Ox00 { } #[repr(C, packed(2))] +//~^ ERROR: attribute should be applied to a struct enum OxFF { V = 0xFF, } @@ -22,8 +27,10 @@ enum OxFF { fn test() { union Superset { a: Ox00, + //~^ ERROR: field must implement `Copy` b: OxFF, } assert::is_transmutable::<Superset, Subset>(); + //~^ ERROR: cannot find type `Subset` } diff --git a/tests/ui/layout/thaw-transmute-invalid-enum.stderr b/tests/ui/layout/thaw-transmute-invalid-enum.stderr new file mode 100644 index 00000000000..e6a5399c66b --- /dev/null +++ b/tests/ui/layout/thaw-transmute-invalid-enum.stderr @@ -0,0 +1,68 @@ +error[E0412]: cannot find type `Subset` in this scope + --> $DIR/thaw-transmute-invalid-enum.rs:34:41 + | +LL | assert::is_transmutable::<Superset, Subset>(); + | ^^^^^^ not found in this scope + | +help: you might be missing a type parameter + | +LL | fn test<Subset>() { + | ++++++++ + +error[E0517]: attribute should be applied to a struct or union + --> $DIR/thaw-transmute-invalid-enum.rs:21:11 + | +LL | #[repr(C, packed(2))] + | ^^^^^^^^^ +LL | +LL | / enum OxFF { +LL | | V = 0xFF, +LL | | } + | |_- not a struct or union + +error[E0658]: use of unstable library feature 'transmutability' + --> $DIR/thaw-transmute-invalid-enum.rs:4:20 + | +LL | use std::mem::{Assume, TransmuteFrom}; + | ^^^^^^ + | + = note: see issue #99571 <https://github.com/rust-lang/rust/issues/99571> for more information + = help: add `#![feature(transmutability)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'transmutability' + --> $DIR/thaw-transmute-invalid-enum.rs:4:28 + | +LL | use std::mem::{Assume, TransmuteFrom}; + | ^^^^^^^^^^^^^ + | + = note: see issue #99571 <https://github.com/rust-lang/rust/issues/99571> for more information + = help: add `#![feature(transmutability)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature 'transmutability' + --> $DIR/thaw-transmute-invalid-enum.rs:10:14 + | +LL | Dst: TransmuteFrom<Src>, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #99571 <https://github.com/rust-lang/rust/issues/99571> for more information + = help: add `#![feature(transmutability)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/thaw-transmute-invalid-enum.rs:29:9 + | +LL | a: Ox00, + | ^^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | a: std::mem::ManuallyDrop<Ox00>, + | +++++++++++++++++++++++ + + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0412, E0517, E0658, E0740. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/crashes/128870.rs b/tests/ui/layout/thaw-validate-invalid-enum.rs index 2b731962144..51aff7fb556 100644 --- a/tests/crashes/128870.rs +++ b/tests/ui/layout/thaw-validate-invalid-enum.rs @@ -1,7 +1,6 @@ -//@ known-bug: rust-lang/rust#128870 //@ compile-flags: -Zvalidate-mir -#[repr(packed)] +#[repr(packed)] //~ ERROR: attribute should be applied to a struct #[repr(u32)] enum E { A, @@ -12,7 +11,7 @@ enum E { fn main() { union InvalidTag { int: u32, - e: E, + e: E, //~ ERROR: field must implement `Copy` } let _invalid_tag = InvalidTag { int: 4 }; } diff --git a/tests/ui/layout/thaw-validate-invalid-enum.stderr b/tests/ui/layout/thaw-validate-invalid-enum.stderr new file mode 100644 index 00000000000..9e522cba96a --- /dev/null +++ b/tests/ui/layout/thaw-validate-invalid-enum.stderr @@ -0,0 +1,29 @@ +error[E0517]: attribute should be applied to a struct or union + --> $DIR/thaw-validate-invalid-enum.rs:3:8 + | +LL | #[repr(packed)] + | ^^^^^^ +LL | #[repr(u32)] +LL | / enum E { +LL | | A, +LL | | B, +LL | | C, +LL | | } + | |_- not a struct or union + +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/thaw-validate-invalid-enum.rs:14:9 + | +LL | e: E, + | ^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | e: std::mem::ManuallyDrop<E>, + | +++++++++++++++++++++++ + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0517, E0740. +For more information about an error, try `rustc --explain E0517`. diff --git a/tests/ui/lint/auxiliary/allow-macro.rs b/tests/ui/lint/auxiliary/allow-macro.rs new file mode 100644 index 00000000000..35980e2e6ac --- /dev/null +++ b/tests/ui/lint/auxiliary/allow-macro.rs @@ -0,0 +1,7 @@ +#[macro_export] +macro_rules! emit_allow { + () => { + #[allow(unsafe_code)] + let _so_safe = 0; + }; +} diff --git a/tests/ui/lint/auxiliary/deny-macro.rs b/tests/ui/lint/auxiliary/deny-macro.rs new file mode 100644 index 00000000000..6106cd0ef17 --- /dev/null +++ b/tests/ui/lint/auxiliary/deny-macro.rs @@ -0,0 +1,7 @@ +#[macro_export] +macro_rules! emit_deny { + () => { + #[deny(unsafe_code)] + let _so_safe = 0; + }; +} diff --git a/tests/ui/lint/auxiliary/forbid-macro.rs b/tests/ui/lint/auxiliary/forbid-macro.rs new file mode 100644 index 00000000000..aa74d0cf314 --- /dev/null +++ b/tests/ui/lint/auxiliary/forbid-macro.rs @@ -0,0 +1,7 @@ +#[macro_export] +macro_rules! emit_forbid { + () => { + #[forbid(unsafe_code)] + let _so_safe = 0; + }; +} diff --git a/tests/ui/lint/auxiliary/warn-macro.rs b/tests/ui/lint/auxiliary/warn-macro.rs new file mode 100644 index 00000000000..8216b65c74b --- /dev/null +++ b/tests/ui/lint/auxiliary/warn-macro.rs @@ -0,0 +1,7 @@ +#[macro_export] +macro_rules! emit_warn { + () => { + #[warn(unsafe_code)] + let _so_safe = 0; + }; +} diff --git a/tests/ui/lint/deny-inside-forbid-ignored.cli_forbid.stderr b/tests/ui/lint/deny-inside-forbid-ignored.cli_forbid.stderr new file mode 100644 index 00000000000..06086cbef8a --- /dev/null +++ b/tests/ui/lint/deny-inside-forbid-ignored.cli_forbid.stderr @@ -0,0 +1,35 @@ +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/deny-inside-forbid-ignored.rs:12:17 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ----------- `forbid` level set here +... +LL | #[allow(unsafe_code)] // let's have some unsafe code in here + | ^^^^^^^^^^^ overruled by previous forbid + +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/deny-inside-forbid-ignored.rs:12:17 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ----------- `forbid` level set here +... +LL | #[allow(unsafe_code)] // let's have some unsafe code in here + | ^^^^^^^^^^^ overruled by previous forbid + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: usage of an `unsafe` block + --> $DIR/deny-inside-forbid-ignored.rs:16:13 + | +LL | unsafe { /* ≽^•⩊•^≼ */ } + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deny-inside-forbid-ignored.rs:8:10 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0453`. diff --git a/tests/ui/lint/deny-inside-forbid-ignored.cli_forbid_warnings.stderr b/tests/ui/lint/deny-inside-forbid-ignored.cli_forbid_warnings.stderr new file mode 100644 index 00000000000..06086cbef8a --- /dev/null +++ b/tests/ui/lint/deny-inside-forbid-ignored.cli_forbid_warnings.stderr @@ -0,0 +1,35 @@ +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/deny-inside-forbid-ignored.rs:12:17 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ----------- `forbid` level set here +... +LL | #[allow(unsafe_code)] // let's have some unsafe code in here + | ^^^^^^^^^^^ overruled by previous forbid + +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/deny-inside-forbid-ignored.rs:12:17 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ----------- `forbid` level set here +... +LL | #[allow(unsafe_code)] // let's have some unsafe code in here + | ^^^^^^^^^^^ overruled by previous forbid + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: usage of an `unsafe` block + --> $DIR/deny-inside-forbid-ignored.rs:16:13 + | +LL | unsafe { /* ≽^•⩊•^≼ */ } + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deny-inside-forbid-ignored.rs:8:10 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0453`. diff --git a/tests/ui/lint/deny-inside-forbid-ignored.rs b/tests/ui/lint/deny-inside-forbid-ignored.rs new file mode 100644 index 00000000000..b14a3e94bb5 --- /dev/null +++ b/tests/ui/lint/deny-inside-forbid-ignored.rs @@ -0,0 +1,20 @@ +//! Ensure that using deny inside forbid is treated as a no-op, and does not override the level to +//! deny. + +//@ revisions: source_only cli_forbid cli_forbid_warnings +//@[cli_forbid] compile-flags: -F unsafe_code +//@[cli_forbid_warnings] compile-flags: -F warnings + +#[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! +fn main() { + #[deny(unsafe_code)] // m-m-maybe we can have unsafe code in here? + { + #[allow(unsafe_code)] // let's have some unsafe code in here + //~^ ERROR allow(unsafe_code) incompatible with previous forbid + //~| ERROR allow(unsafe_code) incompatible with previous forbid + { + unsafe { /* ≽^•⩊•^≼ */ } + //~^ ERROR usage of an `unsafe` block + } + } +} diff --git a/tests/ui/lint/deny-inside-forbid-ignored.source_only.stderr b/tests/ui/lint/deny-inside-forbid-ignored.source_only.stderr new file mode 100644 index 00000000000..06086cbef8a --- /dev/null +++ b/tests/ui/lint/deny-inside-forbid-ignored.source_only.stderr @@ -0,0 +1,35 @@ +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/deny-inside-forbid-ignored.rs:12:17 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ----------- `forbid` level set here +... +LL | #[allow(unsafe_code)] // let's have some unsafe code in here + | ^^^^^^^^^^^ overruled by previous forbid + +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/deny-inside-forbid-ignored.rs:12:17 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ----------- `forbid` level set here +... +LL | #[allow(unsafe_code)] // let's have some unsafe code in here + | ^^^^^^^^^^^ overruled by previous forbid + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: usage of an `unsafe` block + --> $DIR/deny-inside-forbid-ignored.rs:16:13 + | +LL | unsafe { /* ≽^•⩊•^≼ */ } + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deny-inside-forbid-ignored.rs:8:10 + | +LL | #[forbid(unsafe_code)] // NO UNSAFE CODE IN HERE!! + | ^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0453`. diff --git a/tests/ui/lint/forbid-macro-with-deny.allow.stderr b/tests/ui/lint/forbid-macro-with-deny.allow.stderr new file mode 100644 index 00000000000..77735c1c5d3 --- /dev/null +++ b/tests/ui/lint/forbid-macro-with-deny.allow.stderr @@ -0,0 +1,26 @@ +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/forbid-macro-with-deny.rs:39:5 + | +LL | #![forbid(unsafe_code)] + | ----------- `forbid` level set here +... +LL | allow_macro::emit_allow! {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ overruled by previous forbid + | + = note: this error originates in the macro `allow_macro::emit_allow` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0453]: allow(unsafe_code) incompatible with previous forbid + --> $DIR/forbid-macro-with-deny.rs:39:5 + | +LL | #![forbid(unsafe_code)] + | ----------- `forbid` level set here +... +LL | allow_macro::emit_allow! {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ overruled by previous forbid + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `allow_macro::emit_allow` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0453`. diff --git a/tests/ui/lint/forbid-macro-with-deny.rs b/tests/ui/lint/forbid-macro-with-deny.rs new file mode 100644 index 00000000000..85f67ea3631 --- /dev/null +++ b/tests/ui/lint/forbid-macro-with-deny.rs @@ -0,0 +1,45 @@ +//! Ensure that when a macro (or normal code) does `#[deny]` inside a `#[forbid]` context, no error +//! is emitted, as both parties agree on the treatment of the lint. +//! +//! However, still emit an error if the macro does `#[allow]` or `#[warn]`. + +//@ revisions: forbid deny warn allow +//@[forbid] aux-build:forbid-macro.rs +//@[deny] aux-build:deny-macro.rs +//@[warn] aux-build:warn-macro.rs +//@[allow] aux-build:allow-macro.rs + +//@[forbid] check-pass +//@[deny] check-pass + +#![forbid(unsafe_code)] + +#[cfg(allow)] +extern crate allow_macro; +#[cfg(deny)] +extern crate deny_macro; +#[cfg(forbid)] +extern crate forbid_macro; +#[cfg(warn)] +extern crate warn_macro; + +fn main() { + #[cfg(forbid)] + forbid_macro::emit_forbid! {} // OK + + #[cfg(deny)] + deny_macro::emit_deny! {} // OK + + #[cfg(warn)] + warn_macro::emit_warn! {} + //[warn]~^ ERROR warn(unsafe_code) incompatible with previous forbid + //[warn]~| ERROR warn(unsafe_code) incompatible with previous forbid + + #[cfg(allow)] + allow_macro::emit_allow! {} + //[allow]~^ ERROR allow(unsafe_code) incompatible with previous forbid + //[allow]~| ERROR allow(unsafe_code) incompatible with previous forbid + + #[deny(unsafe_code)] // OK + let _ = 0; +} diff --git a/tests/ui/lint/forbid-macro-with-deny.warn.stderr b/tests/ui/lint/forbid-macro-with-deny.warn.stderr new file mode 100644 index 00000000000..10452ebd1b6 --- /dev/null +++ b/tests/ui/lint/forbid-macro-with-deny.warn.stderr @@ -0,0 +1,26 @@ +error[E0453]: warn(unsafe_code) incompatible with previous forbid + --> $DIR/forbid-macro-with-deny.rs:34:5 + | +LL | #![forbid(unsafe_code)] + | ----------- `forbid` level set here +... +LL | warn_macro::emit_warn! {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ overruled by previous forbid + | + = note: this error originates in the macro `warn_macro::emit_warn` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0453]: warn(unsafe_code) incompatible with previous forbid + --> $DIR/forbid-macro-with-deny.rs:34:5 + | +LL | #![forbid(unsafe_code)] + | ----------- `forbid` level set here +... +LL | warn_macro::emit_warn! {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ overruled by previous forbid + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `warn_macro::emit_warn` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0453`. diff --git a/tests/ui/lint/issue-106991.stderr b/tests/ui/lint/issue-106991.stderr index 4704e9ef835..9b4fab68102 100644 --- a/tests/ui/lint/issue-106991.stderr +++ b/tests/ui/lint/issue-106991.stderr @@ -2,7 +2,7 @@ error[E0271]: expected `foo` to be a fn item that returns `i32`, but it returns --> $DIR/issue-106991.rs:5:13 | LL | fn bar() -> impl Iterator<Item = i32> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()` | = note: required for `Map<std::slice::IterMut<'_, Vec<u8>>, for<'a> fn(&'a mut Vec<u8>) {foo}>` to implement `Iterator` diff --git a/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs b/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs index 37d96129317..45b78d75b27 100644 --- a/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs +++ b/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs @@ -19,9 +19,9 @@ fn forbid_first(num: i32) -> i32 { #![forbid(unused)] #![deny(unused)] - //~^ ERROR: deny(unused) incompatible with previous forbid - //~| WARNING being phased out #![warn(unused)] + //~^ ERROR: warn(unused) incompatible with previous forbid + //~| WARNING being phased out #![allow(unused)] num * num diff --git a/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr b/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr index f78bf899b84..407eaf1c60a 100644 --- a/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr +++ b/tests/ui/lint/issue-70819-dont-override-forbid-in-same-scope.stderr @@ -1,9 +1,10 @@ -error: deny(unused) incompatible with previous forbid - --> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:21:13 +error: warn(unused) incompatible with previous forbid + --> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:22:13 | LL | #![forbid(unused)] | ------ `forbid` level set here LL | #![deny(unused)] +LL | #![warn(unused)] | ^^^^^^ overruled by previous forbid | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/tests/ui/lint/issue-80988.rs b/tests/ui/lint/issue-80988.rs deleted file mode 100644 index 80decd8e736..00000000000 --- a/tests/ui/lint/issue-80988.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Regression test for #80988 -// -//@ check-pass - -#![forbid(warnings)] - -#[deny(warnings)] -//~^ WARNING incompatible with previous forbid -//~| WARNING being phased out -fn main() {} diff --git a/tests/ui/lint/issue-80988.stderr b/tests/ui/lint/issue-80988.stderr deleted file mode 100644 index afc93fcfeef..00000000000 --- a/tests/ui/lint/issue-80988.stderr +++ /dev/null @@ -1,15 +0,0 @@ -warning: deny(warnings) incompatible with previous forbid - --> $DIR/issue-80988.rs:7:8 - | -LL | #![forbid(warnings)] - | -------- `forbid` level set here -LL | -LL | #[deny(warnings)] - | ^^^^^^^^ overruled by previous forbid - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #81670 <https://github.com/rust-lang/rust/issues/81670> - = note: `#[warn(forbidden_lint_groups)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/panics/panic-in-ffi.rs b/tests/ui/panics/panic-in-ffi.rs index 88f45f9a871..c0ae1899f4c 100644 --- a/tests/ui/panics/panic-in-ffi.rs +++ b/tests/ui/panics/panic-in-ffi.rs @@ -2,13 +2,22 @@ //@ exec-env:RUST_BACKTRACE=0 //@ check-run-results //@ error-pattern: panic in a function that cannot unwind +//@ error-pattern: Noisy Drop //@ normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@ normalize-stderr-test: "\n +at [^\n]+" -> "" //@ normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" //@ needs-unwind //@ ignore-emscripten "RuntimeError" junk in output +struct Noise; +impl Drop for Noise { + fn drop(&mut self) { + eprintln!("Noisy Drop"); + } +} + extern "C" fn panic_in_ffi() { + let _val = Noise; panic!("Test"); } diff --git a/tests/ui/panics/panic-in-ffi.run.stderr b/tests/ui/panics/panic-in-ffi.run.stderr index fc70847ad9a..58f5187f0da 100644 --- a/tests/ui/panics/panic-in-ffi.run.stderr +++ b/tests/ui/panics/panic-in-ffi.run.stderr @@ -1,6 +1,7 @@ -thread 'main' panicked at $DIR/panic-in-ffi.rs:12:5: +thread 'main' panicked at $DIR/panic-in-ffi.rs:21:5: Test note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +Noisy Drop thread 'main' panicked at core/src/panicking.rs:$LINE:$COL: panic in a function that cannot unwind stack backtrace: diff --git a/tests/ui/precondition-checks/layout.rs b/tests/ui/precondition-checks/layout.rs index 4fd1bbc4a99..4ee66cc9328 100644 --- a/tests/ui/precondition-checks/layout.rs +++ b/tests/ui/precondition-checks/layout.rs @@ -2,8 +2,6 @@ //@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes //@ error-pattern: unsafe precondition(s) violated: Layout::from_size_align_unchecked requires //@ revisions: toolarge badalign -//@[toolarge] compile-flags: --cfg toolarge -//@[badalign] compile-flags: --cfg badalign fn main() { unsafe { diff --git a/tests/ui/range/misleading-field-access-hint.rs b/tests/ui/range/misleading-field-access-hint.rs new file mode 100644 index 00000000000..252f1a4833c --- /dev/null +++ b/tests/ui/range/misleading-field-access-hint.rs @@ -0,0 +1,8 @@ +// Check if rustc still displays the misleading hint to write `.` instead of `..` +fn main() { + let width = 10; + // ... + for _ in 0..w { + //~^ ERROR cannot find value `w` + } +} diff --git a/tests/ui/range/misleading-field-access-hint.stderr b/tests/ui/range/misleading-field-access-hint.stderr new file mode 100644 index 00000000000..9b112a5fdd8 --- /dev/null +++ b/tests/ui/range/misleading-field-access-hint.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find value `w` in this scope + --> $DIR/misleading-field-access-hint.rs:5:17 + | +LL | for _ in 0..w { + | ^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs index d6251fcb768..4dc5932feab 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs @@ -27,3 +27,14 @@ pub enum NonExhaustiveVariants { #[non_exhaustive] Tuple(u32), #[non_exhaustive] Struct { field: u32 } } + +// Note the absence of repr(C): it's not necessary, and recent C code can now use repr hints too. +#[repr(u32)] +#[non_exhaustive] +pub enum NonExhaustiveCLikeEnum { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, +} diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs index 7a9b465bb56..c7f470fb787 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs @@ -6,7 +6,10 @@ extern crate types; // This test checks that non-exhaustive types with `#[repr(C)]` from an extern crate are considered // improper. -use types::{NonExhaustiveEnum, NonExhaustiveVariants, NormalStruct, TupleStruct, UnitStruct}; +use types::{ + NonExhaustiveCLikeEnum, NonExhaustiveEnum, NonExhaustiveVariants, + NormalStruct, TupleStruct, UnitStruct, +}; extern "C" { pub fn non_exhaustive_enum(_: NonExhaustiveEnum); @@ -21,4 +24,9 @@ extern "C" { //~^ ERROR `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe } +// These should pass without remark, as they're C-compatible, despite being "non-exhaustive". +extern "C" { + pub fn non_exhaustive_c_compat_enum(_: NonExhaustiveCLikeEnum); +} + fn main() {} diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr index 43c8e1015e6..afc3d3838ad 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `NonExhaustiveEnum`, which is not FFI-safe - --> $DIR/extern_crate_improper.rs:12:35 + --> $DIR/extern_crate_improper.rs:15:35 | LL | pub fn non_exhaustive_enum(_: NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -12,7 +12,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `NormalStruct`, which is not FFI-safe - --> $DIR/extern_crate_improper.rs:14:44 + --> $DIR/extern_crate_improper.rs:17:44 | LL | pub fn non_exhaustive_normal_struct(_: NormalStruct); | ^^^^^^^^^^^^ not FFI-safe @@ -20,7 +20,7 @@ LL | pub fn non_exhaustive_normal_struct(_: NormalStruct); = note: this struct is non-exhaustive error: `extern` block uses type `UnitStruct`, which is not FFI-safe - --> $DIR/extern_crate_improper.rs:16:42 + --> $DIR/extern_crate_improper.rs:19:42 | LL | pub fn non_exhaustive_unit_struct(_: UnitStruct); | ^^^^^^^^^^ not FFI-safe @@ -28,7 +28,7 @@ LL | pub fn non_exhaustive_unit_struct(_: UnitStruct); = note: this struct is non-exhaustive error: `extern` block uses type `TupleStruct`, which is not FFI-safe - --> $DIR/extern_crate_improper.rs:18:43 + --> $DIR/extern_crate_improper.rs:21:43 | LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct); | ^^^^^^^^^^^ not FFI-safe @@ -36,7 +36,7 @@ LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct); = note: this struct is non-exhaustive error: `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe - --> $DIR/extern_crate_improper.rs:20:38 + --> $DIR/extern_crate_improper.rs:23:38 | LL | pub fn non_exhaustive_variant(_: NonExhaustiveVariants); | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr index 9c1c8df8da4..cf7af41cd4e 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr @@ -30,11 +30,12 @@ error[E0117]: only traits defined in the current crate can be implemented for pr | LL | impl const std::ops::Add for i32 { | ^^^^^^^^^^^-------------^^^^^--- - | | | | - | | | `i32` is not defined in the current crate - | | `i32` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | | + | | `i32` is not defined in the current crate + | `i32` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 4 previous errors diff --git a/tests/ui/sanitizer/cfg.rs b/tests/ui/sanitizer/cfg.rs index b1ba17d5713..7b8f285e41a 100644 --- a/tests/ui/sanitizer/cfg.rs +++ b/tests/ui/sanitizer/cfg.rs @@ -5,19 +5,19 @@ //@ revisions: address cfi kcfi leak memory thread //@compile-flags: -Ctarget-feature=-crt-static //@[address]needs-sanitizer-address -//@[address]compile-flags: -Zsanitizer=address --cfg address +//@[address]compile-flags: -Zsanitizer=address //@[cfi]needs-sanitizer-cfi -//@[cfi]compile-flags: -Zsanitizer=cfi --cfg cfi +//@[cfi]compile-flags: -Zsanitizer=cfi //@[cfi]compile-flags: -Clto -Ccodegen-units=1 //@[kcfi]needs-llvm-components: x86 -//@[kcfi]compile-flags: -Zsanitizer=kcfi --cfg kcfi --target x86_64-unknown-none +//@[kcfi]compile-flags: -Zsanitizer=kcfi --target x86_64-unknown-none //@[kcfi]compile-flags: -C panic=abort //@[leak]needs-sanitizer-leak -//@[leak]compile-flags: -Zsanitizer=leak --cfg leak +//@[leak]compile-flags: -Zsanitizer=leak //@[memory]needs-sanitizer-memory -//@[memory]compile-flags: -Zsanitizer=memory --cfg memory +//@[memory]compile-flags: -Zsanitizer=memory //@[thread]needs-sanitizer-thread -//@[thread]compile-flags: -Zsanitizer=thread --cfg thread +//@[thread]compile-flags: -Zsanitizer=thread #![feature(cfg_sanitize, no_core, lang_items)] #![crate_type="lib"] diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed index 02d667d9844..b7b94a05121 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.no_std.fixed @@ -4,7 +4,7 @@ // For some reason, Rust 2018 or higher is required to reproduce the bug. //@ run-rustfix //@ revisions: no_std std -//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +//@ [no_std]compile-flags: -C panic=abort #![cfg_attr(no_std, no_std)] use core::num::NonZero; diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.rs b/tests/ui/suggestions/core-std-import-order-issue-83564.rs index 5bb5bfe176b..4cfc9a6bf74 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.rs +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.rs @@ -4,7 +4,7 @@ // For some reason, Rust 2018 or higher is required to reproduce the bug. //@ run-rustfix //@ revisions: no_std std -//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +//@ [no_std]compile-flags: -C panic=abort #![cfg_attr(no_std, no_std)] fn main() { diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed b/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed index 3492b42c685..84c7c19d19e 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.std.fixed @@ -4,7 +4,7 @@ // For some reason, Rust 2018 or higher is required to reproduce the bug. //@ run-rustfix //@ revisions: no_std std -//@ [no_std]compile-flags: --cfg=no_std -C panic=abort +//@ [no_std]compile-flags: -C panic=abort #![cfg_attr(no_std, no_std)] use std::num::NonZero; diff --git a/tests/ui/suggestions/dont_suggest_raw_pointer_syntax-issue-127562.rs b/tests/ui/suggestions/dont_suggest_raw_pointer_syntax-issue-127562.rs new file mode 100644 index 00000000000..03b736e60b6 --- /dev/null +++ b/tests/ui/suggestions/dont_suggest_raw_pointer_syntax-issue-127562.rs @@ -0,0 +1,7 @@ +fn main() { + let val = 2; + let ptr = std::ptr::addr_of!(val); + unsafe { + *ptr = 3; //~ ERROR cannot assign to `*ptr`, which is behind a `*const` pointer + } +} diff --git a/tests/ui/suggestions/dont_suggest_raw_pointer_syntax-issue-127562.stderr b/tests/ui/suggestions/dont_suggest_raw_pointer_syntax-issue-127562.stderr new file mode 100644 index 00000000000..5396db7940f --- /dev/null +++ b/tests/ui/suggestions/dont_suggest_raw_pointer_syntax-issue-127562.stderr @@ -0,0 +1,9 @@ +error[E0594]: cannot assign to `*ptr`, which is behind a `*const` pointer + --> $DIR/dont_suggest_raw_pointer_syntax-issue-127562.rs:5:9 + | +LL | *ptr = 3; + | ^^^^^^^^ `ptr` is a `*const` pointer, so the data it refers to cannot be written + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0594`. diff --git a/tests/ui/suggestions/trait-hidden-method.stderr b/tests/ui/suggestions/trait-hidden-method.stderr index 5dec2071846..729523cde55 100644 --- a/tests/ui/suggestions/trait-hidden-method.stderr +++ b/tests/ui/suggestions/trait-hidden-method.stderr @@ -8,14 +8,14 @@ error[E0271]: expected `Box<dyn Iterator>` to be an iterator that yields `u32`, --> $DIR/trait-hidden-method.rs:3:32 | LL | pub fn i_can_has_iterator() -> impl Iterator<Item = u32> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found `u32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found associated type ... LL | Box::new(1..=10) as Box<dyn Iterator> | ------------------------------------- return type was inferred to be `Box<dyn Iterator>` here | - = note: expected associated type `<dyn Iterator as Iterator>::Item` - found type `u32` - = help: consider constraining the associated type `<dyn Iterator as Iterator>::Item` to `u32` or calling a method that returns `<dyn Iterator as Iterator>::Item` + = note: expected type `u32` + found associated type `<dyn Iterator as Iterator>::Item` + = help: consider constraining the associated type `<dyn Iterator as Iterator>::Item` to `u32` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/tests/ui/traits/dyn-drop-principal.rs b/tests/ui/traits/dyn-drop-principal.rs new file mode 100644 index 00000000000..c233127e43d --- /dev/null +++ b/tests/ui/traits/dyn-drop-principal.rs @@ -0,0 +1,68 @@ +//@ run-pass +//@ check-run-results + +use std::{alloc::Layout, any::Any}; + +const fn yeet_principal(x: Box<dyn Any + Send>) -> Box<dyn Send> { + x +} + +trait Bar: Send + Sync {} + +impl<T: Send + Sync> Bar for T {} + +const fn yeet_principal_2(x: Box<dyn Bar>) -> Box<dyn Send> { + x +} + +struct CallMe<F: FnOnce()>(Option<F>); + +impl<F: FnOnce()> CallMe<F> { + fn new(f: F) -> Self { + CallMe(Some(f)) + } +} + +impl<F: FnOnce()> Drop for CallMe<F> { + fn drop(&mut self) { + (self.0.take().unwrap())(); + } +} + +fn goodbye() { + println!("goodbye"); +} + +fn main() { + let x = Box::new(CallMe::new(goodbye)) as Box<dyn Any + Send>; + let x_layout = Layout::for_value(&*x); + let y = yeet_principal(x); + let y_layout = Layout::for_value(&*y); + assert_eq!(x_layout, y_layout); + println!("before"); + drop(y); + + let x = Box::new(CallMe::new(goodbye)) as Box<dyn Bar>; + let x_layout = Layout::for_value(&*x); + let y = yeet_principal_2(x); + let y_layout = Layout::for_value(&*y); + assert_eq!(x_layout, y_layout); + println!("before"); + drop(y); +} + +// Test that upcast works in `const` + +const fn yeet_principal_3(x: &(dyn Any + Send + Sync)) -> &(dyn Send + Sync) { + x +} + +#[used] +pub static FOO: &(dyn Send + Sync) = yeet_principal_3(&false); + +const fn yeet_principal_4(x: &dyn Bar) -> &(dyn Send + Sync) { + x +} + +#[used] +pub static BAR: &(dyn Send + Sync) = yeet_principal_4(&false); diff --git a/tests/ui/traits/dyn-drop-principal.run.stdout b/tests/ui/traits/dyn-drop-principal.run.stdout new file mode 100644 index 00000000000..edd99a114a1 --- /dev/null +++ b/tests/ui/traits/dyn-drop-principal.run.stdout @@ -0,0 +1,4 @@ +before +goodbye +before +goodbye diff --git a/tests/ui/traits/dyn-star-drop-principal.rs b/tests/ui/traits/dyn-star-drop-principal.rs new file mode 100644 index 00000000000..1ad99070339 --- /dev/null +++ b/tests/ui/traits/dyn-star-drop-principal.rs @@ -0,0 +1,12 @@ +#![feature(dyn_star)] +#![allow(incomplete_features)] + +trait Trait {} +impl Trait for usize {} + +fn main() { + // We allow &dyn Trait + Send -> &dyn Send (i.e. dropping principal), + // but we don't (currently?) allow the same for dyn* + let x: dyn* Trait + Send = 1usize; + x as dyn* Send; //~ error: `dyn* Trait + Send` needs to have the same ABI as a pointer +} diff --git a/tests/ui/traits/dyn-star-drop-principal.stderr b/tests/ui/traits/dyn-star-drop-principal.stderr new file mode 100644 index 00000000000..721ae7e191e --- /dev/null +++ b/tests/ui/traits/dyn-star-drop-principal.stderr @@ -0,0 +1,11 @@ +error[E0277]: `dyn* Trait + Send` needs to have the same ABI as a pointer + --> $DIR/dyn-star-drop-principal.rs:11:5 + | +LL | x as dyn* Send; + | ^ `dyn* Trait + Send` needs to be a pointer-like type + | + = help: the trait `PointerLike` is not implemented for `dyn* Trait + Send` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/fully-qualified-syntax-cast.rs b/tests/ui/traits/fully-qualified-syntax-cast.rs new file mode 100644 index 00000000000..740220a074b --- /dev/null +++ b/tests/ui/traits/fully-qualified-syntax-cast.rs @@ -0,0 +1,15 @@ +// Regression test for #98565: Provide diagnostics when the user uses +// the built-in type `str` in a cast where a trait is expected. + +trait Foo { + fn foo(&self); +} + +impl Foo for String { + fn foo(&self) { + <Self as str>::trim(self); + //~^ ERROR expected trait, found builtin type `str` + } +} + +fn main() {} diff --git a/tests/ui/traits/fully-qualified-syntax-cast.stderr b/tests/ui/traits/fully-qualified-syntax-cast.stderr new file mode 100644 index 00000000000..1848c9184c6 --- /dev/null +++ b/tests/ui/traits/fully-qualified-syntax-cast.stderr @@ -0,0 +1,9 @@ +error[E0404]: expected trait, found builtin type `str` + --> $DIR/fully-qualified-syntax-cast.rs:10:18 + | +LL | <Self as str>::trim(self); + | ^^^ not a trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0404`. diff --git a/tests/ui/traits/next-solver/async.fail.stderr b/tests/ui/traits/next-solver/async.fail.stderr index e47da338736..bc89842d16a 100644 --- a/tests/ui/traits/next-solver/async.fail.stderr +++ b/tests/ui/traits/next-solver/async.fail.stderr @@ -2,12 +2,10 @@ error[E0271]: expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future --> $DIR/async.rs:12:17 | LL | needs_async(async {}); - | ----------- ^^^^^^^^ expected `()`, found `i32` + | ----------- ^^^^^^^^ expected `i32`, found `()` | | | required by a bound introduced by this call | - = note: expected unit type `()` - found type `i32` note: required by a bound in `needs_async` --> $DIR/async.rs:8:31 | diff --git a/tests/ui/traits/next-solver/more-object-bound.stderr b/tests/ui/traits/next-solver/more-object-bound.stderr index 043cbdff9ab..39849d4c865 100644 --- a/tests/ui/traits/next-solver/more-object-bound.stderr +++ b/tests/ui/traits/next-solver/more-object-bound.stderr @@ -2,19 +2,14 @@ error[E0271]: type mismatch resolving `<dyn Trait<A = A, B = B> as SuperTrait>:: --> $DIR/more-object-bound.rs:12:5 | LL | fn transmute<A, B>(x: A) -> B { - | - - - | | | - | | expected type parameter - | | found type parameter + | - - expected type parameter + | | | found type parameter - | expected type parameter LL | foo::<A, B, dyn Trait<A = A, B = B>>(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `A`, found type parameter `B` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `B`, found type parameter `A` | - = note: expected type parameter `A` - found type parameter `B` - = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: expected type parameter `B` + found type parameter `A` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters = note: required because it appears within the type `dyn Trait<A = A, B = B>` diff --git a/tests/ui/traits/object/print_vtable_sizes.stdout b/tests/ui/traits/object/print_vtable_sizes.stdout index b43e168df3f..4daf4769576 100644 --- a/tests/ui/traits/object/print_vtable_sizes.stdout +++ b/tests/ui/traits/object/print_vtable_sizes.stdout @@ -1,8 +1,8 @@ -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "E", "entries": "6", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "2", "upcasting_cost_percent": "50" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "G", "entries": "14", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "3", "upcasting_cost_percent": "27.27272727272727" } print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" } +print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "G", "entries": "13", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "2", "upcasting_cost_percent": "18.181818181818183" } print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "D", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } +print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "E", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } diff --git a/tests/ui/traits/upcast_reorder.rs b/tests/ui/traits/upcast_reorder.rs new file mode 100644 index 00000000000..55e6ad4c368 --- /dev/null +++ b/tests/ui/traits/upcast_reorder.rs @@ -0,0 +1,29 @@ +//@ run-pass +// +// issue: <https://github.com/rust-lang/rust/issues/131813> + +#![feature(trait_upcasting)] + +trait Pollable { + #[allow(unused)] + fn poll(&self) {} +} +trait FileIo: Pollable + Send + Sync { + fn read(&self) {} +} +trait Terminal: Send + Sync + FileIo {} + +struct A; + +impl Pollable for A {} +impl FileIo for A {} +impl Terminal for A {} + +fn main() { + let a = A; + + let b = &a as &dyn Terminal; + let c = b as &dyn FileIo; + + c.read(); +} diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr index 4497c703ae8..36ac8b24eb5 100644 --- a/tests/ui/traits/vtable/multiple-markers.stderr +++ b/tests/ui/traits/vtable/multiple-markers.stderr @@ -14,7 +14,6 @@ error: vtable entries for `<S as B>`: [ MetadataSize, MetadataAlign, Method(<S as T>::method), - TraitVPtr(<S as M2>), ] --> $DIR/multiple-markers.rs:24:1 | @@ -26,8 +25,6 @@ error: vtable entries for `<S as C>`: [ MetadataSize, MetadataAlign, Method(<S as T>::method), - TraitVPtr(<S as M1>), - TraitVPtr(<S as M2>), ] --> $DIR/multiple-markers.rs:27:1 | @@ -39,9 +36,6 @@ error: vtable entries for `<S as D>`: [ MetadataSize, MetadataAlign, Method(<S as T>::method), - TraitVPtr(<S as M0>), - TraitVPtr(<S as M1>), - TraitVPtr(<S as M2>), ] --> $DIR/multiple-markers.rs:30:1 | diff --git a/tests/ui/try-block/try-block-bad-type.stderr b/tests/ui/try-block/try-block-bad-type.stderr index 6c41b42dc64..e9e6255cd2a 100644 --- a/tests/ui/try-block/try-block-bad-type.stderr +++ b/tests/ui/try-block/try-block-bad-type.stderr @@ -15,13 +15,13 @@ error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Output == &str --> $DIR/try-block-bad-type.rs:12:9 | LL | "" - | ^^ expected `i32`, found `&str` + | ^^ expected `&str`, found `i32` error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Output == ()` --> $DIR/try-block-bad-type.rs:15:39 | LL | let res: Result<i32, i32> = try { }; - | ^ expected `i32`, found `()` + | ^ expected `()`, found `i32` error[E0277]: a `try` block must return `Result` or `Option` (or another type that implements `Try`) --> $DIR/try-block-bad-type.rs:17:25 diff --git a/tests/ui/try-block/try-block-type-error.stderr b/tests/ui/try-block/try-block-type-error.stderr index 2cdb5fdee79..07b1209de9d 100644 --- a/tests/ui/try-block/try-block-type-error.stderr +++ b/tests/ui/try-block/try-block-type-error.stderr @@ -2,18 +2,13 @@ error[E0271]: type mismatch resolving `<Option<f32> as Try>::Output == {integer} --> $DIR/try-block-type-error.rs:10:9 | LL | 42 - | ^^ expected `f32`, found integer - | -help: use a float literal - | -LL | 42.0 - | ++ + | ^^ expected integer, found `f32` error[E0271]: type mismatch resolving `<Option<i32> as Try>::Output == ()` --> $DIR/try-block-type-error.rs:16:5 | LL | }; - | ^ expected `i32`, found `()` + | ^ expected `()`, found `i32` error: aborting due to 2 previous errors diff --git a/tests/ui/type-alias-impl-trait/coherence.classic.stderr b/tests/ui/type-alias-impl-trait/coherence.classic.stderr index ff059bc5806..98badeef382 100644 --- a/tests/ui/type-alias-impl-trait/coherence.classic.stderr +++ b/tests/ui/type-alias-impl-trait/coherence.classic.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- - | | | - | | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate - | impl doesn't use only types from inside the current crate + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/coherence.next.stderr b/tests/ui/type-alias-impl-trait/coherence.next.stderr index dab2786c1f0..8d718383110 100644 --- a/tests/ui/type-alias-impl-trait/coherence.next.stderr +++ b/tests/ui/type-alias-impl-trait/coherence.next.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl foreign_crate::ForeignTrait for AliasOfForeignType<()> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------- - | | | - | | `AliasOfForeignType<()>` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `AliasOfForeignType<()>` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr b/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr index 21d9ed93366..b05121a489e 100644 --- a/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr +++ b/tests/ui/type-alias-impl-trait/hidden_type_mismatch.stderr @@ -2,18 +2,18 @@ error[E0271]: type mismatch resolving `<() as Proj>::Assoc == i32` --> $DIR/hidden_type_mismatch.rs:43:9 | LL | pub type Sep = impl Sized + std::fmt::Display; - | ------------------------------ the expected opaque type + | ------------------------------ the found opaque type ... LL | Bar { inner: 1i32, _marker: () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<() as Proj>::Assoc == i32` | -note: expected this to be `Sep` +note: expected this to be `i32` --> $DIR/hidden_type_mismatch.rs:20:22 | LL | type Assoc = sus::Sep; | ^^^^^^^^ - = note: expected opaque type `Sep` - found type `i32` + = note: expected type `i32` + found opaque type `Sep` note: required for `Bar<()>` to implement `Copy` --> $DIR/hidden_type_mismatch.rs:32:39 | diff --git a/tests/ui/type-alias-impl-trait/issue-94429.stderr b/tests/ui/type-alias-impl-trait/issue-94429.stderr index 4c2020becbe..f41b781f963 100644 --- a/tests/ui/type-alias-impl-trait/issue-94429.stderr +++ b/tests/ui/type-alias-impl-trait/issue-94429.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving `<{coroutine@$DIR/issue-94429.rs:18:9: 18: --> $DIR/issue-94429.rs:15:26 | LL | fn run(&mut self) -> Self::Coro { - | ^^^^^^^^^^ expected integer, found `()` + | ^^^^^^^^^^ expected `()`, found integer error: aborting due to 1 previous error diff --git a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr index 41f42455bde..df56db031ed 100644 --- a/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr +++ b/tests/ui/type/pattern_types/range_patterns_trait_impls2.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl Eq for Y {} | ^^^^^^^^^^^^- - | | | - | | `(u32) is 1..=` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `(u32) is 1..=` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 1 previous error diff --git a/tests/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr b/tests/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr index 32e6e88fc48..c7d714dcb1a 100644 --- a/tests/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr +++ b/tests/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr @@ -3,10 +3,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl DefaultedTrait for (A,) {} | ^^^^^^^^^^^^^^^^^^^^^^^^---- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types @@ -14,10 +15,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ar | LL | impl !DefaultedTrait for (B,) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---- - | | | - | | this is not defined in the current crate because tuples are always foreign - | impl doesn't use only types from inside the current crate + | | + | this is not defined in the current crate because tuples are always foreign | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `DefaultedTrait`, can only be implemented for a struct/enum type defined in the current crate @@ -31,10 +33,11 @@ error[E0117]: only traits defined in the current crate can be implemented for ty | LL | impl DefaultedTrait for lib::Something<C> {} | ^^^^^^^^^^^^^^^^^^^^^^^^----------------- - | | | - | | `Something` is not defined in the current crate - | impl doesn't use only types from inside the current crate + | | + | `Something` is not defined in the current crate | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules = note: define and implement a trait or new type instead error: aborting due to 4 previous errors diff --git a/triagebot.toml b/triagebot.toml index 33dcbfa55a4..74caa036ae6 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -679,6 +679,15 @@ instead. """ cc = ["@calebzulawski", "@programmerjake"] +[mentions."library/core/src/unicode/unicode_data.rs"] +message = """ +`library/core/src/unicode/unicode_data.rs` is generated by +`src/tools/unicode-table-generator` via `./x run +src/tools/unicode-table-generator`. If you want to modify `unicode_data.rs`, +please modify the tool then regenerate the library source file with the tool +instead of editing the library source file manually. +""" + [mentions."src/librustdoc/clean/types.rs"] cc = ["@camelid"] |
