diff options
653 files changed, 11192 insertions, 6180 deletions
diff --git a/.gitignore b/.gitignore index b16fb6341c2..37972532b36 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ no_llvm_build /dist/ /unicode-downloads /target +/src/bootstrap/target /src/tools/x/target # Created by default with `src/ci/docker/run.sh` /obj/ diff --git a/Cargo.lock b/Cargo.lock index 002d73be7d1..f83678f0ca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -727,6 +727,7 @@ version = "0.1.65" dependencies = [ "arrayvec", "if_chain", + "itertools", "rustc-semver", ] @@ -3207,6 +3208,7 @@ dependencies = [ "rustc_serialize", "rustc_span", "smallvec", + "thin-vec", "tracing", ] @@ -3228,6 +3230,7 @@ dependencies = [ "rustc_span", "rustc_target", "smallvec", + "thin-vec", "tracing", ] @@ -3322,6 +3325,7 @@ dependencies = [ "rustc_span", "rustc_target", "smallvec", + "thin-vec", "tracing", ] @@ -3446,6 +3450,7 @@ dependencies = [ "stable_deref_trait", "stacker", "tempfile", + "thin-vec", "tracing", "winapi", ] @@ -3828,6 +3833,7 @@ dependencies = [ "rustc_target", "rustc_type_ir", "smallvec", + "thin-vec", "tracing", ] @@ -4017,6 +4023,7 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", + "thin-vec", "tracing", ] @@ -4039,6 +4046,7 @@ dependencies = [ "rustc_span", "rustc_target", "smallvec", + "thin-vec", "tracing", ] @@ -4094,6 +4102,7 @@ dependencies = [ "indexmap", "rustc_macros", "smallvec", + "thin-vec", ] [[package]] @@ -4330,6 +4339,7 @@ dependencies = [ "serde_json", "smallvec", "tempfile", + "thin-vec", "tracing", "tracing-subscriber", "tracing-tree", @@ -4884,6 +4894,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] +name = "thin-vec" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104c2cb3180b6fb6d5b2278768e9b88b578d32ba751ea6e8d026688a40d7ed87" + +[[package]] name = "thiserror" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/RELEASES.md b/RELEASES.md index 147ff3561a3..72b2c16a01f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1442,7 +1442,7 @@ Compatibility Notes - [Mixing Option and Result via `?` is no longer permitted in closures for inferred types.][86831] - [Previously unsound code is no longer permitted where different constructors in branches could require different lifetimes.][85574] -- As previously mentioned the [`std::arch` instrinsics now uses stricter const checking][83278] +- As previously mentioned the [`std::arch` intrinsics now uses stricter const checking][83278] than before and may reject some previously accepted code. - [`i128` multiplication on Cortex M0+ platforms currently unconditionally causes overflow when compiled with `codegen-units = 1`.][86063] @@ -2520,7 +2520,7 @@ Compatibility Notes - [Fixed a regression parsing `{} && false` in tail expressions.][74650] - [Added changes to how proc-macros are expanded in `macro_rules!` that should help to preserve more span information.][73084] These changes may cause - compiliation errors if your macro was unhygenic or didn't correctly handle + compilation errors if your macro was unhygenic or didn't correctly handle `Delimiter::None`. - [Moved support for the CloudABI target to tier 3.][75568] - [`linux-gnu` targets now require minimum kernel 2.6.32 and glibc 2.11.][74163] diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index 9822e9864e2..c24180bacfc 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -7,12 +7,13 @@ edition = "2021" doctest = false [dependencies] -rustc_serialize = { path = "../rustc_serialize" } -tracing = "0.1" -rustc_span = { path = "../rustc_span" } +bitflags = "1.2.1" rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -bitflags = "1.2.1" +thin-vec = "0.2.8" +tracing = "0.1" diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fb521073a42..3af6dee9e43 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -25,21 +25,19 @@ pub use UnsafeSource::*; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream}; - use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; -use rustc_data_structures::thin_vec::ThinVec; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; - use std::cmp::Ordering; use std::convert::TryFrom; use std::fmt; use std::mem; +use thin_vec::ThinVec; /// A "Label" is an identifier of some point in sources, /// e.g. in the following code: diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 0947a71b824..79f5820230e 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -279,6 +279,7 @@ macro_rules! impl_has_attrs { impl HasAttrs for $T { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = $inner; + #[inline] fn attrs(&self) -> &[Attribute] { &self.attrs } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 27061f300a2..e5435e3a3d4 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -26,6 +26,9 @@ #[macro_use] extern crate rustc_macros; +#[macro_use] +extern crate tracing; + pub mod util { pub mod classify; pub mod comments; diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 9e4a22e1fa3..b6684c0669b 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -426,7 +426,8 @@ impl TokenStream { let attr_annotated = if attrs.is_empty() { tokens.create_token_stream() } else { - let attr_data = AttributesData { attrs: attrs.to_vec().into(), tokens: tokens.clone() }; + let attr_data = + AttributesData { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() }; AttrAnnotatedTokenStream::new(vec![( AttrAnnotatedTokenTree::Attributes(attr_data), Spacing::Alone, @@ -555,7 +556,7 @@ impl TokenStreamBuilder { // Get the first stream, which will become the result stream. // If it's `None`, create an empty stream. - let mut iter = streams.drain(..); + let mut iter = streams.into_iter(); let mut res_stream_lrc = iter.next().unwrap().0; // Append the subsequent elements to the result stream, after diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 69a78d165ef..6a1578498e6 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -9,7 +9,6 @@ use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; use std::ascii; -use tracing::debug; pub enum LitError { NotLiteral, diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 474aff2e2aa..ce1c8d4997d 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -8,17 +8,18 @@ doctest = false [dependencies] rustc_arena = { path = "../rustc_arena" } -tracing = "0.1" +rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_hir = { path = "../rustc_hir" } -rustc_target = { path = "../rustc_target" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_middle = { path = "../rustc_middle" } rustc_macros = { path = "../rustc_macros" } rustc_query_system = { path = "../rustc_query_system" } -rustc_span = { path = "../rustc_span" } -rustc_errors = { path = "../rustc_errors" } rustc_session = { path = "../rustc_session" } -rustc_ast = { path = "../rustc_ast" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" +tracing = "0.1" diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 17604761688..7df3520422c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -7,7 +7,6 @@ use super::errors::{ use super::ResolverAstLoweringExt; use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs}; use crate::{FnDeclKind, ImplTraitPosition}; - use rustc_ast::attr; use rustc_ast::ptr::P as AstP; use rustc_ast::*; @@ -18,6 +17,7 @@ use rustc_hir::definitions::DefPathData; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident}; use rustc_span::DUMMY_SP; +use thin_vec::thin_vec; impl<'hir> LoweringContext<'_, 'hir> { fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] { @@ -1541,7 +1541,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; attr::mk_attr_outer(allow) }; - let attrs: AttrVec = vec![attr].into(); + let attrs: AttrVec = thin_vec![attr]; // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,` let continue_arm = { diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 2b7431f0990..219e1b81d1e 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -11,8 +11,6 @@ use rustc_session::Session; use rustc_span::source_map::SourceMap; use rustc_span::{Span, DUMMY_SP}; -use tracing::debug; - /// A visitor that walks over the HIR and collects `Node`s into a HIR map. pub(super) struct NodeCollector<'a, 'hir> { /// Source map @@ -31,7 +29,7 @@ pub(super) struct NodeCollector<'a, 'hir> { definitions: &'a definitions::Definitions, } -#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))] +#[instrument(level = "debug", skip(sess, definitions, bodies))] pub(super) fn index_hir<'hir>( sess: &Session, definitions: &definitions::Definitions, @@ -67,7 +65,7 @@ pub(super) fn index_hir<'hir>( } impl<'a, 'hir> NodeCollector<'a, 'hir> { - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) { debug_assert_eq!(self.owner, hir_id.owner); debug_assert_ne!(hir_id.local_id.as_u32(), 0); @@ -142,7 +140,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_item(&mut self, i: &'hir Item<'hir>) { debug_assert_eq!(i.def_id, self.owner); self.with_parent(i.hir_id(), |this| { @@ -156,7 +154,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) { debug_assert_eq!(fi.def_id, self.owner); self.with_parent(fi.hir_id(), |this| { @@ -175,7 +173,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) { debug_assert_eq!(ti.def_id, self.owner); self.with_parent(ti.hir_id(), |this| { @@ -183,7 +181,7 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) { debug_assert_eq!(ii.def_id, self.owner); self.with_parent(ii.hir_id(), |this| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 30a87740c7f..9a960356a85 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -220,7 +220,7 @@ impl ResolverAstLoweringExt for ResolverAstLowering { /// Panics if no map has been pushed. /// Remapping is used when creating lowering `-> impl Trait` return /// types to create the resulting opaque type. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn record_def_id_remap(&mut self, from: LocalDefId, to: LocalDefId) { self.generics_def_id_map.last_mut().expect("no map pushed").insert(from, to); } @@ -771,7 +771,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// Converts a lifetime into a new generic parameter. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn lifetime_res_to_generic_param( &mut self, ident: Ident, @@ -815,7 +815,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// name resolver owing to lifetime elision; this also populates the resolver's node-id->def-id /// map, so that later calls to `opt_node_id_to_def_id` that refer to these extra lifetime /// parameters will be successful. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] #[inline] fn lower_lifetime_binder( &mut self, @@ -1179,7 +1179,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::Ty<'hir> { // Check whether we should interpret this as a bare trait object. // This check mirrors the one in late resolution. We only introduce this special case in - // the rare occurence we need to lower `Fresh` anonymous lifetimes. + // the rare occurrence we need to lower `Fresh` anonymous lifetimes. // The other cases when a qpath should be opportunistically made a trait object are handled // by `ty_path`. if qself.is_none() @@ -1385,7 +1385,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// added explicitly in the HIR). But this includes all the lifetimes, and we only want to /// capture the lifetimes that are referenced in the bounds. Therefore, we add *extra* lifetime parameters /// for the lifetimes that get captured (`'x`, in our example above) and reference those. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn lower_opaque_impl_trait( &mut self, span: Span, @@ -1621,7 +1621,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the // return type. This is used for `async fn` declarations. The `NodeId` is the ID of the // return type `impl Trait` item. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn lower_fn_decl( &mut self, decl: &FnDecl, @@ -1730,7 +1730,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // `output`: unlowered output type (`T` in `-> T`) // `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition) // `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn lower_async_fn_ret_ty( &mut self, output: &FnRetTy, @@ -2013,7 +2013,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.new_named_lifetime(l.id, l.id, span, ident) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn new_named_lifetime_with_res( &mut self, id: NodeId, @@ -2044,7 +2044,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn new_named_lifetime( &mut self, id: NodeId, @@ -2132,7 +2132,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn lower_poly_trait_ref( &mut self, p: &PolyTraitRef, diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 5874d08a94f..897c7215805 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -13,7 +13,6 @@ use rustc_span::symbol::{kw, Ident}; use rustc_span::{BytePos, Span, DUMMY_SP}; use smallvec::smallvec; -use tracing::debug; impl<'a, 'hir> LoweringContext<'a, 'hir> { #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c36c4ad54da..23464bf0a5a 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1800,7 +1800,7 @@ pub(crate) enum ForbiddenLetReason { NotSupportedOr(Span), /// A let chain with invalid parentheses /// - /// For exemple, `let 1 = 1 && (expr && expr)` is allowed + /// For example, `let 1 = 1 && (expr && expr)` is allowed /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not NotSupportedParentheses(Span), } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 6f7e88eb86f..ca5b7a64155 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -11,8 +11,6 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; -use tracing::debug; - macro_rules! gate_feature_fn { ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $help: expr) => {{ let (visitor, has_feature, span, name, explain, help) = diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index f282ff251bd..af25982e288 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -12,6 +12,9 @@ #![feature(let_else)] #![recursion_limit = "256"] +#[macro_use] +extern crate tracing; + pub mod ast_validation; mod errors; pub mod feature_gate; diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index d359d7efb62..35c3df76899 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -6,7 +6,6 @@ use rustc_errors::Diagnostic; use rustc_middle::ty::RegionVid; use smallvec::SmallVec; use std::collections::BTreeMap; -use tracing::debug; use crate::MirBorrowckCtxt; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index a87e8bd5ba1..75fde53b6cd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -265,7 +265,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// *user* has a name for. In that case, we'll be able to map /// `fr` to a `Region<'tcx>`, and that region will be one of /// named variants. - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> { let error_region = self.to_error_region(fr)?; @@ -373,7 +373,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// | fn foo(x: &u32) { .. } /// ------- fully elaborated type of `x` is `&'1 u32` /// ``` - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_arguments( &self, fr: RegionVid, @@ -662,7 +662,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// | let x = Some(&22); /// - fully elaborated type of `x` is `Option<&'1 u32>` /// ``` - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> { let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?; let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( @@ -682,7 +682,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// must be a closure since, in a free fn, such an argument would /// have to either also appear in an argument (if using elision) /// or be early bound (named, not in argument). - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> { let tcx = self.infcx.tcx; let hir = tcx.hir(); @@ -814,7 +814,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } } - #[tracing::instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self))] fn give_name_if_anonymous_region_appears_in_yield_ty( &self, fr: RegionVid, diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f5bd5cd3bea..8dc9368a0b9 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1139,7 +1139,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// include the CFG anyhow. /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub(crate) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { debug!(r = %self.region_value_str(r)); @@ -1151,8 +1151,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { lub = self.universal_region_relations.postdom_upper_bound(lub, ur); } - debug!(?lub); - lub } @@ -1333,15 +1331,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Evaluate whether `sup_region: sub_region`. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { debug!( - "eval_outlives: sup_region's value = {:?} universal={:?}", + "sup_region's value = {:?} universal={:?}", self.region_value_str(sup_region), self.universal_regions.is_universal_region(sup_region), ); debug!( - "eval_outlives: sub_region's value = {:?} universal={:?}", + "sub_region's value = {:?} universal={:?}", self.region_value_str(sub_region), self.universal_regions.is_universal_region(sub_region), ); @@ -1354,7 +1352,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // true if `'sup` outlives static. if !self.universe_compatible(sub_region_scc, sup_region_scc) { debug!( - "eval_outlives: sub universe `{sub_region_scc:?}` is not nameable \ + "sub universe `{sub_region_scc:?}` is not nameable \ by super `{sup_region_scc:?}`, promoting to static", ); @@ -1375,9 +1373,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }); if !universal_outlives { - debug!( - "eval_outlives: returning false because sub region contains a universal region not present in super" - ); + debug!("sub region contains a universal region not present in super"); return false; } @@ -1386,15 +1382,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { if self.universal_regions.is_universal_region(sup_region) { // Micro-opt: universal regions contain all points. - debug!( - "eval_outlives: returning true because super is universal and hence contains all points" - ); + debug!("super is universal and hence contains all points"); return true; } - let result = self.scc_values.contains_points(sup_region_scc, sub_region_scc); - debug!("returning {} because of comparison between points in sup/sub", result); - result + debug!("comparison between points in sup/sub"); + + self.scc_values.contains_points(sup_region_scc, sub_region_scc) } /// Once regions have been propagated, this method is used to see @@ -1971,7 +1965,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Finds some region R such that `fr1: R` and `R` is live at `elem`. - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { trace!(scc = ?self.constraint_sccs.scc(fr1)); trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 127cb4e4083..0392367288c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -60,7 +60,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Calling `universal_upper_bound` for such a region gives `fr_fn_body`, /// which has no `external_name` in which case we use `'empty` as the /// region to pass to `infer_opaque_definition_from_instantiation`. - #[instrument(level = "debug", skip(self, infcx))] + #[instrument(level = "debug", skip(self, infcx), ret)] pub(crate) fn infer_opaque_types( &self, infcx: &InferCtxt<'_, 'tcx>, diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 2a7713bc4df..b9b181681ec 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -768,10 +768,9 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { mir_def_id: LocalDefId, indices: &mut UniversalRegionIndices<'tcx>, ) { - debug!("replace_late_bound_regions_with_nll_infer_vars(mir_def_id={:?})", mir_def_id); let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id()); for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| { - debug!("replace_late_bound_regions_with_nll_infer_vars: r={:?}", r); + debug!(?r); if !indices.indices.contains_key(&r) { let region_vid = self.next_nll_region_var(FR); debug!(?region_vid); diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index 8d8e9d9b5ff..6469d0d7b88 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -7,20 +7,21 @@ edition = "2021" doctest = false [dependencies] -rustc_parse_format = { path = "../rustc_parse_format" } -tracing = "0.1" +rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } +rustc_parse_format = { path = "../rustc_parse_format" } rustc_parse = { path = "../rustc_parse" } -rustc_target = { path = "../rustc_target" } rustc_session = { path = "../rustc_session" } -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -rustc_ast = { path = "../rustc_ast" } -rustc_expand = { path = "../rustc_expand" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" +tracing = "0.1" diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index d2ee4249989..973a8cb85c2 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -13,6 +13,7 @@ use rustc_span::{ symbol::{sym, Ident, Symbol}, Span, }; +use thin_vec::thin_vec; pub(super) struct Context<'cx, 'a> { // An optimization. @@ -116,11 +117,10 @@ impl<'cx, 'a> Context<'cx, 'a> { self.cx.item( self.span, Ident::empty(), - vec![self.cx.attribute(attr::mk_list_item( + thin_vec![self.cx.attribute(attr::mk_list_item( Ident::new(sym::allow, self.span), vec![attr::mk_nested_word_item(Ident::new(sym::unused_imports, self.span))], - ))] - .into(), + ))], ItemKind::Use(UseTree { prefix: self.cx.path(self.span, self.cx.std_path(&[sym::asserting])), kind: UseTreeKind::Nested(vec![ diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index dd7989cf48c..c7f2d95e72f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -1,12 +1,12 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; - use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; +use thin_vec::thin_vec; pub fn expand_deriving_clone( cx: &mut ExtCtxt<'_>, @@ -68,7 +68,7 @@ pub fn expand_deriving_clone( } let inline = cx.meta_word(span, sym::inline); - let attrs = vec![cx.attribute(inline)].into(); + let attrs = thin_vec![cx.attribute(inline)]; let trait_def = TraitDef { span, path: path_std!(clone::Clone), diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 9b6d3e5032f..5b556c5c9b9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -7,6 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; +use thin_vec::thin_vec; pub fn expand_deriving_eq( cx: &mut ExtCtxt<'_>, @@ -20,7 +21,7 @@ pub fn expand_deriving_eq( let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span)); let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]); let no_coverage = cx.meta_word(span, sym::no_coverage); - let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)].into(); + let attrs = thin_vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)]; let trait_def = TraitDef { span, path: path_std!(cmp::Eq), diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 0e17b951787..72625869558 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -1,11 +1,11 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; - use rustc_ast::MetaItem; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; +use thin_vec::thin_vec; pub fn expand_deriving_ord( cx: &mut ExtCtxt<'_>, @@ -15,7 +15,7 @@ pub fn expand_deriving_ord( push: &mut dyn FnMut(Annotatable), ) { let inline = cx.meta_word(span, sym::inline); - let attrs = vec![cx.attribute(inline)].into(); + let attrs = thin_vec![cx.attribute(inline)]; let trait_def = TraitDef { span, path: path_std!(cmp::Ord), diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index ac1325b92a6..42ee65b570a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -1,12 +1,12 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::{path_local, path_std}; - use rustc_ast::ptr::P; use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; +use thin_vec::thin_vec; pub fn expand_deriving_partial_eq( cx: &mut ExtCtxt<'_>, @@ -68,7 +68,7 @@ pub fn expand_deriving_partial_eq( // No need to generate `ne`, the default suffices, and not generating it is // faster. let inline = cx.meta_word(span, sym::inline); - let attrs = vec![cx.attribute(inline)].into(); + let attrs = thin_vec![cx.attribute(inline)]; let methods = vec![MethodDef { name: sym::eq, generics: Bounds::empty(), diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 7763e554017..516892aeda9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,11 +1,11 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::{path_std, pathvec_std}; - use rustc_ast::MetaItem; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; +use thin_vec::thin_vec; pub fn expand_deriving_partial_ord( cx: &mut ExtCtxt<'_>, @@ -19,7 +19,7 @@ pub fn expand_deriving_partial_ord( Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std)); let inline = cx.meta_word(span, sym::inline); - let attrs = vec![cx.attribute(inline)].into(); + let attrs = thin_vec![cx.attribute(inline)]; let partial_cmp_def = MethodDef { name: sym::partial_cmp, diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index f316f01ef66..a94c8a996e6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -1,6 +1,5 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; - use rustc_ast as ast; use rustc_ast::{walk_list, EnumDef, VariantData}; use rustc_errors::Applicability; @@ -9,6 +8,7 @@ use rustc_span::symbol::Ident; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use smallvec::SmallVec; +use thin_vec::thin_vec; pub fn expand_deriving_default( cx: &mut ExtCtxt<'_>, @@ -20,7 +20,7 @@ pub fn expand_deriving_default( item.visit_with(&mut DetectNonVariantDefaultAttr { cx }); let inline = cx.meta_word(span, sym::inline); - let attrs = vec![cx.attribute(inline)].into(); + let attrs = thin_vec![cx.attribute(inline)]; let trait_def = TraitDef { span, path: Path::new(vec![kw::Default, sym::Default]), diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index ecaafd0fc26..adffebd3fd2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -162,10 +162,7 @@ pub use StaticFields::*; pub use SubstructureFields::*; -use std::cell::RefCell; -use std::iter; -use std::vec; - +use crate::deriving; use rustc_ast::ptr::P; use rustc_ast::{self as ast, EnumDef, Expr, Generics, PatKind}; use rustc_ast::{GenericArg, GenericParamKind, VariantData}; @@ -173,11 +170,12 @@ use rustc_attr as attr; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; - +use std::cell::RefCell; +use std::iter; +use std::vec; +use thin_vec::thin_vec; use ty::{Bounds, Path, Ref, Self_, Ty}; -use crate::deriving; - pub mod ty; pub struct TraitDef<'a> { @@ -715,7 +713,7 @@ impl<'a> TraitDef<'a> { let self_type = cx.ty_path(path); let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived)); - let attrs = vec![attr].into(); + let attrs = thin_vec![attr]; let opt_trait_ref = Some(trait_ref); cx.item( diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 2bad9bbce66..45b9b8ab6b6 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -9,6 +9,7 @@ use rustc_ast::{Fn, ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; +use thin_vec::thin_vec; pub fn expand( ecx: &mut ExtCtxt<'_>, @@ -116,7 +117,7 @@ impl AllocFnFactory<'_, '_> { fn attrs(&self) -> AttrVec { let special = sym::rustc_std_internal_symbol; let special = self.cx.meta_word(self.span, special); - vec![self.cx.attribute(special)].into() + thin_vec![self.cx.attribute(special)] } fn arg_ty( diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 11565ba72d7..280fa704511 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -16,6 +16,9 @@ extern crate proc_macro; +#[macro_use] +extern crate tracing; + use crate::deriving::*; use rustc_expand::base::{MacroExpanderFn, ResolverExpand, SyntaxExtensionKind}; diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index 90ea1e457ba..49ef538f04e 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -6,6 +6,7 @@ use rustc_span::edition::Edition::*; use rustc_span::hygiene::AstPass; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::DUMMY_SP; +use thin_vec::thin_vec; pub fn inject( mut krate: ast::Crate, @@ -51,7 +52,7 @@ pub fn inject( cx.item( span, ident, - vec![cx.attribute(cx.meta_word(span, sym::macro_use))].into(), + thin_vec![cx.attribute(cx.meta_word(span, sym::macro_use))], ast::ItemKind::ExternCrate(None), ), ); @@ -78,7 +79,7 @@ pub fn inject( let use_item = cx.item( span, Ident::empty(), - vec![cx.attribute(cx.meta_word(span, sym::prelude_import))].into(), + thin_vec![cx.attribute(cx.meta_word(span, sym::prelude_import))], ast::ItemKind::Use(ast::UseTree { prefix: cx.path(span, import_path), kind: ast::UseTreeKind::Glob, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 03c84f5ec2a..7efb6cc61ee 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -1,7 +1,6 @@ /// The expansion from a test function to the appropriate test struct for libtest /// Ideally, this code would be in libtest but for efficiency and error messages it lives here. use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; - use rustc_ast as ast; use rustc_ast::attr; use rustc_ast::ptr::P; @@ -11,8 +10,8 @@ use rustc_expand::base::*; use rustc_session::Session; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; - use std::iter; +use thin_vec::thin_vec; // #[test_case] is used by custom test authors to mark tests // When building for test, it needs to make the item public and gensym the name @@ -219,7 +218,7 @@ pub fn expand_test_or_bench( let mut test_const = cx.item( sp, Ident::new(item.ident.name, sp), - vec![ + thin_vec![ // #[cfg(test)] cx.attribute(attr::mk_list_item( Ident::new(sym::cfg, attr_sp), @@ -227,8 +226,7 @@ pub fn expand_test_or_bench( )), // #[rustc_test_marker] cx.attribute(cx.meta_word(attr_sp, sym::rustc_test_marker)), - ] - .into(), + ], // const $ident: test::TestDescAndFn = ast::ItemKind::Const( ast::Defaultness::Final, @@ -337,7 +335,7 @@ pub fn expand_test_or_bench( // extern crate test let test_extern = cx.item(sp, test_id, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None)); - tracing::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const)); + debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const)); if is_stmt { vec![ diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 093f0f10a38..079c6ff37cf 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -14,7 +14,7 @@ use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::PanicStrategy; use smallvec::{smallvec, SmallVec}; -use tracing::debug; +use thin_vec::thin_vec; use std::{iter, mem}; @@ -335,7 +335,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> { let main = P(ast::Item { ident: main_id, - attrs: vec![main_attr].into(), + attrs: thin_vec![main_attr], id: ast::DUMMY_NODE_ID, kind: main, vis: ast::Visibility { span: sp, kind: ast::VisibilityKind::Public, tokens: None }, diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 815450f689e..0497c2570e6 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -342,7 +342,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let ret_place = codegen_place(fx, destination); - // Handle special calls like instrinsics and empty drop glue. + // Handle special calls like intrinsics and empty drop glue. let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs) .unwrap() diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index cb5d73a7e0b..9224f499339 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -59,7 +59,7 @@ pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool { ErrorHandled::TooGeneric => { span_bug!( constant.span, - "codgen encountered polymorphic constant: {:?}", + "codegen encountered polymorphic constant: {:?}", err ); } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 95239f415a9..39e9e784a47 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -203,7 +203,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( sym::transmute => { crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", source_info); } - _ => unimplemented!("unsupported instrinsic {}", intrinsic), + _ => unimplemented!("unsupported intrinsic {}", intrinsic), } return; }; diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 2a6612eb86f..5202ac697e9 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -19,7 +19,6 @@ use rustc_target::asm::*; use libc::{c_char, c_uint}; use smallvec::SmallVec; -use tracing::debug; impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_inline_asm( diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2e614e5dd88..38a366095b4 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -190,10 +190,10 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { let output_path_z = rustc_fs_util::path_to_c_string(&output_path); - tracing::trace!("invoking LLVMRustWriteImportLibrary"); - tracing::trace!(" dll_name {:#?}", dll_name_z); - tracing::trace!(" output_path {}", output_path.display()); - tracing::trace!( + trace!("invoking LLVMRustWriteImportLibrary"); + trace!(" dll_name {:#?}", dll_name_z); + trace!(" output_path {}", output_path.display()); + trace!( " import names: {}", dll_imports .iter() diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index e4af6269abc..a89df00e248 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -18,7 +18,6 @@ use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{self, CrateType, Lto}; -use tracing::{debug, info}; use std::ffi::{CStr, CString}; use std::fs::File; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 740a68d0772..a695df8409b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -28,7 +28,6 @@ use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::InnerSpan; use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo}; -use tracing::debug; use libc::{c_char, c_int, c_uint, c_void, size_t}; use std::ffi::CString; diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index e7e373bf45d..63b63c6a1fa 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -27,7 +27,6 @@ use std::ffi::CStr; use std::iter; use std::ops::Deref; use std::ptr; -use tracing::{debug, instrument}; // All Builders must have an llfn associated with them #[must_use] diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index d55f995b933..b83c1e8f08f 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -11,7 +11,6 @@ use crate::context::CodegenCx; use crate::llvm; use crate::value::Value; use rustc_codegen_ssa::traits::*; -use tracing::debug; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeVisitable}; diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 63d3bb40a3f..13e437cfbf7 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -21,7 +21,6 @@ use rustc_target::spec::Target; use libc::{c_char, c_uint}; use std::fmt::Write; -use tracing::debug; /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index d3e33da2799..a559f7f3d57 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -23,7 +23,6 @@ use rustc_target::abi::{ AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange, }; use std::ops::Range; -use tracing::debug; pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value { let alloc = alloc.inner(); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 58f391692c4..0d1df6fb1ac 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -16,8 +16,6 @@ use rustc_middle::ty::TyCtxt; use std::ffi::CString; -use tracing::debug; - /// Generates and exports the Coverage Map. /// /// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 98ba38356a4..964a632b6ee 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -28,7 +28,6 @@ use std::cell::RefCell; use std::ffi::CString; use std::iter; -use tracing::debug; pub mod mapgen; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index d0a6f216858..163ccd9460c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -42,7 +42,6 @@ use rustc_span::{self, FileNameDisplayPreference, SourceFile}; use rustc_symbol_mangling::typeid_for_trait_ref; use rustc_target::abi::{Align, Size}; use smallvec::smallvec; -use tracing::debug; use libc::{c_char, c_longlong, c_uint}; use std::borrow::Cow; @@ -51,7 +50,6 @@ use std::hash::{Hash, Hasher}; use std::iter; use std::path::{Path, PathBuf}; use std::ptr; -use tracing::instrument; impl PartialEq for llvm::Metadata { fn eq(&self, other: &Self) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index cf591295b84..b23fe3fc9d5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -39,7 +39,6 @@ use smallvec::SmallVec; use std::cell::OnceCell; use std::cell::RefCell; use std::iter; -use tracing::debug; mod create_scope_map; pub mod gdb; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 8f243673907..a40cfc8b23f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -6,7 +6,7 @@ use super::CodegenUnitDebugContext; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::ty::{self, DefIdTree, Ty}; -use tracing::trace; +use trace; use crate::common::CodegenCx; use crate::llvm; diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index fa0ecd18fc8..0f663a26732 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -22,7 +22,6 @@ use rustc_codegen_ssa::traits::TypeMembershipMethods; use rustc_middle::ty::Ty; use rustc_symbol_mangling::typeid::typeid_for_fnabi; use smallvec::SmallVec; -use tracing::debug; /// Declare a function. /// diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 636d689a34b..334425ae55b 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -16,6 +16,8 @@ #[macro_use] extern crate rustc_macros; +#[macro_use] +extern crate tracing; use back::write::{create_informational_target_machine, create_target_machine}; diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index f5d676c44e3..1b049dfe979 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -15,7 +15,6 @@ use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; use smallvec::{smallvec, SmallVec}; use std::ffi::{CStr, CString}; -use tracing::debug; use std::mem; use std::path::Path; diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 6e94284852f..1eceb7f5c87 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -11,7 +11,6 @@ use rustc_middle::ty::layout::{FnAbiOf, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitable}; use rustc_session::config::CrateType; use rustc_target::spec::RelocModel; -use tracing::debug; impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 9f0e6c80b19..dc1165835e7 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -11,7 +11,6 @@ use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape}; use rustc_target::abi::{Int, Pointer, F32, F64}; use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants}; use smallvec::{smallvec, SmallVec}; -use tracing::debug; use std::fmt::Write; diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 32b340832ce..8d7e2c5cf39 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -540,7 +540,7 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>( .map(|fnabi| (fnabi.conv, &fnabi.args[..])) .unwrap_or((Conv::Rust, &[])); - // Decorate symbols with prefices, suffices and total number of bytes of arguments. + // Decorate symbols with prefixes, suffixes and total number of bytes of arguments. // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170 let (prefix, suffix) = match conv { Conv::X86Fastcall => ("@", "@"), diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index d6bbcd99234..2b931bfc91d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -191,7 +191,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // errored or at least linted ErrorHandled::Reported(_) | ErrorHandled::Linted => {} ErrorHandled::TooGeneric => { - span_bug!(const_.span, "codgen encountered polymorphic constant: {:?}", err) + span_bug!(const_.span, "codegen encountered polymorphic constant: {:?}", err) } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 268c4d76503..04b8c8636f6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef}; use crate::common::IntPredicate; use crate::glue; use crate::traits::*; -use crate::MemFlags; use rustc_middle::mir; use rustc_middle::mir::tcx::PlaceTy; @@ -343,16 +342,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { .. } => { if variant_index != dataful_variant { - if bx.cx().sess().target.arch == "arm" - || bx.cx().sess().target.arch == "aarch64" - { - // FIXME(#34427): as workaround for LLVM bug on ARM, - // use memset of 0 before assigning niche value. - let fill_byte = bx.cx().const_u8(0); - let size = bx.cx().const_usize(self.layout.size.bytes()); - bx.memset(self.llval, fill_byte, size, self.align, MemFlags::empty()); - } - let niche = self.project_field(bx, tag_field); let niche_llty = bx.cx().immediate_backend_type(niche.layout); let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); 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 b46f71fc78a..a2f14e753ae 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -197,7 +197,7 @@ pub(super) fn op_to_const<'tcx>( } } -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(tcx), level = "debug", ret)] pub(crate) fn turn_into_const_value<'tcx>( tcx: TyCtxt<'tcx>, constant: ConstAlloc<'tcx>, @@ -224,10 +224,7 @@ pub(crate) fn turn_into_const_value<'tcx>( ); // Turn this into a proper constant. - let const_val = op_to_const(&ecx, &mplace.into()); - debug!(?const_val); - - const_val + op_to_const(&ecx, &mplace.into()) } #[instrument(skip(tcx), level = "debug")] diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 373b139c86e..8b7c3cf3377 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -204,7 +204,7 @@ fn get_info_on_unsized_field<'tcx>( (unsized_inner_ty, num_elems) } -#[instrument(skip(ecx), level = "debug")] +#[instrument(skip(ecx), level = "debug", ret)] fn create_pointee_place<'tcx>( ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, ty: Ty<'tcx>, @@ -237,14 +237,11 @@ fn create_pointee_place<'tcx>( let ptr = ecx.allocate_ptr(size, align, MemoryKind::Stack).unwrap(); debug!(?ptr); - let place = MPlaceTy::from_aligned_ptr_with_meta( + MPlaceTy::from_aligned_ptr_with_meta( ptr.into(), layout, MemPlaceMeta::Meta(Scalar::from_machine_usize(num_elems as u64, &tcx)), - ); - debug!(?place); - - place + ) } else { create_mplace_from_layout(ecx, ty) } @@ -253,7 +250,7 @@ fn create_pointee_place<'tcx>( /// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// construction has finished. // FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(tcx), level = "debug", ret)] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, @@ -294,7 +291,7 @@ pub fn valtree_to_const_value<'tcx>( dump_place(&ecx, place.into()); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); - let const_val = match ty.kind() { + match ty.kind() { ty::Ref(_, _, _) => { let ref_place = place.to_ref(&tcx); let imm = @@ -303,10 +300,7 @@ pub fn valtree_to_const_value<'tcx>( op_to_const(&ecx, &imm.into()) } _ => op_to_const(&ecx, &place.into()), - }; - debug!(?const_val); - - const_val + } } ty::Never | ty::Error(_) diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 66ab3f15716..24dbc769529 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -334,7 +334,7 @@ pub enum InternKind { /// tracks where in the value we are and thus can show much better error messages. /// Any errors here would anyway be turned into `const_err` lints, whereas validation failures /// are hard errors. -#[tracing::instrument(level = "debug", skip(ecx))] +#[instrument(level = "debug", skip(ecx))] pub fn intern_const_alloc_recursive< 'mir, 'tcx: 'mir, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a8ec8447f64..adda9639990 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -320,7 +320,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (a_offset, b_offset) = match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) { (Err(a), Err(b)) => { - // Neither poiner points to an allocation. + // Neither pointer points to an allocation. // If these are inequal or null, this *will* fail the deref check below. (a, b) } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 69dbc9592fa..ed155fbfef0 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -437,7 +437,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { msg, }) } - // Ensure we never consider the null pointer dereferencable. + // Ensure we never consider the null pointer dereferenceable. if M::Provenance::OFFSET_IS_ADDR { assert_ne!(ptr.addr(), Size::ZERO); } @@ -914,7 +914,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> { self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val) } - /// Mark the entire referenced range as uninitalized + /// Mark the entire referenced range as uninitialized pub fn write_uninit(&mut self) -> InterpResult<'tcx> { Ok(self .alloc diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 67dc9011ea2..77da8f1041e 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -1,6 +1,6 @@ //! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy. //! -//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not". +//! OpTy and PlaceTy generally work by "let's see if we are actually an MPlaceTy, and do something custom if not". //! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway. //! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields), //! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial, diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index a71a5d4b833..ea366eba772 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -217,7 +217,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // When comparing the PassMode, we have to be smart about comparing the attributes. let arg_attr_compat = |a1: &ArgAttributes, a2: &ArgAttributes| { // There's only one regular attribute that matters for the call ABI: InReg. - // Everything else is things like noalias, dereferencable, nonnull, ... + // Everything else is things like noalias, dereferenceable, nonnull, ... // (This also applies to pointee_size, pointee_align.) if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg) { @@ -556,7 +556,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .tcx .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); let ty::Dynamic(data, ..) = receiver_tail.kind() else { - span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail) + span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) }; // Get the required information from the vtable. diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index b3a511d5a49..cab23b7241f 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -32,7 +32,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(vtable_ptr.into()) } - /// Returns a high-level representation of the entires of the given vtable. + /// Returns a high-level representation of the entries of the given vtable. pub fn get_vtable_entries( &self, vtable: Pointer<Option<M::Provenance>>, diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 5c641f54f68..2d8658db5e6 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,25 +8,26 @@ doctest = false [dependencies] arrayvec = { version = "0.7", default-features = false } +bitflags = "1.2.1" +cfg-if = "0.1.2" ena = "0.14" indexmap = { version = "1.9.1" } -tracing = "0.1" jobserver_crate = { version = "0.1.13", package = "jobserver" } -rustc_serialize = { path = "../rustc_serialize" } -rustc_macros = { path = "../rustc_macros" } -rustc_graphviz = { path = "../rustc_graphviz" } -cfg-if = "0.1.2" -stable_deref_trait = "1.0.0" -rayon = { version = "0.4.0", package = "rustc-rayon", optional = true } +libc = "0.2" +measureme = "10.0.0" rayon-core = { version = "0.4.0", package = "rustc-rayon-core", optional = true } +rayon = { version = "0.4.0", package = "rustc-rayon", optional = true } +rustc_graphviz = { path = "../rustc_graphviz" } rustc-hash = "1.1.0" -smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] } rustc_index = { path = "../rustc_index", package = "rustc_index" } -bitflags = "1.2.1" -measureme = "10.0.0" -libc = "0.2" +rustc_macros = { path = "../rustc_macros" } +rustc_serialize = { path = "../rustc_serialize" } +smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] } +stable_deref_trait = "1.0.0" stacker = "0.1.14" tempfile = "3.2" +thin-vec = "0.2.8" +tracing = "0.1" [dependencies.parking_lot] version = "0.11" diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index 5ff2d18dd2b..a39178016ce 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -29,7 +29,7 @@ impl Fingerprint { // quality hash values, let's still combine the two values because the // Fingerprints in DefPathHash have the StableCrateId portion which is // the same for all DefPathHashes from the same crate. Combining the - // two halfs makes sure we get a good quality hash in such cases too. + // two halves makes sure we get a good quality hash in such cases too. self.0.wrapping_mul(3).wrapping_add(self.1) } @@ -120,7 +120,7 @@ impl FingerprintHasher for crate::unhash::Unhasher { // quality hash values, let's still combine the two values because the // Fingerprints in DefPathHash have the StableCrateId portion which is // the same for all DefPathHashes from the same crate. Combining the - // two halfs makes sure we get a good quality hash in such cases too. + // two halves makes sure we get a good quality hash in such cases too. // // Since `Unhasher` is used only in the context of HashMaps, it is OK // to combine the two components in an order-independent way (which is diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index c8b09cffe01..a7429ed008f 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -75,7 +75,6 @@ pub mod profiling; pub mod sharded; pub mod stack; pub mod sync; -pub mod thin_vec; pub mod tiny_list; pub mod transitive_relation; pub mod vec_linked_list; diff --git a/compiler/rustc_data_structures/src/map_in_place.rs b/compiler/rustc_data_structures/src/map_in_place.rs index d912211443a..a0d4b7ade1f 100644 --- a/compiler/rustc_data_structures/src/map_in_place.rs +++ b/compiler/rustc_data_structures/src/map_in_place.rs @@ -1,6 +1,6 @@ -use crate::thin_vec::ThinVec; use smallvec::{Array, SmallVec}; use std::ptr; +use thin_vec::ThinVec; pub trait MapInPlace<T>: Sized { fn map_in_place<F>(&mut self, mut f: F) diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 9efea1228ab..937cb671573 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -164,7 +164,7 @@ impl<K: Ord, V> SortedMap<K, V> { /// It is up to the caller to make sure that the elements are sorted by key /// and that there are no duplicates. #[inline] - pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) { + pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) { if elements.is_empty() { return; } @@ -173,28 +173,28 @@ impl<K: Ord, V> SortedMap<K, V> { let start_index = self.lookup_index_for(&elements[0].0); - let drain = match start_index { + let elements = match start_index { Ok(index) => { - let mut drain = elements.drain(..); - self.data[index] = drain.next().unwrap(); - drain + let mut elements = elements.into_iter(); + self.data[index] = elements.next().unwrap(); + elements } Err(index) => { if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 { // We can copy the whole range without having to mix with // existing elements. - self.data.splice(index..index, elements.drain(..)); + self.data.splice(index..index, elements.into_iter()); return; } - let mut drain = elements.drain(..); - self.data.insert(index, drain.next().unwrap()); - drain + let mut elements = elements.into_iter(); + self.data.insert(index, elements.next().unwrap()); + elements } }; // Insert the rest - for (k, v) in drain { + for (k, v) in elements { self.insert(k, v); } } diff --git a/compiler/rustc_data_structures/src/thin_vec.rs b/compiler/rustc_data_structures/src/thin_vec.rs deleted file mode 100644 index fce42e709ab..00000000000 --- a/compiler/rustc_data_structures/src/thin_vec.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::stable_hasher::{HashStable, StableHasher}; - -use std::iter::FromIterator; - -/// A vector type optimized for cases where this size is usually 0 (cf. `SmallVec`). -/// The `Option<Box<..>>` wrapping allows us to represent a zero sized vector with `None`, -/// which uses only a single (null) pointer. -#[derive(Clone, Encodable, Decodable, Debug, Hash, Eq, PartialEq)] -pub struct ThinVec<T>(Option<Box<Vec<T>>>); - -impl<T> ThinVec<T> { - pub fn new() -> Self { - ThinVec(None) - } - - pub fn iter(&self) -> std::slice::Iter<'_, T> { - self.into_iter() - } - - pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { - self.into_iter() - } - - pub fn push(&mut self, item: T) { - match *self { - ThinVec(Some(ref mut vec)) => vec.push(item), - ThinVec(None) => *self = vec![item].into(), - } - } - - /// Note: if `set_len(0)` is called on a non-empty `ThinVec`, it will - /// remain in the `Some` form. This is required for some code sequences - /// (such as the one in `flat_map_in_place`) that call `set_len(0)` before - /// an operation that might panic, and then call `set_len(n)` again - /// afterwards. - pub unsafe fn set_len(&mut self, new_len: usize) { - match *self { - ThinVec(None) => { - // A prerequisite of `Vec::set_len` is that `new_len` must be - // less than or equal to capacity(). The same applies here. - if new_len != 0 { - panic!("unsafe ThinVec::set_len({})", new_len); - } - } - ThinVec(Some(ref mut vec)) => vec.set_len(new_len), - } - } - - pub fn insert(&mut self, index: usize, value: T) { - match *self { - ThinVec(None) => { - if index == 0 { - *self = vec![value].into(); - } else { - panic!("invalid ThinVec::insert"); - } - } - ThinVec(Some(ref mut vec)) => vec.insert(index, value), - } - } - - pub fn remove(&mut self, index: usize) -> T { - match self { - ThinVec(None) => panic!("invalid ThinVec::remove"), - ThinVec(Some(vec)) => vec.remove(index), - } - } - - pub fn as_slice(&self) -> &[T] { - match self { - ThinVec(None) => &[], - ThinVec(Some(vec)) => vec.as_slice(), - } - } -} - -impl<T> From<Vec<T>> for ThinVec<T> { - fn from(vec: Vec<T>) -> Self { - if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) } - } -} - -impl<T> Into<Vec<T>> for ThinVec<T> { - fn into(self) -> Vec<T> { - match self { - ThinVec(None) => Vec::new(), - ThinVec(Some(vec)) => *vec, - } - } -} - -impl<T> ::std::ops::Deref for ThinVec<T> { - type Target = [T]; - fn deref(&self) -> &[T] { - match *self { - ThinVec(None) => &[], - ThinVec(Some(ref vec)) => vec, - } - } -} - -impl<T> ::std::ops::DerefMut for ThinVec<T> { - fn deref_mut(&mut self) -> &mut [T] { - match *self { - ThinVec(None) => &mut [], - ThinVec(Some(ref mut vec)) => vec, - } - } -} - -impl<T> FromIterator<T> for ThinVec<T> { - fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { - // `Vec::from_iter()` should not allocate if the iterator is empty. - let vec: Vec<_> = iter.into_iter().collect(); - if vec.is_empty() { ThinVec(None) } else { ThinVec(Some(Box::new(vec))) } - } -} - -impl<T> IntoIterator for ThinVec<T> { - type Item = T; - type IntoIter = std::vec::IntoIter<T>; - - fn into_iter(self) -> Self::IntoIter { - // This is still performant because `Vec::new()` does not allocate. - self.0.map_or_else(Vec::new, |ptr| *ptr).into_iter() - } -} - -impl<'a, T> IntoIterator for &'a ThinVec<T> { - type Item = &'a T; - type IntoIter = std::slice::Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - self.as_ref().iter() - } -} - -impl<'a, T> IntoIterator for &'a mut ThinVec<T> { - type Item = &'a mut T; - type IntoIter = std::slice::IterMut<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - self.as_mut().iter_mut() - } -} - -impl<T> Extend<T> for ThinVec<T> { - fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { - match *self { - ThinVec(Some(ref mut vec)) => vec.extend(iter), - ThinVec(None) => *self = iter.into_iter().collect::<Vec<_>>().into(), - } - } - - fn extend_one(&mut self, item: T) { - self.push(item) - } - - fn extend_reserve(&mut self, additional: usize) { - match *self { - ThinVec(Some(ref mut vec)) => vec.reserve(additional), - ThinVec(None) => *self = Vec::with_capacity(additional).into(), - } - } -} - -impl<T: HashStable<CTX>, CTX> HashStable<CTX> for ThinVec<T> { - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - (**self).hash_stable(hcx, hasher) - } -} - -impl<T> Default for ThinVec<T> { - fn default() -> Self { - Self(None) - } -} - -#[cfg(test)] -mod tests; diff --git a/compiler/rustc_data_structures/src/thin_vec/tests.rs b/compiler/rustc_data_structures/src/thin_vec/tests.rs deleted file mode 100644 index 0221b9912bb..00000000000 --- a/compiler/rustc_data_structures/src/thin_vec/tests.rs +++ /dev/null @@ -1,42 +0,0 @@ -use super::*; - -impl<T> ThinVec<T> { - fn into_vec(self) -> Vec<T> { - self.into() - } -} - -#[test] -fn test_from_iterator() { - assert_eq!(std::iter::empty().collect::<ThinVec<String>>().into_vec(), Vec::<String>::new()); - assert_eq!(std::iter::once(42).collect::<ThinVec<_>>().into_vec(), vec![42]); - assert_eq!([1, 2].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2]); - assert_eq!([1, 2, 3].into_iter().collect::<ThinVec<_>>().into_vec(), vec![1, 2, 3]); -} - -#[test] -fn test_into_iterator_owned() { - assert_eq!(ThinVec::new().into_iter().collect::<Vec<String>>(), Vec::<String>::new()); - assert_eq!(ThinVec::from(vec![1]).into_iter().collect::<Vec<_>>(), vec![1]); - assert_eq!(ThinVec::from(vec![1, 2]).into_iter().collect::<Vec<_>>(), vec![1, 2]); - assert_eq!(ThinVec::from(vec![1, 2, 3]).into_iter().collect::<Vec<_>>(), vec![1, 2, 3]); -} - -#[test] -fn test_into_iterator_ref() { - assert_eq!(ThinVec::new().iter().collect::<Vec<&String>>(), Vec::<&String>::new()); - assert_eq!(ThinVec::from(vec![1]).iter().collect::<Vec<_>>(), vec![&1]); - assert_eq!(ThinVec::from(vec![1, 2]).iter().collect::<Vec<_>>(), vec![&1, &2]); - assert_eq!(ThinVec::from(vec![1, 2, 3]).iter().collect::<Vec<_>>(), vec![&1, &2, &3]); -} - -#[test] -fn test_into_iterator_ref_mut() { - assert_eq!(ThinVec::new().iter_mut().collect::<Vec<&mut String>>(), Vec::<&mut String>::new()); - assert_eq!(ThinVec::from(vec![1]).iter_mut().collect::<Vec<_>>(), vec![&mut 1]); - assert_eq!(ThinVec::from(vec![1, 2]).iter_mut().collect::<Vec<_>>(), vec![&mut 1, &mut 2]); - assert_eq!( - ThinVec::from(vec![1, 2, 3]).iter_mut().collect::<Vec<_>>(), - vec![&mut 1, &mut 2, &mut 3], - ); -} diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 4570c144833..d1d02ed73f9 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" crate-type = ["dylib"] [dependencies] -tracing = { version = "0.1.28" } +tracing = { version = "0.1.35" } serde_json = "1.0.59" rustc_log = { path = "../rustc_log" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index f66b1a2976f..faeacd3e410 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -1,5 +1,6 @@ //! The various pretty-printing routines. +use crate::session_diagnostics::UnprettyDumpFail; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_errors::ErrorGuaranteed; @@ -357,12 +358,15 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) { (src, src_name) } -fn write_or_print(out: &str, ofile: Option<&Path>) { +fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) { match ofile { None => print!("{}", out), Some(p) => { if let Err(e) = std::fs::write(p, out) { - panic!("print-print failed to write {} due to {}", p.display(), e); + sess.emit_fatal(UnprettyDumpFail { + path: p.display().to_string(), + err: e.to_string(), + }); } } } @@ -402,7 +406,7 @@ pub fn print_after_parsing( _ => unreachable!(), }; - write_or_print(&out, ofile); + write_or_print(&out, ofile, sess); } pub fn print_after_hir_lowering<'tcx>( @@ -468,7 +472,7 @@ pub fn print_after_hir_lowering<'tcx>( _ => unreachable!(), }; - write_or_print(&out, ofile); + write_or_print(&out, ofile, tcx.sess); } // In an ideal world, this would be a public function called by the driver after @@ -512,7 +516,7 @@ fn print_with_analysis( _ => unreachable!(), }; - write_or_print(&out, ofile); + write_or_print(&out, ofile, tcx.sess); Ok(()) } diff --git a/compiler/rustc_driver/src/session_diagnostics.rs b/compiler/rustc_driver/src/session_diagnostics.rs index fe64d0fca9b..e9696792d05 100644 --- a/compiler/rustc_driver/src/session_diagnostics.rs +++ b/compiler/rustc_driver/src/session_diagnostics.rs @@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> { #[derive(SessionDiagnostic)] #[diag(driver::rlink_no_a_file)] pub(crate) struct RlinkNotAFile; + +#[derive(SessionDiagnostic)] +#[diag(driver::unpretty_dump_fail)] +pub(crate) struct UnprettyDumpFail { + pub path: String, + pub err: String, +} diff --git a/compiler/rustc_error_messages/locales/en-US/driver.ftl b/compiler/rustc_error_messages/locales/en-US/driver.ftl index 73f084cf329..8ad198c86c9 100644 --- a/compiler/rustc_error_messages/locales/en-US/driver.ftl +++ b/compiler/rustc_error_messages/locales/en-US/driver.ftl @@ -9,3 +9,5 @@ driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}` driver_rlink_no_a_file = rlink must be a file + +driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}` diff --git a/compiler/rustc_error_messages/locales/en-US/query_system.ftl b/compiler/rustc_error_messages/locales/en-US/query_system.ftl new file mode 100644 index 00000000000..167704e46c0 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/query_system.ftl @@ -0,0 +1,25 @@ +query_system_reentrant = internal compiler error: re-entrant incremental verify failure, suppressing message + +query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node} + .help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile + +query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information +query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information + +query_system_cycle = cycle detected when {$stack_bottom} + +query_system_cycle_usage = cycle used when {$usage} + +query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again + +query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle + +query_system_cycle_recursive_ty_alias = type aliases cannot be recursive +query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle +query_system_cycle_recursive_ty_alias_help2 = see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information + +query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive + +query_system_cycle_which_requires = ...which requires {$desc}... + +query_system_query_overflow = queries overflow the depth limit! diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 42fb2d538b0..ed5e092814f 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -5,6 +5,9 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#[macro_use] +extern crate tracing; + use fluent_bundle::FluentResource; use fluent_syntax::parser::ParserError; use rustc_data_structures::sync::Lrc; @@ -16,7 +19,6 @@ use std::fmt; use std::fs; use std::io; use std::path::{Path, PathBuf}; -use tracing::{instrument, trace}; #[cfg(not(parallel_compiler))] use std::cell::LazyCell as Lazy; @@ -50,6 +52,7 @@ fluent_messages! { passes => "../locales/en-US/passes.ftl", plugin_impl => "../locales/en-US/plugin_impl.ftl", privacy => "../locales/en-US/privacy.ftl", + query_system => "../locales/en-US/query_system.ftl", save_analysis => "../locales/en-US/save_analysis.ftl", ty_utils => "../locales/en-US/ty_utils.ftl", typeck => "../locales/en-US/typeck.ftl", diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index f75e2596f36..95ae9765a48 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -974,12 +974,12 @@ impl Diagnostic { fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( &mut self, level: Level, - mut message: Vec<(M, Style)>, + message: Vec<(M, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, ) { let message = message - .drain(..) + .into_iter() .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) .collect(); let sub = SubDiagnostic { level, message, span, render_span }; diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 61d767a1cc6..7e29dc207ac 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -12,7 +12,6 @@ use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::thread::panicking; -use tracing::debug; /// Used for emitting structured error messages and other diagnostic information. /// diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 6c1bfcb9919..e79ce11a6fc 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -34,7 +34,6 @@ use std::iter; use std::path::Path; use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; use termcolor::{Buffer, Color, WriteColor}; -use tracing::*; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 65338f56d9c..4f407badb3f 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -21,7 +21,7 @@ pub trait Translate { /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then /// passed around as a reference thereafter. fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { - FromIterator::from_iter(args.to_vec().drain(..)) + FromIterator::from_iter(args.iter().cloned()) } /// Convert `DiagnosticMessage`s to a string, performing translation if necessary. diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 75dcbd69674..ac0e200b1b7 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -15,6 +15,9 @@ #[macro_use] extern crate rustc_macros; +#[macro_use] +extern crate tracing; + extern crate proc_macro as pm; mod placeholders; diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 4fa91dfeaea..c8bdc39311c 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -430,7 +430,7 @@ impl TtParser { } } MatcherLoc::Delimited => { - // Entering the delimeter is trivial. + // Entering the delimiter is trivial. mp.idx += 1; self.cur_mps.push(mp); } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e009e4f7c68..7764ffd246e 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -32,7 +32,6 @@ use rustc_span::Span; use std::borrow::Cow; use std::collections::hash_map::Entry; use std::{mem, slice}; -use tracing::debug; pub(crate) struct ParserAnyMacro<'a> { parser: Parser<'a>, @@ -976,7 +975,7 @@ impl<'tt> TokenSet<'tt> { self.maybe_empty = false; } - // Adds `tok` to the set for `self`, marking sequence as non-empy. + // Adds `tok` to the set for `self`, marking sequence as non-empty. fn add_one(&mut self, tt: TtHandle<'tt>) { if !self.tokens.contains(&tt) { self.tokens.push(tt); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index beb33c05913..59a7b668a83 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -6,7 +6,7 @@ use rustc_ast::tokenstream::{self, Spacing::*, TokenStream}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Diagnostic, MultiSpan, PResult}; +use rustc_errors::{MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; @@ -15,7 +15,7 @@ use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::{BytePos, FileName, Pos, SourceFile, Span}; use pm::bridge::{ - server, DelimSpan, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, + server, DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree, }; use pm::{Delimiter, Level, LineColumn}; use std::ops::Bound; @@ -368,8 +368,6 @@ impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type SourceFile = Lrc<SourceFile>; - type MultiSpan = Vec<Span>; - type Diagnostic = Diagnostic; type Span = Span; type Symbol = Symbol; } @@ -436,6 +434,21 @@ impl server::FreeFunctions for Rustc<'_, '_> { span: self.call_site, }) } + + fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) { + let mut diag = + rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message); + diag.set_span(MultiSpan::from_spans(diagnostic.spans)); + for child in diagnostic.children { + diag.sub( + child.level.to_internal(), + child.message, + MultiSpan::from_spans(child.spans), + None, + ); + } + self.sess().span_diagnostic.emit_diagnostic(&mut diag); + } } impl server::TokenStream for Rustc<'_, '_> { @@ -583,38 +596,6 @@ impl server::SourceFile for Rustc<'_, '_> { } } -impl server::MultiSpan for Rustc<'_, '_> { - fn new(&mut self) -> Self::MultiSpan { - vec![] - } - - fn push(&mut self, spans: &mut Self::MultiSpan, span: Self::Span) { - spans.push(span) - } -} - -impl server::Diagnostic for Rustc<'_, '_> { - fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { - let mut diag = Diagnostic::new(level.to_internal(), msg); - diag.set_span(MultiSpan::from_spans(spans)); - diag - } - - fn sub( - &mut self, - diag: &mut Self::Diagnostic, - level: Level, - msg: &str, - spans: Self::MultiSpan, - ) { - diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None); - } - - fn emit(&mut self, mut diag: Self::Diagnostic) { - self.sess().span_diagnostic.emit_diagnostic(&mut diag); - } -} - impl server::Span for Rustc<'_, '_> { fn debug(&mut self, span: Self::Span) -> String { if self.ecx.ecfg.span_debug { diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index c2c551e78a4..d85ac960f9b 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -15,7 +15,6 @@ use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::{self, Write}; use std::hash::Hash; -use tracing::debug; /// The `DefPathTable` maps `DefIndex`es to `DefKey`s and vice versa. /// Internally the `DefPathTable` holds a tree of `DefKey`s, where each `DefKey` diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 092029ef09e..1b33cb9c2da 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -18,6 +18,9 @@ extern crate rustc_macros; #[macro_use] +extern crate tracing; + +#[macro_use] extern crate rustc_data_structures; extern crate self as rustc_hir; diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 8bf1de34a9b..d4350aa5734 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -391,7 +391,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { /// Preconditions: /// /// - `for_vid` is a "root vid" - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] fn generalize( &self, ty: Ty<'tcx>, @@ -435,15 +435,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { cache: SsoHashMap::new(), }; - let ty = match generalize.relate(ty, ty) { - Ok(ty) => ty, - Err(e) => { - debug!(?e, "failure"); - return Err(e); - } - }; + let ty = generalize.relate(ty, ty)?; let needs_wf = generalize.needs_wf; - trace!(?ty, ?needs_wf, "success"); Ok(Generalization { ty, needs_wf }) } @@ -499,6 +492,7 @@ struct Generalizer<'cx, 'tcx> { /// Result from a generalization operation. This includes /// not only the generalized type, but also a bool flag /// indicating whether further WF checks are needed. +#[derive(Debug)] struct Generalization<'tcx> { ty: Ty<'tcx>, @@ -856,10 +850,9 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { debug_assert_eq!(t, _t); - debug!("ConstInferUnifier: t={:?}", t); match t.kind() { &ty::Infer(ty::TyVar(vid)) => { @@ -883,12 +876,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { .borrow_mut() .type_variables() .new_var(self.for_universe, origin); - let u = self.tcx().mk_ty_var(new_var_id); - debug!( - "ConstInferUnifier: replacing original vid={:?} with new={:?}", - vid, u - ); - Ok(u) + Ok(self.tcx().mk_ty_var(new_var_id)) } } } @@ -932,14 +920,13 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn consts( &mut self, c: ty::Const<'tcx>, _c: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { debug_assert_eq!(c, _c); - debug!("ConstInferUnifier: c={:?}", c); match c.kind() { ty::ConstKind::Infer(InferConst::Var(vid)) => { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 3a6119a6273..6dad9873d61 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1434,7 +1434,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the message in `secondary_span` as the primary label, and apply the message that would /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on /// E0271, like `src/test/ui/issues/issue-39970.stderr`. - #[tracing::instrument( + #[instrument( level = "debug", skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label) )] diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index e990fe7ecb5..91a05367eee 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -199,7 +199,7 @@ fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { } /// We don't want to directly use `ty_to_string` for closures as their type isn't really -/// something users are familar with. Directly printing the `fn_sig` of closures also +/// something users are familiar with. Directly printing the `fn_sig` of closures also /// doesn't work as they actually use the "rust-call" API. fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { let ty::Closure(_, substs) = ty.kind() else { unreachable!() }; diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index d0d9efe152c..67426fcf0fe 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -69,7 +69,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// For more details visit the relevant sections of the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T where T: TypeFoldable<'tcx> + Copy, @@ -104,9 +104,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }, }; - let result = self.tcx.replace_bound_vars_uncached(binder, delegate); - debug!(?next_universe, ?result); - result + debug!(?next_universe); + self.tcx.replace_bound_vars_uncached(binder, delegate) } /// See [RegionConstraintCollector::leak_check][1]. diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 3783cfb4cc5..13b7e8eb964 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -333,9 +333,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// /// Neither `a` nor `b` may be an inference variable (hence the /// term "concrete regions"). - #[instrument(level = "trace", skip(self))] + #[instrument(level = "trace", skip(self), ret)] fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - let r = match (*a, *b) { + match (*a, *b) { (ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } @@ -399,11 +399,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.tcx().lifetimes.re_static } } - }; - - debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r); - - r + } } /// After expansion is complete, go and check upper bounds (i.e., diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 60ebf8b949d..fe037a458a7 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1333,7 +1333,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// `resolve_vars_if_possible` as well as `fully_resolve`. /// /// Make sure to call [`InferCtxt::process_registered_region_obligations`] - /// first, or preferrably use [`InferCtxt::check_region_obligations_and_report_errors`] + /// first, or preferably use [`InferCtxt::check_region_obligations_and_report_errors`] /// to do both of these operations together. pub fn resolve_regions_and_report_errors( &self, diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index e7e93116a66..bb6f6ae60e2 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -542,7 +542,7 @@ where true } - #[instrument(skip(self, info), level = "trace")] + #[instrument(skip(self, info), level = "trace", ret)] fn relate_with_variance<T: Relate<'tcx>>( &mut self, variance: ty::Variance, @@ -560,8 +560,6 @@ where self.ambient_variance = old_ambient_variance; - debug!(?r); - Ok(r) } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 233a5004a39..d45adf43abf 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -390,7 +390,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }); } - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> { let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let parent_def_id = match self.defining_use_anchor { @@ -421,16 +421,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { in_definition_scope.then_some(*origin) } - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { - let origin = match self.tcx.hir().expect_item(def_id).kind { + match self.tcx.hir().expect_item(def_id).kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin, ref itemkind => { span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind) } - }; - trace!(?origin); - origin + } } } diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index fb12da0cc13..4d124554afb 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -29,7 +29,7 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { } } - #[instrument(level = "debug")] + #[instrument(level = "debug", ret)] pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> { std::mem::take(&mut self.opaque_types) } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 2a085288fb7..2d19d1823fd 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -9,7 +9,7 @@ pub mod verify; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; -#[instrument(level = "debug", skip(param_env))] +#[instrument(level = "debug", skip(param_env), ret)] pub fn explicit_outlives_bounds<'tcx>( param_env: ty::ParamEnv<'tcx>, ) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index fe78890ff6e..74c8bd88d27 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -313,7 +313,7 @@ where self.delegate.push_verify(origin, generic, region, verify_bound); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn projection_must_outlive( &mut self, origin: infer::SubregionOrigin<'tcx>, diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index be03c8b5bae..a5c21f0fb9b 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -34,7 +34,7 @@ use crate::infer::region_constraints::VerifyIfEq; /// like are used. This is a particular challenge since this function is invoked /// very late in inference and hence cannot make use of the normal inference /// machinery. -#[tracing::instrument(level = "debug", skip(tcx, param_env))] +#[instrument(level = "debug", skip(tcx, param_env))] pub fn extract_verify_if_eq<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -71,7 +71,7 @@ pub fn extract_verify_if_eq<'tcx>( } /// True if a (potentially higher-ranked) outlives -#[tracing::instrument(level = "debug", skip(tcx, param_env))] +#[instrument(level = "debug", skip(tcx, param_env))] pub(super) fn can_match_erased_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -110,7 +110,7 @@ impl<'tcx> Match<'tcx> { /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern /// is already bound to a different value. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn bind( &mut self, br: ty::BoundRegion, diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index 74a26ebc39f..611961ab1cc 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -100,7 +100,7 @@ impl Default for InferCtxtUndoLogs<'_> { } /// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any -/// action that is convertable into an UndoLog (per the From impls above). +/// action that is convertible into an UndoLog (per the From impls above). impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx> where UndoLog<'tcx>: From<T>, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index dc4799e4afc..949bd02ad68 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -176,7 +176,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg { let ident = arg.ident().expect("multi-segment cfg key"); names_valid.insert(ident.name.to_string()); } else { - error!("`names()` arguments must be simple identifers"); + error!("`names()` arguments must be simple identifiers"); } } continue 'specs; @@ -204,7 +204,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg { continue 'specs; } else { error!( - "`values()` first argument must be a simple identifer" + "`values()` first argument must be a simple identifier" ); } } else if args.is_empty() { @@ -332,7 +332,7 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R // JUSTIFICATION: before session exists, only config #[allow(rustc::bad_opt_access)] pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R { - tracing::trace!("run_compiler"); + trace!("run_compiler"); util::run_in_thread_pool_with_globals( config.opts.edition, config.opts.unstable_opts.threads, diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 258e38c3bdb..1a8d619fafb 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -8,6 +8,9 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#[macro_use] +extern crate tracing; + mod callbacks; mod errors; pub mod interface; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 66c6a229b89..f8b40949e2e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -38,7 +38,6 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_span::FileName; use rustc_trait_selection::traits; use rustc_typeck as typeck; -use tracing::{info, warn}; use std::any::Any; use std::cell::RefCell; @@ -165,7 +164,7 @@ pub fn create_resolver( krate: &ast::Crate, crate_name: &str, ) -> BoxedResolver { - tracing::trace!("create_resolver"); + trace!("create_resolver"); BoxedResolver::new(sess, move |sess, resolver_arenas| { Resolver::new(sess, krate, crate_name, metadata_loader, resolver_arenas) }) @@ -279,7 +278,7 @@ pub fn configure_and_expand( crate_name: &str, resolver: &mut Resolver<'_>, ) -> Result<ast::Crate> { - tracing::trace!("configure_and_expand"); + trace!("configure_and_expand"); pre_expansion_lint(sess, lint_store, resolver.registered_tools(), &krate, crate_name); rustc_builtin_macros::register_builtin_macros(resolver); diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 65fa8d7495a..6c725a01b53 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -166,7 +166,7 @@ impl<'tcx> Queries<'tcx> { pub fn expansion( &self, ) -> Result<&Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> { - tracing::trace!("expansion"); + trace!("expansion"); self.expansion.compute(|| { let crate_name = self.crate_name()?.peek().clone(); let (krate, lint_store) = self.register_plugins()?.take(); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index e74978485a2..f7e70d355cf 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -1,3 +1,4 @@ +use info; use libloading::Library; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; @@ -31,7 +32,6 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::OnceLock; use std::thread; -use tracing::info; /// Function pointer type that constructs a new CodegenBackend. pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>; diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 868555a72b0..4e9c209a584 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -59,7 +59,6 @@ use rustc_trait_selection::traits::{self, misc::can_type_implement_copy}; use crate::nonstandard_style::{method_context, MethodLateContext}; use std::fmt::Write; -use tracing::{debug, trace}; // hardwired lints from librustc_middle pub use rustc_session::lint::builtin::*; @@ -3173,3 +3172,81 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { } } } + +declare_lint! { + /// The `special_module_name` lint detects module + /// declarations for files that have a special meaning. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// mod lib; + /// + /// fn main() { + /// lib::run(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Cargo recognizes `lib.rs` and `main.rs` as the root of a + /// library or binary crate, so declaring them as modules + /// will lead to miscompilation of the crate unless configured + /// explicitly. + /// + /// To access a library from a binary target within the same crate, + /// use `your_crate_name::` as the path path instead of `lib::`: + /// + /// ```rust,compile_fail + /// // bar/src/lib.rs + /// fn run() { + /// // ... + /// } + /// + /// // bar/src/main.rs + /// fn main() { + /// bar::run(); + /// } + /// ``` + /// + /// Binary targets cannot be used as libraries and so declaring + /// one as a module is not allowed. + pub SPECIAL_MODULE_NAME, + Warn, + "module declarations for files with a special meaning", +} + +declare_lint_pass!(SpecialModuleName => [SPECIAL_MODULE_NAME]); + +impl EarlyLintPass for SpecialModuleName { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { + for item in &krate.items { + if let ast::ItemKind::Mod( + _, + ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _), + ) = item.kind + { + if item.attrs.iter().any(|a| a.has_name(sym::path)) { + continue; + } + + match item.ident.name.as_str() { + "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| { + lint.build("found module declaration for lib.rs") + .note("lib.rs is the root of this crate's library target") + .help("to refer to it from other targets, use the library's name as the path") + .emit() + }), + "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| { + lint.build("found module declaration for main.rs") + .note("a binary crate cannot be used as library") + .emit() + }), + _ => continue + } + } + } + } +} diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 002bba4759b..e3b6c015987 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -45,7 +45,6 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{BytePos, Span}; use rustc_target::abi; -use tracing::debug; use std::cell::Cell; use std::iter; @@ -417,7 +416,7 @@ impl LintStore { None => { // 1. The tool is currently running, so this lint really doesn't exist. // FIXME: should this handle tools that never register a lint, like rustfmt? - tracing::debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>()); + debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>()); let tool_prefix = format!("{}::", tool_name); return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { self.no_lint_suggestion(&complete_name) @@ -510,7 +509,7 @@ impl LintStore { CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) } Some(other) => { - tracing::debug!("got renamed lint {:?}", other); + debug!("got renamed lint {:?}", other); CheckLintNameResult::NoLint(None) } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index cdb5b3c4284..27d173ebde8 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -26,7 +26,6 @@ use rustc_span::symbol::Ident; use rustc_span::Span; use std::slice; -use tracing::debug; macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({ $cx.pass.$f(&$cx.context, $($args),*); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index c26d7824758..16b7d2cbbae 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -12,7 +12,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use tracing::debug; declare_tool_lint! { pub rustc::DEFAULT_HASH_TYPES, @@ -393,8 +392,14 @@ impl LateLintPass<'_> for Diagnostics { return; } + let mut found_parent_with_attr = false; let mut found_impl = false; - for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) { + if let Some(owner_did) = hir_id.as_owner() { + found_parent_with_attr = found_parent_with_attr + || cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics); + } + debug!(?parent); if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent && let Impl { of_trait: Some(of_trait), .. } = impl_ && @@ -407,7 +412,7 @@ impl LateLintPass<'_> for Diagnostics { } } debug!(?found_impl); - if !found_impl { + if !found_parent_with_attr && !found_impl { cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| { lint.build(fluent::lint::diag_out_of_impl).emit(); }) @@ -425,7 +430,7 @@ impl LateLintPass<'_> for Diagnostics { } } debug!(?found_diagnostic_message); - if !found_diagnostic_message { + if !found_parent_with_attr && !found_diagnostic_message { cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| { lint.build(fluent::lint::untranslatable_diag).emit(); }) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 5188ac633d3..8a336844dc2 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -29,7 +29,6 @@ use rustc_span::Span; use std::any::Any; use std::cell::Cell; use std::slice; -use tracing::debug; /// Extract the `LintStore` from the query context. /// This function exists because we've erased `LintStore` as `dyn Any` in the context. diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 89409b58f88..f1d8ef2e47d 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -21,7 +21,6 @@ use rustc_session::parse::{add_feature_diagnostics, feature_err}; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use tracing::debug; use crate::errors::{ MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c3065e4a2d9..3478be1ed5d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -42,6 +42,8 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_session; +#[macro_use] +extern crate tracing; mod array_into_iter; pub mod builtin; @@ -131,6 +133,7 @@ macro_rules! early_lint_passes { UnusedBraces: UnusedBraces, UnusedImportBraces: UnusedImportBraces, UnsafeCode: UnsafeCode, + SpecialModuleName: SpecialModuleName, AnonymousParameters: AnonymousParameters, EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), NonCamelCaseTypes: NonCamelCaseTypes, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 484e541afc5..03166519981 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -19,7 +19,6 @@ use rustc_target::spec::abi::Abi as SpecAbi; use std::cmp; use std::iter; use std::ops::ControlFlow; -use tracing::debug; declare_lint! { /// The `unused_comparisons` lint detects comparisons made useless by diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 58b2f0a4416..1f4e5b48091 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -268,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { }, ty::Closure(..) => { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - // FIXME(davidtwco): this isn't properly translatable becauses of the + // FIXME(davidtwco): this isn't properly translatable because of the // pre/post strings lint.build(fluent::lint::unused_closure) .set_arg("count", plural_len) @@ -281,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Generator(..) => { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - // FIXME(davidtwco): this isn't properly translatable becauses of the + // FIXME(davidtwco): this isn't properly translatable because of the // pre/post strings lint.build(fluent::lint::unused_generator) .set_arg("count", plural_len) @@ -310,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ) -> bool { if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { - // FIXME(davidtwco): this isn't properly translatable becauses of the pre/post + // FIXME(davidtwco): this isn't properly translatable because of the pre/post // strings let mut err = lint.build(fluent::lint::unused_def); err.set_arg("pre", descr_pre_path); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2dca6acdd6d..845563338ea 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3206,7 +3206,7 @@ declare_lint! { /// [future-incompatible]: ../index.md#future-incompatible-lints pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, Warn, - "tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields", + "transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields", @future_incompatible = FutureIncompatibleInfo { reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>", }; diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index a4ccfcace19..2a4fe48a8ac 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -239,7 +239,7 @@ impl DiagnosticDeriveBuilder { } } - Ok(tokens.drain(..).collect()) + Ok(tokens.into_iter().collect()) } fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 8b40e295bd8..c1b82abc1e0 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -12,7 +12,7 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::fmt; use std::str::FromStr; -use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path}; +use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; /// Which kind of suggestion is being created? @@ -28,41 +28,8 @@ enum SubdiagnosticSuggestionKind { Verbose, } -impl FromStr for SubdiagnosticSuggestionKind { - type Err = (); - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "" => Ok(SubdiagnosticSuggestionKind::Normal), - "_short" => Ok(SubdiagnosticSuggestionKind::Short), - "_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden), - "_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose), - _ => Err(()), - } - } -} - -impl SubdiagnosticSuggestionKind { - pub fn to_suggestion_style(&self) -> TokenStream { - match self { - SubdiagnosticSuggestionKind::Normal => { - quote! { rustc_errors::SuggestionStyle::ShowCode } - } - SubdiagnosticSuggestionKind::Short => { - quote! { rustc_errors::SuggestionStyle::HideCodeInline } - } - SubdiagnosticSuggestionKind::Hidden => { - quote! { rustc_errors::SuggestionStyle::HideCodeAlways } - } - SubdiagnosticSuggestionKind::Verbose => { - quote! { rustc_errors::SuggestionStyle::ShowAlways } - } - } - } -} - /// Which kind of subdiagnostic is being created from a variant? -#[derive(Clone)] +#[derive(Clone, Copy)] enum SubdiagnosticKind { /// `#[label(...)]` Label, @@ -73,9 +40,31 @@ enum SubdiagnosticKind { /// `#[warning(...)]` Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` - Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream }, - /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` - MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind }, + Suggestion(SubdiagnosticSuggestionKind), +} + +impl FromStr for SubdiagnosticKind { + type Err = (); + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "label" => Ok(SubdiagnosticKind::Label), + "note" => Ok(SubdiagnosticKind::Note), + "help" => Ok(SubdiagnosticKind::Help), + "warning" => Ok(SubdiagnosticKind::Warn), + "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), + "suggestion_short" => { + Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) + } + "suggestion_hidden" => { + Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden)) + } + "suggestion_verbose" => { + Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose)) + } + _ => Err(()), + } + } } impl quote::IdentFragment for SubdiagnosticKind { @@ -85,9 +74,17 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Warn => write!(f, "warn"), - SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), - SubdiagnosticKind::MultipartSuggestion { .. } => { - write!(f, "multipart_suggestion_with_style") + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { + write!(f, "suggestion") + } + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => { + write!(f, "suggestion_short") + } + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => { + write!(f, "suggestion_hidden") + } + SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => { + write!(f, "suggestion_verbose") } } } @@ -151,9 +148,11 @@ impl<'a> SessionSubdiagnosticDerive<'a> { variant, span, fields: fields_map, + kinds: Vec::new(), + slugs: Vec::new(), + code: None, span_field: None, applicability: None, - has_suggestion_parts: false, }; builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); @@ -194,15 +193,21 @@ struct SessionSubdiagnosticDeriveBuilder<'a> { /// derive builder. fields: HashMap<String, TokenStream>, + /// Subdiagnostic kind of the type/variant. + kinds: Vec<(SubdiagnosticKind, proc_macro::Span)>, + + /// Slugs of the subdiagnostic - corresponds to the Fluent identifier for the message - from the + /// `#[kind(slug)]` attribute on the type or variant. + slugs: Vec<(Path, proc_macro::Span)>, + /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]` + /// attribute on the type or variant. + code: Option<(TokenStream, proc_macro::Span)>, + /// Identifier for the binding to the `#[primary_span]` field. span_field: Option<(proc_macro2::Ident, proc_macro::Span)>, /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a /// `rustc_errors::Applicability::*` variant directly. applicability: Option<(TokenStream, proc_macro::Span)>, - - /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error - /// during finalization if still `false`. - has_suggestion_parts: bool, } impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { @@ -212,133 +217,124 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> { } impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { - fn identify_kind( - &mut self, - ) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> { - let mut kind_slug = None; - - for attr in self.variant.ast().attrs { + fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> { + for (i, attr) in self.variant.ast().attrs.iter().enumerate() { let span = attr.span().unwrap(); let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); let meta = attr.parse_meta()?; - let Meta::List(MetaList { ref nested, .. }) = meta else { - throw_invalid_attr!(attr, &meta); - }; - - let mut kind = match name { - "label" => SubdiagnosticKind::Label, - "note" => SubdiagnosticKind::Note, - "help" => SubdiagnosticKind::Help, - "warning" => SubdiagnosticKind::Warn, - _ => { - if let Some(suggestion_kind) = - name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) - { - SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() } - } else if let Some(suggestion_kind) = - name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) - { - SubdiagnosticKind::MultipartSuggestion { suggestion_kind } - } else { - throw_invalid_attr!(attr, &meta); + let kind = match meta { + Meta::List(MetaList { ref nested, .. }) => { + let mut nested_iter = nested.into_iter(); + if let Some(nested_attr) = nested_iter.next() { + match nested_attr { + NestedMeta::Meta(Meta::Path(path)) => { + self.slugs.push((path.clone(), span)); + } + NestedMeta::Meta(meta @ Meta::NameValue(_)) + if matches!( + meta.path().segments.last().unwrap().ident.to_string().as_str(), + "code" | "applicability" + ) => + { + // don't error for valid follow-up attributes + } + nested_attr => { + throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "first argument of the attribute should be the diagnostic \ + slug", + ) + }) + } + }; } - } - }; - let mut slug = None; - let mut code = None; - - let mut nested_iter = nested.into_iter(); - if let Some(nested_attr) = nested_iter.next() { - match nested_attr { - NestedMeta::Meta(Meta::Path(path)) => { - slug.set_once((path.clone(), span)); - } - NestedMeta::Meta(meta @ Meta::NameValue(_)) - if matches!( - meta.path().segments.last().unwrap().ident.to_string().as_str(), - "code" | "applicability" - ) => - { - // Don't error for valid follow-up attributes. - } - nested_attr => { - throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "first argument of the attribute should be the diagnostic \ - slug", - ) - }) + for nested_attr in nested_iter { + let meta = match nested_attr { + NestedMeta::Meta(ref meta) => meta, + _ => throw_invalid_nested_attr!(attr, &nested_attr), + }; + + let span = meta.span().unwrap(); + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + + match meta { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { + match nested_name { + "code" => { + let formatted_str = self.build_format(&s.value(), s.span()); + self.code.set_once((formatted_str, span)); + } + "applicability" => { + let value = match Applicability::from_str(&s.value()) { + Ok(v) => v, + Err(()) => { + span_err(span, "invalid applicability").emit(); + Applicability::Unspecified + } + }; + self.applicability.set_once((quote! { #value }, span)); + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help( + "only `code` and `applicability` are valid nested \ + attributes", + ) + }), + } + } + _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + if matches!(meta, Meta::Path(_)) { + diag.help( + "a diagnostic slug must be the first argument to the \ + attribute", + ) + } else { + diag + } + }), + } } - }; - } - for nested_attr in nested_iter { - let meta = match nested_attr { - NestedMeta::Meta(ref meta) => meta, - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; + let Ok(kind) = SubdiagnosticKind::from_str(name) else { + throw_invalid_attr!(attr, &meta) + }; - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); + kind + } + _ => throw_invalid_attr!(attr, &meta), + }; - let value = match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, - Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("a diagnostic slug must be the first argument to the attribute") - }), - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; + if matches!( + kind, + SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note + ) && self.code.is_some() + { + throw_span_err!( + span, + &format!("`code` is not a valid nested attribute of a `{}` attribute", name) + ); + } - match nested_name { - "code" => { - if matches!(kind, SubdiagnosticKind::Suggestion { .. }) { - let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once((formatted_str, span)); - } else { - span_err( - span, - &format!( - "`code` is not a valid nested attribute of a `{}` attribute", - name - ), - ) - .emit(); - } - } - "applicability" => { - if matches!( - kind, - SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. } - ) { - let value = - Applicability::from_str(&value.value()).unwrap_or_else(|()| { - span_err(span, "invalid applicability").emit(); - Applicability::Unspecified - }); - self.applicability.set_once((quote! { #value }, span)); - } else { - span_err( - span, - &format!( - "`applicability` is not a valid nested attribute of a `{}` attribute", - name - ) - ).emit(); - } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("only `code` and `applicability` are valid nested attributes") - }), - } + if matches!( + kind, + SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note + ) && self.applicability.is_some() + { + throw_span_err!( + span, + &format!( + "`applicability` is not a valid nested attribute of a `{}` attribute", + name + ) + ); } - let Some((slug, _)) = slug else { + if self.slugs.len() != i + 1 { throw_span_err!( span, &format!( @@ -346,338 +342,146 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { name ) ); - }; - - match kind { - SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { - let Some((code, _)) = code else { - throw_span_err!(span, "suggestion without `code = \"...\"`"); - }; - *code_field = code; - } - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::MultipartSuggestion { .. } => {} } - kind_slug.set_once(((kind, slug), span)) + self.kinds.push((kind, span)); } - Ok(kind_slug.map(|(kind_slug, _)| kind_slug)) - } - - /// Generates the code for a field with no attributes. - fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream { - let ast = binding.ast(); - assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg"); - - let diag = &self.diag; - let ident = ast.ident.as_ref().unwrap(); - quote! { - #diag.set_arg( - stringify!(#ident), - #binding - ); - } + Ok(()) } - /// Generates the necessary code for all attributes on a field. - fn generate_field_attr_code( + fn generate_field_code( &mut self, binding: &BindingInfo<'_>, - kind: &SubdiagnosticKind, - ) -> TokenStream { + have_suggestion: bool, + ) -> Result<TokenStream, DiagnosticDeriveError> { let ast = binding.ast(); - assert!(ast.attrs.len() > 0, "field without attributes generating attr code"); - // Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will - // apply the generated code on each element in the `Vec` or `Option`. let inner_ty = FieldInnerTy::from_type(&ast.ty); - ast.attrs - .iter() - .map(|attr| { - let info = FieldInfo { - binding, - ty: inner_ty.inner_type().unwrap_or(&ast.ty), - span: &ast.span(), - }; - - let generated = self - .generate_field_code_inner(kind, attr, info) - .unwrap_or_else(|v| v.to_compile_error()); - - inner_ty.with(binding, generated) - }) - .collect() - } - - fn generate_field_code_inner( - &mut self, - kind: &SubdiagnosticKind, - attr: &Attribute, - info: FieldInfo<'_>, - ) -> Result<TokenStream, DiagnosticDeriveError> { - let meta = attr.parse_meta()?; - match meta { - Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path), - Meta::List(list @ MetaList { .. }) => { - self.generate_field_code_inner_list(kind, attr, info, list) - } - _ => throw_invalid_attr!(attr, &meta), - } - } - - /// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`). - fn generate_field_code_inner_path( - &mut self, - kind: &SubdiagnosticKind, - attr: &Attribute, - info: FieldInfo<'_>, - path: Path, - ) -> Result<TokenStream, DiagnosticDeriveError> { - let span = attr.span().unwrap(); - let ident = &path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - - match name { - "skip_arg" => Ok(quote! {}), - "primary_span" => { - if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - diag.help( - "multipart suggestions use one or more `#[suggestion_part]`s rather \ - than one `#[primary_span]`", - ) - }) - } - - report_error_if_not_applied_to_span(attr, &info)?; + let info = FieldInfo { + binding: binding, + ty: inner_ty.inner_type().unwrap_or(&ast.ty), + span: &ast.span(), + }; - let binding = info.binding.binding.clone(); - self.span_field.set_once((binding, span)); + for attr in &ast.attrs { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + let span = attr.span().unwrap(); - Ok(quote! {}) - } - "suggestion_part" => { - self.has_suggestion_parts = true; - - match kind { - SubdiagnosticKind::MultipartSuggestion { .. } => { - span_err( - span, - "`#[suggestion_part(...)]` attribute without `code = \"...\"`", - ) - .emit(); - Ok(quote! {}) + let meta = attr.parse_meta()?; + match meta { + Meta::Path(_) => match name { + "primary_span" => { + report_error_if_not_applied_to_span(attr, &info)?; + self.span_field.set_once((binding.binding.clone(), span)); + return Ok(quote! {}); } - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::Suggestion { .. } => { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - diag.help( - "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead", - ) - }); + "applicability" if have_suggestion => { + report_error_if_not_applied_to_applicability(attr, &info)?; + let binding = binding.binding.clone(); + self.applicability.set_once((quote! { #binding }, span)); + return Ok(quote! {}); } - } - } - "applicability" => { - if let SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. } = kind - { - report_error_if_not_applied_to_applicability(attr, &info)?; - - let binding = info.binding.binding.clone(); - self.applicability.set_once((quote! { #binding }, span)); - } else { - span_err(span, "`#[applicability]` is only valid on suggestions").emit(); - } - - Ok(quote! {}) - } - _ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { - "suggestion_part" - } else { - "primary_span" - }; - diag.help(format!( - "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes", - )) - }), - } - } - - /// Generates the code for a `[Meta::List]`-like attribute on a field (e.g. - /// `#[suggestion_part(code = "...")]`). - fn generate_field_code_inner_list( - &mut self, - kind: &SubdiagnosticKind, - attr: &Attribute, - info: FieldInfo<'_>, - list: MetaList, - ) -> Result<TokenStream, DiagnosticDeriveError> { - let span = attr.span().unwrap(); - let ident = &list.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - - match name { - "suggestion_part" => { - if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) { - throw_invalid_attr!(attr, &Meta::List(list), |diag| { + "applicability" => { + span_err(span, "`#[applicability]` is only valid on suggestions").emit(); + return Ok(quote! {}); + } + "skip_arg" => { + return Ok(quote! {}); + } + _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help( - "`#[suggestion_part(...)]` is only valid in multipart suggestions", + "only `primary_span`, `applicability` and `skip_arg` are valid field \ + attributes", ) - }) - } - - self.has_suggestion_parts = true; - - report_error_if_not_applied_to_span(attr, &info)?; - - let mut code = None; - for nested_attr in list.nested.iter() { - let NestedMeta::Meta(ref meta) = nested_attr else { - throw_invalid_nested_attr!(attr, &nested_attr); - }; - - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else { - throw_invalid_nested_attr!(attr, &nested_attr); - }; + }), + }, + _ => throw_invalid_attr!(attr, &meta), + } + } - match nested_name { - "code" => { - let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once((formatted_str, span)); - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`code` is the only valid nested attribute") - }), - } - } + let ident = ast.ident.as_ref().unwrap(); - let Some((code, _)) = code else { - span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") - .emit(); - return Ok(quote! {}); - }; - let binding = info.binding; + let diag = &self.diag; + let generated = quote! { + #diag.set_arg( + stringify!(#ident), + #binding + ); + }; - Ok(quote! { suggestions.push((#binding, #code)); }) - } - _ => throw_invalid_attr!(attr, &Meta::List(list), |diag| { - let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { - "suggestion_part" - } else { - "primary_span" - }; - diag.help(format!( - "only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes", - )) - }), - } + Ok(inner_ty.with(binding, generated)) } - pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { - let Some((kind, slug)) = self.identify_kind()? else { + fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { + self.identify_kind()?; + if self.kinds.is_empty() { throw_span_err!( self.variant.ast().ident.span().unwrap(), "subdiagnostic kind not specified" ); }; + let have_suggestion = + self.kinds.iter().any(|(k, _)| matches!(k, SubdiagnosticKind::Suggestion(_))); + let mut args = TokenStream::new(); + for binding in self.variant.bindings() { + let arg = self + .generate_field_code(binding, have_suggestion) + .unwrap_or_else(|v| v.to_compile_error()); + args.extend(arg); + } + let mut tokens = TokenStream::new(); + for ((kind, _), (slug, _)) in self.kinds.iter().zip(&self.slugs) { + let code = match self.code.as_ref() { + Some((code, _)) => Some(quote! { #code }), + None if have_suggestion => { + span_err(self.span, "suggestion without `code = \"...\"`").emit(); + Some(quote! { /* macro error */ "..." }) + } + None => None, + }; - let init = match &kind { - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::Suggestion { .. } => quote! {}, - SubdiagnosticKind::MultipartSuggestion { .. } => { - quote! { let mut suggestions = Vec::new(); } - } - }; - - let attr_args: TokenStream = self - .variant - .bindings() - .iter() - .filter(|binding| !binding.ast().attrs.is_empty()) - .map(|binding| self.generate_field_attr_code(binding, &kind)) - .collect(); - - let span_field = self.span_field.as_ref().map(|(span, _)| span); - let applicability = self.applicability.take().map_or_else( - || quote! { rustc_errors::Applicability::Unspecified }, - |(applicability, _)| applicability, - ); + let span_field = self.span_field.as_ref().map(|(span, _)| span); + let applicability = match self.applicability.clone() { + Some((applicability, _)) => Some(applicability), + None if have_suggestion => { + span_err(self.span, "suggestion without `applicability`").emit(); + Some(quote! { rustc_errors::Applicability::Unspecified }) + } + None => None, + }; - let diag = &self.diag; - let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); - let message = quote! { rustc_errors::fluent::#slug }; - let call = match kind { - SubdiagnosticKind::Suggestion { suggestion_kind, code } => { + let diag = &self.diag; + let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); + let message = quote! { rustc_errors::fluent::#slug }; + let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) { if let Some(span) = span_field { - let style = suggestion_kind.to_suggestion_style(); - - quote! { #diag.#name(#span, #message, #code, #applicability, #style); } + quote! { #diag.#name(#span, #message, #code, #applicability); } } else { span_err(self.span, "suggestion without `#[primary_span]` field").emit(); quote! { unreachable!(); } } - } - SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => { - if !self.has_suggestion_parts { - span_err( - self.span, - "multipart suggestion without any `#[suggestion_part(...)]` fields", - ) - .emit(); - } - - let style = suggestion_kind.to_suggestion_style(); - - quote! { #diag.#name(#message, suggestions, #applicability, #style); } - } - SubdiagnosticKind::Label => { + } else if matches!(kind, SubdiagnosticKind::Label) { if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } } else { span_err(self.span, "label without `#[primary_span]` field").emit(); quote! { unreachable!(); } } - } - _ => { + } else { if let Some(span) = span_field { quote! { #diag.#name(#span, #message); } } else { quote! { #diag.#name(#message); } } - } - }; + }; + tokens.extend(quote! { + #call + #args + }); + } - let plain_args: TokenStream = self - .variant - .bindings() - .iter() - .filter(|binding| binding.ast().attrs.is_empty()) - .map(|binding| self.generate_field_set_arg(binding)) - .collect(); - - Ok(quote! { - #init - #attr_args - #call - #plain_args - }) + Ok(tokens) } } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 708d0b1fd8a..6a5716600b3 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -29,7 +29,6 @@ use proc_macro::bridge::client::ProcMacro; use std::ops::Fn; use std::path::Path; use std::{cmp, env}; -use tracing::{debug, info}; #[derive(Clone)] pub struct CStore { @@ -263,7 +262,7 @@ impl<'a> CrateLoader<'a> { fn existing_match(&self, name: Symbol, hash: Option<Svh>, kind: PathKind) -> Option<CrateNum> { for (cnum, data) in self.cstore.iter_crate_data() { if data.name() != name { - tracing::trace!("{} did not match {}", data.name(), name); + trace!("{} did not match {}", data.name(), name); continue; } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index b765c34f8e3..1a25e987d3a 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -158,11 +158,11 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let name = tcx.crate_name(cnum); let src = tcx.used_crate_source(cnum); if src.dylib.is_some() { - tracing::info!("adding dylib: {}", name); + info!("adding dylib: {}", name); add_library(tcx, cnum, RequireDynamic, &mut formats); let deps = tcx.dylib_dependency_formats(cnum); for &(depnum, style) in deps.iter() { - tracing::info!("adding {:?}: {}", style, tcx.crate_name(depnum)); + info!("adding {:?}: {}", style, tcx.crate_name(depnum)); add_library(tcx, depnum, style, &mut formats); } } @@ -190,7 +190,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { && tcx.dep_kind(cnum) == CrateDepKind::Explicit { assert!(src.rlib.is_some() || src.rmeta.is_some()); - tracing::info!("adding staticlib: {}", tcx.crate_name(cnum)); + info!("adding staticlib: {}", tcx.crate_name(cnum)); add_library(tcx, cnum, RequireStatic, &mut formats); ret[cnum.as_usize() - 1] = Linkage::Static; } diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 6440f3e390c..337d3cca2ae 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -26,6 +26,9 @@ extern crate rustc_middle; #[macro_use] extern crate rustc_data_structures; +#[macro_use] +extern crate tracing; + pub use rmeta::{provide, provide_extern}; mod dependency_format; diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 2c1c84b0be2..5b7d0c8581a 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -236,7 +236,6 @@ use std::fmt::Write as _; use std::io::{Read, Result as IoResult, Write}; use std::path::{Path, PathBuf}; use std::{cmp, fmt, fs}; -use tracing::{debug, info}; #[derive(Clone)] pub(crate) struct CrateLocator<'a> { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index d0e0aa91480..b28f54fac1a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -42,7 +42,6 @@ use std::iter::TrustedLen; use std::mem; use std::num::NonZeroUsize; use std::path::Path; -use tracing::debug; pub(super) use cstore_impl::provide; pub use cstore_impl::provide_extern; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3482d9f0451..34d8edc30cd 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -44,7 +44,6 @@ use std::io::{Read, Seek, Write}; use std::iter; use std::num::NonZeroUsize; use std::path::{Path, PathBuf}; -use tracing::{debug, trace}; pub(super) struct EncodeContext<'a, 'tcx> { opaque: opaque::FileEncoder, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 21841ae2532..8085675d75c 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -10,7 +10,6 @@ use rustc_span::hygiene::MacroKind; use std::convert::TryInto; use std::marker::PhantomData; use std::num::NonZeroUsize; -use tracing::debug; /// Helper trait, for encoding to, and decoding from, a fixed number of bytes. /// Used mainly for Lazy positions and lengths. diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 008d2c7091c..cca17a4eccd 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -7,34 +7,35 @@ edition = "2021" doctest = false [dependencies] -rustc_arena = { path = "../rustc_arena" } bitflags = "1.2.1" +chalk-ir = "0.80.0" either = "1.5.0" gsgdt = "0.1.2" -tracing = "0.1" -rustc-rayon = { version = "0.4.0", optional = true } -rustc-rayon-core = { version = "0.4.0", optional = true } polonius-engine = "0.13.0" +rand = "0.8.4" +rand_xoshiro = "0.6.0" rustc_apfloat = { path = "../rustc_apfloat" } +rustc_arena = { path = "../rustc_arena" } +rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } -rustc_feature = { path = "../rustc_feature" } -rustc_hir = { path = "../rustc_hir" } -rustc_target = { path = "../rustc_target" } -rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } -rustc_query_system = { path = "../rustc_query_system" } rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } rustc_graphviz = { path = "../rustc_graphviz" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } +rustc_macros = { path = "../rustc_macros" } +rustc_query_system = { path = "../rustc_query_system" } +rustc-rayon-core = { version = "0.4.0", optional = true } +rustc-rayon = { version = "0.4.0", optional = true } rustc_serialize = { path = "../rustc_serialize" } -rustc_ast = { path = "../rustc_ast" } -rustc_span = { path = "../rustc_span" } -chalk-ir = "0.80.0" -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } rustc_type_ir = { path = "../rustc_type_ir" } -rand = "0.8.4" -rand_xoshiro = "0.6.0" +smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" +tracing = "0.1" [features] rustc_use_parallel_compiler = ["rustc-rayon", "rustc-rayon-core"] diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 78080fcd581..752cbdeae6b 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -86,7 +86,7 @@ impl<'tcx> BasicBlocks<'tcx> { /// /// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`]. /// All other methods that allow you to mutate the basic blocks also call this method - /// themselves, thereby avoiding any risk of accidentaly cache invalidation. + /// themselves, thereby avoiding any risk of accidentally cache invalidation. pub fn invalidate_cfg_cache(&mut self) { self.predecessor_cache.invalidate(); self.switch_source_cache.invalidate(); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 7784449d605..f3676604bb0 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1457,7 +1457,7 @@ pub struct PlaceRef<'tcx> { // Once we stop implementing `Ord` for `DefId`, // this impl will be unnecessary. Until then, we'll // leave this impl in place to prevent re-adding a -// dependnecy on the `Ord` impl for `DefId` +// dependency on the `Ord` impl for `DefId` impl<'tcx> !PartialOrd for PlaceRef<'tcx> {} impl<'tcx> Place<'tcx> { @@ -2267,7 +2267,7 @@ impl<'tcx> ConstantKind<'tcx> { Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id), param_env) } - #[instrument(skip(tcx), level = "debug")] + #[instrument(skip(tcx), level = "debug", ret)] pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let body_id = match tcx.hir().get(hir_id) { @@ -2305,21 +2305,18 @@ impl<'tcx> ConstantKind<'tcx> { let substs = ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty }) .substs; - let uneval_const = tcx.mk_const(ty::ConstS { + debug_assert!(!substs.has_free_regions()); + Self::Ty(tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Unevaluated(ty::Unevaluated { def: ty::WithOptConstParam::unknown(def_id).to_global(), substs, promoted: None, }), ty, - }); - debug!(?uneval_const); - debug_assert!(!uneval_const.has_free_regions()); - - Self::Ty(uneval_const) + })) } - #[instrument(skip(tcx), level = "debug")] + #[instrument(skip(tcx), level = "debug", ret)] fn from_opt_const_arg_anon_const( tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>, @@ -2402,24 +2399,21 @@ impl<'tcx> ConstantKind<'tcx> { match tcx.const_eval_resolve(param_env, uneval, Some(span)) { Ok(val) => { - debug!("evaluated const value: {:?}", val); + debug!("evaluated const value"); Self::Val(val, ty) } Err(_) => { debug!("error encountered during evaluation"); // Error was handled in `const_eval_resolve`. Here we just create a // new unevaluated const and error hard later in codegen - let ty_const = tcx.mk_const(ty::ConstS { + Self::Ty(tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Unevaluated(ty::Unevaluated { def: def.to_global(), substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()), promoted: None, }), ty, - }); - debug!(?ty_const); - - Self::Ty(ty_const) + })) } } } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 3426f5f43f0..d7b9d59eced 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -332,7 +332,7 @@ pub enum StatementKind<'tcx> { /// First, all three operands are evaluated. `src` and `dest` must each be a reference, pointer, /// or `Box` pointing to the same type `T`. `count` must evaluate to a `usize`. Then, `src` and /// `dest` are dereferenced, and `count * size_of::<T>()` bytes beginning with the first byte of - /// the `src` place are copied to the continguous range of bytes beginning with the first byte + /// the `src` place are copied to the contiguous range of bytes beginning with the first byte /// of `dest`. /// /// **Needs clarification**: In what order are operands computed and dereferenced? It should @@ -378,7 +378,7 @@ pub enum FakeReadCause { /// Some(closure_def_id). /// Otherwise, the value of the optional LocalDefId will be None. // - // We can use LocaDefId here since fake read statements are removed + // We can use LocalDefId here since fake read statements are removed // before codegen in the `CleanupNonCodegenStatements` pass. ForMatchedPlace(Option<LocalDefId>), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index ddca9820da1..abaef0354ad 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1151,7 +1151,7 @@ rustc_queries! { /// Used by rustdoc. query rendered_const(def_id: DefId) -> String { storage(ArenaCacheSelector<'tcx>) - desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) } + desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } cache_on_disk_if { def_id.is_local() } separate_provide_extern } diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 2465f8e2533..0a2819feecf 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -115,7 +115,7 @@ impl Node { matches!(self, Node::Trait(..)) } - /// Trys to find the associated item that implements `trait_item_def_id` + /// Tries to find the associated item that implements `trait_item_def_id` /// defined in this node. /// /// If this returns `None`, the item can potentially still be found in diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index f8792edc017..2eb5cffa6bc 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -65,8 +65,6 @@ impl<'tcx> Const<'tcx> { tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>, ) -> Self { - debug!("Const::from_anon_const(def={:?})", def); - let body_id = match tcx.hir().get_by_def_id(def.did) { hir::Node::AnonConst(ac) => ac.body, _ => span_bug!( diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0f34aa10a12..052bb1263c9 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1498,17 +1498,17 @@ impl<'tcx> TyCtxt<'tcx> { // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. self.ensure().hir_crate(()); - // Leak a read lock once we start iterating on definitions, to prevent adding new onces + // Leak a read lock once we start iterating on definitions, to prevent adding new ones // while iterating. If some query needs to add definitions, it should be `ensure`d above. let definitions = self.definitions.leak(); definitions.iter_local_def_id() } pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable { - // Create a dependency to the crate to be sure we reexcute this when the amount of + // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. self.ensure().hir_crate(()); - // Leak a read lock once we start iterating on definitions, to prevent adding new onces + // Leak a read lock once we start iterating on definitions, to prevent adding new ones // while iterating. If some query needs to add definitions, it should be `ensure`d above. let definitions = self.definitions.leak(); definitions.def_path_table() @@ -1517,10 +1517,10 @@ impl<'tcx> TyCtxt<'tcx> { pub fn def_path_hash_to_def_index_map( self, ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { - // Create a dependency to the crate to be sure we reexcute this when the amount of + // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. self.ensure().hir_crate(()); - // Leak a read lock once we start iterating on definitions, to prevent adding new onces + // Leak a read lock once we start iterating on definitions, to prevent adding new ones // while iterating. If some query needs to add definitions, it should be `ensure`d above. let definitions = self.definitions.leak(); definitions.def_path_hash_to_def_index_map() @@ -1829,9 +1829,9 @@ pub mod tls { use crate::dep_graph::TaskDepsRef; use crate::ty::query; use rustc_data_structures::sync::{self, Lock}; - use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; use std::mem; + use thin_vec::ThinVec; #[cfg(not(parallel_compiler))] use std::cell::Cell; diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 5e96e278b9c..cb46a9dba57 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -353,7 +353,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx> { t } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { ty::ReLateBound(debruijn, _) if debruijn < self.current_index => { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 42b5d5a6efd..a1d980af921 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -266,7 +266,7 @@ impl<'tcx> Generics { // Filter the default arguments. // // This currently uses structural equality instead - // of semantic equivalance. While not ideal, that's + // of semantic equivalence. While not ideal, that's // good enough for now as this should only be used // for diagnostics anyways. own_params.end -= self diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 980bb8e8615..26b60e4f339 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -756,7 +756,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { // * the element type and length of the single array field, if // the first field is of array type, or // - // * the homogenous field type and the number of fields. + // * the homogeneous field type and the number of fields. let (e_ty, e_len, is_array) = if let ty::Array(e_ty, _) = f0_ty.kind() { // First ADT field is an array: diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 9d8a8116594..cac8560ce1c 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -188,13 +188,11 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> { } impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> { - #[instrument(skip(self), level = "debug")] fn normalize_generic_arg_after_erasing_regions( &self, arg: ty::GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { let arg = self.param_env.and(arg); - debug!(?arg); self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| bug!( "Failed to normalize {:?}, maybe try to call `try_normalize_erasing_regions` instead", diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 541dace5cc2..ac79949fca5 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -256,7 +256,6 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait } // Query provider for `incoherent_impls`. -#[instrument(level = "debug", skip(tcx))] pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { let mut impls = Vec::new(); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 591bb7831b5..a3837512bce 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -627,7 +627,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Expands the given impl trait type, stopping if the type is recursive. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub fn try_expand_impl_trait_type( self, def_id: DefId, @@ -644,7 +644,6 @@ impl<'tcx> TyCtxt<'tcx> { }; let expanded_type = visitor.expand_opaque_ty(def_id, substs).unwrap(); - trace!(?expanded_type); if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) } } diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 5365067209a..5e042c3acfc 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -84,7 +84,7 @@ pub trait TypeVisitable<'tcx>: fmt::Debug + Clone { self.has_vars_bound_at_or_above(ty::INNERMOST) } - #[instrument(level = "trace")] + #[instrument(level = "trace", ret)] fn has_type_flags(&self, flags: TypeFlags) -> bool { self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags) } @@ -560,7 +560,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { type BreakTy = FoundFlags; #[inline] - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { let flags = t.flags(); trace!(t.flags=?t.flags()); @@ -572,7 +572,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(skip(self), level = "trace")] + #[instrument(skip(self), level = "trace", ret)] fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { let flags = r.type_flags(); trace!(r.flags=?flags); @@ -584,7 +584,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(level = "trace")] + #[instrument(level = "trace", ret)] fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { let flags = FlagComputation::for_const(c); trace!(r.flags=?flags); @@ -596,7 +596,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(level = "trace")] + #[instrument(level = "trace", ret)] fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { let flags = FlagComputation::for_unevaluated_const(uv); trace!(r.flags=?flags); @@ -608,7 +608,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { } #[inline] - #[instrument(level = "trace")] + #[instrument(level = "trace", ret)] fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> { debug!( "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 080dab03031..0e5cd6199ac 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// * From each pre-binding block to the next pre-binding block. /// * From each otherwise block to the next pre-binding block. - #[tracing::instrument(level = "debug", skip(self, arms))] + #[instrument(level = "debug", skip(self, arms))] pub(crate) fn match_expr( &mut self, destination: Place<'tcx>, @@ -702,7 +702,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local_id = self.var_local_id(var, for_guard); let source_info = self.source_info(span); self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); - // Altough there is almost always scope for given variable in corner cases + // Although there is almost always scope for given variable in corner cases // like #92893 we might get variable with no scope. if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id) && schedule_drop{ self.schedule_drop(span, region_scope, local_id, DropKind::Storage); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 0c2b117453f..9ed1c064d2b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -268,7 +268,7 @@ impl<'tcx> Cx<'tcx> { // the overall method call for better diagnostics. args[0] // is guaranteed to exist, since a method call always has a receiver. let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span)); - tracing::info!("Using method span: {:?}", expr.span); + info!("Using method span: {:?}", expr.span); let args = self.mirror_exprs(args); self.adjustment_span = old_adjustment_span; ExprKind::Call { diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index f7351a4caa9..b84a84976c7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -77,7 +77,7 @@ impl<'tcx> Cx<'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] pub(crate) fn pattern_from_hir(&mut self, p: &hir::Pat<'_>) -> Pat<'tcx> { let p = match self.tcx.hir().get(p.hir_id) { Node::Pat(p) => p, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index f2045ac19ca..210d77c66e7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -19,7 +19,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(super) fn const_to_pat( &self, cv: mir::ConstantKind<'tcx>, @@ -27,13 +27,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span: Span, mir_structural_match_violation: bool, ) -> Pat<'tcx> { - let pat = self.tcx.infer_ctxt().enter(|infcx| { + self.tcx.infer_ctxt().enter(|infcx| { let mut convert = ConstToPat::new(self, id, span, infcx); convert.to_pat(cv, mir_structural_match_violation) - }); - - debug!(?pat); - pat + }) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 443626d14b9..319183eb9b3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -791,7 +791,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( /// `is_under_guard` is used to inform if the pattern has a guard. If it /// has one it must not be inserted into the matrix. This shouldn't be /// relied on for soundness. -#[instrument(level = "debug", skip(cx, matrix, hir_id))] +#[instrument(level = "debug", skip(cx, matrix, hir_id), ret)] fn is_useful<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, @@ -917,7 +917,6 @@ fn is_useful<'p, 'tcx>( v.head().set_reachable(); } - debug!(?ret); ret } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 170616d4b42..6ec5e9e113d 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -419,7 +419,6 @@ fn collect_items_rec<'tcx>( // We've been here already, no need to search again. return; } - debug!("BEGIN collect_items_rec({})", starting_point.node); let mut neighbors = MonoItems { compute_inlining: true, tcx, items: Vec::new() }; let recursion_depth_reset; @@ -545,8 +544,6 @@ fn collect_items_rec<'tcx>( if let Some((def_id, depth)) = recursion_depth_reset { recursion_depths.insert(def_id, depth); } - - debug!("END collect_items_rec({})", starting_point.node); } /// Format instance name that is already known to be too long for rustc. @@ -1148,23 +1145,18 @@ fn find_vtable_types_for_unsizing<'tcx>( } } -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(tcx), level = "debug", ret)] fn create_fn_mono_item<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, source: Span, ) -> Spanned<MonoItem<'tcx>> { - debug!("create_fn_mono_item(instance={})", instance); - let def_id = instance.def_id(); if tcx.sess.opts.unstable_opts.profile_closures && def_id.is_local() && tcx.is_closure(def_id) { crate::util::dump_closure_profile(tcx, instance); } - let respanned = respan(source, MonoItem::Fn(instance.polymorphize(tcx))); - debug!(?respanned); - - respanned + respan(source, MonoItem::Fn(instance.polymorphize(tcx))) } /// Creates a `MonoItem` for each method that is referenced by the vtable for @@ -1309,7 +1301,7 @@ impl<'v> RootCollector<'_, 'v> { #[instrument(skip(self), level = "debug")] fn push_if_root(&mut self, def_id: LocalDefId) { if self.is_root(def_id) { - debug!("RootCollector::push_if_root: found root def_id={:?}", def_id); + debug!("found root"); let instance = Instance::mono(self.tcx, def_id.to_def_id()); self.output.push(create_fn_mono_item(self.tcx, instance, DUMMY_SP)); diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 6e4ab2a35c3..af4b35db3ba 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -33,7 +33,6 @@ pub fn provide(providers: &mut Providers) { /// /// Returns a bitset where bits representing unused parameters are set (`is_empty` indicates all /// parameters are used). -#[instrument(level = "debug", skip(tcx))] fn unused_generic_params<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 847e64dc2a2..6a4d2df1ead 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -13,7 +13,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In .append(true) .open(&format!("closure_profile_{}.csv", std::process::id())) else { - eprintln!("Cound't open file for writing closure profile"); + eprintln!("Couldn't open file for writing closure profile"); return; }; diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 848e142e59c..63819a2f98d 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -14,8 +14,6 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{edition::Edition, BytePos, Pos, Span}; -use tracing::debug; - mod tokentrees; mod unescape_error_reporting; mod unicode_chars; diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 273827864f1..77c4fadab45 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -20,13 +20,9 @@ pub(crate) fn emit_unescape_error( range: Range<usize>, error: EscapeError, ) { - tracing::debug!( + debug!( "emit_unescape_error: {:?}, {:?}, {:?}, {:?}, {:?}", - lit, - span_with_quotes, - mode, - range, - error + lit, span_with_quotes, mode, range, error ); let last_char = || { let c = lit[range.clone()].chars().rev().next().unwrap(); diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 72ab96b5ca6..77a6bde1c16 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -7,8 +7,6 @@ use rustc_errors::{error_code, Diagnostic, PResult}; use rustc_span::{sym, BytePos, Span}; use std::convert::TryInto; -use tracing::debug; - // Public for rustfmt usage #[derive(Debug)] pub enum InnerAttrPolicy<'a> { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b564f4ad92c..f353ee6df9b 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -355,7 +355,7 @@ impl<'a> Parser<'a> { && matches!(self.capture_state.capturing, Capturing::Yes) && has_cfg_or_cfg_attr(final_attrs) { - let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens }; + let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens }; // Replace the entire AST node that we just parsed, including attributes, // with a `FlatToken::AttrTarget`. If this AST node is inside an item diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index f0ea1dfe297..dd806e2130e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -29,7 +29,6 @@ use std::ops::{Deref, DerefMut}; use std::mem::take; use crate::parser; -use tracing::{debug, trace}; const TURBOFISH_SUGGESTION_STR: &str = "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments"; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c8541609514..d4828a20120 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -944,13 +944,18 @@ impl<'a> Parser<'a> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| { - expr.map(|mut expr| { - attrs.extend(expr.attrs); - expr.attrs = attrs; - expr + let res = self.parse_dot_or_call_expr_with_(e0, lo); + if attrs.is_empty() { + res + } else { + res.map(|expr| { + expr.map(|mut expr| { + attrs.extend(expr.attrs); + expr.attrs = attrs; + expr + }) }) - }) + } } fn parse_dot_or_call_expr_with_(&mut self, mut e: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> { @@ -1578,7 +1583,7 @@ impl<'a> Parser<'a> { Applicability::MachineApplicable, ); - // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`. + // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`. let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span); self.mk_expr(span, ExprKind::Block(blk, label)) @@ -2578,7 +2583,7 @@ impl<'a> Parser<'a> { } pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> { - // Used to check the `let_chains` and `if_let_guard` features mostly by scaning + // Used to check the `let_chains` and `if_let_guard` features mostly by scanning // `&&` tokens. fn check_let_expr(expr: &Expr) -> (bool, bool) { match expr.kind { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index b743162a7e4..5b75d1d5f22 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -22,7 +22,6 @@ use rustc_span::DUMMY_SP; use std::convert::TryFrom; use std::mem; -use tracing::debug; impl<'a> Parser<'a> { /// Parses a source module as a crate. This is the main entry point for the parser. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index f6516d3bd45..5c8f374255c 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -37,7 +37,6 @@ use rustc_errors::{ use rustc_session::parse::ParseSess; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use tracing::debug; use std::ops::Range; use std::{cmp, mem, slice}; @@ -281,7 +280,7 @@ impl TokenCursor { if delim != Delimiter::Invisible { return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone); } - // No open delimeter to return; continue on to the next iteration. + // No open delimiter to return; continue on to the next iteration. } }; } else if let Some(frame) = self.stack.pop() { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index fc7fb866f11..fdc1af27f82 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -13,7 +13,6 @@ use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym, Ident}; use std::mem; -use tracing::debug; /// Specifies how to parse a path. #[derive(Copy, Clone, PartialEq)] diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 399d00b403a..fb1d724d9d8 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -49,7 +49,7 @@ impl Node { /// /// For example, `ast::Visitor` has `visit_ident`, but `Ident`s are always /// stored inline within other AST nodes, so we don't implement `visit_ident` -/// here. In constrast, we do implement `visit_expr` because `ast::Expr` is +/// here. In contrast, we do implement `visit_expr` because `ast::Expr` is /// always stored as `P<ast::Expr>`, and every such expression should be /// measured separately. /// diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 5b79dd3d3ef..2a6889af7c2 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1573,7 +1573,7 @@ impl<'tcx> Liveness<'_, 'tcx> { } } - #[tracing::instrument(skip(self), level = "INFO")] + #[instrument(skip(self), level = "INFO")] fn report_unused( &self, hir_ids_and_spans: Vec<(HirId, Span, Span)>, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5d562f18a81..a9271761358 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -8,6 +8,9 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#[macro_use] +extern crate tracing; + mod errors; use rustc_ast::MacroDef; @@ -1784,7 +1787,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { fn leaks_private_dep(&self, item_id: DefId) -> bool { let ret = self.required_visibility.is_public() && self.tcx.is_private_dep(item_id.krate); - tracing::debug!("leaks_private_dep(item_id={:?})={}", item_id, ret); + debug!("leaks_private_dep(item_id={:?})={}", item_id, ret); ret } } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index c37ae4f3253..e7f12caaf33 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -8,7 +8,6 @@ doctest = false [dependencies] measureme = "10.0.0" -rustc-rayon-core = { version = "0.4.0", optional = true } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -17,10 +16,12 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } +rustc-rayon-core = { version = "0.4.0", optional = true } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } +thin-vec = "0.2.8" tracing = "0.1" [features] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index eab627c5d4c..eabb3166147 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -4,6 +4,9 @@ use crate::keys::Key; use crate::{on_disk_cache, Queries}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lock; +use rustc_errors::{Diagnostic, Handler}; use rustc_middle::dep_graph::{self, DepKind, DepNodeIndex, SerializedDepNodeIndex}; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; @@ -12,14 +15,9 @@ use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame, }; - -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::{Diagnostic, Handler}; - use std::any::Any; use std::num::NonZeroU64; +use thin_vec::ThinVec; #[derive(Copy, Clone)] pub struct QueryCtxt<'tcx> { @@ -557,7 +555,7 @@ macro_rules! define_queries_struct { $($(#[$attr])* #[inline(always)] - #[tracing::instrument(level = "trace", skip(self, tcx))] + #[tracing::instrument(level = "trace", skip(self, tcx), ret)] fn $name( &'tcx self, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index b7787aeb8f7..bafc6b0a082 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -7,9 +7,8 @@ edition = "2021" doctest = false [dependencies] +parking_lot = "0.11" rustc_arena = { path = "../rustc_arena" } -tracing = "0.1" -rustc-rayon-core = { version = "0.4.0", optional = true } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -17,12 +16,14 @@ rustc_feature = { path = "../rustc_feature" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } +rustc-rayon-core = { version = "0.4.0", optional = true } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -parking_lot = "0.11" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" +tracing = "0.1" [features] rustc_use_parallel_compiler = ["rustc-rayon-core"] diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs new file mode 100644 index 00000000000..5f992ec9e21 --- /dev/null +++ b/compiler/rustc_query_system/src/error.rs @@ -0,0 +1,73 @@ +use rustc_errors::AddSubdiagnostic; +use rustc_span::Span; + +pub struct CycleStack { + pub span: Span, + pub desc: String, +} + +impl AddSubdiagnostic for CycleStack { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + diag.span_note(self.span, &format!("...which requires {}...", self.desc)); + } +} + +#[derive(SessionSubdiagnostic)] +pub enum StackCount { + #[note(query_system::cycle_stack_single)] + Single, + #[note(query_system::cycle_stack_multiple)] + Multiple, +} + +#[derive(SessionSubdiagnostic)] +pub enum Alias { + #[note(query_system::cycle_recursive_ty_alias)] + #[help(query_system::cycle_recursive_ty_alias_help1)] + #[help(query_system::cycle_recursive_ty_alias_help2)] + Ty, + #[note(query_system::cycle_recursive_trait_alias)] + Trait, +} + +#[derive(SessionSubdiagnostic)] +#[note(query_system::cycle_usage)] +pub struct CycleUsage { + #[primary_span] + pub span: Span, + pub usage: String, +} + +#[derive(SessionDiagnostic)] +#[diag(query_system::cycle, code = "E0391")] +pub struct Cycle { + #[primary_span] + pub span: Span, + pub stack_bottom: String, + #[subdiagnostic] + pub cycle_stack: Vec<CycleStack>, + #[subdiagnostic] + pub stack_count: StackCount, + #[subdiagnostic] + pub alias: Option<Alias>, + #[subdiagnostic] + pub cycle_usage: Option<CycleUsage>, +} + +#[derive(SessionDiagnostic)] +#[diag(query_system::reentrant)] +pub struct Reentrant; + +#[derive(SessionDiagnostic)] +#[diag(query_system::increment_compilation)] +#[help] +#[note(query_system::increment_compilation_note1)] +#[note(query_system::increment_compilation_note2)] +pub struct IncrementCompilation { + pub run_cmd: String, + pub dep_node: String, +} + +#[derive(SessionDiagnostic)] +#[diag(query_system::query_overflow)] +pub struct QueryOverflow; diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 68284dcaa0b..7067bc5f09c 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -5,6 +5,8 @@ #![feature(min_specialization)] #![feature(extern_types)] #![allow(rustc::potential_query_instability)] +// #![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -15,5 +17,6 @@ extern crate rustc_macros; pub mod cache; pub mod dep_graph; +mod error; pub mod ich; pub mod query; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 6d2aff38172..ddb5cd06344 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,12 +1,11 @@ +use crate::error::CycleStack; use crate::query::plumbing::CycleError; use crate::query::{QueryContext, QueryStackFrame}; -use rustc_hir::def::DefKind; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{ - struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level, -}; -use rustc_session::Session; +use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level}; +use rustc_hir::def::DefKind; +use rustc_session::{Session, SessionDiagnostic}; use rustc_span::Span; use std::hash::Hash; @@ -536,46 +535,44 @@ pub(crate) fn report_cycle<'a>( assert!(!stack.is_empty()); let span = stack[0].query.default_span(stack[1 % stack.len()].span); - let mut err = - struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description); + + let mut cycle_stack = Vec::new(); + + use crate::error::StackCount; + let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; for i in 1..stack.len() { let query = &stack[i].query; let span = query.default_span(stack[(i + 1) % stack.len()].span); - err.span_note(span, &format!("...which requires {}...", query.description)); - } - - if stack.len() == 1 { - err.note(&format!("...which immediately requires {} again", stack[0].query.description)); - } else { - err.note(&format!( - "...which again requires {}, completing the cycle", - stack[0].query.description - )); - } - - if stack.iter().all(|entry| { - entry - .query - .def_kind - .map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias)) - }) { - if stack.iter().all(|entry| { - entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias)) - }) { - err.note("type aliases cannot be recursive"); - err.help("consider using a struct, enum, or union instead to break the cycle"); - err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information"); - } else { - err.note("trait aliases cannot be recursive"); - } + cycle_stack.push(CycleStack { span, desc: query.description.to_owned() }); } + let mut cycle_usage = None; if let Some((span, query)) = usage { - err.span_note(query.default_span(span), &format!("cycle used when {}", query.description)); + cycle_usage = Some(crate::error::CycleUsage { + span: query.default_span(span), + usage: query.description, + }); } - err + let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) { + Some(crate::error::Alias::Ty) + } else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) { + Some(crate::error::Alias::Trait) + } else { + None + }; + + let cycle_diag = crate::error::Cycle { + span, + cycle_stack, + stack_bottom: stack[0].query.description.to_owned(), + alias, + cycle_usage: cycle_usage, + stack_count, + }; + + cycle_diag.into_diagnostic(&sess.parse_sess) } pub fn print_query_stack<CTX: QueryContext>( diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index a1f2b081d43..0b07bb64b6a 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -15,12 +15,11 @@ mod config; pub use self::config::{QueryConfig, QueryDescription, QueryVTable}; use crate::dep_graph::{DepContext, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; - use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::Diagnostic; use rustc_hir::def::DefKind; use rustc_span::Span; +use thin_vec::ThinVec; /// Description of a frame in the query stack. /// @@ -125,6 +124,6 @@ pub trait QueryContext: HasDepContext { ) -> R; fn depth_limit_error(&self) { - self.dep_context().sess().fatal("queries overflow the depth limit!"); + self.dep_context().sess().emit_fatal(crate::error::QueryOverflow); } } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 6ae9147ff77..7bbc22e8293 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -14,7 +14,6 @@ use rustc_data_structures::profiling::TimingGuard; #[cfg(parallel_compiler)] use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sync::Lock; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError}; use rustc_session::Session; use rustc_span::{Span, DUMMY_SP}; @@ -24,6 +23,7 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; use std::ptr; +use thin_vec::ThinVec; pub struct QueryState<K> { #[cfg(parallel_compiler)] @@ -618,16 +618,12 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true)); if old_in_panic { - sess.struct_err( - "internal compiler error: re-entrant incremental verify failure, suppressing message", - ) - .emit(); + sess.emit_err(crate::error::Reentrant); } else { - sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) - .help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) - .note("Please follow the instructions below to create a bug report with the provided information") - .note("See <https://github.com/rust-lang/rust/issues/84970> for more information") - .emit(); + sess.emit_err(crate::error::IncrementCompilation { + run_cmd, + dep_node: format!("{:?}", dep_node), + }); panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); } diff --git a/compiler/rustc_resolve/src/access_levels.rs b/compiler/rustc_resolve/src/access_levels.rs index 3fba923d9fd..882a92c0ebb 100644 --- a/compiler/rustc_resolve/src/access_levels.rs +++ b/compiler/rustc_resolve/src/access_levels.rs @@ -39,7 +39,7 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> { visit::walk_crate(&mut visitor, krate); } - tracing::info!("resolve::access_levels: {:#?}", r.access_levels); + info!("resolve::access_levels: {:#?}", r.access_levels); } fn reset(&mut self) { diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 8f3b6009bd6..cd0b2443da5 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -36,7 +36,6 @@ use rustc_span::Span; use std::cell::Cell; use std::ptr; -use tracing::debug; type Res = def::Res<NodeId>; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 66641fb2cb2..5955d8df16e 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -8,7 +8,6 @@ use rustc_hir::definitions::*; use rustc_span::hygiene::LocalExpnId; use rustc_span::symbol::sym; use rustc_span::Span; -use tracing::debug; pub(crate) fn collect_definitions( resolver: &mut Resolver<'_>, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2d15b1b0a1b..4fd6fe4e36c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -25,7 +25,6 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Span}; -use tracing::debug; use crate::imports::{Import, ImportKind, ImportResolver}; use crate::late::{PatternSource, Rib}; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 2afba94d793..b84a610833d 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -273,7 +273,7 @@ impl<'a> Resolver<'a> { /// /// Invariant: This must only be called during main resolution, not during /// import resolution. - #[tracing::instrument(level = "debug", skip(self, ribs))] + #[instrument(level = "debug", skip(self, ribs))] pub(crate) fn resolve_ident_in_lexical_scope( &mut self, mut ident: Ident, @@ -367,7 +367,7 @@ impl<'a> Resolver<'a> { /// expansion and import resolution (perhaps they can be merged in the future). /// The function is used for resolving initial segments of macro paths (e.g., `foo` in /// `foo::bar!(); or `foo!();`) and also for import paths on 2018 edition. - #[tracing::instrument(level = "debug", skip(self, scope_set))] + #[instrument(level = "debug", skip(self, scope_set))] pub(crate) fn early_resolve_ident_in_lexical_scope( &mut self, orig_ident: Ident, @@ -708,7 +708,7 @@ impl<'a> Resolver<'a> { Err(Determinacy::determined(determinacy == Determinacy::Determined || force)) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] pub(crate) fn maybe_resolve_ident_in_module( &mut self, module: ModuleOrUniformRoot<'a>, @@ -720,7 +720,7 @@ impl<'a> Resolver<'a> { .map_err(|(determinacy, _)| determinacy) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_ident_in_module( &mut self, module: ModuleOrUniformRoot<'a>, @@ -734,7 +734,7 @@ impl<'a> Resolver<'a> { .map_err(|(determinacy, _)| determinacy) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_ident_in_module_ext( &mut self, module: ModuleOrUniformRoot<'a>, @@ -772,7 +772,7 @@ impl<'a> Resolver<'a> { ) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_ident_in_module_unadjusted( &mut self, module: ModuleOrUniformRoot<'a>, @@ -796,7 +796,7 @@ impl<'a> Resolver<'a> { /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_ident_in_module_unadjusted_ext( &mut self, module: ModuleOrUniformRoot<'a>, @@ -1059,7 +1059,7 @@ impl<'a> Resolver<'a> { } /// Validate a local resolution (from ribs). - #[tracing::instrument(level = "debug", skip(self, all_ribs))] + #[instrument(level = "debug", skip(self, all_ribs))] fn validate_res_from_ribs( &mut self, rib_index: usize, @@ -1294,7 +1294,7 @@ impl<'a> Resolver<'a> { res } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] pub(crate) fn maybe_resolve_path( &mut self, path: &[Segment], @@ -1304,7 +1304,7 @@ impl<'a> Resolver<'a> { self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None) } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] pub(crate) fn resolve_path( &mut self, path: &[Segment], diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c2491c6ebde..619ce046220 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -23,8 +23,6 @@ use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; -use tracing::*; - use std::cell::Cell; use std::{mem, ptr}; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 693ec86616e..dbe4d691f04 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -32,7 +32,6 @@ use smallvec::{smallvec, SmallVec}; use rustc_span::source_map::{respan, Spanned}; use std::collections::{hash_map::Entry, BTreeSet}; use std::mem::{replace, take}; -use tracing::debug; mod diagnostics; pub(crate) mod lifetimes; @@ -1390,7 +1389,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { }) } - #[tracing::instrument(level = "debug", skip(self, work))] + #[instrument(level = "debug", skip(self, work))] fn with_lifetime_rib<T>( &mut self, kind: LifetimeRibKind, @@ -1404,7 +1403,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ret } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) { let ident = lifetime.ident; @@ -1508,7 +1507,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); @@ -1573,7 +1572,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.report_missing_lifetime_specifiers(vec![missing_lifetime], None); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) { let id = self.r.next_node_id(); let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) }; @@ -1586,7 +1585,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.resolve_anonymous_lifetime(<, true); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, binder: NodeId) -> LifetimeRes { debug_assert_eq!(ident.name, kw::UnderscoreLifetime); debug!(?ident.span); @@ -1604,7 +1603,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { res } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_elided_lifetimes_in_path( &mut self, path_id: NodeId, @@ -1804,7 +1803,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn record_lifetime_res( &mut self, id: NodeId, @@ -1827,7 +1826,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { panic!( @@ -1838,7 +1837,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } /// Perform resolution of a function signature, accounting for lifetime elision. - #[tracing::instrument(level = "debug", skip(self, inputs))] + #[instrument(level = "debug", skip(self, inputs))] fn resolve_fn_signature( &mut self, fn_id: NodeId, @@ -3268,11 +3267,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { source: PathSource<'ast>, finalize: Finalize, ) -> PartialRes { - tracing::debug!( + debug!( "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})", - qself, - path, - finalize, + qself, path, finalize, ); let ns = source.namespace(); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index d41edea6a25..99d13acbae1 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -33,8 +33,6 @@ use rustc_span::{BytePos, Span}; use std::iter; use std::ops::Deref; -use tracing::debug; - type Res = def::Res<ast::NodeId>; /// A field or associated item from self type suggested in case of resolution failure. @@ -1792,7 +1790,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } }; - let mut suggestable_variants = variants + let suggestable_variants = variants .iter() .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind)) .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) @@ -1802,8 +1800,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { CtorKind::Fictive => format!("({} {{}})", variant), }) .collect::<Vec<_>>(); + let no_suggestable_variant = suggestable_variants.is_empty(); - if !suggestable_variants.is_empty() { + if !no_suggestable_variant { let msg = if suggestable_variants.len() == 1 { "you might have meant to use the following enum variant" } else { @@ -1813,7 +1812,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestions( span, msg, - suggestable_variants.drain(..), + suggestable_variants.into_iter(), Applicability::MaybeIncorrect, ); } @@ -1830,15 +1829,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .collect::<Vec<_>>(); if !suggestable_variants_with_placeholders.is_empty() { - let msg = match ( - suggestable_variants.is_empty(), - suggestable_variants_with_placeholders.len(), - ) { - (true, 1) => "the following enum variant is available", - (true, _) => "the following enum variants are available", - (false, 1) => "alternatively, the following enum variant is available", - (false, _) => "alternatively, the following enum variants are also available", - }; + let msg = + match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) { + (true, 1) => "the following enum variant is available", + (true, _) => "the following enum variants are available", + (false, 1) => "alternatively, the following enum variant is available", + (false, _) => { + "alternatively, the following enum variants are also available" + } + }; err.span_suggestions( span, diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 6ea976a5900..c16eab222f6 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -278,7 +278,7 @@ pub fn provide(providers: &mut ty::query::Providers) { /// lifetimes into a single binder.) This requires us to resolve the /// *trait definition* of `Sub`; basically just enough lifetime information /// to look at the supertraits. -#[tracing::instrument(level = "debug", skip(tcx))] +#[instrument(level = "debug", skip(tcx))] fn resolve_lifetimes_trait_definition( tcx: TyCtxt<'_>, local_def_id: LocalDefId, @@ -289,7 +289,7 @@ fn resolve_lifetimes_trait_definition( /// Computes the `ResolveLifetimes` map that contains data for an entire `Item`. /// You should not read the result of this query directly, but rather use /// `named_region_map`, `is_late_bound_map`, etc. -#[tracing::instrument(level = "debug", skip(tcx))] +#[instrument(level = "debug", skip(tcx))] fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes { convert_named_region_map(do_resolve(tcx, local_def_id, false)) } @@ -647,7 +647,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { match ty.kind { hir::TyKind::BareFn(ref c) => { @@ -930,7 +930,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { match lifetime_ref.name { hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static), @@ -1212,7 +1212,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope: &wrap_scope, trait_definition_only: self.trait_definition_only, }; - let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); + let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope)); { let _enter = span.enter(); f(&mut this); @@ -1287,7 +1287,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.with(scope, walk); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn resolve_lifetime_ref( &mut self, region_def_id: LocalDefId, @@ -1368,7 +1368,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - // We may fail to resolve higher-ranked lifetimes that are mentionned by APIT. + // We may fail to resolve higher-ranked lifetimes that are mentioned by APIT. // AST-based resolution does not care for impl-trait desugaring, which are the // responibility of lowering. This may create a mismatch between the resolution // AST found (`region_def_id`) which points to HRTB, and what HIR allows. @@ -1409,7 +1409,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_segment_args( &mut self, res: Res, @@ -1659,7 +1659,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn visit_fn_like_elision( &mut self, inputs: &'tcx [hir::Ty<'tcx>], @@ -1707,7 +1707,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth)); } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) { debug!( node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id), diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 66090c96d1e..4e8f3a2cae8 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -58,7 +58,6 @@ use smallvec::{smallvec, SmallVec}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; use std::{cmp, fmt, ptr}; -use tracing::debug; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportKind, ImportResolver, NameResolution}; @@ -1991,7 +1990,7 @@ impl<'a> Resolver<'a> { _ => panic!("invalid arg index"), } } - // Cache the lookup to avoid parsing attributes for an iterm multiple times. + // Cache the lookup to avoid parsing attributes for an item multiple times. self.legacy_const_generic_args.insert(def_id, Some(ret.clone())); return Some(ret); } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 0c428aa6cc0..dafa10e9e00 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -441,7 +441,7 @@ impl<'a> ResolverExpand for Resolver<'a> { } PathResult::Indeterminate => indeterminate = true, // We can only be sure that a path doesn't exist after having tested all the - // posibilities, only at that time we can return false. + // possibilities, only at that time we can return false. PathResult::Failed { .. } => {} PathResult::Module(_) => panic!("unexpected path resolution"), } diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index e2e0e1f5b30..ac6c3663b63 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -44,8 +44,6 @@ use rls_data::{ RefKind, Relation, RelationKind, SpanData, }; -use tracing::{debug, error}; - #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5213 macro_rules! down_cast_data { ($id:ident, $kind:ident, $sp:expr) => { diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 619e083d89a..16af5338510 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -7,6 +7,9 @@ #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#[macro_use] +extern crate tracing; + mod dump_visitor; mod dumper; #[macro_use] @@ -49,8 +52,6 @@ use rls_data::{ RefKind, Relation, RelationKind, SpanData, }; -use tracing::{debug, error, info}; - pub struct SaveContext<'tcx> { tcx: TyCtxt<'tcx>, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml index dbc5c15195c..3b0b3144f2c 100644 --- a/compiler/rustc_serialize/Cargo.toml +++ b/compiler/rustc_serialize/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] indexmap = "1.9.1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.8" [dev-dependencies] rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_serialize/src/collection_impls.rs b/compiler/rustc_serialize/src/collection_impls.rs index 5e53f0b104d..8f8c504117c 100644 --- a/compiler/rustc_serialize/src/collection_impls.rs +++ b/compiler/rustc_serialize/src/collection_impls.rs @@ -1,13 +1,12 @@ //! Implementations of serialization for structures found in liballoc -use std::hash::{BuildHasher, Hash}; - use crate::{Decodable, Decoder, Encodable, Encoder}; +use smallvec::{Array, SmallVec}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque}; +use std::hash::{BuildHasher, Hash}; use std::rc::Rc; use std::sync::Arc; - -use smallvec::{Array, SmallVec}; +use thin_vec::ThinVec; impl<S: Encoder, A: Array<Item: Encodable<S>>> Encodable<S> for SmallVec<A> { fn encode(&self, s: &mut S) { @@ -23,6 +22,19 @@ impl<D: Decoder, A: Array<Item: Decodable<D>>> Decodable<D> for SmallVec<A> { } } +impl<S: Encoder, T: Encodable<S>> Encodable<S> for ThinVec<T> { + fn encode(&self, s: &mut S) { + self.as_slice().encode(s); + } +} + +impl<D: Decoder, T: Decodable<D>> Decodable<D> for ThinVec<T> { + fn decode(d: &mut D) -> ThinVec<T> { + let len = d.read_usize(); + (0..len).map(|_| Decodable::decode(d)).collect() + } +} + impl<S: Encoder, T: Encodable<S>> Encodable<S> for LinkedList<T> { fn encode(&self, s: &mut S) { s.emit_usize(self.len()); diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs index 2a4a772f610..2336d99363f 100644 --- a/compiler/rustc_session/src/cgu_reuse_tracker.rs +++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs @@ -10,7 +10,6 @@ use rustc_span::{Span, Symbol}; use std::borrow::Cow; use std::fmt::{self}; use std::sync::{Arc, Mutex}; -use tracing::debug; #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] pub enum CguReuse { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 162fc9aa0a6..7c50fe2d823 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2530,7 +2530,7 @@ fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Optio ), ), }; - tracing::debug!("got unpretty option: {first:?}"); + debug!("got unpretty option: {first:?}"); Some(first) } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index c973e3140ce..e8edb38f503 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -7,7 +7,6 @@ use std::path::{Path, PathBuf}; use crate::search_paths::{PathKind, SearchPath}; use rustc_fs_util::fix_windows_verbatim_for_gcc; -use tracing::debug; #[derive(Copy, Clone)] pub enum FileMatch { diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 429475c573c..02d5d33c8d5 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -14,6 +14,9 @@ extern crate rustc_macros; pub mod errors; +#[macro_use] +extern crate tracing; + pub mod cgu_reuse_tracker; pub mod utils; pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass}; diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index e169d3c7cfb..e8ddb4ed17a 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -41,7 +41,6 @@ use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use std::fmt; use std::hash::Hash; -use tracing::*; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 860af7fe93a..34e2e92bdfc 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -76,8 +76,6 @@ use md5::Md5; use sha1::Sha1; use sha2::Sha256; -use tracing::debug; - #[cfg(test)] mod tests; diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index a32cabab4c4..4d94c92d3f2 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -23,7 +23,6 @@ use std::{convert::TryFrom, unreachable}; use std::fs; use std::io; -use tracing::debug; #[cfg(test)] mod tests; @@ -1060,13 +1059,13 @@ impl FilePathMapping { return remap_path_prefix(&self.mapping, path); - #[instrument(level = "debug", skip(mapping))] + #[instrument(level = "debug", skip(mapping), ret)] fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) { // NOTE: We are iterating over the mapping entries from last to first // because entries specified later on the command line should // take precedence. for &(ref from, ref to) in mapping.iter().rev() { - debug!("Trying to apply {:?} => {:?}", from, to); + debug!("Trying to apply {from:?} => {to:?}"); if let Ok(rest) = path.strip_prefix(from) { let remapped = if rest.as_os_str().is_empty() { @@ -1080,15 +1079,15 @@ impl FilePathMapping { } else { to.join(rest) }; - debug!("Match - remapped {:?} => {:?}", path, remapped); + debug!("Match - remapped"); return (remapped, true); } else { - debug!("No match - prefix {:?} does not match {:?}", from, path); + debug!("No match - prefix {from:?} does not match"); } } - debug!("Path {:?} was not remapped", path); + debug!("not remapped"); (path, false) } } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 9241fd82c74..46c5fe78ffb 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -6,8 +6,6 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitable}; use rustc_middle::util::common::record_time; -use tracing::debug; - use std::fmt::{self, Write}; use std::mem::{self, discriminant}; diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 0c6489acb34..62f44a48032 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -97,6 +97,9 @@ #[macro_use] extern crate rustc_middle; +#[macro_use] +extern crate tracing; + use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -107,8 +110,6 @@ use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::SymbolManglingVersion; -use tracing::debug; - mod legacy; mod v0; diff --git a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs index 1b7161fbb85..b301ce68a1c 100644 --- a/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/aarch64_nintendo_switch_freestanding.rs @@ -18,7 +18,6 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, position_independent_executables: true, dynamic_linking: true, - executables: true, relro_level: RelroLevel::Off, ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs index 2bf83a8782a..9f3e0bd5ef0 100644 --- a/compiler/rustc_target/src/spec/android_base.rs +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); base.os = "android".into(); base.default_dwarf_version = 2; - base.position_independent_executables = true; base.has_thread_local = false; // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867 // for context. (At that time, there was no `-C force-unwind-tables`, so the only solution diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 9bbee88a894..2c72bf88a41 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -1,40 +1,37 @@ use std::{borrow::Cow, env}; -use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, TargetOptions}; +use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, StaticCow, TargetOptions}; use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor}; fn pre_link_args(os: &'static str, arch: &'static str, abi: &'static str) -> LinkArgs { - let mut args = LinkArgs::new(); - - let platform_name = match abi { - "sim" => format!("{}-simulator", os), - "macabi" => "mac-catalyst".to_string(), - _ => os.to_string(), + let platform_name: StaticCow<str> = match abi { + "sim" => format!("{}-simulator", os).into(), + "macabi" => "mac-catalyst".into(), + _ => os.into(), }; - let platform_version = match os.as_ref() { + let platform_version: StaticCow<str> = match os.as_ref() { "ios" => ios_lld_platform_version(), "tvos" => tvos_lld_platform_version(), "watchos" => watchos_lld_platform_version(), "macos" => macos_lld_platform_version(arch), _ => unreachable!(), - }; - - if abi != "macabi" { - args.insert(LinkerFlavor::Gcc, vec!["-arch".into(), arch.into()]); } + .into(); - args.insert( + let mut args = TargetOptions::link_args( LinkerFlavor::Lld(LldFlavor::Ld64), - vec![ - "-arch".into(), - arch.into(), - "-platform_version".into(), - platform_name.into(), - platform_version.clone().into(), - platform_version.into(), - ], + &["-arch", arch, "-platform_version"], ); + // Manually add owned args unsupported by link arg building helpers. + args.entry(LinkerFlavor::Lld(LldFlavor::Ld64)).or_default().extend([ + platform_name, + platform_version.clone(), + platform_version, + ]); + if abi != "macabi" { + super::add_link_args(&mut args, LinkerFlavor::Gcc, &["-arch", arch]); + } args } @@ -127,7 +124,7 @@ pub fn macos_llvm_target(arch: &str) -> String { format!("{}-apple-macosx{}.{}.0", arch, major, minor) } -pub fn macos_link_env_remove() -> Vec<Cow<'static, str>> { +pub fn macos_link_env_remove() -> Vec<StaticCow<str>> { let mut env_remove = Vec::with_capacity(2); // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which // may occur when we're linking a custom build script while targeting iOS for example. diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 1d441e558dd..8cca33cc43b 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions}; /// A base target for AVR devices using the GNU toolchain. /// @@ -21,6 +21,7 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target { late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]), max_atomic_width: Some(0), atomic_cas: false, + relocation_model: RelocModel::Static, ..TargetOptions::default() }, } diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs index cc2c78c69fe..2a24e4459c5 100644 --- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs @@ -10,7 +10,6 @@ pub fn target() -> Target { base.crt_static_default = false; base.has_rpath = true; base.linker_is_gnu = false; - base.dynamic_linking = true; base.c_enum_min_bits = 8; diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index a08756861e5..cab4dd333d4 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -1,4 +1,4 @@ -use crate::spec::{cvs, LinkerFlavor, PanicStrategy, TargetOptions}; +use crate::spec::{cvs, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions}; pub fn opts() -> TargetOptions { TargetOptions { @@ -9,6 +9,7 @@ pub fn opts() -> TargetOptions { linker: Some("l4-bender".into()), linker_is_gnu: false, families: cvs!["unix"], + relocation_model: RelocModel::Static, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index aa73625ff81..2459b0280cd 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -837,15 +837,15 @@ impl fmt::Display for StackProtector { } macro_rules! supported_targets { - ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { + ( $(($triple:literal, $module:ident ),)+ ) => { $(mod $module;)+ /// List of supported targets - pub const TARGETS: &[&str] = &[$($($triple),+),+]; + pub const TARGETS: &[&str] = &[$($triple),+]; fn load_builtin(target: &str) -> Option<Target> { let mut t = match target { - $( $($triple)|+ => $module::target(), )+ + $( $triple => $module::target(), )+ _ => return None, }; t.is_builtin = true; @@ -861,7 +861,7 @@ macro_rules! supported_targets { $( #[test] // `#[test]` fn $module() { - tests_impl::test_target(super::$module::target()); + tests_impl::test_target(super::$module::target(), $triple); } )+ } @@ -1528,7 +1528,7 @@ fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'stati match flavor { LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)), LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)), - LinkerFlavor::Lld(LldFlavor::Wasm) => {} + LinkerFlavor::Lld(LldFlavor::Ld64) | LinkerFlavor::Lld(LldFlavor::Wasm) => {} LinkerFlavor::Lld(lld_flavor) => { panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor) } @@ -2311,7 +2311,7 @@ impl Target { load_builtin(target_triple).expect("built-in target") } TargetTriple::TargetJson { .. } => { - panic!("built-in targets doens't support target-paths") + panic!("built-in targets doesn't support target-paths") } } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs index 516b2de37ea..75ac66c276d 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs @@ -1,5 +1,5 @@ use crate::abi::Endian; -use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; pub fn target() -> Target { let mut base = super::freebsd_base::opts(); @@ -15,7 +15,6 @@ pub fn target() -> Target { options: TargetOptions { endian: Endian::Big, features: "+secure-plt".into(), - relocation_model: RelocModel::Pic, mcount: "_mcount".into(), ..base }, diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 03e579aee0a..4a53b9c173d 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -2,18 +2,20 @@ use super::super::*; use std::assert_matches::assert_matches; // Test target self-consistency and JSON encoding/decoding roundtrip. -pub(super) fn test_target(target: Target) { - target.check_consistency(); +pub(super) fn test_target(target: Target, triple: &str) { + target.check_consistency(triple); assert_eq!(Target::from_json(target.to_json()).map(|(j, _)| j), Ok(target)); } impl Target { - fn check_consistency(&self) { + fn check_consistency(&self, triple: &str) { assert_eq!(self.is_like_osx, self.vendor == "apple"); assert_eq!(self.is_like_solaris, self.os == "solaris" || self.os == "illumos"); assert_eq!(self.is_like_windows, self.os == "windows" || self.os == "uefi"); assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64"); - assert!(self.is_like_windows || !self.is_like_msvc); + if self.is_like_msvc { + assert!(self.is_like_windows); + } // Check that default linker flavor and lld flavor are compatible // with some other key properties. @@ -94,8 +96,9 @@ impl Target { check_noncc(LinkerFlavor::Ld); check_noncc(LinkerFlavor::Lld(LldFlavor::Ld)); } + LldFlavor::Ld64 => check_noncc(LinkerFlavor::Lld(LldFlavor::Ld64)), LldFlavor::Wasm => check_noncc(LinkerFlavor::Lld(LldFlavor::Wasm)), - LldFlavor::Ld64 | LldFlavor::Link => {} + LldFlavor::Link => {} }, _ => {} } @@ -109,20 +112,56 @@ impl Target { ); } - assert!( - (self.pre_link_objects_self_contained.is_empty() - && self.post_link_objects_self_contained.is_empty()) - || self.link_self_contained != LinkSelfContainedDefault::False - ); + if self.link_self_contained == LinkSelfContainedDefault::False { + assert!( + self.pre_link_objects_self_contained.is_empty() + && self.post_link_objects_self_contained.is_empty() + ); + } // If your target really needs to deviate from the rules below, // except it and document the reasons. // Keep the default "unknown" vendor instead. assert_ne!(self.vendor, ""); + assert_ne!(self.os, ""); if !self.can_use_os_unknown() { // Keep the default "none" for bare metal targets instead. assert_ne!(self.os, "unknown"); } + + // Check dynamic linking stuff + // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. + if self.os == "none" && self.arch != "bpf" { + assert!(!self.dynamic_linking); + } + if self.only_cdylib + || self.crt_static_allows_dylibs + || !self.late_link_args_dynamic.is_empty() + { + assert!(self.dynamic_linking); + } + // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs + if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") { + assert_eq!(self.relocation_model, RelocModel::Pic); + } + // PIEs are supported but not enabled by default with linuxkernel target. + if self.position_independent_executables && !triple.ends_with("-linuxkernel") { + assert_eq!(self.relocation_model, RelocModel::Pic); + } + if self.relocation_model == RelocModel::Pic { + assert!(self.dynamic_linking || self.position_independent_executables); + } + if self.static_position_independent_executables { + assert!(self.position_independent_executables); + } + if self.position_independent_executables { + assert!(self.executables); + } + + // Check crt static stuff + if self.crt_static_default || self.crt_static_allows_dylibs { + assert!(self.crt_static_respected); + } } // Add your target to the whitelist if it has `std` library diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index aee8eb2e31c..250da03cbd2 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -9,7 +9,8 @@ // the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all // code runs in the same environment, no process separation is supported. -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy}; +use crate::spec::{RelocModel, StackProbeType, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::msvc_base::opts(); @@ -46,6 +47,7 @@ pub fn opts() -> TargetOptions { stack_probes: StackProbeType::Call, singlethread: true, linker: Some("rust-lld".into()), + relocation_model: RelocModel::Static, ..base } } diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs index bae007dc9f3..f30be25497d 100644 --- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -3,7 +3,7 @@ use crate::spec::{cvs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { // We cannot use `-nodefaultlibs` because compiler-rt has to be passed // as a path since it's not added to linker search path by the default. - // There were attemts to make it behave like libgcc (so one can just use -l<name>) + // There were attempts to make it behave like libgcc (so one can just use -l<name>) // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440 let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gcc, &["-nolibc", "--unwindlib=none"]); diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs index 78189a0c096..26da7e80011 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs @@ -4,8 +4,6 @@ pub fn target() -> Target { let mut base = super::l4re_base::opts(); base.cpu = "x86-64".into(); base.max_atomic_width = Some(64); - base.crt_static_allows_dylibs = false; - base.dynamic_linking = false; base.panic_strategy = PanicStrategy::Abort; Target { diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs index 809fd642d41..b9a345127e3 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs @@ -4,10 +4,8 @@ // `target-cpu` compiler flags to opt-in more hardware-specific // features. -use super::{ - CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, - Target, TargetOptions, -}; +use super::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy}; +use super::{RelroLevel, StackProbeType, Target, TargetOptions}; pub fn target() -> Target { let opts = TargetOptions { @@ -18,7 +16,6 @@ pub fn target() -> Target { position_independent_executables: true, static_position_independent_executables: true, relro_level: RelroLevel::Full, - relocation_model: RelocModel::Pic, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index c0700748c79..26f8e7d34c6 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -18,7 +18,6 @@ use rustc_middle::ty::{self, TyCtxt}; /// obligations *could be* resolved if we wanted to. /// /// This also expects that `trait_ref` is fully normalized. -#[instrument(level = "debug", skip(tcx))] pub fn codegen_fulfill_obligation<'tcx>( tcx: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), @@ -74,7 +73,6 @@ pub fn codegen_fulfill_obligation<'tcx>( // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - debug!("Cache miss: {trait_ref:?} => {impl_source:?}"); Ok(&*tcx.arena.alloc(impl_source)) }) } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index e4af7022239..99046bd126f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2014,7 +2014,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let predicate = self.resolve_vars_if_possible(obligation.predicate); let span = obligation.cause.span; - debug!(?predicate, obligation.cause.code = tracing::field::debug(&obligation.cause.code())); + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); // Ambiguity errors are often caused as fallout from earlier errors. // We ignore them if this `infcx` is tainted in some cases below. diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index d840677f1ca..3763a98c488 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -135,7 +135,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { /// `SomeTrait` or a where-clause that lets us unify `$0` with /// something concrete. If this fails, we'll unify `$0` with /// `projection_ty` again. - #[tracing::instrument(level = "debug", skip(self, infcx, param_env, cause))] + #[instrument(level = "debug", skip(self, infcx, param_env, cause))] fn normalize_projection_type( &mut self, infcx: &InferCtxt<'_, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 444ca6471e2..398635674ab 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -231,7 +231,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_type] for an explanation of the return value. -#[tracing::instrument(level = "debug", skip(selcx))] +#[instrument(level = "debug", skip(selcx))] fn project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionObligation<'tcx>, @@ -1206,7 +1206,7 @@ impl<'tcx> Progress<'tcx> { /// /// IMPORTANT: /// - `obligation` must be fully normalized -#[tracing::instrument(level = "info", skip(selcx))] +#[instrument(level = "info", skip(selcx))] fn project<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -1368,7 +1368,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( ); } -#[tracing::instrument( +#[instrument( level = "debug", skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates) )] @@ -1419,7 +1419,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( } } -#[tracing::instrument(level = "debug", skip(selcx, obligation, candidate_set))] +#[instrument(level = "debug", skip(selcx, obligation, candidate_set))] fn assemble_candidates_from_impls<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, 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 d67bd6292b4..e84c462ca81 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -28,7 +28,7 @@ use super::SelectionCandidate::{self, *}; use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(super) fn candidate_from_obligation<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -48,7 +48,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some(c) = self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) { - debug!(candidate = ?c, "CACHE HIT"); + debug!("CACHE HIT"); return c; } @@ -61,7 +61,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (candidate, dep_node) = self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); - debug!(?candidate, "CACHE MISS"); + debug!("CACHE MISS"); self.insert_candidate_cache( stack.obligation.param_env, cache_fresh_trait_pred, @@ -337,7 +337,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(candidates) } - #[tracing::instrument(level = "debug", skip(self, candidates))] + #[instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_projected_tys( &mut self, obligation: &TraitObligation<'tcx>, @@ -367,7 +367,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// supplied to find out whether it is listed among them. /// /// Never affects the inference environment. - #[tracing::instrument(level = "debug", skip(self, stack, candidates))] + #[instrument(level = "debug", skip(self, stack, candidates))] fn assemble_candidates_from_caller_bounds<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -880,7 +880,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; } - #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] + #[instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_transmutability( &mut self, obligation: &TraitObligation<'tcx>, @@ -898,7 +898,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.vec.push(TransmutabilityCandidate); } - #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] + #[instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_trait_alias( &mut self, obligation: &TraitObligation<'tcx>, @@ -917,7 +917,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Assembles the trait which are built-in to the language itself: /// `Copy`, `Clone` and `Sized`. - #[tracing::instrument(level = "debug", skip(self, candidates))] + #[instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_bound_candidates( &mut self, conditions: BuiltinImplConditions<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 46b50dd92f1..5da8cfab0b1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -295,7 +295,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Attempts to satisfy the obligation. If successful, this will affect the surrounding /// type environment by performing unification. - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn select( &mut self, obligation: &TraitObligation<'tcx>, @@ -325,10 +325,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), - Ok(candidate) => { - debug!(?candidate, "confirmed"); - Ok(Some(candidate)) - } + Ok(candidate) => Ok(Some(candidate)), } } @@ -435,6 +432,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { level = "debug", skip(self, previous_stack), fields(previous_stack = ?previous_stack.head()) + ret, )] fn evaluate_predicate_recursively<'o>( &mut self, @@ -450,7 +448,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => self.check_recursion_limit(&obligation, &obligation)?, } - let result = ensure_sufficient_stack(|| { + ensure_sufficient_stack(|| { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Trait(t) => { @@ -760,14 +758,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("TypeWellFormedFromEnv is only used for chalk") } } - }); - - debug!("finished: {:?} from {:?}", result, obligation); - - result + }) } - #[instrument(skip(self, previous_stack), level = "debug")] + #[instrument(skip(self, previous_stack), level = "debug", ret)] fn evaluate_trait_predicate_recursively<'o>( &mut self, previous_stack: TraitObligationStackList<'o, 'tcx>, @@ -798,12 +792,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If a trait predicate is in the (local or global) evaluation cache, // then we know it holds without cycles. if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) { - debug!(?result, "CACHE HIT"); + debug!("CACHE HIT"); return Ok(result); } if let Some(result) = stack.cache().get_provisional(fresh_trait_pred) { - debug!(?result, "PROVISIONAL CACHE HIT"); + debug!("PROVISIONAL CACHE HIT"); stack.update_reached_depth(result.reached_depth); return Ok(result.result); } @@ -826,11 +820,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let reached_depth = stack.reached_depth.get(); if reached_depth >= stack.depth { - debug!(?result, "CACHE MISS"); + debug!("CACHE MISS"); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); stack.cache().on_completion(stack.dfn); } else { - debug!(?result, "PROVISIONAL"); + debug!("PROVISIONAL"); debug!( "caching provisionally because {:?} \ is a cycle participant (at depth {}, reached depth {})", @@ -1023,7 +1017,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument( level = "debug", skip(self, stack), - fields(depth = stack.obligation.recursion_depth) + fields(depth = stack.obligation.recursion_depth), + ret )] fn evaluate_candidate<'o>( &mut self, @@ -1056,7 +1051,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { result = result.max(EvaluatedToOkModuloRegions); } - debug!(?result); Ok(result) } @@ -1405,7 +1399,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// a projection, look at the bounds of `T::Bar`, see if we can find a /// `Baz` bound. We return indexes into the list returned by /// `tcx.item_bounds` for any applicable bounds. - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &TraitObligation<'tcx>, @@ -1435,7 +1429,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // unnecessary ambiguity. let mut distinct_normalized_bounds = FxHashSet::default(); - let matching_bounds = bounds + bounds .iter() .enumerate() .filter_map(|(idx, bound)| { @@ -1462,10 +1456,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } None }) - .collect(); - - debug!(?matching_bounds); - matching_bounds + .collect() } /// Equates the trait in `obligation` with trait bound. If the two traits @@ -2153,7 +2144,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn match_impl( &mut self, impl_def_id: DefId, @@ -2194,17 +2185,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .at(&cause, obligation.param_env) .define_opaque_types(false) .eq(placeholder_obligation_trait_ref, impl_trait_ref) - .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; + .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{e}`"))?; nested_obligations.extend(obligations); if !self.intercrate && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation { - debug!("match_impl: reservation impls only apply in intercrate mode"); + debug!("reservation impls only apply in intercrate mode"); return Err(()); } - debug!(?impl_substs, ?nested_obligations, "match_impl: success"); Ok(Normalized { value: impl_substs, obligations: nested_obligations }) } @@ -2335,7 +2325,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// impl or trait. The obligations are substituted and fully /// normalized. This is used when confirming an impl or default /// impl. - #[tracing::instrument(level = "debug", skip(self, cause, param_env))] + #[instrument(level = "debug", skip(self, cause, param_env))] fn impl_or_trait_obligations( &mut self, cause: &ObligationCause<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 4bd179d2391..bb6009cb22a 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -841,7 +841,7 @@ pub fn object_region_bounds<'tcx>( /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. -#[instrument(skip(tcx, predicates), level = "debug")] +#[instrument(skip(tcx, predicates), level = "debug", ret)] pub(crate) fn required_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, erased_self_ty: Ty<'tcx>, diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index c7c604e14e3..a166371fed1 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -191,7 +191,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi GenericArgKind::Const(..) => { chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) } - GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), + GenericArgKind::Lifetime(lt) => bug!("unexpected well formed predicate: {:?}", lt), }, ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 93cab7ca533..211c813b800 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -317,7 +317,7 @@ pub(crate) mod rustc { tcx, )?, AdtKind::Enum => { - tracing::trace!(?adt_def, "treeifying enum"); + trace!(?adt_def, "treeifying enum"); let mut tree = Tree::uninhabited(); for (idx, discr) in adt_def.discriminants(tcx) { @@ -381,7 +381,7 @@ pub(crate) mod rustc { let clamp = |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap(); - let variant_span = tracing::trace_span!( + let variant_span = trace_span!( "treeifying variant", min_align = ?min_align, max_align = ?max_align, @@ -396,27 +396,27 @@ pub(crate) mod rustc { // The layout of the variant is prefixed by the discriminant, if any. if let Some(discr) = discr { - tracing::trace!(?discr, "treeifying discriminant"); + trace!(?discr, "treeifying discriminant"); let discr_layout = alloc::Layout::from_size_align( layout_summary.discriminant_size, clamp(layout_summary.discriminant_align), ) .unwrap(); - tracing::trace!(?discr_layout, "computed discriminant layout"); + trace!(?discr_layout, "computed discriminant layout"); variant_layout = variant_layout.extend(discr_layout).unwrap().0; tree = tree.then(Self::from_disr(discr, tcx, layout_summary.discriminant_size)); } // Next come fields. - let fields_span = tracing::trace_span!("treeifying fields").entered(); + let fields_span = trace_span!("treeifying fields").entered(); for field_def in variant_def.fields.iter() { let field_ty = field_def.ty(tcx, substs_ref); - let _span = tracing::trace_span!("treeifying field", field = ?field_ty).entered(); + let _span = trace_span!("treeifying field", field = ?field_ty).entered(); // begin with the field's visibility tree = tree.then(Self::def(Def::Field(field_def))); - // compute the field's layout charactaristics + // compute the field's layout characteristics let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align); // next comes the field's padding @@ -434,7 +434,7 @@ pub(crate) mod rustc { drop(fields_span); // finally: padding - let padding_span = tracing::trace_span!("adding trailing padding").entered(); + let padding_span = trace_span!("adding trailing padding").entered(); let padding_needed = layout_summary.total_size - variant_layout.size(); if padding_needed > 0 { tree = tree.then(Self::padding(padding_needed)); @@ -467,7 +467,7 @@ pub(crate) mod rustc { layout.align().abi.bytes().try_into().unwrap(), ) .unwrap(); - tracing::trace!(?ty, ?layout, "computed layout for type"); + trace!(?ty, ?layout, "computed layout for type"); Ok(layout) } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 076d922d1b7..248ff1ec241 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -110,7 +110,7 @@ where // Remove all `Def` nodes from `src`, without checking their visibility. let src = src.prune(&|def| true); - tracing::trace!(?src, "pruned src"); + trace!(?src, "pruned src"); // Remove all `Def` nodes from `dst`, additionally... let dst = if assume_visibility { @@ -121,7 +121,7 @@ where dst.prune(&|def| context.is_accessible_from(def, scope)) }; - tracing::trace!(?dst, "pruned dst"); + trace!(?dst, "pruned dst"); // Convert `src` from a tree-based representation to an NFA-based representation. // If the conversion fails because `src` is uninhabited, conclude that the transmutation diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 9c2cf4c9a92..adab343ac98 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -82,7 +82,7 @@ mod rustc { false }; - tracing::trace!(?ret, "ret"); + trace!(?ret, "ret"); ret } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index bd1d568cd9a..661e413fc5b 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -15,8 +15,6 @@ use std::collections::btree_map::Entry; use std::collections::BTreeMap; use std::ops::ControlFlow; -use tracing::debug; - // FIXME(#86795): `BoundVarsCollector` here should **NOT** be used // outside of `resolve_associated_item`. It's just to address #64494, // #83765, and #85848 which are creating bound types/regions that lose diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index acfeefb4d12..9d640672cf9 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -104,7 +104,6 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain } /// See `ParamEnv` struct definition for details. -#[instrument(level = "debug", skip(tcx))] fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // The param_env of an impl Trait type is its defining function's param_env if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) { @@ -410,7 +409,6 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { } /// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead. -#[instrument(level = "debug", skip(tcx))] pub fn conservative_is_privately_uninhabited_raw<'tcx>( tcx: TyCtxt<'tcx>, param_env_and: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 5c96f1c4623..ef927058df4 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -144,7 +144,7 @@ enum ConvertedBindingKind<'a, 'tcx> { /// instantiated with some generic arguments providing `'a` explicitly, /// we taint those arguments with `ExplicitLateBound::Yes` so that we /// can provide an appropriate diagnostic later. -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum ExplicitLateBound { Yes, No, @@ -167,7 +167,7 @@ pub(crate) enum GenericArgPosition { /// A marker denoting that the generic arguments that were /// provided did not match the respective generic parameters. -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct GenericArgCountMismatch { /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`). pub reported: Option<ErrorGuaranteed>, @@ -177,7 +177,7 @@ pub struct GenericArgCountMismatch { /// Decorates the result of a generic argument count mismatch /// check with whether explicit late bounds were provided. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct GenericArgCountResult { pub explicit_late_bound: ExplicitLateBound, pub correct: Result<(), GenericArgCountMismatch>, @@ -201,7 +201,7 @@ pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> { } impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn ast_region_to_region( &self, lifetime: &hir::Lifetime, @@ -210,7 +210,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let tcx = self.tcx(); let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id)); - let r = match tcx.named_region(lifetime.hir_id) { + match tcx.named_region(lifetime.hir_id) { Some(rl::Region::Static) => tcx.lifetimes.re_static, Some(rl::Region::LateBound(debruijn, index, def_id)) => { @@ -255,9 +255,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.lifetimes.re_static }) } - }; - debug!("ast_region_to_region(lifetime={:?}) yields {:?}", lifetime, r); - r + } } /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, @@ -317,7 +315,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// `[Vec<u8>, u8]` and `generic_args` are the arguments for the associated /// type itself: `['a]`. The returned `SubstsRef` concatenates these two /// lists: `[Vec<u8>, u8, 'a]`. - #[tracing::instrument(level = "debug", skip(self, span))] + #[instrument(level = "debug", skip(self, span), ret)] fn create_substs_for_ast_path<'a>( &self, span: Span, @@ -537,11 +535,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &mut substs_ctx, ); - debug!( - "create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}", - generics, self_ty, substs - ); - (substs, arg_count) } @@ -716,7 +709,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, /// however. - #[tracing::instrument(level = "debug", skip(self, span, constness, bounds, speculative))] + #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))] pub(crate) fn instantiate_poly_trait_ref( &self, trait_ref: &hir::TraitRef<'_>, @@ -808,7 +801,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::TraitRef::new(trait_def_id, substs) } - #[tracing::instrument(level = "debug", skip(self, span))] + #[instrument(level = "debug", skip(self, span))] fn create_substs_for_ast_trait_ref<'a>( &self, span: Span, @@ -922,7 +915,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// **A note on binders:** there is an implied binder around /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` /// for more details. - #[tracing::instrument(level = "debug", skip(self, ast_bounds, bounds))] + #[instrument(level = "debug", skip(self, ast_bounds, bounds))] pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>( &self, param_ty: Ty<'tcx>, @@ -1028,10 +1021,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* /// the binder (e.g., `&'a u32`) and hence may reference bound regions. - #[tracing::instrument( - level = "debug", - skip(self, bounds, speculative, dup_bindings, path_span) - )] + #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))] fn add_predicates_for_ast_type_binding( &self, hir_ref_id: hir::HirId, @@ -2599,7 +2589,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> { let tcx = self.tcx(); @@ -2703,8 +2693,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir::TyKind::Err => tcx.ty_error(), }; - debug!(?result_ty); - self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); result_ty } diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 2d50412007d..25bafdfe859 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -12,7 +12,7 @@ use rustc_trait_selection::traits::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub fn check_match( &self, expr: &'tcx hir::Expr<'tcx>, @@ -212,9 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We won't diverge unless the scrutinee or all arms diverge. self.diverges.set(scrut_diverges | all_arms_diverge); - let match_ty = coercion.complete(self); - debug!(?match_ty); - match_ty + coercion.complete(self) } /// When the previously checked expression (the scrutinee) diverges, diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 5cdb2acd9f3..29a128f27b8 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -101,7 +101,7 @@ pub(super) fn check_fn<'a, 'tcx>( decl.output.span(), param_env, )); - // If we replaced declared_ret_ty with infer vars, then we must be infering + // If we replaced declared_ret_ty with infer vars, then we must be inferring // an opaque type, so set a flag so we can improve diagnostics. fcx.return_type_has_opaque = ret_ty != declared_ret_ty; @@ -1543,7 +1543,7 @@ fn detect_discriminant_duplicate<'tcx>( None => { // At this point we know this discriminant is a duplicate, and was not explicitly // assigned by the user. Here we iterate backwards to fetch the HIR for the last - // explictly assigned discriminant, and letting the user know that this was the + // explicitly assigned discriminant, and letting the user know that this was the // increment startpoint, and how many steps from there leading to the duplicate if let Some((n, hir::Variant { span, ident, .. })) = vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some()) @@ -1566,7 +1566,7 @@ fn detect_discriminant_duplicate<'tcx>( }; // Here we loop through the discriminants, comparing each discriminant to another. - // When a duplicate is detected, we instatiate an error and point to both + // When a duplicate is detected, we instantiate an error and point to both // initial and duplicate value. The duplicate discriminant is then discarded by swapping // it with the last element and decrementing the `vec.len` (which is why we have to evaluate // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index fee872155f5..bc3fec6e7d6 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) } - #[instrument(skip(self, expr, body, decl), level = "debug")] + #[instrument(skip(self, expr, body, decl), level = "debug", ret)] fn check_closure( &self, expr: &hir::Expr<'_>, @@ -158,11 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs); - - debug!(?expr.hir_id, ?closure_type); - - closure_type + self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs) } /// Given the expected type, figures out what it can about this closure we @@ -262,7 +258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// The `cause_span` should be the span that caused us to /// have this expected signature, or `None` if we can't readily /// know that. - #[instrument(level = "debug", skip(self, cause_span))] + #[instrument(level = "debug", skip(self, cause_span), ret)] fn deduce_sig_from_projection( &self, cause_span: Option<Span>, @@ -317,7 +313,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Unsafety::Normal, Abi::Rust, )); - debug!(?sig); Some(ExpectedSig { cause_span, sig }) } @@ -576,7 +571,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// types that the user gave into a signature. /// /// Also, record this closure signature for later. - #[instrument(skip(self, decl, body), level = "debug")] + #[instrument(skip(self, decl, body), level = "debug", ret)] fn supplied_sig_of_closure( &self, hir_id: hir::HirId, @@ -629,8 +624,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_vars, ); - debug!(?result); - let c_result = self.inh.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); @@ -643,7 +636,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// user specified. The "desugared" return type is an `impl /// Future<Output = T>`, so we do this by searching through the /// obligations to extract the `T`. - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn deduce_future_output_from_obligations( &self, expr_def_id: DefId, @@ -704,7 +697,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.register_predicates(obligations); - debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty); Some(output_ty) } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 804306814a2..b6bc244d2b1 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -1308,7 +1308,7 @@ fn compare_type_predicate_entailment<'tcx>( /// For default associated types the normalization is not possible (the value /// from the impl could be overridden). We also can't normalize generic /// associated types (yet) because they contain bound parameters. -#[tracing::instrument(level = "debug", skip(tcx))] +#[instrument(level = "debug", skip(tcx))] pub fn check_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, trait_ty: &ty::AssocItem, diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 07046f3f032..b9054898a2e 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` /// will be permitted if the diverges flag is currently "always". - #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] + #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, expr: &hir::Expr<'tcx>, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 20d25d508d2..66b737a4930 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -83,7 +83,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {}) } - #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")] + #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)] pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment( &self, mut ty: Ty<'tcx>, @@ -107,10 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // indirect dependencies that don't seem worth tracking // precisely. self.select_obligations_where_possible(false, mutate_fulfillment_errors); - ty = self.resolve_vars_if_possible(ty); - - debug!(?ty); - ty + self.resolve_vars_if_possible(ty) } pub(in super::super) fn record_deferred_call_resolution( @@ -1405,7 +1402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - #[tracing::instrument(level = "debug", skip(self, code, span, def_id, substs))] + #[instrument(level = "debug", skip(self, code, span, def_id, substs))] fn add_required_obligations_with_code( &self, span: Span, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 03bd485096a..7ff4aef2d25 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -153,7 +153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let tcx = self.tcx; - // Conceptually, we've got some number of expected inputs, and some number of provided aguments + // Conceptually, we've got some number of expected inputs, and some number of provided arguments // and we can form a grid of whether each argument could satisfy a given input: // in1 | in2 | in3 | ... // arg1 ? | | | diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 85a0d4e4499..2a8c460bb11 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -17,7 +17,6 @@ use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::sym; use rustc_span::Span; -use tracing::debug; mod drop_ranges; diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index de26a9e56e2..a9071cd1fd9 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -168,7 +168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`) /// * `self_expr`: the self expression (`foo`) /// * `args`: the expressions of the arguments (`a, b + 1, ...`) - #[instrument(level = "debug", skip(self, call_expr, self_expr))] + #[instrument(level = "debug", skip(self))] pub fn lookup_method( &self, self_ty: Ty<'tcx>, @@ -178,11 +178,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_expr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> { - debug!( - "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", - segment.ident, self_ty, call_expr, self_expr - ); - let pick = self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; @@ -383,7 +378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// In particular, it doesn't really do any probing: it simply constructs /// an obligation for a particular trait with the given self type and checks /// whether that trait is implemented. - #[instrument(level = "debug", skip(self, span, opt_input_types))] + #[instrument(level = "debug", skip(self, span))] pub(super) fn lookup_method_in_trait( &self, span: Span, @@ -392,11 +387,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - debug!( - "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", - self_ty, m_name, trait_def_id, opt_input_types - ); - let (obligation, substs) = self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); self.construct_obligation_for_trait( @@ -576,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `self_ty`: the type to search within (`Foo`) /// * `self_ty_span` the span for the type being searched within (span of `Foo`) /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn resolve_fully_qualified_call( &self, span: Span, @@ -585,11 +575,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty_span: Span, expr_id: hir::HirId, ) -> Result<(DefKind, DefId), MethodError<'tcx>> { - debug!( - "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}", - method_name, self_ty, expr_id, - ); - let tcx = self.tcx; // Check if we have an enum variant. @@ -633,21 +618,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &pick, ); - debug!("resolve_fully_qualified_call: pick={:?}", pick); + debug!(?pick); { let mut typeck_results = self.typeck_results.borrow_mut(); let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); for import_id in pick.import_ids { - debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id); + debug!(used_trait_import=?import_id); used_trait_imports.insert(import_id); } } let def_kind = pick.item.kind.as_def_kind(); - debug!( - "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}", - def_kind, pick.item.def_id - ); tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); Ok((def_kind, pick.item.def_id)) } diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index d9870060a40..e9f55ab3406 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -253,7 +253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self))] pub fn probe_for_return_type( &self, span: Span, @@ -262,10 +262,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, ) -> Vec<ty::AssocItem> { - debug!( - "probe(self_ty={:?}, return_type={}, scope_expr_id={})", - self_ty, return_type, scope_expr_id - ); let method_names = self .probe_op( span, @@ -299,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self))] pub fn probe_for_name( &self, span: Span, @@ -310,10 +306,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scope_expr_id: hir::HirId, scope: ProbeScope, ) -> PickResult<'tcx> { - debug!( - "probe(self_ty={:?}, item_name={}, scope_expr_id={})", - self_ty, item_name, scope_expr_id - ); self.probe_op( span, mode, diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 3281dd8298b..66281448d40 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -341,7 +341,6 @@ fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::T typeck_with_fallback(tcx, def_id, fallback) } -#[instrument(skip(tcx, fallback))] fn typeck_with_fallback<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index ba42453bd60..86cf12d2240 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -972,7 +972,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } } -#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))] +#[instrument(level = "debug", skip(tcx, span, sig_if_method))] fn check_associated_item( tcx: TyCtxt<'_>, item_id: LocalDefId, @@ -1225,7 +1225,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo }); } -#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] +#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>, @@ -1472,7 +1472,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id wfcx.register_obligations(obligations); } -#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))] +#[instrument(level = "debug", skip(wfcx, span, hir_decl))] fn check_fn_or_method<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, @@ -1536,7 +1536,7 @@ const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut se `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \ of the previous types except `Self`)"; -#[tracing::instrument(level = "debug", skip(wfcx))] +#[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, fn_sig: &hir::FnSig<'_>, diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index ba687bc4da4..9ecf34e9ad3 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -501,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if !errors_buffer.is_empty() { errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for mut diag in errors_buffer.drain(..) { + for mut diag in errors_buffer { self.tcx().sess.diagnostic().emit_diagnostic(&mut diag); } } diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index f1dbe64f13a..9f931de6fde 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -19,7 +19,6 @@ use crate::errors::UnconstrainedOpaqueType; /// Computes the relevant generic parameter for a potential generic const argument. /// /// This should be called using the query `tcx.opt_const_param_of`. -#[instrument(level = "debug", skip(tcx))] pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> { use hir::*; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index 74a5b6e42c3..bfc4f061b70 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -497,7 +497,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { let expr_place = return_if_err!(self.mc.cat_expr(expr)); f(self); if let Some(els) = els { - // borrowing because we need to test the descriminant + // borrowing because we need to test the discriminant self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); self.walk_block(els) } diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index 6aa1c915542..d4b5e5e2fe4 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { if self.not_enough_args_provided() { self.suggest_adding_args(err); } else if self.too_many_args_provided() { + self.suggest_moving_args_from_assoc_fn_to_trait(err); self.suggest_removing_args_or_generics(err); } else { unreachable!(); @@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { } } + /// Suggests moving redundant argument(s) of an associate function to the + /// trait it belongs to. + /// + /// ```compile_fail + /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)` + /// ``` + fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) { + let trait_ = match self.tcx.trait_of_item(self.def_id) { + Some(def_id) => def_id, + None => return, + }; + + // Skip suggestion when the associated function is itself generic, it is unclear + // how to split the provided parameters between those to suggest to the trait and + // those to remain on the associated type. + let num_assoc_fn_expected_args = + self.num_expected_type_or_const_args() + self.num_expected_lifetime_args(); + if num_assoc_fn_expected_args > 0 { + return; + } + + let num_assoc_fn_excess_args = + self.num_excess_type_or_const_args() + self.num_excess_lifetime_args(); + + let trait_generics = self.tcx.generics_of(trait_); + let num_trait_generics_except_self = + trait_generics.count() - if trait_generics.has_self { 1 } else { 0 }; + + let msg = format!( + "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}", + these = pluralize!("this", num_assoc_fn_excess_args), + s = pluralize!(num_assoc_fn_excess_args), + name = self.tcx.item_name(trait_), + num = num_trait_generics_except_self, + ); + + if let Some(hir_id) = self.path_segment.hir_id + && let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id) + && let Some(parent_node) = self.tcx.hir().find(parent_node) + && let hir::Node::Expr(expr) = parent_node { + match expr.kind { + hir::ExprKind::Path(ref qpath) => { + self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + err, + qpath, + msg, + num_assoc_fn_excess_args, + num_trait_generics_except_self + ) + }, + hir::ExprKind::MethodCall(..) => { + self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + err, + trait_, + expr, + msg, + num_assoc_fn_excess_args, + num_trait_generics_except_self + ) + }, + _ => return, + } + } + } + + fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path( + &self, + err: &mut Diagnostic, + qpath: &'tcx hir::QPath<'tcx>, + msg: String, + num_assoc_fn_excess_args: usize, + num_trait_generics_except_self: usize, + ) { + if let hir::QPath::Resolved(_, path) = qpath + && let Some(trait_path_segment) = path.segments.get(0) { + let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params(); + + if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait { + if let Some(span) = self.gen_args.span_ext() + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let sugg = vec![ + (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)), + (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned()) + ]; + + err.multipart_suggestion( + msg, + sugg, + Applicability::MaybeIncorrect + ); + } + } + } + } + + fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call( + &self, + err: &mut Diagnostic, + trait_: DefId, + expr: &'tcx hir::Expr<'tcx>, + msg: String, + num_assoc_fn_excess_args: usize, + num_trait_generics_except_self: usize, + ) { + if let hir::ExprKind::MethodCall(_, args, _) = expr.kind { + assert_eq!(args.len(), 1); + if num_assoc_fn_excess_args == num_trait_generics_except_self { + if let Some(gen_args) = self.gen_args.span_ext() + && let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args) + && let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) { + let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args); + err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect); + } + } + } + } + /// Suggests to remove redundant argument(s): /// /// ```text diff --git a/config.toml.example b/config.toml.example index b320cdcc109..a967d881b02 100644 --- a/config.toml.example +++ b/config.toml.example @@ -666,6 +666,10 @@ changelog-seen = 2 # target. #llvm-config = <none> (path) +# Override detection of whether this is a Rust-patched LLVM. This would be used +# in conjunction with either an llvm-config or build.submodules = false. +#llvm-has-rust-patches = if llvm-config { false } else { true } + # Normally the build system can find LLVM's FileCheck utility, but if # not, you can specify an explicit file name for it. #llvm-filecheck = "/path/to/llvm-version/bin/FileCheck" diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index e21c8aa3bd5..6480fcaf93d 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1570,7 +1570,7 @@ impl<'a, T> CursorMut<'a, T> { /// that the cursor points to is unchanged, even if it is the "ghost" node. /// /// This operation should compute in *O*(1) time. - // `push_front` continues to point to "ghost" when it addes a node to mimic + // `push_front` continues to point to "ghost" when it adds a node to mimic // the behavior of `insert_before` on an empty list. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn push_front(&mut self, elt: T) { diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1f19b9e5945..60b36af5e67 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -436,7 +436,7 @@ impl<T> Vec<T> { /// an explanation of the difference between length and capacity, see /// *[Capacity and reallocation]*. /// - /// If it is imporant to know the exact allocated capacity of a `Vec`, + /// If it is important to know the exact allocated capacity of a `Vec`, /// always use the [`capacity`] method after construction. /// /// For `Vec<T>` where `T` is a zero-sized type, there will be no allocation @@ -591,7 +591,7 @@ impl<T, A: Allocator> Vec<T, A> { /// an explanation of the difference between length and capacity, see /// *[Capacity and reallocation]*. /// - /// If it is imporant to know the exact allocated capacity of a `Vec`, + /// If it is important to know the exact allocated capacity of a `Vec`, /// always use the [`capacity`] method after construction. /// /// For `Vec<T, A>` where `T` is a zero-sized type, there will be no allocation diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 81b6d5737ea..20340d42962 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -31,7 +31,7 @@ use crate::intrinsics; /// /// `unreachable_unchecked()` can be used in situations where the compiler /// can't prove invariants that were previously established. Such situations -/// have a higher chance of occuring if those invariants are upheld by +/// have a higher chance of occurring if those invariants are upheld by /// external code that the compiler can't analyze. /// ``` /// fn prepare_inputs(divisors: &mut Vec<u32>) { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 5f8e6efa0cf..d610f0a02f4 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1082,7 +1082,7 @@ extern "rust-intrinsic" { /// Note that using `transmute` to turn a pointer to a `usize` is (as noted above) [undefined /// behavior][ub] in `const` contexts. Also outside of consts, this operation might not behave /// as expected -- this is touching on many unspecified aspects of the Rust memory model. - /// Depending on what the code is doing, the following alternatives are preferrable to + /// Depending on what the code is doing, the following alternatives are preferable to /// pointer-to-integer transmutation: /// - If the code just wants to store data of arbitrary type in some buffer and needs to pick a /// type for that buffer, it can use [`MaybeUninit`][mem::MaybeUninit]. diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 0bd9c8e9acf..b37e9142d88 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -350,10 +350,12 @@ macro_rules! matches { /// Unwraps a result or propagates its error. /// -/// The `?` operator was added to replace `try!` and should be used instead. -/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use -/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`. +/// The [`?` operator][propagating-errors] was added to replace `try!` +/// and should be used instead. Furthermore, `try` is a reserved word +/// in Rust 2018, so if you must use it, you will need to use the +/// [raw-identifier syntax][ris]: `r#try`. /// +/// [propagating-errors]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator /// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html /// /// `try!` matches the given [`Result`]. In case of the `Ok` variant, the diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 6196c4da4e3..1cf306f2103 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -309,8 +309,8 @@ macro_rules! nonzero_unsigned_operations { ( $( $Ty: ident($Int: ident); )+ ) => { $( impl $Ty { - /// Add an unsigned integer to a non-zero value. - /// Check for overflow and return [`None`] on overflow + /// Adds an unsigned integer to a non-zero value. + /// Checks for overflow and returns [`None`] on overflow. /// As a consequence, the result cannot wrap to zero. /// /// @@ -346,7 +346,7 @@ macro_rules! nonzero_unsigned_operations { } } - /// Add an unsigned integer to a non-zero value. + /// Adds an unsigned integer to a non-zero value. #[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")] /// /// # Examples @@ -377,7 +377,7 @@ macro_rules! nonzero_unsigned_operations { unsafe { $Ty::new_unchecked(self.get().saturating_add(other)) } } - /// Add an unsigned integer to a non-zero value, + /// Adds an unsigned integer to a non-zero value, /// assuming overflow cannot occur. /// Overflow is unchecked, and it is undefined behaviour to overflow /// *even if the result would wrap to a non-zero value*. @@ -409,7 +409,7 @@ macro_rules! nonzero_unsigned_operations { } /// Returns the smallest power of two greater than or equal to n. - /// Check for overflow and return [`None`] + /// Checks for overflow and returns [`None`] /// if the next power of two is greater than the type’s maximum value. /// As a consequence, the result cannot wrap to zero. /// @@ -545,7 +545,7 @@ macro_rules! nonzero_signed_operations { } /// Checked absolute value. - /// Check for overflow and returns [`None`] if + /// Checks for overflow and returns [`None`] if #[doc = concat!("`self == ", stringify!($Int), "::MIN`.")] /// The result cannot be zero. /// @@ -740,8 +740,8 @@ macro_rules! nonzero_unsigned_signed_operations { ( $( $signedness:ident $Ty: ident($Int: ty); )+ ) => { $( impl $Ty { - /// Multiply two non-zero integers together. - /// Check for overflow and return [`None`] on overflow. + /// Multiplies two non-zero integers together. + /// Checks for overflow and returns [`None`] on overflow. /// As a consequence, the result cannot wrap to zero. /// /// # Examples @@ -777,7 +777,7 @@ macro_rules! nonzero_unsigned_signed_operations { } } - /// Multiply two non-zero integers together. + /// Multiplies two non-zero integers together. #[doc = concat!("Return [`", stringify!($Int), "::MAX`] on overflow.")] /// /// # Examples @@ -809,7 +809,7 @@ macro_rules! nonzero_unsigned_signed_operations { unsafe { $Ty::new_unchecked(self.get().saturating_mul(other.get())) } } - /// Multiply two non-zero integers together, + /// Multiplies two non-zero integers together, /// assuming overflow cannot occur. /// Overflow is unchecked, and it is undefined behaviour to overflow /// *even if the result would wrap to a non-zero value*. @@ -849,8 +849,8 @@ macro_rules! nonzero_unsigned_signed_operations { unsafe { $Ty::new_unchecked(self.get().unchecked_mul(other.get())) } } - /// Raise non-zero value to an integer power. - /// Check for overflow and return [`None`] on overflow. + /// Raises non-zero value to an integer power. + /// Checks for overflow and returns [`None`] on overflow. /// As a consequence, the result cannot wrap to zero. /// /// # Examples diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index ef7b3b1d147..f5c72d79755 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -154,7 +154,7 @@ impl<T: ?Sized> *const T { /// This is similar to `self as usize`, which semantically discards *provenance* and /// *address-space* information. However, unlike `self as usize`, casting the returned address /// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To - /// properly restore the lost information and obtain a dereferencable pointer, use + /// properly restore the lost information and obtain a dereferenceable pointer, use /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. /// /// If using those APIs is not possible because there is no way to preserve a pointer with the diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 203531f66aa..41a2685d361 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -90,7 +90,7 @@ //! isn't *pointer*-sized but address-space/offset/allocation-sized (we'll probably continue //! to conflate these notions). This would potentially make it possible to more efficiently //! target platforms where pointers are larger than offsets, such as CHERI and maybe some -//! segmented architecures. +//! segmented architectures. //! //! ## Provenance //! @@ -172,7 +172,7 @@ //! a pointer to a usize is generally an operation which *only* extracts the address. It is //! therefore *impossible* to construct a valid pointer from a usize because there is no way //! to restore the address-space and provenance. In other words, pointer-integer-pointer -//! roundtrips are not possible (in the sense that the resulting pointer is not dereferencable). +//! roundtrips are not possible (in the sense that the resulting pointer is not dereferenceable). //! //! The key insight to making this model *at all* viable is the [`with_addr`][] method: //! @@ -272,7 +272,7 @@ //! //! * Create an invalid pointer from just an address (see [`ptr::invalid`][]). This can //! be used for sentinel values like `null` *or* to represent a tagged pointer that will -//! never be dereferencable. In general, it is always sound for an integer to pretend +//! never be dereferenceable. In general, it is always sound for an integer to pretend //! to be a pointer "for fun" as long as you don't use operations on it which require //! it to be valid (offset, read, write, etc). //! diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 6a3b9ee9a7d..3e4c3ae0756 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -160,7 +160,7 @@ impl<T: ?Sized> *mut T { /// This is similar to `self as usize`, which semantically discards *provenance* and /// *address-space* information. However, unlike `self as usize`, casting the returned address /// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To - /// properly restore the lost information and obtain a dereferencable pointer, use + /// properly restore the lost information and obtain a dereferenceable pointer, use /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. /// /// If using those APIs is not possible because there is no way to preserve a pointer with the diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index f43b780ec9a..395c5678451 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2754,10 +2754,10 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { None => 0, }; // SAFETY: This type ensures that self.v is a valid pointer with a correct len. - // Therefore the bounds check in split_at_mut guarantess the split point is inbounds. + // Therefore the bounds check in split_at_mut guarantees the split point is inbounds. let (head, tail) = unsafe { self.v.split_at_mut(start) }; // SAFETY: This type ensures that self.v is a valid pointer with a correct len. - // Therefore the bounds check in split_at_mut guarantess the split point is inbounds. + // Therefore the bounds check in split_at_mut guarantees the split point is inbounds. let (nth, _) = unsafe { tail.split_at_mut(end - start) }; self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index d5706c388f0..1958745b586 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2321,7 +2321,7 @@ impl<T> [T] { } /// Binary searches this slice for a given element. - /// This behaves similary to [`contains`] if this slice is sorted. + /// This behaves similarly to [`contains`] if this slice is sorted. /// /// If the value is found then [`Result::Ok`] is returned, containing the /// index of the matching element. If there are multiple matches, then any @@ -3530,7 +3530,7 @@ impl<T> [T] { // alignment targeted for U. // `crate::ptr::align_offset` is called with a correctly aligned and // valid pointer `ptr` (it comes from a reference to `self`) and with - // a size that is a power of two (since it comes from the alignement for U), + // a size that is a power of two (since it comes from the alignment for U), // satisfying its safety constraints. let offset = unsafe { crate::ptr::align_offset(ptr, mem::align_of::<U>()) }; if offset > self.len() { diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 153dc4dbb08..4f29ecc0fba 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -1280,7 +1280,7 @@ macro_rules! try_from_secs { let rem_msb = nanos_tmp & rem_msb_mask == 0; let add_ns = !(rem_msb || (is_even && is_tie)); - // f32 does not have enough presicion to trigger the second branch + // f32 does not have enough precision to trigger the second branch // since it can not represent numbers between 0.999_999_940_395 and 1.0. let nanos = nanos + add_ns as u32; if ($mant_bits == 23) || (nanos != NANOS_PER_SEC) { (0, nanos) } else { (1, 0) } @@ -1299,9 +1299,9 @@ macro_rules! try_from_secs { let rem_msb = nanos_tmp & rem_msb_mask == 0; let add_ns = !(rem_msb || (is_even && is_tie)); - // f32 does not have enough presicion to trigger the second branch. + // f32 does not have enough precision to trigger the second branch. // For example, it can not represent numbers between 1.999_999_880... - // and 2.0. Bigger values result in even smaller presicion of the + // and 2.0. Bigger values result in even smaller precision of the // fractional part. let nanos = nanos + add_ns as u32; if ($mant_bits == 23) || (nanos != NANOS_PER_SEC) { diff --git a/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs index 65d3ce9be65..2235f016c71 100644 --- a/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/to_bitmask.rs @@ -70,7 +70,7 @@ impl_integer_intrinsic! { impl ToBitMask<BitMask=u64> for Mask<_, 64> } -/// Returns the minimum numnber of bytes in a bitmask with `lanes` lanes. +/// Returns the minimum number of bytes in a bitmask with `lanes` lanes. #[cfg(feature = "generic_const_exprs")] pub const fn bitmask_len(lanes: usize) -> usize { (lanes + 7) / 8 diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index 1516f084ab8..4461b21802a 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -176,8 +176,6 @@ define_handles! { FreeFunctions, TokenStream, SourceFile, - MultiSpan, - Diagnostic, 'interned: Span, diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index 5cde966bf17..4c1e196b5ad 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -57,6 +57,7 @@ macro_rules! with_api { fn track_env_var(var: &str, value: Option<&str>); fn track_path(path: &str); fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>; + fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); }, TokenStream { fn drop($self: $S::TokenStream); @@ -87,22 +88,6 @@ macro_rules! with_api { fn path($self: &$S::SourceFile) -> String; fn is_real($self: &$S::SourceFile) -> bool; }, - MultiSpan { - fn drop($self: $S::MultiSpan); - fn new() -> $S::MultiSpan; - fn push($self: &mut $S::MultiSpan, span: $S::Span); - }, - Diagnostic { - fn drop($self: $S::Diagnostic); - fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic; - fn sub( - $self: &mut $S::Diagnostic, - level: Level, - msg: &str, - span: $S::MultiSpan, - ); - fn emit($self: $S::Diagnostic); - }, Span { fn debug($self: $S::Span) -> String; fn source_file($self: $S::Span) -> $S::SourceFile; @@ -510,6 +495,18 @@ compound_traits!( } ); +#[derive(Clone, Debug)] +pub struct Diagnostic<Span> { + pub level: Level, + pub message: String, + pub spans: Vec<Span>, + pub children: Vec<Diagnostic<Span>>, +} + +compound_traits!( + struct Diagnostic<Span> { level, message, spans, children } +); + /// Globals provided alongside the initial inputs for a macro expansion. /// Provides values such as spans which are used frequently to avoid RPC. #[derive(Clone)] diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index e068ec60b6a..e47a77f6c13 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -11,8 +11,6 @@ pub trait Types { type FreeFunctions: 'static; type TokenStream: 'static + Clone; type SourceFile: 'static + Clone; - type MultiSpan: 'static; - type Diagnostic: 'static; type Span: 'static + Copy + Eq + Hash; type Symbol: 'static; } diff --git a/library/proc_macro/src/diagnostic.rs b/library/proc_macro/src/diagnostic.rs index 6e46dc0367d..5a209f7c7aa 100644 --- a/library/proc_macro/src/diagnostic.rs +++ b/library/proc_macro/src/diagnostic.rs @@ -161,22 +161,15 @@ impl Diagnostic { /// Emit the diagnostic. #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub fn emit(self) { - fn to_internal(spans: Vec<Span>) -> crate::bridge::client::MultiSpan { - let mut multi_span = crate::bridge::client::MultiSpan::new(); - for span in spans { - multi_span.push(span.0); + fn to_internal(diag: Diagnostic) -> crate::bridge::Diagnostic<crate::bridge::client::Span> { + crate::bridge::Diagnostic { + level: diag.level, + message: diag.message, + spans: diag.spans.into_iter().map(|s| s.0).collect(), + children: diag.children.into_iter().map(to_internal).collect(), } - multi_span } - let mut diag = crate::bridge::client::Diagnostic::new( - self.level, - &self.message[..], - to_internal(self.spans), - ); - for c in self.children { - diag.sub(c.level, &c.message[..], to_internal(c.spans)); - } - diag.emit(); + crate::bridge::client::FreeFunctions::emit_diagnostic(to_internal(self)); } } diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 7157b5af00c..a4b0522b050 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1921,7 +1921,7 @@ mod type_keyword {} /// and [proposal]s exist to use `unsafe {}` blocks inside such functions when /// making `unsafe` operations. /// -/// See the [Rustnomicon] and the [Reference] for more informations. +/// See the [Rustnomicon] and the [Reference] for more information. /// /// # Examples /// @@ -2113,7 +2113,7 @@ mod use_keyword {} /// Add constraints that must be upheld to use an item. /// /// `where` allows specifying constraints on lifetime and generic parameters. -/// The [RFC] introducing `where` contains detailed informations about the +/// The [RFC] introducing `where` contains detailed information about the /// keyword. /// /// # Examples @@ -2355,7 +2355,7 @@ mod dyn_keyword {} /// println!("f = {f} and i = {i}"); /// ``` /// -/// See the [Reference][union] for more informations on `union`s. +/// See the [Reference][union] for more information on `union`s. /// /// [`struct`]: keyword.struct.html /// [union]: ../reference/items/unions.html diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs index ce427599c3b..dbb9829bb66 100644 --- a/library/std/src/sys/unix/locks/fuchsia_mutex.rs +++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs @@ -138,7 +138,7 @@ impl Mutex { } } - // The state has changed or a wakeup occured, try to lock the mutex. + // The state has changed or a wakeup occurred, try to lock the mutex. match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) { Ok(_) => return, Err(updated) => state = updated, diff --git a/library/std/src/sys/unix/locks/futex_mutex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs index 99ba86e5f99..1b5be46c605 100644 --- a/library/std/src/sys/unix/locks/futex_mutex.rs +++ b/library/std/src/sys/unix/locks/futex_mutex.rs @@ -53,7 +53,7 @@ impl Mutex { // We avoid an unnecessary write if it as already set to 2, // to be friendlier for the caches. if state != 2 && self.futex.swap(2, Acquire) == 0 { - // We changed it from 0 to 2, so we just succesfully locked it. + // We changed it from 0 to 2, so we just successfully locked it. return; } diff --git a/library/std/src/sys/unix/locks/futex_rwlock.rs b/library/std/src/sys/unix/locks/futex_rwlock.rs index b3bbbf743f8..0cc92244eca 100644 --- a/library/std/src/sys/unix/locks/futex_rwlock.rs +++ b/library/std/src/sys/unix/locks/futex_rwlock.rs @@ -54,7 +54,7 @@ fn is_read_lockable(state: u32) -> bool { // We don't allow read-locking if there's readers waiting, even if the lock is unlocked // and there's no writers waiting. The only situation when this happens is after unlocking, // at which point the unlocking thread might be waking up writers, which have priority over readers. - // The unlocking thread will clear the readers waiting bit and wake up readers, if necssary. + // The unlocking thread will clear the readers waiting bit and wake up readers, if necessary. state & MASK < MAX_READERS && !has_readers_waiting(state) && !has_writers_waiting(state) } diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs index 1a5421facd0..43ab8c7ee65 100644 --- a/library/std/src/sys/windows/cmath.rs +++ b/library/std/src/sys/windows/cmath.rs @@ -44,7 +44,7 @@ mod shims { } // On 32-bit x86 MSVC these functions aren't defined, so we just define shims -// which promote everything fo f64, perform the calculation, and then demote +// which promote everything to f64, perform the calculation, and then demote // back to f32. While not precisely correct should be "correct enough" for now. #[cfg(all(target_env = "msvc", target_arch = "x86"))] mod shims { diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 0aa7c50ded1..98c8834d384 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -3,7 +3,7 @@ use crate::os::windows::prelude::*; use crate::ffi::OsString; use crate::fmt; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; +use crate::mem::{self, MaybeUninit}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::path::{Path, PathBuf}; use crate::ptr; @@ -326,7 +326,8 @@ impl File { cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut b = + Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { reparse_tag = (*buf).ReparseTag; } @@ -389,7 +390,8 @@ impl File { attr.file_size = info.AllocationSize as u64; attr.number_of_links = Some(info.NumberOfLinks); if attr.file_type().is_reparse_point() { - let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut b = + Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); if let Ok((_, buf)) = self.reparse_point(&mut b) { attr.reparse_tag = (*buf).ReparseTag; } @@ -463,7 +465,7 @@ impl File { // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. fn reparse_point( &self, - space: &mut Align8<[u8]>, + space: &mut Align8<[MaybeUninit<u8>]>, ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { unsafe { let mut bytes = 0; @@ -488,7 +490,7 @@ impl File { } fn readlink(&self) -> io::Result<PathBuf> { - let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let (_bytes, buf) = self.reparse_point(&mut space)?; unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { @@ -658,12 +660,16 @@ impl File { /// A buffer for holding directory entries. struct DirBuff { - buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>, + buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>, } impl DirBuff { const BUFFER_SIZE: usize = 1024; fn new() -> Self { - Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) } + Self { + // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need + // initialization. + buffer: unsafe { Box::new_uninit().assume_init() }, + } } fn capacity(&self) -> usize { self.buffer.0.len() @@ -676,8 +682,8 @@ impl DirBuff { DirBuffIter::new(self) } } -impl AsRef<[u8]> for DirBuff { - fn as_ref(&self) -> &[u8] { +impl AsRef<[MaybeUninit<u8>]> for DirBuff { + fn as_ref(&self) -> &[MaybeUninit<u8>] { &self.buffer.0 } } @@ -686,7 +692,7 @@ impl AsRef<[u8]> for DirBuff { /// /// Currently only returns file names (UTF-16 encoded). struct DirBuffIter<'a> { - buffer: Option<&'a [u8]>, + buffer: Option<&'a [MaybeUninit<u8>]>, cursor: usize, } impl<'a> DirBuffIter<'a> { @@ -701,9 +707,13 @@ impl<'a> Iterator for DirBuffIter<'a> { let buffer = &self.buffer?[self.cursor..]; // Get the name and next entry from the buffer. - // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the - // last field (the file name) is unsized. So an offset has to be - // used to get the file name slice. + // SAFETY: + // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last + // field (the file name) is unsized. So an offset has to be used to + // get the file name slice. + // - The OS has guaranteed initialization of the fields of + // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least + // `FileNameLength` bytes) let (name, is_directory, next_entry) = unsafe { let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>(); // Guaranteed to be aligned in documentation for @@ -1349,7 +1359,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { let h = f.as_inner().as_raw_handle(); unsafe { - let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); + let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]); let data_ptr = data.0.as_mut_ptr(); let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); diff --git a/library/std/src/sys/windows/path/tests.rs b/library/std/src/sys/windows/path/tests.rs index 2f7ec433bf2..a7117506905 100644 --- a/library/std/src/sys/windows/path/tests.rs +++ b/library/std/src/sys/windows/path/tests.rs @@ -115,7 +115,7 @@ fn test_parse_prefix_verbatim_device() { assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe")); } -// See #93586 for more infomation. +// See #93586 for more information. #[test] fn test_windows_prefix_components() { use crate::path::Path; diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 9301c5a2ff3..cc08ae5f99f 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -85,7 +85,7 @@ def _download(path, url, probably_big, verbose, exception): option = "-#" else: option = "-s" - # If curl is not present on Win32, we shoud not sys.exit + # If curl is not present on Win32, we should not sys.exit # but raise `CalledProcessError` or `OSError` instead require(["curl", "--version"], exception=platform_is_win32) run(["curl", option, @@ -793,6 +793,8 @@ class RustBuild(object): def check_vendored_status(self): """Check that vendoring is configured properly""" + # keep this consistent with the equivalent check in rustbuild: + # https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/lib.rs#L399-L405 if 'SUDO_USER' in os.environ and not self.use_vendored_sources: if os.getuid() == 0: self.use_vendored_sources = True diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 1e764811ea7..14e8ebd6876 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -946,7 +946,7 @@ impl<'a> Builder<'a> { }; patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); if !fname.extension().map_or(false, |ext| ext == "so") { - // Finally, set the corret .interp for binaries + // Finally, set the correct .interp for binaries let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ... let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path)))); @@ -962,7 +962,7 @@ impl<'a> Builder<'a> { let tempfile = self.tempdir().join(dest_path.file_name().unwrap()); // While bootstrap itself only supports http and https downloads, downstream forks might // need to download components from other protocols. The match allows them adding more - // protocols without worrying about merge conficts if we change the HTTP implementation. + // protocols without worrying about merge conflicts if we change the HTTP implementation. match url.split_once("://").map(|(proto, _)| proto) { Some("http") | Some("https") => { self.download_http_with_retries(&tempfile, url, help_on_error) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 13e3049f2c8..7c062460c4f 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -388,6 +388,7 @@ impl PartialEq<&str> for TargetSelection { pub struct Target { /// Some(path to llvm-config) if using an external LLVM. pub llvm_config: Option<PathBuf>, + pub llvm_has_rust_patches: Option<bool>, /// Some(path to FileCheck) if one was specified. pub llvm_filecheck: Option<PathBuf>, pub llvm_libunwind: Option<LlvmLibunwind>, @@ -733,6 +734,7 @@ define_config! { default_linker: Option<PathBuf> = "default-linker", linker: Option<String> = "linker", llvm_config: Option<String> = "llvm-config", + llvm_has_rust_patches: Option<bool> = "llvm-has-rust-patches", llvm_filecheck: Option<String> = "llvm-filecheck", llvm_libunwind: Option<String> = "llvm-libunwind", android_ndk: Option<String> = "android-ndk", @@ -1109,6 +1111,7 @@ impl Config { if let Some(ref s) = cfg.llvm_config { target.llvm_config = Some(config.src.join(s)); } + target.llvm_has_rust_patches = cfg.llvm_has_rust_patches; if let Some(ref s) = cfg.llvm_filecheck { target.llvm_filecheck = Some(config.src.join(s)); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 952943b78c6..9336f958cf2 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -112,6 +112,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str; +use config::Target; use filetime::FileTime; use once_cell::sync::OnceCell; @@ -395,13 +396,18 @@ impl Build { let src = config.src.clone(); let out = config.out.clone(); + #[cfg(unix)] + // keep this consistent with the equivalent check in x.py: + // https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/bootstrap.py#L796-L797 let is_sudo = match env::var_os("SUDO_USER") { - Some(sudo_user) => match env::var_os("USER") { - Some(user) => user != sudo_user, - None => false, - }, + Some(_sudo_user) => { + let uid = unsafe { libc::getuid() }; + uid == 0 + } None => false, }; + #[cfg(not(unix))] + let is_sudo = false; let ignore_git = config.ignore_git; let rust_info = channel::GitInfo::new(ignore_git, &src); @@ -834,12 +840,13 @@ impl Build { /// /// If no custom `llvm-config` was specified then Rust's llvm will be used. fn is_rust_llvm(&self, target: TargetSelection) -> bool { - if self.config.llvm_from_ci && target == self.config.build { - return true; - } - match self.config.target_config.get(&target) { - Some(ref c) => c.llvm_config.is_none(), + Some(Target { llvm_has_rust_patches: Some(patched), .. }) => *patched, + Some(Target { llvm_config, .. }) => { + // If the user set llvm-config we assume Rust is not patched, + // but first check to see if it was configured by llvm-from-ci. + (self.config.llvm_from_ci && target == self.config.build) || llvm_config.is_none() + } None => true, } } @@ -1629,7 +1636,7 @@ fn chmod(_path: &Path, _perms: u32) {} /// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.) /// If the test is running and code is an error code, it will cause a panic. fn detail_exit(code: i32) -> ! { - // if in test and code is an error code, panic with staus code provided + // if in test and code is an error code, panic with status code provided if cfg!(test) && code != 0 { panic!("status code: {}", code); } else { diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index e1cc8d671d7..62b56994afe 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -638,7 +638,7 @@ fn configure_cmake( if target.contains("darwin") { // Make sure that CMake does not build universal binaries on macOS. - // Explicitly specifiy the one single target architecture. + // Explicitly specify the one single target architecture. if target.starts_with("aarch64") { // macOS uses a different name for building arm64 cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64"); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index f1c2a21de59..f61c9583085 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -854,7 +854,10 @@ fn get_browser_ui_test_version_inner(npm: &Path, global: bool) -> Option<String> .output() .map(|output| String::from_utf8_lossy(&output.stdout).into_owned()) .unwrap_or(String::new()); - lines.lines().find_map(|l| l.split(":browser-ui-test@").skip(1).next()).map(|v| v.to_owned()) + lines + .lines() + .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@")) + .map(|v| v.to_owned()) } fn get_browser_ui_test_version(npm: &Path) -> Option<String> { diff --git a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile index 71ea13071bd..dd74726f856 100644 --- a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++-multilib \ make \ @@ -20,18 +21,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - RUN mkdir -p /config RUN echo "[rust]" > /config/nopt-std-config.toml RUN echo "optimize = false" >> /config/nopt-std-config.toml -# We are intentionally allowing an old toolchain on this builder (and that's -# incompatible with LLVM downloads today). -ENV NO_DOWNLOAD_CI_LLVM 1 - -ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests \ - --set llvm.allow-old-toolchain +ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \ && python3 ../x.py --stage 2 test diff --git a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile index ebeab3dbdb7..0c36cfd66bd 100644 --- a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++-multilib \ make \ @@ -20,14 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - -# We are intentionally allowing an old toolchain on this builder (and that's -# incompatible with LLVM downloads today). -ENV NO_DOWNLOAD_CI_LLVM 1 -ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu \ - --set llvm.allow-old-toolchain +ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu # Exclude some tests that are unlikely to be platform specific, to speed up # this slow job. ENV SCRIPT python3 ../x.py --stage 2 test \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile index 321b3d6ace4..d55d5b56ad3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-aux/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ @@ -23,13 +24,5 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - -# We are intentionally allowing an old toolchain on this builder (and that's -# incompatible with LLVM downloads today). -ENV NO_DOWNLOAD_CI_LLVM 1 - -ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \ - --set llvm.allow-old-toolchain +ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu ENV RUST_CHECK_TARGET check-aux diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index be4def85f14..80a004501a8 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ @@ -19,14 +20,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - -# We are intentionally allowing an old toolchain on this builder (and that's -# incompatible with LLVM downloads today). +# We are disabling CI LLVM since distcheck is an offline build. ENV NO_DOWNLOAD_CI_LLVM 1 -ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false \ - --set llvm.allow-old-toolchain +ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false ENV SCRIPT python3 ../x.py --stage 2 test distcheck ENV DIST_SRC 1 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index e2ec4f27500..4350ca20586 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ @@ -27,6 +28,7 @@ RUN apt-get install -y \ libdbus-1-3 \ libexpat1 \ libfontconfig1 \ + libgbm1 \ libgcc1 \ libgconf-2-4 \ libgdk-pixbuf2.0-0 \ @@ -59,13 +61,10 @@ RUN apt-get install -y \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY scripts/cmake.sh /scripts/ -RUN /scripts/cmake.sh - COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/ -RUN curl -sL https://nodejs.org/dist/v14.4.0/node-v14.4.0-linux-x64.tar.xz | tar -xJ -ENV NODE_FOLDER=/node-v14.4.0-linux-x64/bin +RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ +ENV NODE_FOLDER=/node-v14.20.0-linux-x64/bin ENV PATH="$NODE_FOLDER:${PATH}" COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ @@ -80,14 +79,10 @@ COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ # the local version of the package is different than the one used by the CI. RUN npm install -g browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true -# We are intentionally allowing an old toolchain on this builder (and that's -# incompatible with LLVM downloads today). -ENV NO_DOWNLOAD_CI_LLVM 1 - ENV RUST_CONFIGURE_ARGS \ - --set llvm.allow-old-toolchain \ --build=x86_64-unknown-linux-gnu \ --save-toolstates=/tmp/toolstate/toolstates.json ENV SCRIPT /tmp/checktools.sh ../x.py && \ - NODE_PATH=`npm root -g` python3 ../x.py test src/test/rustdoc-gui --stage 2 + NODE_PATH=`npm root -g` python3 ../x.py test src/test/rustdoc-gui --stage 2 \ + --test-args "'--no-sandbox --jobs 1'" diff --git a/src/doc/rustc/src/platform-support/fuchsia.md b/src/doc/rustc/src/platform-support/fuchsia.md index 10d12f88454..53a510f080e 100644 --- a/src/doc/rustc/src/platform-support/fuchsia.md +++ b/src/doc/rustc/src/platform-support/fuchsia.md @@ -79,7 +79,7 @@ the following commands: ```sh rustup target add x86_64-fuchsia -rustup target add aarch_64-fuchsia +rustup target add aarch64-fuchsia ``` After installing our Fuchsia targets, we can now compile a Rust binary that targets diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md index d325ba3346a..b18a125f3b0 100644 --- a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md +++ b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md @@ -87,7 +87,7 @@ Rust programs can be built for that target: rustc --target m68k-unknown-linux-gnu your-code.rs ``` -Very simple progams can be run using the `qemu-m68k-static` program: +Very simple programs can be run using the `qemu-m68k-static` program: ```text $ qemu-m68k-static your-code diff --git a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md index 721c234c6e6..fb0cea05d44 100644 --- a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md +++ b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md @@ -25,7 +25,7 @@ Like with any other Windows target created binaries are in PE format. ## Building the target -For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring corss compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors. +For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring cross compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors. Native bootstrapping builds require rather fragile hacks until host artifacts are available so I won't describe them here. ## Building Rust programs diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md index 8f90d9c7453..295dec0f0e4 100644 --- a/src/doc/rustc/src/platform-support/unknown-uefi.md +++ b/src/doc/rustc/src/platform-support/unknown-uefi.md @@ -133,7 +133,7 @@ There are 3 common ways to compile native C code for UEFI targets: - Use native Windows targets. This means compiling your C code for the Windows platform as if it was the UEFI platform. This works for static libraries, but needs adjustments when linking into an UEFI executable. You can, however, - link such static libraries seemlessly into rust code compiled for UEFI + link such static libraries seamlessly into rust code compiled for UEFI targets. Be wary of any includes that are not specifically suitable for UEFI targets (especially the C standard library includes are not always compatible). Freestanding compilations are recommended to avoid diff --git a/src/doc/rustc/src/platform-support/wasm64-unknown-unknown.md b/src/doc/rustc/src/platform-support/wasm64-unknown-unknown.md index 021b904debd..6932e6a5764 100644 --- a/src/doc/rustc/src/platform-support/wasm64-unknown-unknown.md +++ b/src/doc/rustc/src/platform-support/wasm64-unknown-unknown.md @@ -30,7 +30,7 @@ is 8-bytes large as well as pointers. The tradeoff, though, is that the maximum memory size is now the full 64-bit address space instead of the 4GB as limited by the 32-bit address space for `wasm32-unknown-unknown`. -This target is not a stable target. The [memory64] WebAssembly proposal is stil +This target is not a stable target. The [memory64] WebAssembly proposal is still in-progress and not standardized. This means that there are not many engines which implement the `memory64` feature and if they do they're likely behind a flag, for example: diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md index bfa92e7d32a..321992f7b0d 100644 --- a/src/doc/unstable-book/src/compiler-flags/check-cfg.md +++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md @@ -143,7 +143,7 @@ fn do_features() {} #[cfg(has_feathers = "zapping")] // This is expected as "has_feathers" was provided in names() // and because no value checking was enable for "has_feathers" - // no warning is emited for the value "zapping" + // no warning is emitted for the value "zapping" fn do_zapping() {} #[cfg(has_mumble_frotz)] // This is UNEXPECTED because names checking is enable and diff --git a/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md b/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md index 977d258529f..3890a12b7e6 100644 --- a/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md +++ b/src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md @@ -8,7 +8,7 @@ This flag will rewrite absolute paths under the current working directory, replacing the current working directory prefix with a specified value. The given value may be absolute or relative, or empty. This switch takes -precidence over `--remap-path-prefix` in case they would both match a given +precedence over `--remap-path-prefix` in case they would both match a given path. This flag helps to produce deterministic output, by removing the current working diff --git a/src/etc/cpu-usage-over-time-plot.sh b/src/etc/cpu-usage-over-time-plot.sh index 1c342559194..2617378ba5f 100755 --- a/src/etc/cpu-usage-over-time-plot.sh +++ b/src/etc/cpu-usage-over-time-plot.sh @@ -15,7 +15,7 @@ # Improvements to this script are greatly appreciated! if [[ $# != 2 ]]; then - echo "expected 2 arguments, recieved $#" + echo "expected 2 arguments, received $#" echo "example usage: './src/etc/cpu-usage-over-time-plot.sh \ 7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c \ x86_64-gnu'" diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index baf95627c70..c97fb4b8054 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -386,7 +386,7 @@ def check_tree_attr(tree, path, attr, pat, regexp): return ret -# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`). +# Returns the number of occurrences matching the regex (`regexp`) and the text (`pat`). def check_tree_text(tree, path, pat, regexp, stop_at_first): path = normalize_xpath(path) match_count = 0 diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index cbdfea89efb..7bc35c7d551 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -10,18 +10,19 @@ path = "lib.rs" arrayvec = { version = "0.7", default-features = false } askama = { version = "0.11", default-features = false, features = ["config"] } atty = "0.2" -pulldown-cmark = { version = "0.9.2", default-features = false } +itertools = "0.10.1" minifier = "0.2.2" -serde = { version = "1.0", features = ["derive"] } +once_cell = "1.10.0" +pulldown-cmark = { version = "0.9.2", default-features = false } +regex = "1" +rustdoc-json-types = { path = "../rustdoc-json-types" } serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } smallvec = "1.8.1" tempfile = "3" -itertools = "0.10.1" -regex = "1" -rustdoc-json-types = { path = "../rustdoc-json-types" } +thin-vec = "0.2.8" tracing = "0.1" tracing-tree = "0.2.0" -once_cell = "1.10.0" [dependencies.tracing-subscriber] version = "0.3.3" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 756e4f3b127..175472797cb 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -123,7 +123,7 @@ where kind: Box::new(ImplItem(Box::new(Impl { unsafety: hir::Unsafety::Normal, generics: new_generics, - trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, &[])), + trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), for_: clean_middle_ty(ty, self.cx, None), items: Vec::new(), polarity, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index da15c3c2b1f..cc734389e07 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -115,7 +115,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { ), // FIXME(eddyb) compute both `trait_` and `for_` from // the post-inference `trait_ref`, as it's more accurate. - trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, &[])), + trait_: Some(clean_trait_ref_with_bindings(cx, trait_ref.0, ThinVec::new())), for_: clean_middle_ty(ty.0, cx, None), items: cx.tcx .associated_items(impl_def_id) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 31b805f2ed7..c8aa51c3a49 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -3,9 +3,10 @@ use std::iter::once; use std::sync::Arc; +use thin_vec::ThinVec; + use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -459,7 +460,7 @@ pub(crate) fn build_impl( ), }; let polarity = tcx.impl_polarity(did); - let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, &[])); + let trait_ = associated_trait.map(|t| clean_trait_ref_with_bindings(cx, t, ThinVec::new())); if trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait() { super::build_deref_target_impls(cx, &trait_items, ret); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 41f9eb3cdf6..ebf6c55ee35 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -33,7 +33,8 @@ use std::collections::hash_map::Entry; use std::collections::BTreeMap; use std::default::Default; use std::hash::Hash; -use std::{mem, vec}; +use std::mem; +use thin_vec::ThinVec; use crate::core::{self, DocContext, ImplTraitParam}; use crate::formats::item_type::ItemType; @@ -125,7 +126,7 @@ fn clean_generic_bound<'tcx>( bug!("clean: parenthesized `GenericBound::LangItemTrait`"); }; - let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, &bindings); + let trait_ = clean_trait_ref_with_bindings(cx, trait_ref, bindings); GenericBound::TraitBound( PolyTrait { trait_, generic_params: vec![] }, hir::TraitBoundModifier::None, @@ -147,14 +148,14 @@ fn clean_generic_bound<'tcx>( pub(crate) fn clean_trait_ref_with_bindings<'tcx>( cx: &mut DocContext<'tcx>, trait_ref: ty::TraitRef<'tcx>, - bindings: &[TypeBinding], + bindings: ThinVec<TypeBinding>, ) -> Path { let kind = cx.tcx.def_kind(trait_ref.def_id).into(); if !matches!(kind, ItemType::Trait | ItemType::TraitAlias) { span_bug!(cx.tcx.def_span(trait_ref.def_id), "`TraitRef` had unexpected kind {:?}", kind); } inline::record_extern_fqn(cx, trait_ref.def_id, kind); - let path = external_path(cx, trait_ref.def_id, true, bindings.to_vec(), trait_ref.substs); + let path = external_path(cx, trait_ref.def_id, true, bindings, trait_ref.substs); debug!("ty::TraitRef\n subst: {:?}\n", trait_ref.substs); @@ -164,7 +165,7 @@ pub(crate) fn clean_trait_ref_with_bindings<'tcx>( fn clean_poly_trait_ref_with_bindings<'tcx>( cx: &mut DocContext<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, - bindings: &[TypeBinding], + bindings: ThinVec<TypeBinding>, ) -> GenericBound { let poly_trait_ref = poly_trait_ref.lift_to_tcx(cx.tcx).unwrap(); @@ -327,7 +328,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { ty: clean_middle_ty(poly_trait_ref.skip_binder().self_ty(), cx, None), - bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, &[])], + bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) } @@ -402,7 +403,7 @@ fn clean_projection<'tcx>( def_id: Option<DefId>, ) -> Type { let lifted = ty.lift_to_tcx(cx.tcx).unwrap(); - let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), &[]); + let trait_ = clean_trait_ref_with_bindings(cx, lifted.trait_ref(cx.tcx), ThinVec::new()); let self_type = clean_middle_ty(ty.self_ty(), cx, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) @@ -1591,12 +1592,12 @@ pub(crate) fn clean_middle_ty<'tcx>( AdtKind::Enum => ItemType::Enum, }; inline::record_extern_fqn(cx, did, kind); - let path = external_path(cx, did, false, vec![], substs); + let path = external_path(cx, did, false, ThinVec::new(), substs); Type::Path { path } } ty::Foreign(did) => { inline::record_extern_fqn(cx, did, ItemType::ForeignType); - let path = external_path(cx, did, false, vec![], InternalSubsts::empty()); + let path = external_path(cx, did, false, ThinVec::new(), InternalSubsts::empty()); Type::Path { path } } ty::Dynamic(obj, ref reg) => { @@ -1620,7 +1621,7 @@ pub(crate) fn clean_middle_ty<'tcx>( let mut bounds = dids .map(|did| { let empty = cx.tcx.intern_substs(&[]); - let path = external_path(cx, did, false, vec![], empty); + let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); PolyTrait { trait_: path, generic_params: Vec::new() } }) @@ -1696,7 +1697,7 @@ pub(crate) fn clean_middle_ty<'tcx>( } } - let bindings: Vec<_> = bounds + let bindings: ThinVec<_> = bounds .iter() .filter_map(|bound| { if let ty::PredicateKind::Projection(proj) = bound.kind().skip_binder() @@ -1717,7 +1718,7 @@ pub(crate) fn clean_middle_ty<'tcx>( }) .collect(); - Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, &bindings)) + Some(clean_poly_trait_ref_with_bindings(cx, trait_ref, bindings)) }) .collect::<Vec<_>>(); bounds.extend(regions); @@ -1848,12 +1849,8 @@ fn clean_generic_args<'tcx>( }) .collect::<Vec<_>>() .into(); - let bindings = generic_args - .bindings - .iter() - .map(|x| clean_type_binding(x, cx)) - .collect::<Vec<_>>() - .into(); + let bindings = + generic_args.bindings.iter().map(|x| clean_type_binding(x, cx)).collect::<ThinVec<_>>(); GenericArgs::AngleBracketed { args, bindings } } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 909a47d07b1..2808b400a0b 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -8,6 +8,7 @@ use std::sync::OnceLock as OnceCell; use std::{cmp, fmt, iter}; use arrayvec::ArrayVec; +use thin_vec::ThinVec; use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; @@ -15,7 +16,6 @@ use rustc_ast::{self as ast, AttrStyle}; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; @@ -1303,7 +1303,7 @@ impl GenericBound { pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { let did = cx.tcx.require_lang_item(LangItem::Sized, None); let empty = cx.tcx.intern_substs(&[]); - let path = external_path(cx, did, false, vec![], empty); + let path = external_path(cx, did, false, ThinVec::new(), empty); inline::record_extern_fqn(cx, did, ItemType::Trait); GenericBound::TraitBound( PolyTrait { trait_: path, generic_params: Vec::new() }, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 718cbbd2b83..ac9ab339616 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -12,7 +12,6 @@ use crate::visit_lib::LibEmbargoVisitor; use rustc_ast as ast; use rustc_ast::tokenstream::TokenTree; -use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -23,6 +22,7 @@ use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use std::fmt::Write as _; use std::mem; +use thin_vec::ThinVec; #[cfg(test)] mod tests; @@ -102,7 +102,7 @@ fn external_generic_args<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, - bindings: Vec<TypeBinding>, + bindings: ThinVec<TypeBinding>, substs: SubstsRef<'tcx>, ) -> GenericArgs { let args = substs_to_args(cx, substs, has_self); @@ -112,7 +112,7 @@ fn external_generic_args<'tcx>( // The trait's first substitution is the one after self, if there is one. match substs.iter().nth(if has_self { 1 } else { 0 }).unwrap().expect_ty().kind() { ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(t, cx, None)).collect::<Vec<_>>().into(), - _ => return GenericArgs::AngleBracketed { args: args.into(), bindings: bindings.into() }, + _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, }; let output = None; // FIXME(#20299) return type comes from a projection now @@ -130,7 +130,7 @@ pub(super) fn external_path<'tcx>( cx: &mut DocContext<'tcx>, did: DefId, has_self: bool, - bindings: Vec<TypeBinding>, + bindings: ThinVec<TypeBinding>, substs: SubstsRef<'tcx>, ) -> Path { let def_kind = cx.tcx.def_kind(did); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 6f49f00f93e..be10a5c101f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -349,8 +349,7 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( let where_preds = comma_sep(where_predicates, false); let clause = if f.alternate() { if ending == Ending::Newline { - // add a space so stripping <br> tags and breaking spaces still renders properly - format!(" where{where_preds}, ") + format!(" where{where_preds},") } else { format!(" where{where_preds}") } @@ -364,20 +363,16 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( if ending == Ending::Newline { let mut clause = " ".repeat(indent.saturating_sub(1)); - // add a space so stripping <br> tags and breaking spaces still renders properly - write!( - clause, - " <span class=\"where fmt-newline\">where{where_preds}, </span>" - )?; + write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?; clause } else { // insert a <br> tag after a single space but before multiple spaces at the start if indent == 0 { - format!(" <br><span class=\"where\">where{where_preds}</span>") + format!("<br><span class=\"where\">where{where_preds}</span>") } else { let mut clause = br_with_padding; clause.truncate(clause.len() - 5 * " ".len()); - write!(clause, " <span class=\"where\">where{where_preds}</span>")?; + write!(clause, "<span class=\"where\">where{where_preds}</span>")?; clause } } diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 4a12d74ddef..bb8e46af762 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -450,7 +450,7 @@ impl<'a> PeekIter<'a> { fn new(iter: TokenIter<'a>) -> Self { Self { stored: VecDeque::new(), peek_pos: 0, iter } } - /// Returns the next item after the current one. It doesn't interfer with `peek_next` output. + /// Returns the next item after the current one. It doesn't interfere with `peek_next` output. fn peek(&mut self) -> Option<&(TokenKind, &'a str)> { if self.stored.is_empty() { if let Some(next) = self.iter.next() { @@ -459,7 +459,7 @@ impl<'a> PeekIter<'a> { } self.stored.front() } - /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output. + /// Returns the next item after the last one peeked. It doesn't interfere with `peek` output. fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> { self.peek_pos += 1; if self.peek_pos - 1 < self.stored.len() { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index eff34047e3c..7577c719623 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1737,8 +1737,8 @@ pub(crate) fn render_impl_summary( // in documentation pages for trait with automatic implementations like "Send" and "Sync". aliases: &[String], ) { - let id = - cx.derive_id(get_id_for_impl(&i.inner_impl().for_, i.inner_impl().trait_.as_ref(), cx)); + let inner_impl = i.inner_impl(); + let id = cx.derive_id(get_id_for_impl(&inner_impl.for_, inner_impl.trait_.as_ref(), cx)); let aliases = if aliases.is_empty() { String::new() } else { @@ -1750,9 +1750,9 @@ pub(crate) fn render_impl_summary( write!(w, "<h3 class=\"code-header in-band\">"); if let Some(use_absolute) = use_absolute { - write!(w, "{}", i.inner_impl().print(use_absolute, cx)); + write!(w, "{}", inner_impl.print(use_absolute, cx)); if show_def_docs { - for it in &i.inner_impl().items { + for it in &inner_impl.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = *it.kind { w.write_str("<span class=\"where fmt-newline\"> "); assoc_type( @@ -1770,11 +1770,11 @@ pub(crate) fn render_impl_summary( } } } else { - write!(w, "{}", i.inner_impl().print(false, cx)); + write!(w, "{}", inner_impl.print(false, cx)); } write!(w, "</h3>"); - let is_trait = i.inner_impl().trait_.is_some(); + let is_trait = inner_impl.trait_.is_some(); if is_trait { if let Some(portability) = portability(&i.impl_item, Some(parent)) { write!(w, "<span class=\"item-info\">{}</span>", portability); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index c117e3ac40d..00d23008772 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -759,14 +759,6 @@ pre, .rustdoc.source .example-wrap { margin-bottom: 15px; } -.content .docblock > .impl-items { - margin-left: 20px; - margin-top: -34px; -} -.content .docblock >.impl-items table td { - padding: 0; -} - .item-info { display: block; } diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 7d7a63c5384..c27f0ce18c1 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -477,7 +477,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // If there's no `::`, it's not an associated item. // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. .ok_or_else(|| { - debug!("found no `::`, assumming {} was correctly not in scope", item_name); + debug!("found no `::`, assuming {} was correctly not in scope", item_name); UnresolvedPath { item_id, module_id, @@ -750,7 +750,7 @@ fn resolve_associated_trait_item<'a>( /// /// This is just a wrapper around [`TyCtxt::impl_item_implementor_ids()`] and /// [`TyCtxt::associated_item()`] (with some helpful logging added). -#[instrument(level = "debug", skip(tcx))] +#[instrument(level = "debug", skip(tcx), ret)] fn trait_assoc_to_impl_assoc_item<'tcx>( tcx: TyCtxt<'tcx>, impl_id: DefId, @@ -760,9 +760,7 @@ fn trait_assoc_to_impl_assoc_item<'tcx>( debug!(?trait_to_impl_assoc_map); let impl_assoc_id = *trait_to_impl_assoc_map.get(&trait_assoc_id)?; debug!(?impl_assoc_id); - let impl_assoc = tcx.associated_item(impl_assoc_id); - debug!(?impl_assoc); - Some(impl_assoc) + Some(tcx.associated_item(impl_assoc_id)) } /// Given a type, return all trait impls in scope in `module` for that type. @@ -1256,7 +1254,7 @@ impl LinkCollector<'_, '_> { &mut self, key: ResolutionInfo, diag: DiagnosticInfo<'_>, - // If errors are cached then they are only reported on first ocurrence + // 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, ) -> Option<(Res, Option<UrlFragment>)> { diff --git a/src/test/codegen/abi-efiapi.rs b/src/test/codegen/abi-efiapi.rs index b4fda5f8c84..4dc9d183b0b 100644 --- a/src/test/codegen/abi-efiapi.rs +++ b/src/test/codegen/abi-efiapi.rs @@ -24,7 +24,7 @@ trait Freeze { } #[lang="copy"] trait Copy { } -//x86_64: define win64cc void @has_efiapi +//x86_64: define dso_local win64cc void @has_efiapi //i686: define void @has_efiapi //aarch64: define dso_local void @has_efiapi //arm: define dso_local void @has_efiapi diff --git a/src/test/codegen/avr/avr-func-addrspace.rs b/src/test/codegen/avr/avr-func-addrspace.rs index 530164edd46..a038dfe76f7 100644 --- a/src/test/codegen/avr/avr-func-addrspace.rs +++ b/src/test/codegen/avr/avr-func-addrspace.rs @@ -77,7 +77,7 @@ fn update_bar_value() { } } -// CHECK: define void @test(){{.+}}addrspace(1) +// CHECK: define dso_local void @test(){{.+}}addrspace(1) #[no_mangle] pub extern "C" fn test() { let mut buf = 7; diff --git a/src/test/codegen/issue-34634.rs b/src/test/codegen/issue-34634.rs index 6c18adbcb3c..f53fa240cd1 100644 --- a/src/test/codegen/issue-34634.rs +++ b/src/test/codegen/issue-34634.rs @@ -1,5 +1,5 @@ // Test that `wrapping_div` only checks divisor once. -// This test checks that there is only a single compare agains -1 and -1 is not present as a +// This test checks that there is only a single compare against -1 and -1 is not present as a // switch case (the second check present until rustc 1.12). // This test also verifies that a single panic call is generated (for the division by zero case). diff --git a/src/test/codegen/unwind-abis/aapcs-unwind-abi.rs b/src/test/codegen/unwind-abis/aapcs-unwind-abi.rs index 1fe04806860..c092e28a05a 100644 --- a/src/test/codegen/unwind-abis/aapcs-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/aapcs-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `aapcs` and +// Test that `nounwind` attributes are correctly applied to exported `aapcs` and // `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs index e817d5715a1..8447bbeb1ed 100644 --- a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs +++ b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs @@ -1,6 +1,6 @@ // compile-flags: -C panic=abort -// Test that `nounwind` atributes are also applied to extern `C-unwind` Rust functions +// Test that `nounwind` attributes are also applied to extern `C-unwind` Rust functions // when the code is compiled with `panic=abort`. #![crate_type = "lib"] diff --git a/src/test/codegen/unwind-abis/c-unwind-abi.rs b/src/test/codegen/unwind-abis/c-unwind-abi.rs index f1576536753..e258dbcacd2 100644 --- a/src/test/codegen/unwind-abis/c-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/c-unwind-abi.rs @@ -1,6 +1,6 @@ // compile-flags: -C opt-level=0 -// Test that `nounwind` atributes are correctly applied to exported `C` and `C-unwind` extern +// Test that `nounwind` attributes are correctly applied to exported `C` and `C-unwind` extern // functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above // to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/cdecl-unwind-abi.rs b/src/test/codegen/unwind-abis/cdecl-unwind-abi.rs index 52e0d2d6e02..19a7228839a 100644 --- a/src/test/codegen/unwind-abis/cdecl-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/cdecl-unwind-abi.rs @@ -1,6 +1,6 @@ // compile-flags: -C opt-level=0 -// Test that `nounwind` atributes are correctly applied to exported `cdecl` and +// Test that `nounwind` attributes are correctly applied to exported `cdecl` and // `cdecl-unwind` extern functions. `cdecl-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/fastcall-unwind-abi.rs b/src/test/codegen/unwind-abis/fastcall-unwind-abi.rs index ed23235ebfa..b74099a5d96 100644 --- a/src/test/codegen/unwind-abis/fastcall-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/fastcall-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `fastcall` and +// Test that `nounwind` attributes are correctly applied to exported `fastcall` and // `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs index 9a4b3d3b484..106d593b21d 100644 --- a/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs +++ b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs @@ -3,7 +3,7 @@ #![crate_type = "lib"] -// We disable optimizations to prevent LLVM from infering the attribute. +// We disable optimizations to prevent LLVM from inferring the attribute. // CHECK: Function Attrs:{{.*}}nounwind // CHECK-NEXT: @foo diff --git a/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs index 2783c83d3ef..c1c5bbdda34 100644 --- a/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs +++ b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs @@ -3,7 +3,7 @@ #![crate_type = "lib"] -// We disable optimizations to prevent LLVM from infering the attribute. +// We disable optimizations to prevent LLVM from inferring the attribute. extern "C" { fn bar(); diff --git a/src/test/codegen/unwind-abis/nounwind.rs b/src/test/codegen/unwind-abis/nounwind.rs index cfc140361f6..c46d717331b 100644 --- a/src/test/codegen/unwind-abis/nounwind.rs +++ b/src/test/codegen/unwind-abis/nounwind.rs @@ -4,7 +4,7 @@ #![crate_type = "lib"] #![feature(c_unwind)] -// We disable optimizations to prevent LLVM from infering the attribute. +// We disable optimizations to prevent LLVM from inferring the attribute. // CHECK: Function Attrs:{{.*}}nounwind // CHECK-NEXT: @foo diff --git a/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs index f1dff27ad67..8eff0719f8f 100644 --- a/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `stdcall` and `stdcall-unwind` +// Test that `nounwind` attributes are correctly applied to exported `stdcall` and `stdcall-unwind` // extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable // optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/system-unwind-abi.rs b/src/test/codegen/unwind-abis/system-unwind-abi.rs index c4d51328352..2591c1d4814 100644 --- a/src/test/codegen/unwind-abis/system-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/system-unwind-abi.rs @@ -1,6 +1,6 @@ // compile-flags: -C opt-level=0 -// Test that `nounwind` atributes are correctly applied to exported `system` and `system-unwind` +// Test that `nounwind` attributes are correctly applied to exported `system` and `system-unwind` // extern functions. `system-unwind` functions MUST NOT have this attribute. We disable // optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/sysv64-unwind-abi.rs b/src/test/codegen/unwind-abis/sysv64-unwind-abi.rs index a38736f2a1f..694fde17c3c 100644 --- a/src/test/codegen/unwind-abis/sysv64-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/sysv64-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `sysv64` and +// Test that `nounwind` attributes are correctly applied to exported `sysv64` and // `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs index d2cf041b72d..7e81367fc5b 100644 --- a/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `thiscall` and +// Test that `nounwind` attributes are correctly applied to exported `thiscall` and // `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/vectorcall-unwind-abi.rs b/src/test/codegen/unwind-abis/vectorcall-unwind-abi.rs index 0fb9612a5e4..d7eca2a9700 100644 --- a/src/test/codegen/unwind-abis/vectorcall-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/vectorcall-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `vectorcall` and +// Test that `nounwind` attributes are correctly applied to exported `vectorcall` and // `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute. // We disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-abis/win64-unwind-abi.rs b/src/test/codegen/unwind-abis/win64-unwind-abi.rs index 5d8482da630..6591348c35d 100644 --- a/src/test/codegen/unwind-abis/win64-unwind-abi.rs +++ b/src/test/codegen/unwind-abis/win64-unwind-abi.rs @@ -5,7 +5,7 @@ #[lang="sized"] trait Sized { } -// Test that `nounwind` atributes are correctly applied to exported `win64` and +// Test that `nounwind` attributes are correctly applied to exported `win64` and // `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We // disable optimizations above to prevent LLVM from inferring the attribute. diff --git a/src/test/codegen/unwind-extern-exports.rs b/src/test/codegen/unwind-extern-exports.rs index c939235fb50..6ac3c079f81 100644 --- a/src/test/codegen/unwind-extern-exports.rs +++ b/src/test/codegen/unwind-extern-exports.rs @@ -5,7 +5,7 @@ #![feature(c_unwind)] // Make sure these all do *not* get the attribute. -// We disable optimizations to prevent LLVM from infering the attribute. +// We disable optimizations to prevent LLVM from inferring the attribute. // CHECK-NOT: nounwind // "C" ABI diff --git a/src/test/incremental/hygiene/load_cached_hygiene.rs b/src/test/incremental/hygiene/load_cached_hygiene.rs index 8124141418b..355d3345852 100644 --- a/src/test/incremental/hygiene/load_cached_hygiene.rs +++ b/src/test/incremental/hygiene/load_cached_hygiene.rs @@ -2,7 +2,7 @@ // compile-flags: -Z query-dep-graph // aux-build:cached_hygiene.rs -// This tests the folllowing scenario +// This tests the following scenario // 1. A foreign crate is compiled with incremental compilation. // This causes hygiene information to be saved to the incr cache. // 2. One function is the foreign crate is modified. This causes the diff --git a/src/test/incremental/issue-49043.rs b/src/test/incremental/issue-49043.rs index 50d8fb86930..8d13718b8d8 100644 --- a/src/test/incremental/issue-49043.rs +++ b/src/test/incremental/issue-49043.rs @@ -1,5 +1,5 @@ // Regression test for hashing involving canonical variables. In this -// test -- which has an intensional error -- the type of the value +// test -- which has an intentional error -- the type of the value // being dropped winds up including a type variable. Canonicalization // would then produce a `?0` which -- in turn -- triggered an ICE in // hashing. diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs b/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs index 4d48a5f0ac5..95f3b8ae4d9 100644 --- a/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs +++ b/src/test/incremental/thinlto/cgu_invalidated_when_export_added.rs @@ -3,7 +3,7 @@ // rust-lang/rust#69798: // -// This is analgous to cgu_invalidated_when_import_added, but it covers a +// This is analogous to cgu_invalidated_when_import_added, but it covers a // problem uncovered where a change to the *export* set caused a link failure // when reusing post-LTO optimized object code. diff --git a/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs b/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs index e85b4856f3a..e86ebd354b1 100644 --- a/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs +++ b/src/test/incremental/thinlto/cgu_invalidated_when_export_removed.rs @@ -3,7 +3,7 @@ // rust-lang/rust#69798: // -// This is analgous to cgu_invalidated_when_export_added, but it covers the +// This is analogous to cgu_invalidated_when_export_added, but it covers the // other direction. This is analogous to cgu_invalidated_when_import_added: we // include it, because it may uncover bugs in variant implementation strategies. diff --git a/src/test/mir-opt/dest-prop/union.rs b/src/test/mir-opt/dest-prop/union.rs index 68c834dfbbf..eb6cb09fc45 100644 --- a/src/test/mir-opt/dest-prop/union.rs +++ b/src/test/mir-opt/dest-prop/union.rs @@ -1,4 +1,4 @@ -//! Tests that we can propogate into places that are projections into unions +//! Tests that we can propagate into places that are projections into unions // compile-flags: -Zunsound-mir-opts fn val() -> u32 { 1 diff --git a/src/test/run-make-fulldeps/issue-64153/Makefile b/src/test/run-make-fulldeps/issue-64153/Makefile index 51dc6b53a45..f42ea620fb9 100644 --- a/src/test/run-make-fulldeps/issue-64153/Makefile +++ b/src/test/run-make-fulldeps/issue-64153/Makefile @@ -19,7 +19,7 @@ all: # Dump all the symbols from the staticlib into `syms` "$(LLVM_BIN_DIR)"/llvm-objdump -t $(TMPDIR)/libdownstream.a > $(TMPDIR)/syms # Count the global instances of `issue64153_test_function`. There'll be 2 - # if the `upstream` object file got erronously included twice. + # if the `upstream` object file got erroneously included twice. # The line we are testing for with the regex looks something like: # 0000000000000000 g F .text.issue64153_test_function 00000023 issue64153_test_function grep -c -e "[[:space:]]g[[:space:]]*F[[:space:]].*issue64153_test_function" $(TMPDIR)/syms > $(TMPDIR)/count diff --git a/src/test/run-make/coverage-reports/Makefile b/src/test/run-make/coverage-reports/Makefile index cac881ece12..6fc2a6bada9 100644 --- a/src/test/run-make/coverage-reports/Makefile +++ b/src/test/run-make/coverage-reports/Makefile @@ -60,7 +60,7 @@ endif # for now, but it is effectively ignored for all tests that don't include this file anyway. # # (Note that it's also possible the `_counters.<test>.txt` and `<test>.json` files (if generated) -# may order results from multiple files inconsistently, which might also have to be accomodated +# may order results from multiple files inconsistently, which might also have to be accommodated # if and when we allow `llvm-cov` to produce results for multiple files. Note, the path separators # appear to be normalized to `/` in those files, thankfully.) LLVM_COV_IGNORE_FILES=\ @@ -157,7 +157,7 @@ else # `// ignore-llvm-cov-show-diffs` anymore. This directive exists to work around a limitation # with `llvm-cov show`. When reporting coverage for multiple instantiations of a generic function, # with different type substitutions, `llvm-cov show` prints these in a non-deterministic order, - # breaking the `diff` comparision. + # breaking the `diff` comparison. # # A partial workaround is implemented below, with `diff --ignore-matching-lines=RE` # to ignore each line prefixing each generic instantiation coverage code region. diff --git a/src/test/run-make/coverage-reports/expected_show_coverage.async.txt b/src/test/run-make/coverage-reports/expected_show_coverage.async.txt index 2f69adbd81c..87ccb6c43ea 100644 --- a/src/test/run-make/coverage-reports/expected_show_coverage.async.txt +++ b/src/test/run-make/coverage-reports/expected_show_coverage.async.txt @@ -55,7 +55,7 @@ 53| 1| 1 // This line appears covered, but the 1-character expression span covering the `1` ^0 54| 1| // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because - 55| 1| // `fn j()` executes the open brace for the funciton body, followed by the function's + 55| 1| // `fn j()` executes the open brace for the function body, followed by the function's 56| 1| // first executable statement, `match x`. Inner function declarations are not 57| 1| // "visible" to the MIR for `j()`, so the code region counts all lines between the 58| 1| // open brace and the first statement as executed, which is, in a sense, true. diff --git a/src/test/run-make/coverage/async.rs b/src/test/run-make/coverage/async.rs index a6e38774706..efd9e62d64e 100644 --- a/src/test/run-make/coverage/async.rs +++ b/src/test/run-make/coverage/async.rs @@ -52,7 +52,7 @@ fn j(x: u8) { if x == 8 { 1 // This line appears covered, but the 1-character expression span covering the `1` // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because - // `fn j()` executes the open brace for the funciton body, followed by the function's + // `fn j()` executes the open brace for the function body, followed by the function's // first executable statement, `match x`. Inner function declarations are not // "visible" to the MIR for `j()`, so the code region counts all lines between the // open brace and the first statement as executed, which is, in a sense, true. diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh index 54645e9e257..944343df6e5 100644 --- a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh @@ -45,7 +45,7 @@ check cc_plus_one_cxx cc_plus_one_cxx.checks check cc_plus_one_cxx_asm cc_plus_one_cxx_asm.checks check cc_plus_one_asm cc_plus_one_asm.checks \ || echo "warning: the cc crate forwards assembly files to the CC compiler." \ - "Clang uses its own intergrated assembler, which does not include the LVI passes." + "Clang uses its own integrated assembler, which does not include the LVI passes." check cmake_plus_one_c cmake_plus_one_c.checks check cmake_plus_one_c_asm cmake_plus_one_c_asm.checks diff --git a/src/test/rustdoc-gui/src/lib2/lib.rs b/src/test/rustdoc-gui/src/lib2/lib.rs index 87f91be3ac8..7f3172878bf 100644 --- a/src/test/rustdoc-gui/src/lib2/lib.rs +++ b/src/test/rustdoc-gui/src/lib2/lib.rs @@ -143,3 +143,30 @@ pub struct LongItemInfo2; /// Some docs. #[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))] impl SimpleTrait for LongItemInfo2 {} + +pub struct WhereWhitespace<T>; + +impl<T> WhereWhitespace<T> { + pub fn new<F>(f: F) -> Self + where + F: FnMut() -> i32, + {} +} + +impl<K, T> Whitespace<&K> for WhereWhitespace<T> +where + K: std::fmt::Debug, +{ + type Output = WhereWhitespace<T>; + fn index(&self, _key: &K) -> &Self::Output { + self + } +} + +pub trait Whitespace<Idx> +where + Idx: ?Sized, +{ + type Output; + fn index(&self, index: Idx) -> &Self::Output; +} diff --git a/src/test/rustdoc-gui/type-declation-overflow.goml b/src/test/rustdoc-gui/type-declation-overflow.goml index 22212a31728..85e03c45e70 100644 --- a/src/test/rustdoc-gui/type-declation-overflow.goml +++ b/src/test/rustdoc-gui/type-declation-overflow.goml @@ -32,6 +32,6 @@ assert-property: (".item-decl pre", {"scrollWidth": "950"}) size: (600, 600) goto: file://|DOC_PATH|/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html // It shouldn't have an overflow in the topbar either. -assert-property: (".mobile-topbar .location", {"scrollWidth": "492"}) -assert-property: (".mobile-topbar .location", {"clientWidth": "492"}) +assert-property: (".mobile-topbar .location", {"scrollWidth": "502"}) +assert-property: (".mobile-topbar .location", {"clientWidth": "502"}) assert-css: (".mobile-topbar .location", {"overflow-x": "hidden"}) diff --git a/src/test/rustdoc-gui/where-whitespace.goml b/src/test/rustdoc-gui/where-whitespace.goml new file mode 100644 index 00000000000..1a3ff1f491c --- /dev/null +++ b/src/test/rustdoc-gui/where-whitespace.goml @@ -0,0 +1,27 @@ +// This test ensures that the where conditions are correctly displayed. +goto: file://|DOC_PATH|/lib2/trait.Whitespace.html +show-text: true +// First, we check in the trait definition if the where clause is "on its own" (not on the same +// line than "pub trait Whitespace<Idx>"). +compare-elements-position-false: (".item-decl code", ".where.fmt-newline", ("y")) +// And that the code following it isn't on the same line either. +compare-elements-position-false: (".item-decl .fnname", ".where.fmt-newline", ("y")) + +goto: file://|DOC_PATH|/lib2/struct.WhereWhitespace.html +// We make the screen a bit wider to ensure that the trait impl is on one line. +size: (915, 915) + +compare-elements-position-false: ("#method\.new .fnname", "#method\.new .where.fmt-newline", ("y")) +// We ensure that both the trait name and the struct name are on the same line in +// "impl<K, T> Whitespace<&K> for WhereWhitespace<T>". +compare-elements-position: ( + "#trait-implementations-list .impl h3 .trait", + "#trait-implementations-list .impl h3 .struct", + ("y"), +) +// And we now check that the where condition isn't on the same line. +compare-elements-position-false: ( + "#trait-implementations-list .impl h3 .trait", + "#trait-implementations-list .impl h3 .where.fmt-newline", + ("y"), +) diff --git a/src/test/rustdoc-ui/normalize-cycle.rs b/src/test/rustdoc-ui/normalize-cycle.rs index f48cad373cd..14ffac1e1dc 100644 --- a/src/test/rustdoc-ui/normalize-cycle.rs +++ b/src/test/rustdoc-ui/normalize-cycle.rs @@ -1,5 +1,5 @@ // check-pass -// Regresion test for <https://github.com/rust-lang/rust/issues/79459>. +// Regression test for <https://github.com/rust-lang/rust/issues/79459>. pub trait Query {} pub trait AsQuery { diff --git a/src/test/rustdoc/const-generics/const-generics-docs.rs b/src/test/rustdoc/const-generics/const-generics-docs.rs index 352a8e646bb..87d2f29e260 100644 --- a/src/test/rustdoc/const-generics/const-generics-docs.rs +++ b/src/test/rustdoc/const-generics/const-generics-docs.rs @@ -31,12 +31,12 @@ impl Trait<{1 + 2}> for u8 {} impl<const N: usize> Trait<N> for [u8; N] {} // @has foo/struct.Foo.html '//pre[@class="rust struct"]' \ -// 'pub struct Foo<const N: usize> where u8: Trait<N>' +// 'pub struct Foo<const N: usize>where u8: Trait<N>' pub struct Foo<const N: usize> where u8: Trait<N>; // @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar<T, const N: usize>(_)' pub struct Bar<T, const N: usize>([T; N]); -// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M> where u8: Trait<M>' +// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M>where u8: Trait<M>' impl<const M: usize> Foo<M> where u8: Trait<M> { // @has - '//*[@id="associatedconstant.FOO_ASSOC"]' 'pub const FOO_ASSOC: usize' pub const FOO_ASSOC: usize = M + 13; @@ -50,14 +50,14 @@ impl<const M: usize> Foo<M> where u8: Trait<M> { // @has foo/struct.Bar.html '//*[@id="impl-Bar%3Cu8%2C%20M%3E"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Bar<u8, M>' impl<const M: usize> Bar<u8, M> { // @has - '//*[@id="method.hey"]' \ - // 'pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N>' + // 'pub fn hey<const N: usize>(&self) -> Foo<N>where u8: Trait<N>' pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N> { Foo } } // @has foo/fn.test.html '//pre[@class="rust fn"]' \ -// 'pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N>' +// 'pub fn test<const N: usize>() -> impl Trait<N>where u8: Trait<N>' pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N> { 2u8 } diff --git a/src/test/rustdoc/elided-lifetime.rs b/src/test/rustdoc/elided-lifetime.rs index 9b4ceb4f9cd..006132ef8aa 100644 --- a/src/test/rustdoc/elided-lifetime.rs +++ b/src/test/rustdoc/elided-lifetime.rs @@ -3,7 +3,7 @@ // rust-lang/rust#75225 // // Since Rust 2018 we encourage writing out <'_> explicitly to make it clear -// that borrowing is occuring. Make sure rustdoc is following the same idiom. +// that borrowing is occurring. Make sure rustdoc is following the same idiom. #![crate_name = "foo"] diff --git a/src/test/rustdoc/generic-associated-types/gats.rs b/src/test/rustdoc/generic-associated-types/gats.rs index ae981b9499a..2b9d4952d04 100644 --- a/src/test/rustdoc/generic-associated-types/gats.rs +++ b/src/test/rustdoc/generic-associated-types/gats.rs @@ -3,7 +3,7 @@ // @has foo/trait.LendingIterator.html pub trait LendingIterator { - // @has - '//*[@id="associatedtype.Item"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a" + // @has - '//*[@id="associatedtype.Item"]//h4[@class="code-header"]' "type Item<'a>where Self: 'a" type Item<'a> where Self: 'a; // @has - '//*[@id="tymethod.next"]//h4[@class="code-header"]' \ @@ -24,7 +24,7 @@ impl LendingIterator for () { pub struct Infinite<T>(T); // @has foo/trait.LendingIterator.html -// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a> where Self: 'a = &'a T" +// @has - '//*[@id="associatedtype.Item-2"]//h4[@class="code-header"]' "type Item<'a>where Self: 'a = &'a T" impl<T> LendingIterator for Infinite<T> { type Item<'a> where Self: 'a = &'a T; diff --git a/src/test/rustdoc/higher-ranked-trait-bounds.rs b/src/test/rustdoc/higher-ranked-trait-bounds.rs index b75b8de52f9..59b5b6e5797 100644 --- a/src/test/rustdoc/higher-ranked-trait-bounds.rs +++ b/src/test/rustdoc/higher-ranked-trait-bounds.rs @@ -4,7 +4,7 @@ pub trait Trait<'x> {} // @has foo/fn.test1.html -// @has - '//pre' "pub fn test1<T>() where for<'a> &'a T: Iterator," +// @has - '//pre' "pub fn test1<T>()where for<'a> &'a T: Iterator," pub fn test1<T>() where for<'a> &'a T: Iterator, @@ -12,7 +12,7 @@ where } // @has foo/fn.test2.html -// @has - '//pre' "pub fn test2<T>() where for<'a, 'b> &'a T: Trait<'b>," +// @has - '//pre' "pub fn test2<T>()where for<'a, 'b> &'a T: Trait<'b>," pub fn test2<T>() where for<'a, 'b> &'a T: Trait<'b>, @@ -20,7 +20,7 @@ where } // @has foo/fn.test3.html -// @has - '//pre' "pub fn test3<F>() where F: for<'a, 'b> Fn(&'a u8, &'b u8)," +// @has - '//pre' "pub fn test3<F>()where F: for<'a, 'b> Fn(&'a u8, &'b u8)," pub fn test3<F>() where F: for<'a, 'b> Fn(&'a u8, &'b u8), @@ -38,7 +38,7 @@ pub struct Foo<'a> { // @has - '//span[@id="structfield.some_trait"]' "some_trait: &'a dyn for<'b> Trait<'b>" impl<'a> Foo<'a> { - // @has - '//h4[@class="code-header"]' "pub fn bar<T>() where T: Trait<'a>," + // @has - '//h4[@class="code-header"]' "pub fn bar<T>()where T: Trait<'a>," pub fn bar<T>() where T: Trait<'a>, diff --git a/src/test/rustdoc/impl-parts.rs b/src/test/rustdoc/impl-parts.rs index 249158c1a1f..b1481e1f279 100644 --- a/src/test/rustdoc/impl-parts.rs +++ b/src/test/rustdoc/impl-parts.rs @@ -6,7 +6,7 @@ pub auto trait AnAutoTrait {} pub struct Foo<T> { field: T } // @has impl_parts/struct.Foo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync," +// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync," // @has impl_parts/trait.AnAutoTrait.html '//*[@class="item-list"]//h3[@class="code-header in-band"]' \ -// "impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync," +// "impl<T: Clone> !AnAutoTrait for Foo<T>where T: Sync," impl<T: Clone> !AnAutoTrait for Foo<T> where T: Sync {} diff --git a/src/test/rustdoc/infinite-redirection.rs b/src/test/rustdoc/infinite-redirection.rs index 96a43323ce2..f037a8e1a83 100644 --- a/src/test/rustdoc/infinite-redirection.rs +++ b/src/test/rustdoc/infinite-redirection.rs @@ -7,7 +7,7 @@ // @has 'foo/builders/struct.ActionRowBuilder.html' // @has - '//*[@id="synthetic-implementations"]' 'Auto Trait Implementations' -// And that the link in the module is targetting it. +// And that the link in the module is targeting it. // @has 'foo/builders/index.html' // @has - '//a[@href="struct.ActionRowBuilder.html"]' 'ActionRowBuilder' diff --git a/src/test/rustdoc/issue-20727-4.rs b/src/test/rustdoc/issue-20727-4.rs index 84fc6f94a26..643f9387590 100644 --- a/src/test/rustdoc/issue-20727-4.rs +++ b/src/test/rustdoc/issue-20727-4.rs @@ -25,7 +25,7 @@ pub trait IndexMut<Idx: ?Sized>: Index<Idx> { pub mod reexport { // @has issue_20727_4/reexport/trait.Index.html - // @has - '//*[@class="rust trait"]' 'trait Index<Idx> where Idx: ?Sized, {' + // @has - '//*[@class="rust trait"]' 'trait Index<Idx>where Idx: ?Sized,{' // @has - '//*[@class="rust trait"]' 'type Output: ?Sized' // @has - '//*[@class="rust trait"]' \ // 'fn index(&self, index: Idx) -> &Self::Output' @@ -33,7 +33,7 @@ pub mod reexport { // @has issue_20727_4/reexport/trait.IndexMut.html // @has - '//*[@class="rust trait"]' \ - // 'trait IndexMut<Idx>: Index<Idx> where Idx: ?Sized, {' + // 'trait IndexMut<Idx>: Index<Idx>where Idx: ?Sized,{' // @has - '//*[@class="rust trait"]' \ // 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;' pub use issue_20727::IndexMut; diff --git a/src/test/rustdoc/issue-21801.rs b/src/test/rustdoc/issue-21801.rs index 2a586b6ff6c..29d2ec64c20 100644 --- a/src/test/rustdoc/issue-21801.rs +++ b/src/test/rustdoc/issue-21801.rs @@ -5,5 +5,5 @@ extern crate issue_21801; // @has issue_21801/struct.Foo.html // @has - '//*[@id="method.new"]' \ -// 'fn new<F>(f: F) -> Foo where F: FnMut() -> i32' +// 'fn new<F>(f: F) -> Foowhere F: FnMut() -> i32' pub use issue_21801::Foo; diff --git a/src/test/rustdoc/issue-29503.rs b/src/test/rustdoc/issue-29503.rs index 635c3175f81..134821e1ef3 100644 --- a/src/test/rustdoc/issue-29503.rs +++ b/src/test/rustdoc/issue-29503.rs @@ -5,7 +5,7 @@ pub trait MyTrait { fn my_string(&self) -> String; } -// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait-for-T']//h3[@class='code-header in-band']" "impl<T> MyTrait for T where T: Debug" +// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait-for-T']//h3[@class='code-header in-band']" "impl<T> MyTrait for Twhere T: Debug" impl<T> MyTrait for T where T: fmt::Debug, diff --git a/src/test/rustdoc/issue-34928.rs b/src/test/rustdoc/issue-34928.rs index 4184086f622..91b67757453 100644 --- a/src/test/rustdoc/issue-34928.rs +++ b/src/test/rustdoc/issue-34928.rs @@ -2,5 +2,5 @@ pub trait Bar {} -// @has foo/struct.Foo.html '//pre' 'pub struct Foo<T>(pub T) where T: Bar;' +// @has foo/struct.Foo.html '//pre' 'pub struct Foo<T>(pub T)where T: Bar;' pub struct Foo<T>(pub T) where T: Bar; diff --git a/src/test/rustdoc/issue-50159.rs b/src/test/rustdoc/issue-50159.rs index d88c2921702..43fb705f589 100644 --- a/src/test/rustdoc/issue-50159.rs +++ b/src/test/rustdoc/issue-50159.rs @@ -11,8 +11,8 @@ impl<B, C> Signal2 for B where B: Signal<Item = C> { } // @has issue_50159/struct.Switch.html -// @has - '//h3[@class="code-header in-band"]' 'impl<B> Send for Switch<B> where <B as Signal>::Item: Send' -// @has - '//h3[@class="code-header in-band"]' 'impl<B> Sync for Switch<B> where <B as Signal>::Item: Sync' +// @has - '//h3[@class="code-header in-band"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send' +// @has - '//h3[@class="code-header in-band"]' 'impl<B> Sync for Switch<B>where <B as Signal>::Item: Sync' // @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0 // @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5 pub struct Switch<B: Signal> { diff --git a/src/test/rustdoc/issue-51236.rs b/src/test/rustdoc/issue-51236.rs index ee11ccc6811..aa5890a8451 100644 --- a/src/test/rustdoc/issue-51236.rs +++ b/src/test/rustdoc/issue-51236.rs @@ -8,7 +8,7 @@ pub mod traits { // @has issue_51236/struct.Owned.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<T> Send for Owned<T> where <T as Owned<'static>>::Reader: Send" +// "impl<T> Send for Owned<T>where <T as Owned<'static>>::Reader: Send" pub struct Owned<T> where T: for<'a> ::traits::Owned<'a> { marker: PhantomData<<T as ::traits::Owned<'static>>::Reader>, } diff --git a/src/test/rustdoc/issue-54705.rs b/src/test/rustdoc/issue-54705.rs index bedaf5c4ddc..ce0f85d25da 100644 --- a/src/test/rustdoc/issue-54705.rs +++ b/src/test/rustdoc/issue-54705.rs @@ -1,13 +1,11 @@ pub trait ScopeHandle<'scope> {} - - // @has issue_54705/struct.ScopeFutureContents.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'scope, S> Send for ScopeFutureContents<'scope, S> where S: Sync" +// "impl<'scope, S> Send for ScopeFutureContents<'scope, S>where S: Sync" // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S> where S: Sync" +// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S>where S: Sync" pub struct ScopeFutureContents<'scope, S> where S: ScopeHandle<'scope>, { diff --git a/src/test/rustdoc/issue-98697.rs b/src/test/rustdoc/issue-98697.rs index 83e08094c09..a8841f137fe 100644 --- a/src/test/rustdoc/issue-98697.rs +++ b/src/test/rustdoc/issue-98697.rs @@ -8,7 +8,7 @@ extern crate issue_98697_reexport_with_anonymous_lifetime; -// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>() where F: Fn(&str)' +// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>()where F: Fn(&str)' // @!has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'for<' pub use issue_98697_reexport_with_anonymous_lifetime::repro; diff --git a/src/test/rustdoc/macro-document-private-duplicate.rs b/src/test/rustdoc/macro-document-private-duplicate.rs index ee301051441..d3cf7e14065 100644 --- a/src/test/rustdoc/macro-document-private-duplicate.rs +++ b/src/test/rustdoc/macro-document-private-duplicate.rs @@ -4,7 +4,7 @@ // (yes, that's a thing), rustdoc lists both of them on the index page, // but only documents the first one on the page for the macro. // Fortunately, this can only happen in document private items mode, -// but it still isn't ideal beahvior. +// but it still isn't ideal behavior. // // See https://github.com/rust-lang/rust/pull/88019#discussion_r693920453 // diff --git a/src/test/rustdoc/primitive-slice-auto-trait.rs b/src/test/rustdoc/primitive-slice-auto-trait.rs index b3f511bc1f1..7f8f74ff457 100644 --- a/src/test/rustdoc/primitive-slice-auto-trait.rs +++ b/src/test/rustdoc/primitive-slice-auto-trait.rs @@ -7,8 +7,8 @@ // @has - '//span[@class="in-band"]' 'Primitive Type slice' // @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!' // @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations' -// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T] where T: Send' -// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Sync for [T] where T: Sync' +// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T]where T: Send' +// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Sync for [T]where T: Sync' #[doc(primitive = "slice")] /// this is a test! mod slice_prim {} diff --git a/src/test/rustdoc/synthetic_auto/basic.rs b/src/test/rustdoc/synthetic_auto/basic.rs index 54c54fdbf68..19138fd1ace 100644 --- a/src/test/rustdoc/synthetic_auto/basic.rs +++ b/src/test/rustdoc/synthetic_auto/basic.rs @@ -1,6 +1,6 @@ // @has basic/struct.Foo.html -// @has - '//h3[@class="code-header in-band"]' 'impl<T> Send for Foo<T> where T: Send' -// @has - '//h3[@class="code-header in-band"]' 'impl<T> Sync for Foo<T> where T: Sync' +// @has - '//h3[@class="code-header in-band"]' 'impl<T> Send for Foo<T>where T: Send' +// @has - '//h3[@class="code-header in-band"]' 'impl<T> Sync for Foo<T>where T: Sync' // @count - '//*[@id="implementations-list"]//*[@class="impl has-srclink"]' 0 // @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5 pub struct Foo<T> { diff --git a/src/test/rustdoc/synthetic_auto/complex.rs b/src/test/rustdoc/synthetic_auto/complex.rs index f9017b90cae..39f78983da2 100644 --- a/src/test/rustdoc/synthetic_auto/complex.rs +++ b/src/test/rustdoc/synthetic_auto/complex.rs @@ -21,7 +21,7 @@ mod foo { // @has complex/struct.NotOuter.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K> where K: for<'b> Fn((&'b bool, &'a u8)) \ +// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where K: for<'b> Fn((&'b bool, &'a u8)) \ // -> &'b i8, T: MyTrait<'a>, <T as MyTrait<'a>>::MyItem: Copy, 'a: 'static" pub use foo::{Foo, Inner as NotInner, MyTrait as NotMyTrait, Outer as NotOuter}; diff --git a/src/test/rustdoc/synthetic_auto/lifetimes.rs b/src/test/rustdoc/synthetic_auto/lifetimes.rs index ee1393f9729..0c94850e786 100644 --- a/src/test/rustdoc/synthetic_auto/lifetimes.rs +++ b/src/test/rustdoc/synthetic_auto/lifetimes.rs @@ -10,10 +10,10 @@ where // @has lifetimes/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'c, K> Send for Foo<'c, K> where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static" +// "impl<'c, K> Send for Foo<'c, K>where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static" // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'c, K> Sync for Foo<'c, K> where K: Sync" +// "impl<'c, K> Sync for Foo<'c, K>where K: Sync" pub struct Foo<'c, K: 'c> { inner_field: Inner<'c, K>, } diff --git a/src/test/rustdoc/synthetic_auto/manual.rs b/src/test/rustdoc/synthetic_auto/manual.rs index 49bad162211..35047e3e8c0 100644 --- a/src/test/rustdoc/synthetic_auto/manual.rs +++ b/src/test/rustdoc/synthetic_auto/manual.rs @@ -1,6 +1,6 @@ // @has manual/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// 'impl<T> Sync for Foo<T> where T: Sync' +// 'impl<T> Sync for Foo<T>where T: Sync' // // @has - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ // 'impl<T> Send for Foo<T>' diff --git a/src/test/rustdoc/synthetic_auto/nested.rs b/src/test/rustdoc/synthetic_auto/nested.rs index 69edbee619e..09587bcc30f 100644 --- a/src/test/rustdoc/synthetic_auto/nested.rs +++ b/src/test/rustdoc/synthetic_auto/nested.rs @@ -10,10 +10,10 @@ where // @has nested/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// 'impl<T> Send for Foo<T> where T: Copy' +// 'impl<T> Send for Foo<T>where T: Copy' // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// 'impl<T> Sync for Foo<T> where T: Sync' +// 'impl<T> Sync for Foo<T>where T: Sync' pub struct Foo<T> { inner_field: Inner<T>, } diff --git a/src/test/rustdoc/synthetic_auto/no-redundancy.rs b/src/test/rustdoc/synthetic_auto/no-redundancy.rs index 16ab876e829..41375decc8a 100644 --- a/src/test/rustdoc/synthetic_auto/no-redundancy.rs +++ b/src/test/rustdoc/synthetic_auto/no-redundancy.rs @@ -10,7 +10,7 @@ where // @has no_redundancy/struct.Outer.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<T> Send for Outer<T> where T: Send + Copy" +// "impl<T> Send for Outer<T>where T: Send + Copy" pub struct Outer<T> { inner_field: Inner<T>, } diff --git a/src/test/rustdoc/synthetic_auto/project.rs b/src/test/rustdoc/synthetic_auto/project.rs index 8b020582563..e80b1b1dc9b 100644 --- a/src/test/rustdoc/synthetic_auto/project.rs +++ b/src/test/rustdoc/synthetic_auto/project.rs @@ -24,10 +24,10 @@ where // @has project/struct.Foo.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'c, K> Send for Foo<'c, K> where K: MyTrait<MyItem = bool>, 'c: 'static" +// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait<MyItem = bool>, 'c: 'static" // // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<'c, K> Sync for Foo<'c, K> where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, \ +// "impl<'c, K> Sync for Foo<'c, K>where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, \ // 'c: 'static," pub struct Foo<'c, K: 'c> { inner_field: Inner<'c, K>, diff --git a/src/test/rustdoc/synthetic_auto/self-referential.rs b/src/test/rustdoc/synthetic_auto/self-referential.rs index ccef901b18d..d15a8de7d2f 100644 --- a/src/test/rustdoc/synthetic_auto/self-referential.rs +++ b/src/test/rustdoc/synthetic_auto/self-referential.rs @@ -24,6 +24,6 @@ impl<T> Pattern for Wrapper<T> { // @has self_referential/struct.WriteAndThen.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<P1> Send for WriteAndThen<P1> where <P1 as Pattern>::Value: Send" +// "impl<P1> Send for WriteAndThen<P1>where <P1 as Pattern>::Value: Send" pub struct WriteAndThen<P1>(pub P1::Value,pub <Constrain<P1, Wrapper<P1::Value>> as Pattern>::Value) where P1: Pattern; diff --git a/src/test/rustdoc/synthetic_auto/static-region.rs b/src/test/rustdoc/synthetic_auto/static-region.rs index 36e985144b0..08e9567313e 100644 --- a/src/test/rustdoc/synthetic_auto/static-region.rs +++ b/src/test/rustdoc/synthetic_auto/static-region.rs @@ -4,7 +4,7 @@ pub trait OwnedTrait<'a> { // @has static_region/struct.Owned.html // @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<T> Send for Owned<T> where <T as OwnedTrait<'static>>::Reader: Send" +// "impl<T> Send for Owned<T>where <T as OwnedTrait<'static>>::Reader: Send" pub struct Owned<T> where T: OwnedTrait<'static> { marker: <T as OwnedTrait<'static>>::Reader, } diff --git a/src/test/rustdoc/where-clause-order.rs b/src/test/rustdoc/where-clause-order.rs index 3150a8ea05f..b8502e10a48 100644 --- a/src/test/rustdoc/where-clause-order.rs +++ b/src/test/rustdoc/where-clause-order.rs @@ -7,7 +7,7 @@ where } // @has 'foo/trait.SomeTrait.html' -// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, " +// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, " impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, diff --git a/src/test/rustdoc/where.rs b/src/test/rustdoc/where.rs index 50a5722fbaf..c1a630e25ba 100644 --- a/src/test/rustdoc/where.rs +++ b/src/test/rustdoc/where.rs @@ -3,17 +3,17 @@ pub trait MyTrait { fn dummy(&self) { } } -// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_) where A: MyTrait" +// @has foo/struct.Alpha.html '//pre' "pub struct Alpha<A>(_)where A: MyTrait" pub struct Alpha<A>(A) where A: MyTrait; -// @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B> where B: MyTrait" +// @has foo/trait.Bravo.html '//pre' "pub trait Bravo<B>where B: MyTrait" pub trait Bravo<B> where B: MyTrait { fn get(&self, B: B); } -// @has foo/fn.charlie.html '//pre' "pub fn charlie<C>() where C: MyTrait" +// @has foo/fn.charlie.html '//pre' "pub fn charlie<C>()where C: MyTrait" pub fn charlie<C>() where C: MyTrait {} pub struct Delta<D>(D); // @has foo/struct.Delta.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<D> Delta<D> where D: MyTrait" +// "impl<D> Delta<D>where D: MyTrait" impl<D> Delta<D> where D: MyTrait { pub fn delta() {} } @@ -33,19 +33,19 @@ pub trait TraitWhere { } // @has foo/struct.Echo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<E> MyTrait for Echo<E> where E: MyTrait" +// "impl<E> MyTrait for Echo<E>where E: MyTrait" // @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \ -// "impl<E> MyTrait for Echo<E> where E: MyTrait" -impl<E> MyTrait for Echo<E> where E: MyTrait {} +// "impl<E> MyTrait for Echo<E>where E: MyTrait" +impl<E> MyTrait for Echo<E>where E: MyTrait {} pub enum Foxtrot<F> { Foxtrot1(F) } // @has foo/enum.Foxtrot.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ -// "impl<F> MyTrait for Foxtrot<F> where F: MyTrait" +// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait" // @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \ -// "impl<F> MyTrait for Foxtrot<F> where F: MyTrait" -impl<F> MyTrait for Foxtrot<F> where F: MyTrait {} +// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait" +impl<F> MyTrait for Foxtrot<F>where F: MyTrait {} // @has foo/type.Golf.html '//pre[@class="rust typedef"]' \ -// "type Golf<T> where T: Clone, = (T, T)" +// "type Golf<T>where T: Clone, = (T, T)" pub type Golf<T> where T: Clone = (T, T); diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.rs b/src/test/ui-fulldeps/internal-lints/diagnostics.rs index 33192433bbf..0e449256153 100644 --- a/src/test/ui-fulldeps/internal-lints/diagnostics.rs +++ b/src/test/ui-fulldeps/internal-lints/diagnostics.rs @@ -1,6 +1,7 @@ // compile-flags: -Z unstable-options #![crate_type = "lib"] +#![feature(rustc_attrs)] #![feature(rustc_private)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -71,3 +72,10 @@ pub fn make_diagnostics<'a>(sess: &'a ParseSess) { //~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls //~^^ ERROR diagnostics should be created using translatable messages } + +// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted. + +#[rustc_lint_diagnostics] +pub fn skipped_because_of_annotation<'a>(sess: &'a ParseSess) { + let _diag = sess.struct_err("untranslatable diagnostic"); // okay! +} diff --git a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr index bae78ffdc02..ed5105dabcd 100644 --- a/src/test/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/src/test/ui-fulldeps/internal-lints/diagnostics.stderr @@ -1,41 +1,41 @@ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:36:14 + --> $DIR/diagnostics.rs:37:14 | LL | sess.struct_err("untranslatable diagnostic") | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/diagnostics.rs:5:9 + --> $DIR/diagnostics.rs:6:9 | LL | #![deny(rustc::untranslatable_diagnostic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:53:14 + --> $DIR/diagnostics.rs:54:14 | LL | diag.note("untranslatable diagnostic"); | ^^^^ error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls - --> $DIR/diagnostics.rs:67:22 + --> $DIR/diagnostics.rs:68:22 | LL | let _diag = sess.struct_err(fluent::parser::expect_path); | ^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/diagnostics.rs:6:9 + --> $DIR/diagnostics.rs:7:9 | LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls - --> $DIR/diagnostics.rs:70:22 + --> $DIR/diagnostics.rs:71:22 | LL | let _diag = sess.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:70:22 + --> $DIR/diagnostics.rs:71:22 | LL | let _diag = sess.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index b1f557cb94d..c1c109ac1ea 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -2,7 +2,7 @@ // Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)] // normalize-stderr-test "the following other types implement trait `IntoDiagnosticArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr" - +// normalize-stderr-test "diagnostic_builder\.rs:[0-9]+:[0-9]+" -> "diagnostic_builder.rs:LL:CC" // The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, // changing the output of this test. Since SessionDiagnostic is strictly internal to the compiler // the test is just ignored on stable and beta: diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 621c59f4489..ab5c28fe473 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -453,7 +453,7 @@ LL | #[derive(SessionDiagnostic)] | = help: normalized in stderr note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg` - --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:569:19 + --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC | LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 89eaec78c6f..f0843c60543 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -167,8 +167,8 @@ enum P { #[derive(SessionSubdiagnostic)] enum Q { #[bar] - //~^ ERROR `#[bar]` is not a valid attribute - //~^^ ERROR cannot find attribute `bar` in this scope +//~^ ERROR `#[bar]` is not a valid attribute +//~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -179,8 +179,8 @@ enum Q { #[derive(SessionSubdiagnostic)] enum R { #[bar = "..."] - //~^ ERROR `#[bar = ...]` is not a valid attribute - //~^^ ERROR cannot find attribute `bar` in this scope +//~^ ERROR `#[bar = ...]` is not a valid attribute +//~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -191,8 +191,8 @@ enum R { #[derive(SessionSubdiagnostic)] enum S { #[bar = 4] - //~^ ERROR `#[bar = ...]` is not a valid attribute - //~^^ ERROR cannot find attribute `bar` in this scope +//~^ ERROR `#[bar = ...]` is not a valid attribute +//~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -203,8 +203,8 @@ enum S { #[derive(SessionSubdiagnostic)] enum T { #[bar("...")] - //~^ ERROR `#[bar(...)]` is not a valid attribute - //~^^ ERROR cannot find attribute `bar` in this scope +//~^ ERROR `#[bar("...")]` is not a valid attribute +//~^^ ERROR cannot find attribute `bar` in this scope A { #[primary_span] span: Span, @@ -215,7 +215,7 @@ enum T { #[derive(SessionSubdiagnostic)] enum U { #[label(code = "...")] - //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute +//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute A { #[primary_span] span: Span, @@ -232,7 +232,7 @@ enum V { var: String, }, B { - //~^ ERROR subdiagnostic kind not specified +//~^ ERROR subdiagnostic kind not specified #[primary_span] span: Span, var: String, @@ -308,16 +308,6 @@ union AC { } #[derive(SessionSubdiagnostic)] -#[label(parser::add_paren)] -//~^ NOTE previously specified here -#[label(parser::add_paren)] -//~^ ERROR specified multiple times -struct AD { - #[primary_span] - span: Span, -} - -#[derive(SessionSubdiagnostic)] #[label(parser::add_paren, parser::add_paren)] //~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute struct AE { @@ -329,16 +319,16 @@ struct AE { #[label(parser::add_paren)] struct AF { #[primary_span] - //~^ NOTE previously specified here +//~^ NOTE previously specified here span_a: Span, #[primary_span] - //~^ ERROR specified multiple times +//~^ ERROR specified multiple times span_b: Span, } #[derive(SessionSubdiagnostic)] struct AG { - //~^ ERROR subdiagnostic kind not specified +//~^ ERROR subdiagnostic kind not specified #[primary_span] span: Span, } @@ -390,25 +380,27 @@ struct AK { #[primary_span] span: Span, #[applicability] - //~^ NOTE previously specified here +//~^ NOTE previously specified here applicability_a: Applicability, #[applicability] - //~^ ERROR specified multiple times +//~^ ERROR specified multiple times applicability_b: Applicability, } #[derive(SessionSubdiagnostic)] #[suggestion(parser::add_paren, code = "...")] +//~^ ERROR suggestion without `applicability` struct AL { #[primary_span] span: Span, #[applicability] - //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` +//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` applicability: Span, } #[derive(SessionSubdiagnostic)] #[suggestion(parser::add_paren, code = "...")] +//~^ ERROR suggestion without `applicability` struct AM { #[primary_span] span: Span, @@ -444,7 +436,8 @@ struct AQ; #[derive(SessionSubdiagnostic)] #[suggestion(parser::add_paren, code = "...")] -//~^ ERROR suggestion without `#[primary_span]` field +//~^ ERROR suggestion without `applicability` +//~^^ ERROR suggestion without `#[primary_span]` field struct AR { var: String, } @@ -514,120 +507,3 @@ struct AZ { #[primary_span] span: Span, } - -#[derive(SessionSubdiagnostic)] -#[suggestion(parser::add_paren, code = "...")] -//~^ ERROR suggestion without `#[primary_span]` field -struct BA { - #[suggestion_part] - //~^ ERROR `#[suggestion_part]` is not a valid attribute - span: Span, - #[suggestion_part(code = "...")] - //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute - span2: Span, - #[applicability] - applicability: Applicability, - var: String, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] -//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields -//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute -struct BBa { - var: String, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -struct BBb { - #[suggestion_part] - //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` - span1: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -struct BBc { - #[suggestion_part()] - //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` - span1: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren)] -//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields -struct BC { - #[primary_span] - //~^ ERROR `#[primary_span]` is not a valid attribute - span: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren)] -struct BD { - #[suggestion_part] - //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` - span1: Span, - #[suggestion_part()] - //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` - span2: Span, - #[suggestion_part(foo = "bar")] - //~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute - span4: Span, - #[suggestion_part(code = "...")] - //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - s1: String, - #[suggestion_part()] - //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - s2: String, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -struct BE { - #[suggestion_part(code = "...", code = ",,,")] - //~^ ERROR specified multiple times - //~| NOTE previously specified here - span: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -struct BF { - #[suggestion_part(code = "(")] - first: Span, - #[suggestion_part(code = ")")] - second: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren)] -struct BG { - #[applicability] - appl: Applicability, - #[suggestion_part(code = "(")] - first: Span, - #[suggestion_part(code = ")")] - second: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -//~^ NOTE previously specified here -struct BH { - #[applicability] - //~^ ERROR specified multiple times - appl: Applicability, - #[suggestion_part(code = "(")] - first: Span, - #[suggestion_part(code = ")")] - second: Span, -} - -#[derive(SessionSubdiagnostic)] -#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -struct BI { - #[suggestion_part(code = "")] - spans: Vec<Span>, -} diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 75a34f44bbe..6bd9144dbf6 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -65,16 +65,16 @@ LL | #[label()] | ^^^^^^^^^^ error: `code` is not a valid nested attribute of a `label` attribute - --> $DIR/subdiagnostic-derive.rs:137:28 + --> $DIR/subdiagnostic-derive.rs:137:1 | LL | #[label(parser::add_paren, code = "...")] - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `applicability` is not a valid nested attribute of a `label` attribute - --> $DIR/subdiagnostic-derive.rs:146:28 + --> $DIR/subdiagnostic-derive.rs:146:1 | LL | #[label(parser::add_paren, applicability = "machine-applicable")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsupported type attribute for subdiagnostic enum --> $DIR/subdiagnostic-derive.rs:155:1 @@ -100,11 +100,13 @@ error: `#[bar = ...]` is not a valid attribute LL | #[bar = 4] | ^^^^^^^^^^ -error: `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:205:5 +error: `#[bar("...")]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:205:11 | LL | #[bar("...")] - | ^^^^^^^^^^^^^ + | ^^^^^ + | + = help: first argument of the attribute should be the diagnostic slug error: diagnostic slug must be first argument of a `#[label(...)]` attribute --> $DIR/subdiagnostic-derive.rs:217:5 @@ -161,8 +163,6 @@ error: `#[bar(...)]` is not a valid attribute | LL | #[bar("...")] | ^^^^^^^^^^^^^ - | - = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: unexpected unsupported untagged union --> $DIR/subdiagnostic-derive.rs:304:1 @@ -174,20 +174,8 @@ LL | | b: u64 LL | | } | |_^ -error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:313:1 - | -LL | #[label(parser::add_paren)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/subdiagnostic-derive.rs:311:1 - | -LL | #[label(parser::add_paren)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: `#[label(parser::add_paren)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:321:28 + --> $DIR/subdiagnostic-derive.rs:311:28 | LL | #[label(parser::add_paren, parser::add_paren)] | ^^^^^^^^^^^^^^^^^ @@ -195,225 +183,133 @@ LL | #[label(parser::add_paren, parser::add_paren)] = help: a diagnostic slug must be the first argument to the attribute error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:334:5 + --> $DIR/subdiagnostic-derive.rs:324:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:331:5 + --> $DIR/subdiagnostic-derive.rs:321:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ error: subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:340:8 + --> $DIR/subdiagnostic-derive.rs:330:8 | LL | struct AG { | ^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:377:47 + --> $DIR/subdiagnostic-derive.rs:367:47 | LL | #[suggestion(parser::add_paren, code = "...", code = "...")] | ^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:377:33 + --> $DIR/subdiagnostic-derive.rs:367:33 | LL | #[suggestion(parser::add_paren, code = "...", code = "...")] | ^^^^^^^^^^^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:395:5 + --> $DIR/subdiagnostic-derive.rs:385:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:392:5 + --> $DIR/subdiagnostic-derive.rs:382:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:405:5 + --> $DIR/subdiagnostic-derive.rs:396:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ -error: suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:418:1 - | -LL | #[suggestion(parser::add_paren)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: invalid applicability - --> $DIR/subdiagnostic-derive.rs:428:46 - | -LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")] - | ^^^^^^^^^^^^^^^^^^^^^ - -error: suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:446:1 +error: suggestion without `applicability` + --> $DIR/subdiagnostic-derive.rs:391:1 | LL | / #[suggestion(parser::add_paren, code = "...")] LL | | -LL | | struct AR { -LL | | var: String, +LL | | struct AL { +LL | | #[primary_span] +... | +LL | | applicability: Span, LL | | } | |_^ -error: unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:460:1 - | -LL | #[label] - | ^^^^^^^^ - -error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:480:39 - | -LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] - | ^^^^^^^ - -error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:499:43 - | -LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] - | ^^^^^^^ - -error: `#[suggestion_part]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:522:5 - | -LL | #[suggestion_part] - | ^^^^^^^^^^^^^^^^^^ - | - = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead - -error: `#[suggestion_part(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:525:5 +error: suggestion without `applicability` + --> $DIR/subdiagnostic-derive.rs:402:1 | -LL | #[suggestion_part(code = "...")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: `#[suggestion_part(...)]` is only valid in multipart suggestions +LL | / #[suggestion(parser::add_paren, code = "...")] +LL | | +LL | | struct AM { +LL | | #[primary_span] +LL | | span: Span, +LL | | } + | |_^ -error: suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:519:1 +error: suggestion without `code = "..."` + --> $DIR/subdiagnostic-derive.rs:410:1 | -LL | / #[suggestion(parser::add_paren, code = "...")] +LL | / #[suggestion(parser::add_paren)] LL | | -LL | | struct BA { -LL | | #[suggestion_part] +LL | | struct AN { +LL | | #[primary_span] ... | -LL | | var: String, +LL | | applicability: Applicability, LL | | } | |_^ -error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute - --> $DIR/subdiagnostic-derive.rs:534:43 +error: invalid applicability + --> $DIR/subdiagnostic-derive.rs:420:46 | -LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] - | ^^^^^^^^^^^^ +LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")] + | ^^^^^^^^^^^^^^^^^^^^^ -error: multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:534:1 +error: suggestion without `applicability` + --> $DIR/subdiagnostic-derive.rs:438:1 | -LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] +LL | / #[suggestion(parser::add_paren, code = "...")] LL | | LL | | -LL | | struct BBa { +LL | | struct AR { LL | | var: String, LL | | } | |_^ -error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:544:5 - | -LL | #[suggestion_part] - | ^^^^^^^^^^^^^^^^^^ - -error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:552:5 - | -LL | #[suggestion_part()] - | ^^^^^^^^^^^^^^^^^^^^ - -error: `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:561:5 - | -LL | #[primary_span] - | ^^^^^^^^^^^^^^^ - | - = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` - -error: multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:558:1 +error: suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive.rs:438:1 | -LL | / #[multipart_suggestion(parser::add_paren)] +LL | / #[suggestion(parser::add_paren, code = "...")] LL | | -LL | | struct BC { -LL | | #[primary_span] LL | | -LL | | span: Span, +LL | | struct AR { +LL | | var: String, LL | | } | |_^ -error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:569:5 - | -LL | #[suggestion_part] - | ^^^^^^^^^^^^^^^^^^ - -error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:572:5 - | -LL | #[suggestion_part()] - | ^^^^^^^^^^^^^^^^^^^^ - -error: `#[suggestion_part(foo = ...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:575:23 - | -LL | #[suggestion_part(foo = "bar")] - | ^^^^^^^^^^^ - | - = help: `code` is the only valid nested attribute - -error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:578:5 - | -LL | #[suggestion_part(code = "...")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:581:5 +error: unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive.rs:453:1 | -LL | #[suggestion_part()] - | ^^^^^^^^^^^^^^^^^^^^ +LL | #[label] + | ^^^^^^^^ -error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:589:37 - | -LL | #[suggestion_part(code = "...", code = ",,,")] - | ^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/subdiagnostic-derive.rs:589:23 +error: `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive.rs:473:39 | -LL | #[suggestion_part(code = "...", code = ",,,")] - | ^^^^^^^^^^^^ +LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] + | ^^^^^^^ -error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:619:5 - | -LL | #[applicability] - | ^^^^^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/subdiagnostic-derive.rs:616:43 +error: `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive.rs:492:43 | -LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] + | ^^^^^^^ error: cannot find attribute `foo` in this scope --> $DIR/subdiagnostic-derive.rs:63:3 @@ -475,6 +371,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent` LL | #[label(slug)] | ^^^^ not found in `rustc_errors::fluent` -error: aborting due to 64 previous errors +error: aborting due to 50 previous errors For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs index 54b7c8bb9c6..851da231a73 100644 --- a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs +++ b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs @@ -86,7 +86,7 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { // Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed. // However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions -// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't +// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't // unwind. So, for this test case we will define the symbol. #[lang = "eh_personality"] extern fn rust_eh_personality() {} diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs index ffa331a992c..30ce0f162c7 100644 --- a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs +++ b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs @@ -73,7 +73,7 @@ fn panic(panic_info: &core::panic::PanicInfo) -> ! { // Because we are compiling this code with `-C panic=abort`, this wouldn't normally be needed. // However, `core` and `alloc` are both compiled with `-C panic=unwind`, which means that functions -// in these libaries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't +// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't // unwind. So, for this test case we will define the symbol. #[lang = "eh_personality"] extern fn rust_eh_personality() {} diff --git a/src/test/ui/associated-type-bounds/elision.rs b/src/test/ui/associated-type-bounds/elision.rs index 4a533939931..d00def57166 100644 --- a/src/test/ui/associated-type-bounds/elision.rs +++ b/src/test/ui/associated-type-bounds/elision.rs @@ -1,7 +1,7 @@ #![feature(associated_type_bounds)] #![feature(anonymous_lifetime_in_impl_trait)] -// The same thing should happen for constaints in dyn trait. +// The same thing should happen for constraints in dyn trait. fn f(x: &mut dyn Iterator<Item: Iterator<Item = &'_ ()>>) -> Option<&'_ ()> { x.next() } //~^ ERROR missing lifetime specifier //~| ERROR mismatched types diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs b/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs index 3d395d1f264..da238205b40 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.rs @@ -131,13 +131,13 @@ fn coerce_index_op() { let mut i = I(10); i[i[3]] = 4; //~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502] - // Shoud be accepted with g2p + // Should be accepted with g2p i[3] = i[4]; i[i[3]] = i[4]; //~^ ERROR cannot borrow `i` as immutable because it is also borrowed as mutable [E0502] - // Shoud be accepted with g2p + // Should be accepted with g2p } fn main() { diff --git a/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs b/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs index e0f4afa7527..0463e22b3c2 100644 --- a/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs +++ b/src/test/ui/borrowck/two-phase-reservation-sharing-interference.rs @@ -40,6 +40,6 @@ fn main() { // // (At least in theory; part of the reason this test fails is that // the constructed MIR throws in extra &mut reborrows which - // flummoxes our attmpt to delay the activation point here.) + // flummoxes our attempt to delay the activation point here.) delay.push(2); } diff --git a/src/test/ui/check-cfg/invalid-arguments.names_simple_ident.stderr b/src/test/ui/check-cfg/invalid-arguments.names_simple_ident.stderr index bdfbc3d54a2..8fadcc1f9f0 100644 --- a/src/test/ui/check-cfg/invalid-arguments.names_simple_ident.stderr +++ b/src/test/ui/check-cfg/invalid-arguments.names_simple_ident.stderr @@ -1,2 +1,2 @@ -error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifers) +error: invalid `--check-cfg` argument: `names("NOT_IDENT")` (`names()` arguments must be simple identifiers) diff --git a/src/test/ui/check-cfg/invalid-arguments.values_simple_ident.stderr b/src/test/ui/check-cfg/invalid-arguments.values_simple_ident.stderr index b25882baaf3..061d3f0e971 100644 --- a/src/test/ui/check-cfg/invalid-arguments.values_simple_ident.stderr +++ b/src/test/ui/check-cfg/invalid-arguments.values_simple_ident.stderr @@ -1,2 +1,2 @@ -error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifer) +error: invalid `--check-cfg` argument: `values("NOT_IDENT")` (`values()` first argument must be a simple identifier) diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs index 93131b2ac4e..f97e60daf43 100644 --- a/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/arrays.rs @@ -68,7 +68,7 @@ fn arrays_5() { arr[1] += 10; }; - // c will capture `arr` completely, therefore we cannot borrow other indecies + // c will capture `arr` completely, therefore we cannot borrow other indices // into the array. println!("{:#?}", &arr[3..2]); //~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable diff --git a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs index 3ed780f51c7..f23670f63ac 100644 --- a/src/test/ui/closures/2229_closure_analysis/repr_packed.rs +++ b/src/test/ui/closures/2229_closure_analysis/repr_packed.rs @@ -31,7 +31,7 @@ fn test_alignment_not_affected() { c(); } -// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed). +// `String`, `u16` are not aligned at a one byte boundary and are thus affected by repr(packed). // // Here we test that the closure doesn't capture a reference point to `foo.x` but // rather capture `foo` entirely. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs index 2c828aed528..f8752fe1cec 100644 --- a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs @@ -1,8 +1,8 @@ // edition:2021 // run-pass -// Test that ByValue captures compile sucessefully especially when the captures are -// derefenced within the closure. +// Test that ByValue captures compile successfully especially when the captures are +// dereferenced within the closure. #[derive(Debug, Default)] struct SomeLargeType; diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs index 88a9816a052..03400e0ee8d 100644 --- a/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs @@ -1,7 +1,7 @@ // edition:2021 // run-pass -// Tests that if a closure uses indivual fields of the same object +// Tests that if a closure uses individual fields of the same object // then that case is handled properly. #![allow(unused)] diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs index b8e46403181..624e0ff2256 100644 --- a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs @@ -1,10 +1,10 @@ // edition:2021 // run-pass -// Test that closures can catpure paths that are more precise than just one level +// Test that closures can capture paths that are more precise than just one level // from the root variable. // -// If the closures can handle such precison we should be able to mutate one path in the closure +// If the closures can handle such precision we should be able to mutate one path in the closure // while being able to mutate another path outside the closure, where the two paths are disjoint // after applying two projections on the root variable. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs index 11a324d8a34..bd8addd3781 100644 --- a/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs @@ -3,7 +3,7 @@ #![allow(unused)] -// If the closures can handle such precison we should be able to read one path in the closure +// If the closures can handle such precision we should be able to read one path in the closure // while being able mutate another path outside the closure, where the two paths are disjoint // after applying two projections on the root variable. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs index bb784774b8c..a85335438a9 100644 --- a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs @@ -5,7 +5,7 @@ // that is captured by the closure // More specifically we test that the if the mutable reference isn't root variable of a capture -// but rather accessed while acessing the precise capture. +// but rather accessed while accessing the precise capture. fn mut_tuple() { let mut t = (10, 10); diff --git a/src/test/ui/closures/issue-84128.rs b/src/test/ui/closures/issue-84128.rs index f81d7cfaa65..30733871b85 100644 --- a/src/test/ui/closures/issue-84128.rs +++ b/src/test/ui/closures/issue-84128.rs @@ -1,5 +1,5 @@ // test for issue 84128 -// missing suggestion for similar ADT type with diffetent generic paramenter +// missing suggestion for similar ADT type with diffetent generic parameter // on closure ReturnNoExpression struct Foo<T>(T); diff --git a/src/test/ui/command/command-current-dir.rs b/src/test/ui/command/command-current-dir.rs index 91d8e4f381a..69a0b486d68 100644 --- a/src/test/ui/command/command-current-dir.rs +++ b/src/test/ui/command/command-current-dir.rs @@ -18,7 +18,7 @@ fn main() { let exe = me.file_name().unwrap(); let cwd = me.parent().unwrap(); eprintln!("cwd={:?}", cwd); - // Change directory to where the exectuable is located, since this test + // Change directory to where the executable is located, since this test // fundamentally needs to use relative paths. In some cases (like // remote-test-server), the current_dir can be somewhere else, so make // sure it is something we can use. We assume we can write to this diff --git a/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr b/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr index b1b359619dc..d955b4f9651 100644 --- a/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr +++ b/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr @@ -2,15 +2,22 @@ error[E0107]: this associated function takes 0 generic arguments but 1 generic a --> $DIR/invalid-const-arg-for-type-param.rs:6:23 | LL | let _: u32 = 5i32.try_into::<32>().unwrap(); - | ^^^^^^^^------ help: remove these generics - | | - | expected 0 generic arguments + | ^^^^^^^^ expected 0 generic arguments | note: associated function defined here, with 0 generic parameters --> $SRC_DIR/core/src/convert/mod.rs:LL:COL | LL | fn try_into(self) -> Result<T, Self::Error>; | ^^^^^^^^ +help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument + | +LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: remove these generics + | +LL - let _: u32 = 5i32.try_into::<32>().unwrap(); +LL + let _: u32 = 5i32.try_into().unwrap(); + | error[E0599]: no method named `f` found for struct `S` in the current scope --> $DIR/invalid-const-arg-for-type-param.rs:9:7 diff --git a/src/test/ui/const-generics/issues/issue-83466.rs b/src/test/ui/const-generics/issues/issue-83466.rs index c488a663fbb..73c9301011d 100644 --- a/src/test/ui/const-generics/issues/issue-83466.rs +++ b/src/test/ui/const-generics/issues/issue-83466.rs @@ -1,5 +1,5 @@ // regression test for #83466- tests that generic arg mismatch errors between -// consts and types are not supressed when there are explicit late bound lifetimes +// consts and types are not suppressed when there are explicit late bound lifetimes struct S; impl S { diff --git a/src/test/ui/consts/const-eval/ub-enum.rs b/src/test/ui/consts/const-eval/ub-enum.rs index 0e8744e790f..d8dc6d057a7 100644 --- a/src/test/ui/consts/const-eval/ub-enum.rs +++ b/src/test/ui/consts/const-eval/ub-enum.rs @@ -93,7 +93,7 @@ const BAD_OPTION_CHAR: Option<(char, char)> = Some(('x', unsafe { mem::transmute //~^ ERROR is undefined behavior // All variants are uninhabited but also have data. -// Use `0` as constant to make behavior endianess-independent. +// Use `0` as constant to make behavior endianness-independent. const BAD_UNINHABITED_WITH_DATA1: Result<(i32, Never), (i32, !)> = unsafe { mem::transmute(0u64) }; //~^ ERROR evaluation of constant value failed const BAD_UNINHABITED_WITH_DATA2: Result<(i32, !), (i32, Never)> = unsafe { mem::transmute(0u64) }; diff --git a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs index 850d83be684..f412ba84c6b 100644 --- a/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs +++ b/src/test/ui/generic-associated-types/issue-78113-lifetime-mismatch-dyn-trait-box.rs @@ -1,4 +1,4 @@ -// Test for diagnostics when we have mismatched lifetime due to implict 'static lifetime in GATs +// Test for diagnostics when we have mismatched lifetime due to implicit 'static lifetime in GATs // check-fail diff --git a/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs b/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs index 403cf970bcb..cdb130d600c 100644 --- a/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs +++ b/src/test/ui/issues/issue-23611-enum-swap-in-drop.rs @@ -97,7 +97,7 @@ pub fn main() { // ever hands f_A off to instances of GaspA, and thus one should // be able to prove the invariant that f_A is *only* invoked from // from an instance of GaspA (either via the GaspA drop - // implementation or the E drop implementaton). Yet the old (bad) + // implementation or the E drop implementation). Yet the old (bad) // behavior allowed a call to f_A to leak in while we are tearing // down a value of type GaspB. } diff --git a/src/test/ui/iterators/issue-58952-filter-type-length.rs b/src/test/ui/iterators/issue-58952-filter-type-length.rs index ffbe89a14e3..6d12db8d137 100644 --- a/src/test/ui/iterators/issue-58952-filter-type-length.rs +++ b/src/test/ui/iterators/issue-58952-filter-type-length.rs @@ -1,6 +1,6 @@ // run-pass //! This snippet causes the type length to blowup exponentially, -//! so check that we don't accidentially exceed the type length limit. +//! so check that we don't accidentally exceed the type length limit. // FIXME: Once the size of iterator adaptors is further reduced, // increase the complexity of this test. use std::collections::VecDeque; diff --git a/src/test/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs b/src/test/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs index c66037e9a73..b4fc3317487 100644 --- a/src/test/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs +++ b/src/test/ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs @@ -5,7 +5,7 @@ // If you turn off deduplicate diagnostics (which rustc turns on by default but // compiletest turns off when it runs ui tests), then the errors are // (unfortunately) repeated here because the checking is done as we read in the -// errors, and curretly that happens two or three different times, depending on +// errors, and currently that happens two or three different times, depending on // compiler flags. // // I decided avoiding the redundant output was not worth the time in engineering diff --git a/src/test/ui/modules/auxiliary/dummy_lib.rs b/src/test/ui/modules/auxiliary/dummy_lib.rs new file mode 100644 index 00000000000..ef805c1f020 --- /dev/null +++ b/src/test/ui/modules/auxiliary/dummy_lib.rs @@ -0,0 +1,2 @@ +#[allow(dead_code)] +pub struct Dummy; diff --git a/src/test/ui/modules/special_module_name.rs b/src/test/ui/modules/special_module_name.rs new file mode 100644 index 00000000000..15c59b2da82 --- /dev/null +++ b/src/test/ui/modules/special_module_name.rs @@ -0,0 +1,8 @@ +mod lib; +//~^ WARN found module declaration for lib.rs +//~| ERROR file not found for module `lib` +mod main; +//~^ WARN found module declaration for main.rs +//~| ERROR file not found for module `main` + +fn main() {} diff --git a/src/test/ui/modules/special_module_name.stderr b/src/test/ui/modules/special_module_name.stderr new file mode 100644 index 00000000000..8b3da29386d --- /dev/null +++ b/src/test/ui/modules/special_module_name.stderr @@ -0,0 +1,37 @@ +error[E0583]: file not found for module `lib` + --> $DIR/special_module_name.rs:1:1 + | +LL | mod lib; + | ^^^^^^^^ + | + = help: to create the module `lib`, create file "$DIR/lib.rs" or "$DIR/lib/mod.rs" + +error[E0583]: file not found for module `main` + --> $DIR/special_module_name.rs:4:1 + | +LL | mod main; + | ^^^^^^^^^ + | + = help: to create the module `main`, create file "$DIR/main.rs" or "$DIR/main/mod.rs" + +warning: found module declaration for lib.rs + --> $DIR/special_module_name.rs:1:1 + | +LL | mod lib; + | ^^^^^^^^ + | + = note: `#[warn(special_module_name)]` on by default + = note: lib.rs is the root of this crate's library target + = help: to refer to it from other targets, use the library's name as the path + +warning: found module declaration for main.rs + --> $DIR/special_module_name.rs:4:1 + | +LL | mod main; + | ^^^^^^^^^ + | + = note: a binary crate cannot be used as library + +error: aborting due to 2 previous errors; 2 warnings emitted + +For more information about this error, try `rustc --explain E0583`. diff --git a/src/test/ui/modules/special_module_name_ignore.rs b/src/test/ui/modules/special_module_name_ignore.rs new file mode 100644 index 00000000000..07cea9b2b05 --- /dev/null +++ b/src/test/ui/modules/special_module_name_ignore.rs @@ -0,0 +1,9 @@ +// run-pass + +#[path = "auxiliary/dummy_lib.rs"] +mod lib; + +#[path = "auxiliary/dummy_lib.rs"] +mod main; + +fn main() {} diff --git a/src/test/ui/never_type/fallback-closure-wrap.rs b/src/test/ui/never_type/fallback-closure-wrap.rs index 35052da6760..f88355bb285 100644 --- a/src/test/ui/never_type/fallback-closure-wrap.rs +++ b/src/test/ui/never_type/fallback-closure-wrap.rs @@ -3,7 +3,7 @@ // // This particular test case currently fails as the inference to `()` rather // than `!` happens as a result of an `as` cast, which is not currently tracked. -// Crater did not find many cases of this occuring, but it is included for +// Crater did not find many cases of this occurring, but it is included for // awareness. // // revisions: nofallback fallback diff --git a/src/test/ui/nll/issue-21232-partial-init-and-use.rs b/src/test/ui/nll/issue-21232-partial-init-and-use.rs index 4cd1e406f94..ad3eb248351 100644 --- a/src/test/ui/nll/issue-21232-partial-init-and-use.rs +++ b/src/test/ui/nll/issue-21232-partial-init-and-use.rs @@ -53,7 +53,7 @@ impl<F> R<F> { fn new(f: F) -> Self { R { w: 0, f } } } // * local/field: Is the structure in a local or a field // * fully/partial/void: Are we fully initializing it before using any part? // Is whole type empty due to a void component? -// * init/reinit: First initialization, or did we previously inititalize and then move out? +// * init/reinit: First initialization, or did we previously initialize and then move out? // * struct/tuple: Is this a struct or a (X, Y). // // As a shorthand for the cases above, adding a numeric summary to diff --git a/src/test/ui/nll/polonius/assignment-kills-loans.rs b/src/test/ui/nll/polonius/assignment-kills-loans.rs index c4cf20389ac..696bf61cefd 100644 --- a/src/test/ui/nll/polonius/assignment-kills-loans.rs +++ b/src/test/ui/nll/polonius/assignment-kills-loans.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] // This tests the various kinds of assignments there are. Polonius used to generate `killed` -// facts only on simple assigments, but not projections, incorrectly causing errors to be emitted +// facts only on simple assignments, but not projections, incorrectly causing errors to be emitted // for code accepted by NLL. They are all variations from example code in the NLL RFC. // check-pass diff --git a/src/test/ui/pattern/rest-pat-semantic-disallowed.rs b/src/test/ui/pattern/rest-pat-semantic-disallowed.rs index 84552f2e733..156285e0f9f 100644 --- a/src/test/ui/pattern/rest-pat-semantic-disallowed.rs +++ b/src/test/ui/pattern/rest-pat-semantic-disallowed.rs @@ -1,5 +1,5 @@ // Here we test that rest patterns, i.e. `..`, are not allowed -// outside of slice (+ ident patterns witin those), tuple, +// outside of slice (+ ident patterns within those), tuple, // and tuple struct patterns and that duplicates are caught in these contexts. #![feature(box_patterns)] diff --git a/src/test/ui/proc-macro/crt-static.rs b/src/test/ui/proc-macro/crt-static.rs index 8c1a9dc8026..6103acb7b6b 100644 --- a/src/test/ui/proc-macro/crt-static.rs +++ b/src/test/ui/proc-macro/crt-static.rs @@ -9,7 +9,7 @@ // FIXME: This don't work when crate-type is specified by attribute // `#![crate_type = "proc-macro"]`, not by `--crate-type=proc-macro` -// command line flag. This is beacuse the list of `cfg` symbols is generated +// command line flag. This is because the list of `cfg` symbols is generated // before attributes are parsed. See rustc_interface::util::add_configuration #[cfg(target_feature = "crt-static")] compile_error!("crt-static is enabled"); diff --git a/src/test/ui/regions/issue-56537-closure-uses-region-from-container.rs b/src/test/ui/regions/issue-56537-closure-uses-region-from-container.rs index bdd1ae91f7d..a8f7a41c442 100644 --- a/src/test/ui/regions/issue-56537-closure-uses-region-from-container.rs +++ b/src/test/ui/regions/issue-56537-closure-uses-region-from-container.rs @@ -5,7 +5,7 @@ // by the function. // // This works today, which precludes changing things so that closures -// follow the same lifetime-elision rules used elsehwere. See +// follow the same lifetime-elision rules used elsewhere. See // rust-lang/rust#56537 // check-pass diff --git a/src/test/ui/resolve/issue-73427.rs b/src/test/ui/resolve/issue-73427.rs index 3c62782a897..5c2459a5903 100644 --- a/src/test/ui/resolve/issue-73427.rs +++ b/src/test/ui/resolve/issue-73427.rs @@ -22,6 +22,10 @@ enum D { Unit, } +enum E { + TupleWithFields(()), +} + fn main() { // Only variants without fields are suggested (and others mentioned in a note) where an enum // is used rather than a variant. @@ -34,6 +38,8 @@ fn main() { //~^ ERROR expected value, found enum `C` D.foo(); //~^ ERROR expected value, found enum `D` + E.foo(); + //~^ ERROR expected value, found enum `E` // Only tuple variants are suggested in calls or tuple struct pattern matching. diff --git a/src/test/ui/resolve/issue-73427.stderr b/src/test/ui/resolve/issue-73427.stderr index 59bb98a340a..a2ca46f0ce9 100644 --- a/src/test/ui/resolve/issue-73427.stderr +++ b/src/test/ui/resolve/issue-73427.stderr @@ -1,5 +1,5 @@ error[E0423]: expected value, found enum `A` - --> $DIR/issue-73427.rs:29:5 + --> $DIR/issue-73427.rs:33:5 | LL | A.foo(); | ^ @@ -23,7 +23,7 @@ LL | (A::Tuple()).foo(); | ~~~~~~~~~~~~ LL | A::Unit.foo(); | ~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | (A::StructWithFields { /* fields */ }).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -31,7 +31,7 @@ LL | (A::TupleWithFields(/* fields */)).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0423]: expected value, found enum `B` - --> $DIR/issue-73427.rs:31:5 + --> $DIR/issue-73427.rs:35:5 | LL | B.foo(); | ^ @@ -52,7 +52,7 @@ LL | (B::TupleWithFields(/* fields */)).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0423]: expected value, found enum `C` - --> $DIR/issue-73427.rs:33:5 + --> $DIR/issue-73427.rs:37:5 | LL | C.foo(); | ^ @@ -70,7 +70,7 @@ help: you might have meant to use the following enum variant | LL | C::Unit.foo(); | ~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | (C::StructWithFields { /* fields */ }).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -78,7 +78,7 @@ LL | (C::TupleWithFields(/* fields */)).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0423]: expected value, found enum `D` - --> $DIR/issue-73427.rs:35:5 + --> $DIR/issue-73427.rs:39:5 | LL | D.foo(); | ^ @@ -95,13 +95,37 @@ help: you might have meant to use the following enum variant | LL | D::Unit.foo(); | ~~~~~~~ -help: the following enum variant is available +help: alternatively, the following enum variant is available | LL | (D::TupleWithFields(/* fields */)).foo(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +error[E0423]: expected value, found enum `E` + --> $DIR/issue-73427.rs:41:5 + | +LL | E.foo(); + | ^ + | +note: the enum is defined here + --> $DIR/issue-73427.rs:25:1 + | +LL | / enum E { +LL | | TupleWithFields(()), +LL | | } + | |_^ +help: the following enum variant is available + | +LL | (E::TupleWithFields(/* fields */)).foo(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: consider importing one of these items instead + | +LL | use std::f32::consts::E; + | +LL | use std::f64::consts::E; + | + error[E0423]: expected function, tuple struct or tuple variant, found enum `A` - --> $DIR/issue-73427.rs:40:13 + --> $DIR/issue-73427.rs:46:13 | LL | let x = A(3); | ^ @@ -126,7 +150,7 @@ LL | let x = A::TupleWithFields(3); | ~~~~~~~~~~~~~~~~~~ error[E0532]: expected tuple struct or tuple variant, found enum `A` - --> $DIR/issue-73427.rs:42:12 + --> $DIR/issue-73427.rs:48:12 | LL | if let A(3) = x { } | ^ @@ -150,7 +174,7 @@ LL | if let A::Tuple(3) = x { } LL | if let A::TupleWithFields(3) = x { } | ~~~~~~~~~~~~~~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0423, E0532. For more information about an error, try `rustc --explain E0423`. diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr index 7cf32775a33..a369dc6db52 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -19,7 +19,7 @@ help: you might have meant to use the following enum variant | LL | m::Z::Unit; | ~~~~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | (m::Z::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -47,7 +47,7 @@ help: you might have meant to use the following enum variant | LL | m::Z::Unit; | ~~~~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | (m::Z::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -89,7 +89,7 @@ help: you might have meant to use the following enum variant | LL | let _: E = E::Unit; | ~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | let _: E = (E::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~ @@ -143,7 +143,7 @@ help: you might have meant to use the following enum variant | LL | let _: E = E::Unit; | ~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | let _: E = (E::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~ @@ -203,7 +203,7 @@ help: you might have meant to use the following enum variant | LL | let _: Z = m::Z::Unit; | ~~~~~~~~~~ -help: the following enum variants are available +help: alternatively, the following enum variants are also available | LL | let _: Z = (m::Z::Fn(/* fields */)); | ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/test/ui/specialization/issue-33017.rs b/src/test/ui/specialization/issue-33017.rs index 4d19230df6b..8dbadf58d5c 100644 --- a/src/test/ui/specialization/issue-33017.rs +++ b/src/test/ui/specialization/issue-33017.rs @@ -1,4 +1,4 @@ -// Test to ensure that trait bounds are propertly +// Test to ensure that trait bounds are properly // checked on specializable associated types #![allow(incomplete_features)] diff --git a/src/test/ui/suggestions/issue-89064.rs b/src/test/ui/suggestions/issue-89064.rs new file mode 100644 index 00000000000..fa5fc899dc0 --- /dev/null +++ b/src/test/ui/suggestions/issue-89064.rs @@ -0,0 +1,35 @@ +use std::convert::TryInto; + +trait A<T> { + fn foo() {} +} + +trait B<T, U> { + fn bar() {} +} + +struct S; + +impl<T> A<T> for S {} +impl<T, U> B<T, U> for S {} + +fn main() { + let _ = A::foo::<S>(); + //~^ ERROR + //~| HELP remove these generics + //~| HELP consider moving this generic argument + + let _ = B::bar::<S, S>(); + //~^ ERROR + //~| HELP remove these generics + //~| HELP consider moving these generic arguments + + let _ = A::<S>::foo::<S>(); + //~^ ERROR + //~| HELP remove these generics + + let _ = 42.into::<Option<_>>(); + //~^ ERROR + //~| HELP remove these generics + //~| HELP consider moving this generic argument +} diff --git a/src/test/ui/suggestions/issue-89064.stderr b/src/test/ui/suggestions/issue-89064.stderr new file mode 100644 index 00000000000..8b2a3881628 --- /dev/null +++ b/src/test/ui/suggestions/issue-89064.stderr @@ -0,0 +1,82 @@ +error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/issue-89064.rs:17:16 + | +LL | let _ = A::foo::<S>(); + | ^^^ expected 0 generic arguments + | +note: associated function defined here, with 0 generic parameters + --> $DIR/issue-89064.rs:4:8 + | +LL | fn foo() {} + | ^^^ +help: consider moving this generic argument to the `A` trait, which takes up to 1 argument + | +LL - let _ = A::foo::<S>(); +LL + let _ = A::<S>::foo(); + | +help: remove these generics + | +LL - let _ = A::foo::<S>(); +LL + let _ = A::foo(); + | + +error[E0107]: this associated function takes 0 generic arguments but 2 generic arguments were supplied + --> $DIR/issue-89064.rs:22:16 + | +LL | let _ = B::bar::<S, S>(); + | ^^^ expected 0 generic arguments + | +note: associated function defined here, with 0 generic parameters + --> $DIR/issue-89064.rs:8:8 + | +LL | fn bar() {} + | ^^^ +help: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments + | +LL - let _ = B::bar::<S, S>(); +LL + let _ = B::<S, S>::bar(); + | +help: remove these generics + | +LL - let _ = B::bar::<S, S>(); +LL + let _ = B::bar(); + | + +error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/issue-89064.rs:27:21 + | +LL | let _ = A::<S>::foo::<S>(); + | ^^^----- help: remove these generics + | | + | expected 0 generic arguments + | +note: associated function defined here, with 0 generic parameters + --> $DIR/issue-89064.rs:4:8 + | +LL | fn foo() {} + | ^^^ + +error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/issue-89064.rs:31:16 + | +LL | let _ = 42.into::<Option<_>>(); + | ^^^^ expected 0 generic arguments + | +note: associated function defined here, with 0 generic parameters + --> $SRC_DIR/core/src/convert/mod.rs:LL:COL + | +LL | fn into(self) -> T; + | ^^^^ +help: consider moving this generic argument to the `Into` trait, which takes up to 1 argument + | +LL | let _ = Into::<Option<_>>::into(42); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +help: remove these generics + | +LL - let _ = 42.into::<Option<_>>(); +LL + let _ = 42.into(); + | + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/suggestions/suggest-blanket-impl-local-trait.rs b/src/test/ui/suggestions/suggest-blanket-impl-local-trait.rs index 7cf536f7966..14fef1b5248 100644 --- a/src/test/ui/suggestions/suggest-blanket-impl-local-trait.rs +++ b/src/test/ui/suggestions/suggest-blanket-impl-local-trait.rs @@ -1,5 +1,5 @@ // Ensure that the compiler include the blanklet implementation suggestion -// when inside a `impl` statment are used two local traits. +// when inside a `impl` statement are used two local traits. // // edition:2021 use std::fmt; diff --git a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs index 1314f9cb093..17ddaa312f7 100644 --- a/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs +++ b/src/test/ui/traits/negative-impls/explicitly-unimplemented-error-message.rs @@ -1,5 +1,5 @@ // This tests issue #79683: note in the error message that the trait is -// explicitely unimplemented instead of suggesting to implement it. +// explicitly unimplemented instead of suggesting to implement it. #![feature(negative_impls)] diff --git a/src/test/ui/type/type-alias-bounds.rs b/src/test/ui/type/type-alias-bounds.rs index 65b79650d4d..e49731725d5 100644 --- a/src/test/ui/type/type-alias-bounds.rs +++ b/src/test/ui/type/type-alias-bounds.rs @@ -52,7 +52,7 @@ type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases // Do this instead: type T4<U> = <U as Bound>::Assoc; -// Make sure the help about associatd types is not shown incorrectly +// Make sure the help about associated types is not shown incorrectly type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases diff --git a/src/test/ui/unpretty/avoid-crash.rs b/src/test/ui/unpretty/avoid-crash.rs new file mode 100644 index 00000000000..fd84b70d944 --- /dev/null +++ b/src/test/ui/unpretty/avoid-crash.rs @@ -0,0 +1,4 @@ +// normalize-stderr-test "error `.*`" -> "$$ERROR_MESSAGE" +// compile-flags: -o/tmp/ -Zunpretty=ast-tree + +fn main() {} diff --git a/src/test/ui/unpretty/avoid-crash.stderr b/src/test/ui/unpretty/avoid-crash.stderr new file mode 100644 index 00000000000..11cd3866fa8 --- /dev/null +++ b/src/test/ui/unpretty/avoid-crash.stderr @@ -0,0 +1,4 @@ +error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE + +error: aborting due to previous error + diff --git a/src/test/ui/variance/variance-use-contravariant-struct-1.rs b/src/test/ui/variance/variance-use-contravariant-struct-1.rs index 7f59067483b..40781fbf082 100644 --- a/src/test/ui/variance/variance-use-contravariant-struct-1.rs +++ b/src/test/ui/variance/variance-use-contravariant-struct-1.rs @@ -1,4 +1,4 @@ -// Test various uses of structs with distint variances to make sure +// Test various uses of structs with distinct variances to make sure // they permit lifetimes to be approximated as expected. struct SomeStruct<T>(fn(T)); diff --git a/src/test/ui/variance/variance-use-contravariant-struct-2.rs b/src/test/ui/variance/variance-use-contravariant-struct-2.rs index 2113eb2addb..d4b2d08342a 100644 --- a/src/test/ui/variance/variance-use-contravariant-struct-2.rs +++ b/src/test/ui/variance/variance-use-contravariant-struct-2.rs @@ -1,4 +1,4 @@ -// Test various uses of structs with distint variances to make sure +// Test various uses of structs with distinct variances to make sure // they permit lifetimes to be approximated as expected. #![allow(dead_code)] diff --git a/src/test/ui/variance/variance-use-invariant-struct-1.rs b/src/test/ui/variance/variance-use-invariant-struct-1.rs index d40dbceb5f8..72f50f3459d 100644 --- a/src/test/ui/variance/variance-use-invariant-struct-1.rs +++ b/src/test/ui/variance/variance-use-invariant-struct-1.rs @@ -1,4 +1,4 @@ -// Test various uses of structs with distint variances to make sure +// Test various uses of structs with distinct variances to make sure // they permit lifetimes to be approximated as expected. struct SomeStruct<T>(*mut T); diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 0e27cc927ac..fac2c99714d 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -24,6 +24,7 @@ env: RUST_BACKTRACE: 1 CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 + CARGO_INCREMENTAL: 0 jobs: base: diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 97453303cd6..30607af4901 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -10,6 +10,7 @@ env: RUST_BACKTRACE: 1 CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 + CARGO_INCREMENTAL: 0 defaults: run: diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 380cd451987..c488c142e46 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,157 @@ document. ## Unreleased / In Rust Nightly -[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master) +[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master) + +## Rust 1.63 + +Current stable, released 2022-08-11 + +[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0) + +### New Lints + +* [`borrow_deref_ref`] + [#7930](https://github.com/rust-lang/rust-clippy/pull/7930) +* [`doc_link_with_quotes`] + [#8385](https://github.com/rust-lang/rust-clippy/pull/8385) +* [`no_effect_replace`] + [#8754](https://github.com/rust-lang/rust-clippy/pull/8754) +* [`rc_clone_in_vec_init`] + [#8769](https://github.com/rust-lang/rust-clippy/pull/8769) +* [`derive_partial_eq_without_eq`] + [#8796](https://github.com/rust-lang/rust-clippy/pull/8796) +* [`mismatching_type_param_order`] + [#8831](https://github.com/rust-lang/rust-clippy/pull/8831) +* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832) +* [`unused_rounding`] + [#8866](https://github.com/rust-lang/rust-clippy/pull/8866) +* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882) +* [`swap_ptr_to_ref`] + [#8916](https://github.com/rust-lang/rust-clippy/pull/8916) +* [`almost_complete_letter_range`] + [#8918](https://github.com/rust-lang/rust-clippy/pull/8918) +* [`needless_parens_on_range_literals`] + [#8933](https://github.com/rust-lang/rust-clippy/pull/8933) +* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934) + +### Moves and Deprecations + +* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to + `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621) + +### Enhancements + +* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations + [#8761](https://github.com/rust-lang/rust-clippy/pull/8761) +* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros + [#8790](https://github.com/rust-lang/rust-clippy/pull/8790) +* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests` + option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802) +* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests` + option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802) +* [`disallowed_methods`]: Now also lints indirect usages + [#8852](https://github.com/rust-lang/rust-clippy/pull/8852) +* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice + [#8862](https://github.com/rust-lang/rust-clippy/pull/8862) +* [`manual_range_contains`]: Now also lints on chains of `&&` and `||` + [#8884](https://github.com/rust-lang/rust-clippy/pull/8884) +* [`rc_clone_in_vec_init`]: Now also lints on `Weak` + [#8885](https://github.com/rust-lang/rust-clippy/pull/8885) +* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option + [#8897](https://github.com/rust-lang/rust-clippy/pull/8897) +* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns + [#8899](https://github.com/rust-lang/rust-clippy/pull/8899) +* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex + method chains inside `map` + [#8930](https://github.com/rust-lang/rust-clippy/pull/8930) +* [`needless_return`]: Now also lints on macro expressions in return statements + [#8932](https://github.com/rust-lang/rust-clippy/pull/8932) +* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config + should extend the default and not replace it + [#8944](https://github.com/rust-lang/rust-clippy/pull/8944) +* [`disallowed_names`]: Users can now indicate, that the `disallowed-names` + config should extend the default and not replace it + [#8944](https://github.com/rust-lang/rust-clippy/pull/8944) +* [`never_loop`]: Now checks for `continue` in struct expression + [#9002](https://github.com/rust-lang/rust-clippy/pull/9002) + +### False Positive Fixes + +* [`useless_transmute`]: No longer lints on types with erased regions + [#8564](https://github.com/rust-lang/rust-clippy/pull/8564) +* [`vec_init_then_push`]: No longer lints when further extended + [#8699](https://github.com/rust-lang/rust-clippy/pull/8699) +* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types + [#8807](https://github.com/rust-lang/rust-clippy/pull/8807) +* [`redundant_allocation`]: No longer lints on fat pointers that would become + thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813) +* [`derive_partial_eq_without_eq`]: + * Handle differing predicates applied by `#[derive(PartialEq)]` and + `#[derive(Eq)]` + [#8869](https://github.com/rust-lang/rust-clippy/pull/8869) + * No longer lints on non-public types and better handles generics + [#8950](https://github.com/rust-lang/rust-clippy/pull/8950) +* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner + string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892) +* [`branches_sharing_code`]: No longer lints when using different binding names + [#8901](https://github.com/rust-lang/rust-clippy/pull/8901) +* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await` + desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902) +* [`checked_conversions`]: No longer lints in `const` contexts + [#8907](https://github.com/rust-lang/rust-clippy/pull/8907) +* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when + `T::Item` doesn't implement `IntoIterator` + [#8960](https://github.com/rust-lang/rust-clippy/pull/8960) + +### Suggestion Fixes/Improvements + +* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible + [#8699](https://github.com/rust-lang/rust-clippy/pull/8699) +* [`manual_range_contains`]: Fix suggestion for integers with different signs + [#8763](https://github.com/rust-lang/rust-clippy/pull/8763) +* [`identity_op`]: Add parenthesis to suggestions where required + [#8786](https://github.com/rust-lang/rust-clippy/pull/8786) +* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64` + [#8778](https://github.com/rust-lang/rust-clippy/pull/8778) +* [`rc_clone_in_vec_init`]: Add suggestion + [#8814](https://github.com/rust-lang/rust-clippy/pull/8814) +* The "unknown field" error messages for config files now wraps the field names + [#8823](https://github.com/rust-lang/rust-clippy/pull/8823) +* [`cast_abs_to_unsigned`]: Do not remove cast if it's required + [#8876](https://github.com/rust-lang/rust-clippy/pull/8876) +* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not + references and not trivially clone-able + [#8902](https://github.com/rust-lang/rust-clippy/pull/8902) +* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`, + `iter_mut()` or `into_iter()` + [#8941](https://github.com/rust-lang/rust-clippy/pull/8941) + +### ICE Fixes + +* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type + [#8835](https://github.com/rust-lang/rust-clippy/pull/8835) +* Fix ICEs on callable `static`/`const`s + [#8896](https://github.com/rust-lang/rust-clippy/pull/8896) +* [`needless_late_init`] + [#8912](https://github.com/rust-lang/rust-clippy/pull/8912) +* Fix ICE in shadow lints + [#8913](https://github.com/rust-lang/rust-clippy/pull/8913) + +### Documentation Improvements + +* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now! + [#7359](https://github.com/rust-lang/rust-clippy/pull/7359) +* Add a *copy lint name*-button to Clippy's lint list + [#8839](https://github.com/rust-lang/rust-clippy/pull/8839) +* Display past names of renamed lints on Clippy's lint list + [#8843](https://github.com/rust-lang/rust-clippy/pull/8843) +* Add the ability to show the lint output in the lint list + [#8947](https://github.com/rust-lang/rust-clippy/pull/8947) ## Rust 1.62 -Current stable, released 2022-06-30 +Released 2022-06-30 [d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b) @@ -3481,6 +3627,7 @@ Released 2018-09-13 [`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut [`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes +[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp @@ -3496,6 +3643,7 @@ Released 2018-09-13 [`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match +[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime @@ -3656,6 +3804,8 @@ Released 2018-09-13 [`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero +[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections +[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain @@ -3697,6 +3847,7 @@ Released 2018-09-13 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat +[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or @@ -3747,6 +3898,7 @@ Released 2018-09-13 [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic [`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one +[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments [`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions [`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl [`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate @@ -3827,6 +3979,7 @@ Released 2018-09-13 [`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch +[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl @@ -3872,6 +4025,7 @@ Released 2018-09-13 [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used +[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else @@ -3930,6 +4084,7 @@ Released 2018-09-13 [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn +[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments @@ -4002,6 +4157,7 @@ Released 2018-09-13 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label +[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs index f5c51b9474f..92b2771f3fe 100644 --- a/src/tools/clippy/clippy_dev/src/bless.rs +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -37,7 +37,7 @@ fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) { return; } - let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file"); + let test_output_file = fs::read(test_output_path).expect("Unable to read test output file"); let reference_file = fs::read(&reference_file_path).unwrap_or_default(); if test_output_file != reference_file { diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 3b27f061eb0..357cf6fc43a 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -46,7 +46,7 @@ pub fn run(check: bool, verbose: bool) { // dependency if fs::read_to_string(project_root.join("Cargo.toml")) .expect("Failed to read clippy Cargo.toml") - .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") { return Err(CliError::IntellijSetupActive); } @@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { let args = &["--version"]; if context.verbose { - println!("{}", format_command(&program, &dir, args)); + println!("{}", format_command(program, &dir, args)); } - let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?; + let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?; if output.status.success() { Ok(()) @@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { Err(CliError::RustfmtNotInstalled) } else { Err(CliError::CommandFailed( - format_command(&program, &dir, args), + format_command(program, &dir, args), std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), )) } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 10a8f31f457..be05e67d724 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -155,7 +155,7 @@ fn to_camel_case(name: &str) -> String { name.split('_') .map(|s| { if s.is_empty() { - String::from("") + String::new() } else { [&s[0..1].to_uppercase(), &s[1..]].concat() } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 05e79a24188..c503142e5e4 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -418,7 +418,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io .expect("failed to find `impl_lint_pass` terminator"); impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) { + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); for c in content[lint_name_end..impl_lint_pass_end].chars() { // Remove trailing whitespace @@ -451,7 +451,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io } let mut content = - fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); eprintln!( "warn: you will have to manually remove any code related to `{}` from `{}`", diff --git a/src/tools/clippy/clippy_lints/src/as_underscore.rs b/src/tools/clippy/clippy_lints/src/as_underscore.rs deleted file mode 100644 index 0bdef9d0a7e..00000000000 --- a/src/tools/clippy/clippy_lints/src/as_underscore.rs +++ /dev/null @@ -1,74 +0,0 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, TyKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Check for the usage of `as _` conversion using inferred type. - /// - /// ### Why is this bad? - /// The conversion might include lossy conversion and dangerous cast that might go - /// undetected du to the type being inferred. - /// - /// The lint is allowed by default as using `_` is less wordy than always specifying the type. - /// - /// ### Example - /// ```rust - /// fn foo(n: usize) {} - /// let n: u16 = 256; - /// foo(n as _); - /// ``` - /// Use instead: - /// ```rust - /// fn foo(n: usize) {} - /// let n: u16 = 256; - /// foo(n as usize); - /// ``` - #[clippy::version = "1.63.0"] - pub AS_UNDERSCORE, - restriction, - "detects `as _` conversion" -} -declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]); - -impl<'tcx> LateLintPass<'tcx> for AsUnderscore { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind { - - let ty_resolved = cx.typeck_results().expr_ty(expr); - if let ty::Error(_) = ty_resolved.kind() { - span_lint_and_help( - cx, - AS_UNDERSCORE, - expr.span, - "using `as _` conversion", - None, - "consider giving the type explicitly", - ); - } else { - span_lint_and_then( - cx, - AS_UNDERSCORE, - expr.span, - "using `as _` conversion", - |diag| { - diag.span_suggestion( - ty.span, - "consider giving the type explicitly", - ty_resolved, - Applicability::MachineApplicable, - ); - } - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs deleted file mode 100644 index 0993adbae2e..00000000000 --- a/src/tools/clippy/clippy_lints/src/borrow_as_ptr.rs +++ /dev/null @@ -1,99 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_no_std_crate; -use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_tool_lint, impl_lint_pass}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for the usage of `&expr as *const T` or - /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or - /// `ptr::addr_of_mut` instead. - /// - /// ### Why is this bad? - /// This would improve readability and avoid creating a reference - /// that points to an uninitialized value or unaligned place. - /// Read the `ptr::addr_of` docs for more information. - /// - /// ### Example - /// ```rust - /// let val = 1; - /// let p = &val as *const i32; - /// - /// let mut val_mut = 1; - /// let p_mut = &mut val_mut as *mut i32; - /// ``` - /// Use instead: - /// ```rust - /// let val = 1; - /// let p = std::ptr::addr_of!(val); - /// - /// let mut val_mut = 1; - /// let p_mut = std::ptr::addr_of_mut!(val_mut); - /// ``` - #[clippy::version = "1.60.0"] - pub BORROW_AS_PTR, - pedantic, - "borrowing just to cast to a raw pointer" -} - -impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]); - -pub struct BorrowAsPtr { - msrv: Option<RustcVersion>, -} - -impl BorrowAsPtr { - #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { - Self { msrv } - } -} - -impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { - return; - } - - if expr.span.from_expansion() { - return; - } - - if_chain! { - if let ExprKind::Cast(left_expr, ty) = &expr.kind; - if let TyKind::Ptr(_) = ty.kind; - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind; - - then { - let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; - let macro_name = match mutability { - Mutability::Not => "addr_of", - Mutability::Mut => "addr_of_mut", - }; - - span_lint_and_sugg( - cx, - BORROW_AS_PTR, - expr.span, - "borrow as raw pointer", - "try", - format!( - "{}::ptr::{}!({})", - core_or_std, - macro_name, - snippet_opt(cx, e.span).unwrap() - ), - Applicability::MachineApplicable, - ); - } - } - } - - extract_msrv_attr!(LateContext); -} diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 937765b6614..c4520d00392 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -29,22 +29,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// fn foo(_x: &str) {} - /// /// let s = &String::new(); /// /// let a: &String = &* s; - /// foo(&*s); /// ``` /// /// Use instead: /// ```rust - /// # fn foo(_x: &str) {} /// # let s = &String::new(); /// let a: &String = s; - /// foo(&**s); /// ``` - #[clippy::version = "1.59.0"] + #[clippy::version = "1.63.0"] pub BORROW_DEREF_REF, complexity, "deref on an immutable reference returns the same type as itself" diff --git a/src/tools/clippy/clippy_lints/src/bytecount.rs b/src/tools/clippy/clippy_lints/src/bytecount.rs deleted file mode 100644 index 326ce34082a..00000000000 --- a/src/tools/clippy/clippy_lints/src/bytecount.rs +++ /dev/null @@ -1,103 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::match_type; -use clippy_utils::visitors::is_local_used; -use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, UintTy}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for naive byte counts - /// - /// ### Why is this bad? - /// The [`bytecount`](https://crates.io/crates/bytecount) - /// crate has methods to count your bytes faster, especially for large slices. - /// - /// ### Known problems - /// If you have predominantly small slices, the - /// `bytecount::count(..)` method may actually be slower. However, if you can - /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be - /// faster in those cases. - /// - /// ### Example - /// ```rust - /// # let vec = vec![1_u8]; - /// let count = vec.iter().filter(|x| **x == 0u8).count(); - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// # let vec = vec![1_u8]; - /// let count = bytecount::count(&vec, 0u8); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub NAIVE_BYTECOUNT, - pedantic, - "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values" -} - -declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]); - -impl<'tcx> LateLintPass<'tcx> for ByteCount { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind; - if count.ident.name == sym::count; - if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind; - if filter.ident.name == sym!(filter); - if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind; - let body = cx.tcx.hir().body(body); - if let [param] = body.params; - if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind; - if let ExprKind::Binary(ref op, l, r) = body.value.kind; - if op.node == BinOpKind::Eq; - if match_type(cx, - cx.typeck_results().expr_ty(filter_recv).peel_refs(), - &paths::SLICE_ITER); - let operand_is_arg = |expr| { - let expr = peel_ref_operators(cx, peel_blocks(expr)); - path_to_local_id(expr, arg_id) - }; - let needle = if operand_is_arg(l) { - r - } else if operand_is_arg(r) { - l - } else { - return; - }; - if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); - if !is_local_used(cx, needle, arg_id); - then { - let haystack = if let ExprKind::MethodCall(path, args, _) = - filter_recv.kind { - let p = path.ident.name; - if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { - &args[0] - } else { - filter_recv - } - } else { - filter_recv - }; - let mut applicability = Applicability::MaybeIncorrect; - span_lint_and_sugg( - cx, - NAIVE_BYTECOUNT, - expr.span, - "you appear to be counting bytes the naive way", - "consider using the bytecount crate", - format!("bytecount::count({}, {})", - snippet_with_applicability(cx, haystack.span, "..", &mut applicability), - snippet_with_applicability(cx, needle.span, "..", &mut applicability)), - applicability, - ); - } - }; - } -} diff --git a/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs deleted file mode 100644 index d70dbf5b239..00000000000 --- a/src/tools/clippy/clippy_lints/src/bytes_count_to_len.rs +++ /dev/null @@ -1,70 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// It checks for `str::bytes().count()` and suggests replacing it with - /// `str::len()`. - /// - /// ### Why is this bad? - /// `str::bytes().count()` is longer and may not be as performant as using - /// `str::len()`. - /// - /// ### Example - /// ```rust - /// "hello".bytes().count(); - /// String::from("hello").bytes().count(); - /// ``` - /// Use instead: - /// ```rust - /// "hello".len(); - /// String::from("hello").len(); - /// ``` - #[clippy::version = "1.62.0"] - pub BYTES_COUNT_TO_LEN, - complexity, - "Using `bytes().count()` when `len()` performs the same functionality" -} - -declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); - -impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, expr_def_id, &paths::ITER_COUNT); - - if let [bytes_expr] = &**expr_args; - if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind; - if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id); - if match_def_path(cx, bytes_def_id, &paths::STR_BYTES); - - if let [str_expr] = &**bytes_args; - let ty = cx.typeck_results().expr_ty(str_expr).peel_refs(); - - if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_COUNT_TO_LEN, - expr.span, - "using long and hard to read `.bytes().count()`", - "consider calling `.len()` instead", - format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)), - applicability - ); - } - }; - } -} diff --git a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs deleted file mode 100644 index 7eff71d5007..00000000000 --- a/src/tools/clippy/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ /dev/null @@ -1,86 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_hir::{Expr, ExprKind, PathSegment}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Spanned, symbol::sym, Span}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for calls to `ends_with` with possible file extensions - /// and suggests to use a case-insensitive approach instead. - /// - /// ### Why is this bad? - /// `ends_with` is case-sensitive and may not detect files with a valid extension. - /// - /// ### Example - /// ```rust - /// fn is_rust_file(filename: &str) -> bool { - /// filename.ends_with(".rs") - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn is_rust_file(filename: &str) -> bool { - /// let filename = std::path::Path::new(filename); - /// filename.extension() - /// .map(|ext| ext.eq_ignore_ascii_case("rs")) - /// .unwrap_or(false) - /// } - /// ``` - #[clippy::version = "1.51.0"] - pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - pedantic, - "Checks for calls to ends_with with case-sensitive file extensions" -} - -declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]); - -fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> { - if_chain! { - if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind; - if ident.as_str() == "ends_with"; - if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; - if (2..=6).contains(&ext_literal.as_str().len()); - if ext_literal.as_str().starts_with('.'); - if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) - || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); - then { - let mut ty = ctx.typeck_results().expr_ty(obj); - ty = match ty.kind() { - ty::Ref(_, ty, ..) => *ty, - _ => ty - }; - - match ty.kind() { - ty::Str => { - return Some(span); - }, - ty::Adt(def, _) => { - if ctx.tcx.is_diagnostic_item(sym::String, def.did()) { - return Some(span); - } - }, - _ => { return None; } - } - } - } - None -} - -impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { - fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { - span_lint_and_help( - ctx, - CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - span, - "case-sensitive file extension comparison", - None, - "consider using a case-insensitive comparison instead", - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs new file mode 100644 index 00000000000..56e894c6261 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs @@ -0,0 +1,25 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Ty, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::AS_UNDERSCORE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) { + if matches!(ty.kind, TyKind::Infer) { + span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| { + let ty_resolved = cx.typeck_results().expr_ty(expr); + if let ty::Error(_) = ty_resolved.kind() { + diag.help("consider giving the type explicitly"); + } else { + diag.span_suggestion( + ty.span, + "consider giving the type explicitly", + ty_resolved, + Applicability::MachineApplicable, + ); + } + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs new file mode 100644 index 00000000000..6e1f8cd64f0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_no_std_crate; +use clippy_utils::source::snippet_with_context; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind}; +use rustc_lint::LateContext; + +use super::BORROW_AS_PTR; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + cast_expr: &'tcx Expr<'_>, + cast_to: &'tcx Ty<'_>, +) { + if matches!(cast_to.kind, TyKind::Ptr(_)) + && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind + { + let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; + let macro_name = match mutability { + Mutability::Not => "addr_of", + Mutability::Mut => "addr_of_mut", + }; + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0; + + span_lint_and_sugg( + cx, + BORROW_AS_PTR, + expr.span, + "borrow as raw pointer", + "try", + format!("{}::ptr::{}!({})", core_or_std, macro_name, snip), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs new file mode 100644 index 00000000000..284ef165998 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def_id::DefId, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_semver::RustcVersion; + +use super::CAST_SLICE_FROM_RAW_PARTS; + +enum RawPartsKind { + Immutable, + Mutable, +} + +fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> { + if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) { + Some(RawPartsKind::Immutable) + } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) { + Some(RawPartsKind::Mutable) + } else { + None + } +} + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_to: Ty<'_>, + msrv: Option<RustcVersion>, +) { + if_chain! { + if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS); + if let ty::RawPtr(ptrty) = cast_to.kind(); + if let ty::Slice(_) = ptrty.ty.kind(); + if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + if let Some(rpk) = raw_parts_kind(cx, fun_def_id); + then { + let func = match rpk { + RawPartsKind::Immutable => "from_raw_parts", + RawPartsKind::Mutable => "from_raw_parts_mut" + }; + let span = expr.span; + let mut applicability = Applicability::MachineApplicable; + let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability); + let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability); + span_lint_and_sugg( + cx, + CAST_SLICE_FROM_RAW_PARTS, + span, + &format!("casting the result of `{func}` to {cast_to}"), + "replace with", + format!("core::ptr::slice_{func}({ptr}, {len})"), + applicability + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index af3798a0cc8..cc5d346b954 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -1,3 +1,5 @@ +mod as_underscore; +mod borrow_as_ptr; mod cast_abs_to_unsigned; mod cast_enum_constructor; mod cast_lossless; @@ -8,6 +10,7 @@ mod cast_ptr_alignment; mod cast_ref_to_mut; mod cast_sign_loss; mod cast_slice_different_sizes; +mod cast_slice_from_raw_parts; mod char_lit_as_u8; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; @@ -16,7 +19,7 @@ mod ptr_as_ptr; mod unnecessary_cast; mod utils; -use clippy_utils::is_hir_ty_cfg_dependant; +use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -506,6 +509,93 @@ declare_clippy_lint! { "casting the result of `abs()` to an unsigned integer can panic" } +declare_clippy_lint! { + /// ### What it does + /// Check for the usage of `as _` conversion using inferred type. + /// + /// ### Why is this bad? + /// The conversion might include lossy conversion and dangerous cast that might go + /// undetected due to the type being inferred. + /// + /// The lint is allowed by default as using `_` is less wordy than always specifying the type. + /// + /// ### Example + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as _); + /// ``` + /// Use instead: + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as usize); + /// ``` + #[clippy::version = "1.63.0"] + pub AS_UNDERSCORE, + restriction, + "detects `as _` conversion" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or + /// `ptr::addr_of_mut` instead. + /// + /// ### Why is this bad? + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `ptr::addr_of` docs for more information. + /// + /// ### Example + /// ```rust + /// let val = 1; + /// let p = &val as *const i32; + /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let val = 1; + /// let p = std::ptr::addr_of!(val); + /// + /// let mut val_mut = 1; + /// let p_mut = std::ptr::addr_of_mut!(val_mut); + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, + pedantic, + "borrowing just to cast to a raw pointer" +} +declare_clippy_lint! { + /// ### What it does + /// Checks for a raw slice being cast to a slice pointer + /// + /// ### Why is this bad? + /// This can result in multiple `&mut` references to the same location when only a pointer is + /// required. + /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require + /// the same [safety requirements] to be upheld. + /// + /// ### Example + /// ```rust,ignore + /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; + /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; + /// ``` + /// Use instead: + /// ```rust,ignore + /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); + /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); + /// ``` + /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety + #[clippy::version = "1.64.0"] + pub CAST_SLICE_FROM_RAW_PARTS, + suspicious, + "casting a slice created from a pointer and length to a slice pointer" +} + pub struct Casts { msrv: Option<RustcVersion>, } @@ -534,7 +624,10 @@ impl_lint_pass!(Casts => [ PTR_AS_PTR, CAST_ENUM_TRUNCATION, CAST_ENUM_CONSTRUCTOR, - CAST_ABS_TO_UNSIGNED + CAST_ABS_TO_UNSIGNED, + AS_UNDERSCORE, + BORROW_AS_PTR, + CAST_SLICE_FROM_RAW_PARTS ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -547,8 +640,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } - if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { - if is_hir_ty_cfg_dependant(cx, cast_to) { + if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind { + if is_hir_ty_cfg_dependant(cx, cast_to_hir) { return; } let (cast_from, cast_to) = ( @@ -559,7 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { return; } - + cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); @@ -575,6 +668,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } + + as_underscore::check(cx, expr, cast_to_hir); + + if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { + borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); + } } cast_ref_to_mut::check(cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index fff7da8e33f..19d2e6e1d12 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -90,13 +90,20 @@ pub(super) fn check<'tcx>( fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; + let replaced_literal; + let matchless = if literal_str.contains(['(', ')']) { + replaced_literal = literal_str.replace(['(', ')'], ""); + &replaced_literal + } else { + literal_str + }; span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), + format!("{}_{}", matchless.trim_end_matches('.'), cast_to), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 59f10247a11..1506ea604f0 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1,24 +1,34 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res}; -use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage}; +use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; +use clippy_utils::{ + fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local, + walk_to_expr_usage, +}; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, - ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, - TraitItemKind, TyKind, UnOp, + self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, + GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, + Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp, }; +use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults}; +use rustc_middle::ty::{ + self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, + ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults, +}; +use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; +use std::collections::VecDeque; declare_clippy_lint! { /// ### What it does @@ -151,6 +161,7 @@ pub struct Dereferencing { /// been finished. Note we can't lint at the end of every body as they can be nested within each /// other. current_body: Option<BodyId>, + /// The list of locals currently being checked by the lint. /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted. /// This is needed for or patterns where one of the branches can be linted, but another can not @@ -158,6 +169,19 @@ pub struct Dereferencing { /// /// e.g. `m!(x) | Foo::Bar(ref x)` ref_locals: FxIndexMap<HirId, Option<RefPat>>, + + // `IntoIterator` for arrays requires Rust 1.53. + msrv: Option<RustcVersion>, +} + +impl Dereferencing { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { + msrv, + ..Dereferencing::default() + } + } } struct StateData { @@ -170,6 +194,7 @@ struct StateData { struct DerefedBorrow { count: usize, msg: &'static str, + snip_expr: Option<HirId>, } enum State { @@ -250,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, expr); + let (position, adjustments) = walk_parents(cx, expr, self.msrv); match kind { RefOp::Deref => { @@ -331,20 +356,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { let deref_msg = "this expression creates a reference which is immediately dereferenced by the compiler"; let borrow_msg = "this expression borrows a value the compiler would automatically borrow"; + let impl_msg = "the borrowed expression implements the required traits"; - let (required_refs, msg) = if position.can_auto_borrow() { - (1, if deref_count == 1 { borrow_msg } else { deref_msg }) + let (required_refs, msg, snip_expr) = if position.can_auto_borrow() { + (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None) + } else if let Position::ImplArg(hir_id) = position { + (0, impl_msg, Some(hir_id)) } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) = next_adjust.map(|a| &a.kind) { if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable() { - (3, deref_msg) + (3, deref_msg, None) } else { - (2, deref_msg) + (2, deref_msg, None) } } else { - (2, deref_msg) + (2, deref_msg, None) }; if deref_count >= required_refs { @@ -354,6 +382,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { // can't be removed without breaking the code. See earlier comment. count: deref_count - required_refs, msg, + snip_expr, }), StateData { span: expr.span, hir_id: expr.hir_id, position }, )); @@ -510,7 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { spans: vec![pat.span], app, replacements: vec![(pat.span, snip.into())], - hir_id: pat.hir_id + hir_id: pat.hir_id, }), ); } @@ -542,6 +571,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { self.current_body = None; } } + + extract_msrv_attr!(LateContext); } fn try_parse_ref_op<'tcx>( @@ -594,6 +625,7 @@ enum Position { /// The method is defined on a reference type. e.g. `impl Foo for &T` MethodReceiverRefImpl, Callee, + ImplArg(HirId), FieldAccess(Symbol), Postfix, Deref, @@ -630,7 +662,7 @@ impl Position { | Self::Callee | Self::FieldAccess(_) | Self::Postfix => PREC_POSTFIX, - Self::Deref => PREC_PREFIX, + Self::ImplArg(_) | Self::Deref => PREC_PREFIX, Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, } } @@ -639,8 +671,12 @@ impl Position { /// Walks up the parent expressions attempting to determine both how stable the auto-deref result /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow /// locations as those follow different rules. -#[allow(clippy::too_many_lines)] -fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) { +#[expect(clippy::too_many_lines)] +fn walk_parents<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + msrv: Option<RustcVersion>, +) -> (Position, &'tcx [Adjustment<'tcx>]) { let mut adjustments = [].as_slice(); let mut precedence = 0i8; let ctxt = e.span.ctxt(); @@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, & .iter() .position(|arg| arg.hir_id == child_id) .zip(expr_sig(cx, func)) - .and_then(|(i, sig)| sig.input_with_hir(i)) - .map(|(hir_ty, ty)| match hir_ty { - // Type inference for closures can depend on how they're called. Only go by the explicit - // types here. - Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), - None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) - .position_for_arg(), + .and_then(|(i, sig)| { + sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty { + // Type inference for closures can depend on how they're called. Only go by the explicit + // types here. + Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), + None => { + if let ty::Param(param_ty) = ty.skip_binder().kind() { + needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + } else { + ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) + .position_for_arg() + } + }, + }) }), ExprKind::MethodCall(_, args, _) => { let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); @@ -773,7 +816,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, & .and_then(|subs| subs.get(1..)) { Some(subs) => cx.tcx.mk_substs(subs.iter().copied()), - None => cx.tcx.mk_substs([].iter()), + None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()), } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() { // Trait methods taking `&self` sub_ty @@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, & Position::MethodReceiver } } else { - ty_auto_deref_stability( - cx, - cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)), - precedence, - ) - .position_for_arg() + let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i]; + if let ty::Param(param_ty) = ty.kind() { + needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv) + } else { + ty_auto_deref_stability( + cx, + cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)), + precedence, + ) + .position_for_arg() + } } }) }, @@ -948,6 +996,205 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } +// Checks whether: +// * child is an expression of the form `&e` in an argument position requiring an `impl Trait` +// * `e`'s type implements `Trait` and is copyable +// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. +// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to +// be moved, but it cannot be. +fn needless_borrow_impl_arg_position<'tcx>( + cx: &LateContext<'tcx>, + parent: &Expr<'tcx>, + arg_index: usize, + param_ty: ParamTy, + mut expr: &Expr<'tcx>, + precedence: i8, + msrv: Option<RustcVersion>, +) -> Position { + let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); + let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); + + let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) }; + let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder(); + let substs_with_expr_ty = cx + .typeck_results() + .node_substs(if let ExprKind::Call(callee, _) = parent.kind { + callee.hir_id + } else { + parent.hir_id + }); + + let predicates = cx.tcx.param_env(callee_def_id).caller_bounds(); + let projection_predicates = predicates + .iter() + .filter_map(|predicate| { + if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { + Some(projection_predicate) + } else { + None + } + }) + .collect::<Vec<_>>(); + + let mut trait_with_ref_mut_self_method = false; + + // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return. + if predicates + .iter() + .filter_map(|predicate| { + if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx) + { + Some(trait_predicate.trait_ref.def_id) + } else { + None + } + }) + .inspect(|trait_def_id| { + trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id); + }) + .all(|trait_def_id| { + Some(trait_def_id) == destruct_trait_def_id + || Some(trait_def_id) == sized_trait_def_id + || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id) + }) + { + return Position::Other(precedence); + } + + // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same + // elements are modified each time `check_referent` is called. + let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); + + let mut check_referent = |referent| { + let referent_ty = cx.typeck_results().expr_ty(referent); + + if !is_copy(cx, referent_ty) { + return false; + } + + // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 + if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + return false; + } + + if !replace_types( + cx, + param_ty, + referent_ty, + fn_sig, + arg_index, + &projection_predicates, + &mut substs_with_referent_ty, + ) { + return false; + } + + predicates.iter().all(|predicate| { + if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() + && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id) + && let ty::Param(param_ty) = trait_predicate.self_ty().kind() + && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() + && ty.is_array() + && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR) + { + return false; + } + + let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty); + let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate); + cx.tcx + .infer_ctxt() + .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation)) + }) + }; + + let mut needless_borrow = false; + while let ExprKind::AddrOf(_, _, referent) = expr.kind { + if !check_referent(referent) { + break; + } + expr = referent; + needless_borrow = true; + } + + if needless_borrow { + Position::ImplArg(expr.hir_id) + } else { + Position::Other(precedence) + } +} + +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { + cx.tcx + .associated_items(trait_def_id) + .in_definition_order() + .any(|assoc_item| { + if assoc_item.fn_has_self_parameter { + let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0]; + matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut)) + } else { + false + } + }) +} + +// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting +// projected type that is a type parameter. Returns `false` if replacing the types would have an +// effect on the function signature beyond substituting `new_ty` for `param_ty`. +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757 +fn replace_types<'tcx>( + cx: &LateContext<'tcx>, + param_ty: ParamTy, + new_ty: Ty<'tcx>, + fn_sig: FnSig<'tcx>, + arg_index: usize, + projection_predicates: &[ProjectionPredicate<'tcx>], + substs: &mut [ty::GenericArg<'tcx>], +) -> bool { + let mut replaced = BitSet::new_empty(substs.len()); + + let mut deque = VecDeque::with_capacity(substs.len()); + deque.push_back((param_ty, new_ty)); + + while let Some((param_ty, new_ty)) = deque.pop_front() { + // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in. + if !fn_sig + .inputs_and_output + .iter() + .enumerate() + .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx))) + { + return false; + } + + substs[param_ty.index as usize] = ty::GenericArg::from(new_ty); + + // The `replaced.insert(...)` check provides some protection against infinite loops. + if replaced.insert(param_ty.index) { + for projection_predicate in projection_predicates { + if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx) + && let ty::Term::Ty(term_ty) = projection_predicate.term + && let ty::Param(term_param_ty) = term_ty.kind() + { + let item_def_id = projection_predicate.projection_ty.item_def_id; + let assoc_item = cx.tcx.associated_item(item_def_id); + let projection = cx.tcx + .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[])); + + if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection) + && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty) + { + deque.push_back((*term_param_ty, projected_ty)); + } + } + } + } + } + + true +} + struct TyPosition<'tcx> { position: Position, ty: Option<Ty<'tcx>>, @@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data }, State::DerefedBorrow(state) => { let mut app = Applicability::MachineApplicable; - let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id)); + let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app); span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee); let sugg = if !snip_is_macro diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index a982990e418..9ca443b7dff 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -516,7 +516,10 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate { - trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())), + trait_ref: TraitRef::new( + eq_trait_id, + tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))), + ), constness: BoundConstness::NotConst, polarity: ImplPolarity::Positive, }))) diff --git a/src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs b/src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs index cb07f57e870..0ff1d2755da 100644 --- a/src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs +++ b/src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// /// See also: [`foo`] /// fn bar() {} /// ``` - #[clippy::version = "1.60.0"] + #[clippy::version = "1.63.0"] pub DOC_LINK_WITH_QUOTES, pedantic, "possible typo for an intra-doc link" diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index e1eb3b6324c..7ff7068f0b0 100644 --- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs +++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { /// // a.rs /// use crate::b; /// ``` - #[clippy::version = "1.62.0"] + #[clippy::version = "1.63.0"] pub DUPLICATE_MOD, suspicious, "file loaded as module multiple times" diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 1ac7bfba06b..327865e4c85 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_hir; -use clippy_utils::ty::contains_ty; use rustc_hir::intravisit; use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; use rustc_infer::infer::TyCtxtInferExt; @@ -30,18 +29,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// # fn foo(bar: usize) {} - /// let x = Box::new(1); - /// foo(*x); - /// println!("{}", *x); + /// fn foo(x: Box<u32>) {} /// ``` /// /// Use instead: /// ```rust - /// # fn foo(bar: usize) {} - /// let x = 1; - /// foo(x); - /// println!("{}", x); + /// fn foo(x: u32) {} /// ``` #[clippy::version = "pre 1.29.0"] pub BOXED_LOCAL, @@ -172,7 +165,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { // skip if there is a `self` parameter binding to a type // that contains `Self` (i.e.: `self: Box<Self>`), see #4804 if let Some(trait_self_ty) = self.trait_self_ty { - if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) { + if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) { return; } } 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 df9b41d2c98..bb50e8fcabb 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -172,7 +172,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { expr.span, "logarithm for bases 2, 10 and e can be computed more accurately", "consider using", - format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method), + format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method), Applicability::MachineApplicable, ); } @@ -263,13 +263,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { ( SUBOPTIMAL_FLOPS, "square-root of a number can be computed more efficiently and accurately", - format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")), + format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()), ) } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value { ( IMPRECISE_FLOPS, "cube-root of a number can be computed more accurately", - format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")), + format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()), ) } else if let Some(exponent) = get_integer_from_float_constant(&value) { ( @@ -277,7 +277,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { "exponentiation with integer powers can be computed more efficiently", format!( "{}.powi({})", - Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &args[0], "..").maybe_par(), numeric_literal::format(&exponent.to_string(), None, false) ), ) @@ -327,7 +327,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { "consider using", format!( "{}.mul_add({}, {})", - Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &args[0], "..").maybe_par(), Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, other_addend, ".."), ), @@ -418,7 +418,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { "consider using", format!( "{}.exp_m1()", - Sugg::hir(cx, self_arg, "..") + Sugg::hir(cx, self_arg, "..").maybe_par() ), Applicability::MachineApplicable, ); @@ -550,11 +550,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { then { let positive_abs_sugg = ( "manual implementation of `abs` method", - format!("{}.abs()", Sugg::hir(cx, body, "..")), + format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), ); let negative_abs_sugg = ( "manual implementation of negation of `abs` method", - format!("-{}.abs()", Sugg::hir(cx, body, "..")), + format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), ); let sugg = if is_testing_positive(cx, cond, body) { if if_expr_positive { @@ -621,7 +621,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "log base can be expressed more clearly", "consider using", - format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),), + format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),), Applicability::MachineApplicable, ); } @@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue) { - let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); + let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); if_chain! { if let ExprKind::Lit(ref literal) = mul_lhs.kind; if let ast::LitKind::Float(ref value, float_type) = literal.node; @@ -677,7 +677,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) { - let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")); + let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); if_chain! { if let ExprKind::Lit(ref literal) = mul_lhs.kind; if let ast::LitKind::Float(ref value, float_type) = literal.node; diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 925a8cb8dee..0c5851cdbed 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; -use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -56,29 +56,27 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { }; let mut applicability = Applicability::MachineApplicable; - if format_args.value_args.is_empty() { - match *format_args.format_string_parts { + if format_args.args.is_empty() { + match *format_args.format_string.parts { [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), [_] => { - if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) { - // Simulate macro expansion, converting {{ and }} to { and }. - let s_expand = s_src.replace("{{", "{").replace("}}", "}"); - let sugg = format!("{}.to_string()", s_expand); - span_useless_format(cx, call_site, sugg, applicability); - } + // Simulate macro expansion, converting {{ and }} to { and }. + let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}"); + let sugg = format!("{}.to_string()", s_expand); + span_useless_format(cx, call_site, sugg, applicability); }, [..] => {}, } - } else if let [value] = *format_args.value_args { + } else if let [arg] = &*format_args.args { + let value = arg.param.value; if_chain! { - if format_args.format_string_parts == [kw::Empty]; + if format_args.format_string.parts == [kw::Empty]; if match cx.typeck_results().expr_ty(value).peel_refs().kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), ty::Str => true, _ => false, }; - if let Some(args) = format_args.args(); - if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting()); + if !arg.format.has_string_formatting(); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 1e6feaac26c..9fb9fd99748 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; -use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn}; +use clippy_utils::macros::{is_format_macro, FormatArgsExpn}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use if_chain::if_chain; +use itertools::Itertools; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; @@ -74,20 +75,16 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if let Some(macro_def_id) = outermost_expn_data.macro_def_id; if is_format_macro(cx, macro_def_id); if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; - if let Some(args) = format_args.args(); then { - for (i, arg) in args.iter().enumerate() { - if arg.format_trait != sym::Display { + for arg in &format_args.args { + if arg.format.has_string_formatting() { continue; } - if arg.has_string_formatting() { + if is_aliased(&format_args, arg.param.value.hir_id) { continue; } - if is_aliased(&args, i) { - continue; - } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value); - check_to_string_in_format_args(cx, name, arg.value); + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); + check_to_string_in_format_args(cx, name, arg.param.value); } } } @@ -134,45 +131,56 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex if is_diag_trait_item(cx, method_def_id, sym::ToString); let receiver_ty = cx.typeck_results().expr_ty(receiver); if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); + let (n_needed_derefs, target) = + count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()); + if implements_trait(cx, target, display_trait_id, &[]); + if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait(); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - let (n_needed_derefs, target) = count_needed_derefs( - receiver_ty, - cx.typeck_results().expr_adjustments(receiver).iter(), - ); - if implements_trait(cx, target, display_trait_id, &[]) { - if n_needed_derefs == 0 { - span_lint_and_sugg( - cx, - TO_STRING_IN_FORMAT_ARGS, - value.span.with_lo(receiver.span.hi()), - &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), - "remove this", - String::new(), - Applicability::MachineApplicable, - ); - } else { - span_lint_and_sugg( - cx, - TO_STRING_IN_FORMAT_ARGS, - value.span, - &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), - "use this", - format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs), - Applicability::MachineApplicable, - ); - } + let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); + if n_needed_derefs == 0 && !needs_ref { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span.with_lo(receiver.span.hi()), + &format!( + "`to_string` applied to a type that implements `Display` in `{}!` args", + name + ), + "remove this", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_sugg( + cx, + TO_STRING_IN_FORMAT_ARGS, + value.span, + &format!( + "`to_string` applied to a type that implements `Display` in `{}!` args", + name + ), + "use this", + format!( + "{}{:*>width$}{}", + if needs_ref { "&" } else { "" }, + "", + receiver_snippet, + width = n_needed_derefs + ), + Applicability::MachineApplicable, + ); } } } } -// Returns true if `args[i]` "refers to" or "is referred to by" another argument. -fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool { - let value = args[i].value; - args.iter() - .enumerate() - .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value)) +// Returns true if `hir_id` is referred to by multiple format params +fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool { + args.params() + .filter(|param| param.value.hir_id == hir_id) + .at_most_one() + .is_err() } fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>) diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 04b5be6c80e..d8bc0bf08f2 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn}; +use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -168,10 +168,9 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, if let macro_def_id = outer_macro.def_id; if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn); if is_format_macro(cx, macro_def_id); - if let Some(args) = format_args.args(); then { - for arg in args { - if arg.format_trait != impl_trait.name { + for arg in format_args.args { + if arg.format.r#trait != impl_trait.name { continue; } check_format_arg_self(cx, expr, &arg, impl_trait); @@ -180,11 +179,11 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, } } -fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) { +fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) { // Handle multiple dereferencing of references e.g. &&self // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl) // Since the argument to fmt is itself a reference: &self - let reference = peel_ref_operators(cx, arg.value); + let reference = peel_ref_operators(cx, arg.param.value); let map = cx.tcx.hir(); // Is the reference self? if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) { diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 73261fb8a44..90911e0bf25 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -1,6 +1,6 @@ mod must_use; mod not_unsafe_ptr_arg_deref; -mod result_unit_err; +mod result; mod too_many_arguments; mod too_many_lines; @@ -217,17 +217,62 @@ declare_clippy_lint! { "public function returning `Result` with an `Err` type of `()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for functions that return `Result` with an unusually large + /// `Err`-variant. + /// + /// ### Why is this bad? + /// A `Result` is at least as large as the `Err`-variant. While we + /// expect that variant to be seldomly used, the compiler needs to reserve + /// and move that much memory every single time. + /// + /// ### Known problems + /// The size determined by Clippy is platform-dependent. + /// + /// ### Examples + /// ```rust + /// pub enum ParseError { + /// UnparsedBytes([u8; 512]), + /// UnexpectedEof, + /// } + /// + /// // The `Result` has at least 512 bytes, even in the `Ok`-case + /// pub fn parse() -> Result<(), ParseError> { + /// Ok(()) + /// } + /// ``` + /// should be + /// ``` + /// pub enum ParseError { + /// UnparsedBytes(Box<[u8; 512]>), + /// UnexpectedEof, + /// } + /// + /// // The `Result` is slightly larger than a pointer + /// pub fn parse() -> Result<(), ParseError> { + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.64.0"] + pub RESULT_LARGE_ERR, + perf, + "function returning `Result` with large `Err` type" +} + #[derive(Copy, Clone)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, + large_error_threshold: u64, } impl Functions { - pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self { + pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self { Self { too_many_arguments_threshold, too_many_lines_threshold, + large_error_threshold, } } } @@ -240,6 +285,7 @@ impl_lint_pass!(Functions => [ DOUBLE_MUST_USE, MUST_USE_CANDIDATE, RESULT_UNIT_ERR, + RESULT_LARGE_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -259,18 +305,18 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { must_use::check_item(cx, item); - result_unit_err::check_item(cx, item); + result::check_item(cx, item, self.large_error_threshold); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { must_use::check_impl_item(cx, item); - result_unit_err::check_impl_item(cx, item); + result::check_impl_item(cx, item, self.large_error_threshold); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold); not_unsafe_ptr_arg_deref::check_trait_item(cx, item); must_use::check_trait_item(cx, item); - result_unit_err::check_trait_item(cx, item); + result::check_trait_item(cx, item, self.large_error_threshold); } } diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs new file mode 100644 index 00000000000..af520a493ed --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -0,0 +1,100 @@ +use rustc_errors::Diagnostic; +use rustc_hir as hir; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{sym, Span}; +use rustc_typeck::hir_ty_to_ty; + +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use clippy_utils::trait_ref_of_method; +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; + +use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; + +/// The type of the `Err`-variant in a `std::result::Result` returned by the +/// given `FnDecl` +fn result_err_ty<'tcx>( + cx: &LateContext<'tcx>, + decl: &hir::FnDecl<'tcx>, + item_span: Span, +) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> { + if !in_external_macro(cx.sess(), item_span) + && let hir::FnRetTy::Return(hir_ty) = decl.output + && let ty = hir_ty_to_ty(cx.tcx, hir_ty) + && is_type_diagnostic_item(cx, ty, sym::Result) + && let ty::Adt(_, substs) = ty.kind() + { + let err_ty = substs.type_at(1); + Some((hir_ty, err_ty)) + } else { + None + } +} + +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) { + if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind + && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) + { + if cx.access_levels.is_exported(item.def_id) { + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + check_result_unit_err(cx, err_ty, fn_header_span); + } + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); + } +} + +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) { + // Don't lint if method is a trait's implementation, we can't do anything about those + if let hir::ImplItemKind::Fn(ref sig, _) = item.kind + && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) + && trait_ref_of_method(cx, item.def_id).is_none() + { + if cx.access_levels.is_exported(item.def_id) { + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + check_result_unit_err(cx, err_ty, fn_header_span); + } + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); + } +} + +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) { + if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) { + if cx.access_levels.is_exported(item.def_id) { + check_result_unit_err(cx, err_ty, fn_header_span); + } + check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold); + } + } +} + +fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) { + if err_ty.is_unit() { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "this returns a `Result<_, ()>`", + None, + "use a custom `Error` type instead", + ); + } +} + +fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs b/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs deleted file mode 100644 index 2e63a1f920d..00000000000 --- a/src/tools/clippy/clippy_lints/src/functions/result_unit_err.rs +++ /dev/null @@ -1,66 +0,0 @@ -use rustc_hir as hir; -use rustc_lint::{LateContext, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty; -use rustc_span::{sym, Span}; -use rustc_typeck::hir_ty_to_ty; - -use if_chain::if_chain; - -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::is_type_diagnostic_item; - -use super::RESULT_UNIT_ERR; - -pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind { - let is_public = cx.access_levels.is_exported(item.def_id); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public { - check_result_unit_err(cx, sig.decl, item.span, fn_header_span); - } - } -} - -pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { - if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { - let is_public = cx.access_levels.is_exported(item.def_id); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public && trait_ref_of_method(cx, item.def_id).is_none() { - check_result_unit_err(cx, sig.decl, item.span, fn_header_span); - } - } -} - -pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { - if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { - let is_public = cx.access_levels.is_exported(item.def_id); - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if is_public { - check_result_unit_err(cx, sig.decl, item.span, fn_header_span); - } - } -} - -fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { - if_chain! { - if !in_external_macro(cx.sess(), item_span); - if let hir::FnRetTy::Return(ty) = decl.output; - let ty = hir_ty_to_ty(cx.tcx, ty); - if is_type_diagnostic_item(cx, ty, sym::Result); - if let ty::Adt(_, substs) = ty.kind(); - let err_ty = substs.type_at(1); - if err_ty.is_unit(); - then { - span_lint_and_help( - cx, - RESULT_UNIT_ERR, - fn_header_span, - "this returns a `Result<_, ()>`", - None, - "use a custom `Error` type instead", - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/get_first.rs b/src/tools/clippy/clippy_lints/src/get_first.rs deleted file mode 100644 index 529f7babaa5..00000000000 --- a/src/tools/clippy/clippy_lints/src/get_first.rs +++ /dev/null @@ -1,68 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_slice_of_primitives, match_def_path, paths}; -use if_chain::if_chain; -use rustc_ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Checks for using `x.get(0)` instead of - /// `x.first()`. - /// - /// ### Why is this bad? - /// Using `x.first()` is easier to read and has the same - /// result. - /// - /// ### Example - /// ```rust - /// let x = vec![2, 3, 5]; - /// let first_element = x.get(0); - /// ``` - /// - /// Use instead: - /// ```rust - /// let x = vec![2, 3, 5]; - /// let first_element = x.first(); - /// ``` - #[clippy::version = "1.63.0"] - pub GET_FIRST, - style, - "Using `x.get(0)` when `x.first()` is simpler" -} -declare_lint_pass!(GetFirst => [GET_FIRST]); - -impl<'tcx> LateLintPass<'tcx> for GetFirst { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind; - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, expr_def_id, &paths::SLICE_GET); - - if let Some(_) = is_slice_of_primitives(cx, struct_calling_on); - if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind; - - then { - let mut applicability = Applicability::MachineApplicable; - let slice_name = snippet_with_applicability( - cx, - struct_calling_on.span, "..", - &mut applicability, - ); - span_lint_and_sugg( - cx, - GET_FIRST, - expr.span, - &format!("accessing first element with `{0}.get(0)`", slice_name), - "try", - format!("{}.first()", slice_name), - applicability, - ); - } - } - } -} 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 e9501700784..4d703d691ac 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::SpanlessEq; use if_chain::if_chain; +use rustc_errors::Diagnostic; use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]); impl<'tcx> LateLintPass<'tcx> for IfLetMutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let mut arm_visit = ArmVisitor { - mutex_lock_called: false, - found_mutex: None, - cx, - }; - let mut op_visit = OppVisitor { - mutex_lock_called: false, - found_mutex: None, - cx, - }; + let mut arm_visit = ArmVisitor { found_mutex: None, cx }; + let mut op_visit = OppVisitor { found_mutex: None, cx }; if let Some(higher::IfLet { let_expr, if_then, @@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { }) = higher::IfLet::hir(cx, expr) { op_visit.visit_expr(let_expr); - if op_visit.mutex_lock_called { + if let Some(op_mutex) = op_visit.found_mutex { arm_visit.visit_expr(if_then); arm_visit.visit_expr(if_else); - if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) { - span_lint_and_help( + if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) { + let diag = |diag: &mut Diagnostic| { + diag.span_label( + op_mutex.span, + "this Mutex will remain locked for the entire `if let`-block...", + ); + diag.span_label( + arm_mutex.span, + "... and is tried to lock again here, which will always deadlock.", + ); + diag.help("move the lock call outside of the `if let ...` expression"); + }; + span_lint_and_then( cx, IF_LET_MUTEX, expr.span, "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", - None, - "move the lock call outside of the `if let ...` expression", + diag, ); } } @@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { /// Checks if `Mutex::lock` is called in the `if let` expr. pub struct OppVisitor<'a, 'tcx> { - mutex_lock_called: bool, found_mutex: Option<&'tcx Expr<'tcx>>, cx: &'a LateContext<'tcx>, } @@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { self.found_mutex = Some(mutex); - self.mutex_lock_called = true; return; } visit::walk_expr(self, expr); @@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { /// Checks if `Mutex::lock` is called in any of the branches. pub struct ArmVisitor<'a, 'tcx> { - mutex_lock_called: bool, found_mutex: Option<&'tcx Expr<'tcx>>, cx: &'a LateContext<'tcx>, } @@ -111,7 +111,6 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { self.found_mutex = Some(mutex); - self.mutex_lock_called = true; return; } visit::walk_expr(self, expr); @@ -119,9 +118,12 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { } impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { - self.found_mutex - .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)) + fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> { + self.found_mutex.and_then(|arm_mutex| { + SpanlessEq::new(self.cx) + .eq_expr(op_mutex, arm_mutex) + .then_some(arm_mutex) + }) } } @@ -129,7 +131,7 @@ fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Opt if_chain! { if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; if path.ident.as_str() == "lock"; - let ty = cx.typeck_results().expr_ty(self_arg); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if is_type_diagnostic_item(cx, ty, sym::Mutex); then { Some(self_arg) diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index b8d227855d9..11c43247868 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; -use if_chain::if_chain; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// ### What it does - /// Checks for if-else that could be written to `bool::then`. + /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`. /// /// ### Why is this bad? - /// Looks a little redundant. Using `bool::then` helps it have less lines of code. + /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity. + /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated + /// in comparison to `bool::then`. /// /// ### Example /// ```rust @@ -39,7 +41,7 @@ declare_clippy_lint! { #[clippy::version = "1.53.0"] pub IF_THEN_SOME_ELSE_NONE, restriction, - "Finds if-else that could be written using `bool::then`" + "Finds if-else that could be written using either `bool::then` or `bool::then_some`" } pub struct IfThenSomeElseNone { @@ -56,7 +58,7 @@ impl IfThenSomeElseNone { impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { return; } @@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { return; } - if_chain! { - if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr); - if let ExprKind::Block(then_block, _) = then.kind; - if let Some(then_expr) = then_block.expr; - if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; - if let ExprKind::Path(ref then_call_qpath) = then_call.kind; - if is_lang_ctor(cx, then_call_qpath, OptionSome); - if let ExprKind::Path(ref qpath) = peel_blocks(els).kind; - if is_lang_ctor(cx, qpath, OptionNone); - if !stmts_contains_early_return(then_block.stmts); - then { - let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); - let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { - format!("({})", cond_snip) - } else { - cond_snip.into_owned() - }; - let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, ""); - let closure_body = if then_block.stmts.is_empty() { - arg_snip.into_owned() - } else { - format!("{{ /* snippet */ {} }}", arg_snip) - }; - let help = format!( - "consider using `bool::then` like: `{}.then(|| {})`", - cond_snip, - closure_body, - ); - span_lint_and_help( - cx, - IF_THEN_SOME_ELSE_NONE, - expr.span, - "this could be simplified with `bool::then`", - None, - &help, - ); - } + if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr) + && let ExprKind::Block(then_block, _) = then.kind + && let Some(then_expr) = then_block.expr + && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind + && let ExprKind::Path(ref then_call_qpath) = then_call.kind + && is_lang_ctor(cx, then_call_qpath, OptionSome) + && let ExprKind::Path(ref qpath) = peel_blocks(els).kind + && is_lang_ctor(cx, qpath, OptionNone) + && !stmts_contains_early_return(then_block.stmts) + { + let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); + let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { + format!("({})", cond_snip) + } else { + cond_snip.into_owned() + }; + let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, ""); + let mut method_body = if then_block.stmts.is_empty() { + arg_snip.into_owned() + } else { + format!("{{ /* snippet */ {} }}", arg_snip) + }; + let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + "then_some" + } else { + method_body.insert_str(0, "|| "); + "then" + }; + + let help = format!( + "consider using `bool::{}` like: `{}.{}({})`", + method_name, cond_snip, method_name, method_body, + ); + span_lint_and_help( + cx, + IF_THEN_SOME_ELSE_NONE, + expr.span, + &format!("this could be simplified with `bool::{}`", method_name), + None, + &help, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs index 01082cc8eeb..134cbbf7b5c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs @@ -20,12 +20,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), + LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::FN_TO_NUMERIC_CAST), LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), @@ -80,9 +80,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(functions::RESULT_LARGE_ERR), LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_first::GET_FIRST), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(infinite_iter::INFINITE_ITER), @@ -128,7 +128,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), LintId::of(manual_retain::MANUAL_RETAIN), LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_clone::MAP_CLONE), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), @@ -150,17 +149,20 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::BYTES_COUNT_TO_LEN), LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_ON_COPY), + LintId::of(methods::COLLAPSIBLE_STR_REPLACE), LintId::of(methods::ERR_EXPECT), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_FIRST), LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), @@ -178,13 +180,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MANUAL_STR_REPEAT), + LintId::of(methods::MAP_CLONE), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), + LintId::of(methods::MUT_MUTEX_LOCK), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::OBFUSCATED_IF_ELSE), LintId::of(methods::OK_EXPECT), @@ -193,6 +198,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::OPTION_MAP_OR_NONE), LintId::of(methods::OR_FUN_CALL), LintId::of(methods::OR_THEN_UNWRAP), + LintId::of(methods::RANGE_ZIP_WITH_LEN), + LintId::of(methods::REPEAT_ONCE), LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), @@ -202,14 +209,18 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::STRING_EXTEND_CHARS), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(methods::SUSPICIOUS_SPLITN), + LintId::of(methods::SUSPICIOUS_TO_OWNED), LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::UNIT_HASH), LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FIND_MAP), LintId::of(methods::UNNECESSARY_FOLD), LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + LintId::of(methods::UNNECESSARY_SORT_BY), LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), LintId::of(methods::USELESS_ASREF), + LintId::of(methods::VEC_RESIZE_TO_ZERO), LintId::of(methods::WRONG_SELF_CONVENTION), LintId::of(methods::ZST_OFFSET), LintId::of(minmax::MIN_MAX), @@ -224,8 +235,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(misc_early::ZERO_PREFIXED_LITERAL), LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), + LintId::of(multi_assignments::MULTI_ASSIGNMENTS), LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), @@ -245,7 +256,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(operators::ABSURD_EXTREME_COMPARISONS), LintId::of(operators::ASSIGN_OP_PATTERN), LintId::of(operators::BAD_BIT_MASK), @@ -275,7 +286,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(question_mark::QUESTION_MARK), LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(ranges::REVERSED_EMPTY_RANGES), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), @@ -286,7 +296,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(reference::DEREF_ADDROF), LintId::of(regex::INVALID_REGEX), - LintId::of(repeat_once::REPEAT_ONCE), LintId::of(returns::LET_AND_RETURN), LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), @@ -314,10 +323,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::TRANSMUTING_NULL), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(transmuting_null::TRANSMUTING_NULL), LintId::of(types::BORROWED_BOX), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), @@ -325,7 +334,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(types::VEC_BOX), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unit_types::UNIT_ARG), @@ -333,9 +341,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), + LintId::of(unused_peekable::UNUSED_PEEKABLE), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(unwrap::PANICKING_UNWRAP), LintId::of(unwrap::UNNECESSARY_UNWRAP), @@ -343,7 +351,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(useless_conversion::USELESS_CONVERSION), LintId::of(vec::USELESS_VEC), LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), + LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS), LintId::of(write::PRINTLN_EMPTY_STRING), LintId::of(write::PRINT_LITERAL), LintId::of(write::PRINT_WITH_NEWLINE), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs index 3784d3c68dc..aa247352f88 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs @@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(dereference::EXPLICIT_AUTO_DEREF), @@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::BYTES_COUNT_TO_LEN), LintId::of(methods::CLONE_ON_COPY), LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), @@ -51,10 +51,13 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::OR_THEN_UNWRAP), + LintId::of(methods::RANGE_ZIP_WITH_LEN), + LintId::of(methods::REPEAT_ONCE), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FIND_MAP), + LintId::of(methods::UNNECESSARY_SORT_BY), LintId::of(methods::USELESS_ASREF), LintId::of(misc::SHORT_CIRCUIT_STATEMENT), LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), @@ -69,6 +72,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::UNNECESSARY_OPERATION), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(operators::DOUBLE_COMPARISONS), LintId::of(operators::DURATION_SUBSEC), LintId::of(operators::IDENTITY_OP), @@ -76,11 +80,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(precedence::PRECEDENCE), LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_slicing::REDUNDANT_SLICING), LintId::of(reference::DEREF_ADDROF), - LintId::of(repeat_once::REPEAT_ONCE), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(swap::MANUAL_SWAP), @@ -99,7 +101,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), LintId::of(unit_types::UNIT_ARG), - LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unwrap::UNNECESSARY_UNWRAP), LintId::of(useless_conversion::USELESS_CONVERSION), LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs index 006275d1383..bb94037ec2e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs @@ -39,12 +39,14 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), + LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), LintId::of(methods::SUSPICIOUS_SPLITN), LintId::of(methods::UNINIT_ASSUMED_INIT), + LintId::of(methods::UNIT_HASH), + LintId::of(methods::VEC_RESIZE_TO_ZERO), LintId::of(methods::ZST_OFFSET), LintId::of(minmax::MIN_MAX), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(operators::ABSURD_EXTREME_COMPARISONS), LintId::of(operators::BAD_BIT_MASK), LintId::of(operators::CMP_NAN), @@ -62,17 +64,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(swap::ALMOST_SWAPPED), + LintId::of(transmute::TRANSMUTING_NULL), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(transmuting_null::TRANSMUTING_NULL), LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(unwrap::PANICKING_UNWRAP), - LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO), ]) diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs index c540573b802..fd20e016578 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs @@ -38,7 +38,6 @@ store.register_lints(&[ almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, as_conversions::AS_CONVERSIONS, - as_underscore::AS_UNDERSCORE, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, assertions_on_constants::ASSERTIONS_ON_CONSTANTS, @@ -59,16 +58,14 @@ store.register_lints(&[ bool_assert_comparison::BOOL_ASSERT_COMPARISON, booleans::NONMINIMAL_BOOL, booleans::OVERLY_COMPLEX_BOOL_EXPR, - borrow_as_ptr::BORROW_AS_PTR, borrow_deref_ref::BORROW_DEREF_REF, - bytecount::NAIVE_BYTECOUNT, - bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES, cargo::WILDCARD_DEPENDENCIES, - case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + casts::AS_UNDERSCORE, + casts::BORROW_AS_PTR, casts::CAST_ABS_TO_UNSIGNED, casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_TRUNCATION, @@ -80,6 +77,7 @@ store.register_lints(&[ casts::CAST_REF_TO_MUT, casts::CAST_SIGN_LOSS, casts::CAST_SLICE_DIFFERENT_SIZES, + casts::CAST_SLICE_FROM_RAW_PARTS, casts::CHAR_LIT_AS_U8, casts::FN_TO_NUMERIC_CAST, casts::FN_TO_NUMERIC_CAST_ANY, @@ -173,11 +171,11 @@ store.register_lints(&[ functions::MUST_USE_CANDIDATE, functions::MUST_USE_UNIT, functions::NOT_UNSAFE_PTR_ARG_DEREF, + functions::RESULT_LARGE_ERR, functions::RESULT_UNIT_ERR, functions::TOO_MANY_ARGUMENTS, functions::TOO_MANY_LINES, future_not_send::FUTURE_NOT_SEND, - get_first::GET_FIRST, if_let_mutex::IF_LET_MUTEX, if_not_else::IF_NOT_ELSE, if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, @@ -246,12 +244,10 @@ store.register_lints(&[ manual_bits::MANUAL_BITS, manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - manual_ok_or::MANUAL_OK_OR, manual_rem_euclid::MANUAL_REM_EUCLID, manual_retain::MANUAL_RETAIN, + manual_string_new::MANUAL_STRING_NEW, manual_strip::MANUAL_STRIP, - map_clone::MAP_CLONE, - map_err_ignore::MAP_ERR_IGNORE, map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN, match_result_ok::MATCH_RESULT_OK, @@ -284,13 +280,16 @@ store.register_lints(&[ mem_replace::MEM_REPLACE_WITH_DEFAULT, mem_replace::MEM_REPLACE_WITH_UNINIT, methods::BIND_INSTEAD_OF_MAP, + methods::BYTES_COUNT_TO_LEN, methods::BYTES_NTH, + methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, methods::CHARS_LAST_CMP, methods::CHARS_NEXT_CMP, methods::CLONED_INSTEAD_OF_COPIED, methods::CLONE_DOUBLE_REF, methods::CLONE_ON_COPY, methods::CLONE_ON_REF_PTR, + methods::COLLAPSIBLE_STR_REPLACE, methods::ERR_EXPECT, methods::EXPECT_FUN_CALL, methods::EXPECT_USED, @@ -302,6 +301,7 @@ store.register_lints(&[ methods::FLAT_MAP_IDENTITY, methods::FLAT_MAP_OPTION, methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_FIRST, methods::GET_LAST_WITH_LEN, methods::GET_UNWRAP, methods::IMPLICIT_CLONE, @@ -315,22 +315,30 @@ store.register_lints(&[ methods::ITER_NEXT_SLICE, methods::ITER_NTH, methods::ITER_NTH_ZERO, + methods::ITER_ON_EMPTY_COLLECTIONS, + methods::ITER_ON_SINGLE_ITEMS, methods::ITER_OVEREAGER_CLONED, methods::ITER_SKIP_NEXT, methods::ITER_WITH_DRAIN, methods::MANUAL_FILTER_MAP, methods::MANUAL_FIND_MAP, + methods::MANUAL_OK_OR, methods::MANUAL_SATURATING_ARITHMETIC, methods::MANUAL_SPLIT_ONCE, methods::MANUAL_STR_REPEAT, + methods::MAP_CLONE, methods::MAP_COLLECT_RESULT_UNIT, + methods::MAP_ERR_IGNORE, methods::MAP_FLATTEN, methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, + methods::MUT_MUTEX_LOCK, + methods::NAIVE_BYTECOUNT, methods::NEEDLESS_OPTION_AS_DEREF, methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, + methods::NONSENSICAL_OPEN_OPTIONS, methods::NO_EFFECT_REPLACE, methods::OBFUSCATED_IF_ELSE, methods::OK_EXPECT, @@ -339,25 +347,34 @@ store.register_lints(&[ methods::OPTION_MAP_OR_NONE, methods::OR_FUN_CALL, methods::OR_THEN_UNWRAP, + methods::PATH_BUF_PUSH_OVERWRITE, + methods::RANGE_ZIP_WITH_LEN, + methods::REPEAT_ONCE, methods::RESULT_MAP_OR_INTO_OPTION, methods::SEARCH_IS_SOME, methods::SHOULD_IMPLEMENT_TRAIT, methods::SINGLE_CHAR_ADD_STR, methods::SINGLE_CHAR_PATTERN, methods::SKIP_WHILE_NEXT, + methods::STABLE_SORT_PRIMITIVE, methods::STRING_EXTEND_CHARS, methods::SUSPICIOUS_MAP, methods::SUSPICIOUS_SPLITN, + methods::SUSPICIOUS_TO_OWNED, methods::UNINIT_ASSUMED_INIT, + methods::UNIT_HASH, methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FIND_MAP, methods::UNNECESSARY_FOLD, methods::UNNECESSARY_JOIN, methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNNECESSARY_SORT_BY, methods::UNNECESSARY_TO_OWNED, methods::UNWRAP_OR_ELSE_DEFAULT, methods::UNWRAP_USED, methods::USELESS_ASREF, + methods::VEC_RESIZE_TO_ZERO, + methods::VERBOSE_FILE_READS, methods::WRONG_SELF_CONVENTION, methods::ZST_OFFSET, minmax::MIN_MAX, @@ -384,9 +401,9 @@ store.register_lints(&[ mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, module_style::MOD_MODULE_FILES, module_style::SELF_NAMED_MODULE_FILES, + multi_assignments::MULTI_ASSIGNMENTS, mut_key::MUTABLE_KEY_TYPE, mut_mut::MUT_MUT, - mut_mutex_lock::MUT_MUTEX_LOCK, mut_reference::UNNECESSARY_MUT_PASSED, mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, mutex_atomic::MUTEX_ATOMIC, @@ -418,7 +435,6 @@ store.register_lints(&[ nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, octal_escapes::OCTAL_ESCAPES, only_used_in_recursion::ONLY_USED_IN_RECURSION, - open_options::NONSENSICAL_OPEN_OPTIONS, operators::ABSURD_EXTREME_COMPARISONS, operators::ARITHMETIC, operators::ASSIGN_OP_PATTERN, @@ -457,7 +473,6 @@ store.register_lints(&[ partialeq_to_none::PARTIALEQ_TO_NONE, pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, - path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, pattern_type_mismatch::PATTERN_TYPE_MISMATCH, precedence::PRECEDENCE, ptr::CMP_NULL, @@ -470,7 +485,6 @@ store.register_lints(&[ ranges::MANUAL_RANGE_CONTAINS, ranges::RANGE_MINUS_ONE, ranges::RANGE_PLUS_ONE, - ranges::RANGE_ZIP_WITH_LEN, ranges::REVERSED_EMPTY_RANGES, rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, read_zero_byte_vec::READ_ZERO_BYTE_VEC, @@ -486,7 +500,6 @@ store.register_lints(&[ reference::DEREF_ADDROF, regex::INVALID_REGEX, regex::TRIVIAL_REGEX, - repeat_once::REPEAT_ONCE, return_self_not_must_use::RETURN_SELF_NOT_MUST_USE, returns::LET_AND_RETURN, returns::NEEDLESS_RETURN, @@ -501,7 +514,6 @@ store.register_lints(&[ single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - stable_sort_primitive::STABLE_SORT_PRIMITIVE, std_instead_of_core::ALLOC_INSTEAD_OF_CORE, std_instead_of_core::STD_INSTEAD_OF_ALLOC, std_instead_of_core::STD_INSTEAD_OF_CORE, @@ -537,10 +549,10 @@ store.register_lints(&[ transmute::TRANSMUTE_PTR_TO_PTR, transmute::TRANSMUTE_PTR_TO_REF, transmute::TRANSMUTE_UNDEFINED_REPR, + transmute::TRANSMUTING_NULL, transmute::UNSOUND_COLLECTION_TRANSMUTE, transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, - transmuting_null::TRANSMUTING_NULL, types::BORROWED_BOX, types::BOX_COLLECTION, types::LINKEDLIST, @@ -555,7 +567,6 @@ store.register_lints(&[ unicode::NON_ASCII_LITERAL, unicode::UNICODE_NOT_NFC, uninit_vec::UNINIT_VEC, - unit_hash::UNIT_HASH, unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, unit_types::LET_UNIT_VALUE, unit_types::UNIT_ARG, @@ -564,12 +575,12 @@ store.register_lints(&[ unnamed_address::VTABLE_ADDRESS_COMPARISONS, unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, - unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, unnested_or_patterns::UNNESTED_OR_PATTERNS, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, + unused_peekable::UNUSED_PEEKABLE, unused_rounding::UNUSED_ROUNDING, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, @@ -581,10 +592,9 @@ store.register_lints(&[ useless_conversion::USELESS_CONVERSION, vec::USELESS_VEC, vec_init_then_push::VEC_INIT_THEN_PUSH, - vec_resize_to_zero::VEC_RESIZE_TO_ZERO, - verbose_file_reads::VERBOSE_FILE_READS, wildcard_imports::ENUM_GLOB_USE, wildcard_imports::WILDCARD_IMPORTS, + write::POSITIONAL_NAMED_FORMAT_PARAMETERS, write::PRINTLN_EMPTY_STRING, write::PRINT_LITERAL, write::PRINT_STDERR, diff --git a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs index 91210b23afe..e319e7ee72c 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_nursery.rs @@ -14,16 +14,17 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE), LintId::of(let_if_seq::USELESS_LET_IF_SEQ), LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), + LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS), + LintId::of(methods::ITER_ON_SINGLE_ITEMS), LintId::of(methods::ITER_WITH_DRAIN), + LintId::of(methods::PATH_BUF_PUSH_OVERWRITE), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), - LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), LintId::of(strings::STRING_LIT_AS_BYTES), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs index bd7d1a15ab4..584ccf55e51 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs @@ -4,9 +4,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(attrs::INLINE_ALWAYS), - LintId::of(borrow_as_ptr::BORROW_AS_PTR), - LintId::of(bytecount::NAIVE_BYTECOUNT), - LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), + LintId::of(casts::BORROW_AS_PTR), LintId::of(casts::CAST_LOSSLESS), LintId::of(casts::CAST_POSSIBLE_TRUNCATION), LintId::of(casts::CAST_POSSIBLE_WRAP), @@ -50,20 +48,24 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(macro_use::MACRO_USE_IMPORTS), LintId::of(manual_assert::MANUAL_ASSERT), LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED), - LintId::of(manual_ok_or::MANUAL_OK_OR), + LintId::of(manual_string_new::MANUAL_STRING_NEW), LintId::of(matches::MATCH_BOOL), LintId::of(matches::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_SAME_ARMS), LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), LintId::of(matches::SINGLE_MATCH_ELSE), + LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(methods::CLONED_INSTEAD_OF_COPIED), LintId::of(methods::FILTER_MAP_NEXT), LintId::of(methods::FLAT_MAP_OPTION), LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), + LintId::of(methods::MANUAL_OK_OR), LintId::of(methods::MAP_UNWRAP_OR), + LintId::of(methods::NAIVE_BYTECOUNT), + LintId::of(methods::STABLE_SORT_PRIMITIVE), LintId::of(methods::UNNECESSARY_JOIN), LintId::of(misc::USED_UNDERSCORE_BINDING), LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), @@ -85,7 +87,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(ref_option_ref::REF_OPTION_REF), LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs index e1b90acb93c..195ce41e31e 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs @@ -7,12 +7,14 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(functions::RESULT_LARGE_ERR), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::MISSING_SPIN_LOOP), LintId::of(loops::NEEDLESS_COLLECT), LintId::of(manual_retain::MANUAL_RETAIN), + LintId::of(methods::COLLAPSIBLE_STR_REPLACE), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::ITER_NTH), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs index a7339ef2721..dd1e1e1a8e3 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs @@ -4,11 +4,11 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(as_conversions::AS_CONVERSIONS), - LintId::of(as_underscore::AS_UNDERSCORE), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), + LintId::of(casts::AS_UNDERSCORE), LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), LintId::of(create_dir::CREATE_DIR), LintId::of(dbg_macro::DBG_MACRO), @@ -30,7 +30,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(large_include_file::LARGE_INCLUDE_FILE), LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(map_err_ignore::MAP_ERR_IGNORE), LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), LintId::of(matches::TRY_ERR), LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), @@ -39,7 +38,9 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(methods::EXPECT_USED), LintId::of(methods::FILETYPE_IS_FILE), LintId::of(methods::GET_UNWRAP), + LintId::of(methods::MAP_ERR_IGNORE), LintId::of(methods::UNWRAP_USED), + LintId::of(methods::VERBOSE_FILE_READS), LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX), LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), @@ -81,7 +82,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(verbose_file_reads::VERBOSE_FILE_READS), LintId::of(write::PRINT_STDERR), LintId::of(write::PRINT_STDOUT), LintId::of(write::USE_DEBUG), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs index bfa654238f1..b5cb078e7a3 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs @@ -29,7 +29,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(get_first::GET_FIRST), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), @@ -45,7 +44,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(map_clone::MAP_CLONE), LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), @@ -61,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::ERR_EXPECT), + LintId::of(methods::GET_FIRST), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITER_CLONED_COLLECT), @@ -68,7 +67,9 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::ITER_NTH_ZERO), LintId::of(methods::ITER_SKIP_NEXT), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MAP_CLONE), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), + LintId::of(methods::MUT_MUTEX_LOCK), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OBFUSCATED_IF_ELSE), LintId::of(methods::OK_EXPECT), @@ -88,7 +89,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs index 964992bd94f..8f131bbf98b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs +++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs @@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), + LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::FORGET_NON_DROP), @@ -24,6 +25,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::MUT_RANGE_BOUND), LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), + LintId::of(methods::SUSPICIOUS_TO_OWNED), + LintId::of(multi_assignments::MULTI_ASSIGNMENTS), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), @@ -32,4 +35,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), + LintId::of(unused_peekable::UNUSED_PEEKABLE), + LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS), ]) diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index ec5c73c1357..dfdaf90f09f 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -171,7 +171,6 @@ mod renamed_lints; mod almost_complete_letter_range; mod approx_const; mod as_conversions; -mod as_underscore; mod asm_syntax; mod assertions_on_constants; mod assertions_on_result_states; @@ -181,12 +180,8 @@ mod await_holding_invalid; mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; -mod borrow_as_ptr; mod borrow_deref_ref; -mod bytecount; -mod bytes_count_to_len; mod cargo; -mod case_sensitive_file_extension_comparisons; mod casts; mod checked_conversions; mod cognitive_complexity; @@ -239,7 +234,6 @@ mod from_over_into; mod from_str_radix_10; mod functions; mod future_not_send; -mod get_first; mod if_let_mutex; mod if_not_else; mod if_then_some_else_none; @@ -276,12 +270,10 @@ mod manual_async_fn; mod manual_bits; mod manual_instant_elapsed; mod manual_non_exhaustive; -mod manual_ok_or; mod manual_rem_euclid; mod manual_retain; +mod manual_string_new; mod manual_strip; -mod map_clone; -mod map_err_ignore; mod map_unit_fn; mod match_result_ok; mod matches; @@ -298,9 +290,9 @@ mod missing_enforced_import_rename; mod missing_inline; mod mixed_read_write_in_expression; mod module_style; +mod multi_assignments; mod mut_key; mod mut_mut; -mod mut_mutex_lock; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; @@ -325,7 +317,6 @@ mod non_send_fields_in_send_ty; mod nonstandard_macro_braces; mod octal_escapes; mod only_used_in_recursion; -mod open_options; mod operators; mod option_env_unwrap; mod option_if_let_else; @@ -335,7 +326,6 @@ mod panic_unimplemented; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; -mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; mod ptr; @@ -355,7 +345,6 @@ mod redundant_static_lifetimes; mod ref_option_ref; mod reference; mod regex; -mod repeat_once; mod return_self_not_must_use; mod returns; mod same_name_method; @@ -367,7 +356,6 @@ mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; mod slow_vector_initialization; -mod stable_sort_primitive; mod std_instead_of_core; mod strings; mod strlen_on_c_strings; @@ -381,23 +369,21 @@ mod to_digit_is_some; mod trailing_empty_array; mod trait_bounds; mod transmute; -mod transmuting_null; mod types; mod undocumented_unsafe_blocks; mod unicode; mod uninit_vec; -mod unit_hash; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; -mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; +mod unused_peekable; mod unused_rounding; mod unused_self; mod unused_unit; @@ -408,8 +394,6 @@ mod use_self; mod useless_conversion; mod vec; mod vec_init_then_push; -mod vec_resize_to_zero; -mod verbose_file_reads; mod wildcard_imports; mod write; mod zero_div_zero; @@ -597,7 +581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); store.register_late_pass(|| Box::new(unicode::Unicode)); store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); - store.register_late_pass(|| Box::new(unit_hash::UnitHash)); store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); store.register_late_pass(|| Box::new(strings::StringAdd)); store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn)); @@ -635,8 +618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark)); store.register_late_pass(move || Box::new(casts::Casts::new(msrv))); store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); - store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv))); - store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|| Box::new(same_name_method::SameNameMethod)); let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; @@ -646,7 +627,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: msrv, )) }); - store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore)); store.register_late_pass(|| Box::new(shadow::Shadow::default())); store.register_late_pass(|| Box::new(unit_types::UnitTypes)); store.register_late_pass(|| Box::new(loops::Loops)); @@ -654,7 +634,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|| Box::new(entry::HashMapPass)); store.register_late_pass(|| Box::new(minmax::MinMaxPass)); - store.register_late_pass(|| Box::new(open_options::OpenOptions)); store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv)); store.register_late_pass(|| Box::new(mutex_atomic::Mutex)); store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate)); @@ -690,10 +669,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone()))); let too_many_arguments_threshold = conf.too_many_arguments_threshold; let too_many_lines_threshold = conf.too_many_lines_threshold; + let large_error_threshold = conf.large_error_threshold; store.register_late_pass(move || { Box::new(functions::Functions::new( too_many_arguments_threshold, too_many_lines_threshold, + large_error_threshold, )) }); let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); @@ -720,7 +701,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_late_pass(move || Box::new(pass_by_ref_or_value)); store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef)); - store.register_late_pass(|| Box::new(bytecount::ByteCount)); store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody)); store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default())); @@ -738,12 +718,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit)); - store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy)); store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates)); - store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull)); - store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite)); store.register_late_pass(|| Box::new(inherent_to_string::InherentToString)); let max_trait_bounds = conf.max_trait_bounds; store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); @@ -819,18 +796,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); - store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads)); store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default())); store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(|| Box::new(dereference::Dereferencing::default())); + store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv))); store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex)); store.register_late_pass(|| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality)); - store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock)); store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn)); - store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero)); store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn)); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || { @@ -842,10 +816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher))); store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default())); store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch)); - store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive)); - store.register_late_pass(|| Box::new(repeat_once::RepeatOnce)); store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult)); - store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr)); store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync)); let disallowed_methods = conf.disallowed_methods.clone(); @@ -857,9 +828,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default())); - store.register_late_pass(|| { - Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons) - }); store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); @@ -894,11 +862,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); - store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); + store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default())); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; @@ -912,18 +879,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); - store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); - store.register_late_pass(|| Box::new(get_first::GetFirst)); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); - store.register_late_pass(|| Box::new(as_underscore::AsUnderscore)); store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv))); @@ -934,6 +898,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default())); store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed)); store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone)); + store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew)); + store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable)); + store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs index ddaffc75188..6d987f393fa 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_collect.rs @@ -1,5 +1,6 @@ use super::NEEDLESS_COLLECT; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; +use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; @@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { fn visit_block(&mut self, block: &'tcx Block<'tcx>) { for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) { + if check_loop_kind(expr).is_some() { + continue; + } self.visit_block_expr(expr, hir_id); } if let Some(expr) = block.expr { - self.visit_block_expr(expr, None); + if let Some(loop_kind) = check_loop_kind(expr) { + if let LoopKind::Conditional(block_expr) = loop_kind { + self.visit_block_expr(block_expr, None); + } + } else { + self.visit_block_expr(expr, None); + } } } @@ -264,6 +274,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { } } +enum LoopKind<'tcx> { + Conditional(&'tcx Expr<'tcx>), + Loop, +} + +fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> { + if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) { + return Some(LoopKind::Conditional(let_expr)); + } + if let Some(higher::While { condition, .. }) = higher::While::hir(expr) { + return Some(LoopKind::Conditional(condition)); + } + if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) { + return Some(LoopKind::Conditional(arg)); + } + if let ExprKind::Loop { .. } = expr.kind { + return Some(LoopKind::Loop); + } + + None +} + impl<'tcx> IterFunctionVisitor<'_, 'tcx> { fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) { self.current_statement_hir_id = hir_id; diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index a0ca7e6ff1e..2502c8f880d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, match output.kind { TyKind::Tup(tys) if tys.is_empty() => { let sugg = "remove the return type"; - Some((sugg, "".into())) + Some((sugg, String::new())) }, _ => { let sugg = "return the output of the future directly"; diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs deleted file mode 100644 index cf5004399b8..00000000000 --- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs +++ /dev/null @@ -1,95 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_lang_ctor, path_to_local_id}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{ResultErr, ResultOk}; -use rustc_hir::{Closure, Expr, ExprKind, PatKind}; -use rustc_lint::LintContext; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; - -declare_clippy_lint! { - /// ### What it does - /// - /// Finds patterns that reimplement `Option::ok_or`. - /// - /// ### Why is this bad? - /// - /// Concise code helps focusing on behavior instead of boilerplate. - /// - /// ### Examples - /// ```rust - /// let foo: Option<i32> = None; - /// foo.map_or(Err("error"), |v| Ok(v)); - /// ``` - /// - /// Use instead: - /// ```rust - /// let foo: Option<i32> = None; - /// foo.ok_or("error"); - /// ``` - #[clippy::version = "1.49.0"] - pub MANUAL_OK_OR, - pedantic, - "finds patterns that can be encoded more concisely with `Option::ok_or`" -} - -declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); - -impl<'tcx> LateLintPass<'tcx> for ManualOkOr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), scrutinee.span) { - return; - } - - if_chain! { - if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind; - if method_segment.ident.name == sym!(map_or); - let ty = cx.typeck_results().expr_ty(receiver); - if is_type_diagnostic_item(cx, ty, sym::Option); - if is_ok_wrapping(cx, map_expr); - if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; - if is_lang_ctor(cx, err_path, ResultErr); - if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span); - if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); - if let Some(indent) = indent_of(cx, scrutinee.span); - then { - let reindented_err_arg_snippet = - reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); - span_lint_and_sugg( - cx, - MANUAL_OK_OR, - scrutinee.span, - "this pattern reimplements `Option::ok_or`", - "replace with", - format!( - "{}.ok_or({})", - method_receiver_snippet, - reindented_err_arg_snippet - ), - Applicability::MachineApplicable, - ); - } - } - } -} - -fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { - if let ExprKind::Path(ref qpath) = map_expr.kind { - if is_lang_ctor(cx, qpath, ResultOk) { - return true; - } - } - if_chain! { - if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind; - let body = cx.tcx.hir().body(body); - if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; - if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; - if is_lang_ctor(cx, ok_path, ResultOk); - then { path_to_local_id(ok_arg, param_id) } else { false } - } -} diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs new file mode 100644 index 00000000000..a90eaa8fdcb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -0,0 +1,140 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::LitKind; +use rustc_errors::Applicability::MachineApplicable; +use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, symbol, Span}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`, + /// `String::from("")` and others. + /// + /// ### Why is this bad? + /// + /// Different ways of creating an empty string makes your code less standardized, which can + /// be confusing. + /// + /// ### Example + /// ```rust + /// let a = "".to_string(); + /// let b: String = "".into(); + /// ``` + /// Use instead: + /// ```rust + /// let a = String::new(); + /// let b = String::new(); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_STRING_NEW, + pedantic, + "empty String is being created manually" +} +declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]); + +impl LateLintPass<'_> for ManualStringNew { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + let ty = cx.typeck_results().expr_ty(expr); + match ty.kind() { + ty::Adt(adt_def, _) if adt_def.is_struct() => { + if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) { + return; + } + }, + _ => return, + } + + match expr.kind { + ExprKind::Call(func, args) => { + parse_call(cx, expr.span, func, args); + }, + ExprKind::MethodCall(path_segment, args, _) => { + parse_method_call(cx, expr.span, path_segment, args); + }, + _ => (), + } + } +} + +/// Checks if an expression's kind corresponds to an empty &str. +fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool { + if let ExprKind::Lit(lit) = expr_kind && + let LitKind::Str(value, _) = lit.node && + value == symbol::kw::Empty + { + return true; + } + + false +} + +fn warn_then_suggest(cx: &LateContext<'_>, span: Span) { + span_lint_and_sugg( + cx, + MANUAL_STRING_NEW, + span, + "empty String is being created manually", + "consider using", + "String::new()".into(), + MachineApplicable, + ); +} + +/// Tries to parse an expression as a method call, emitting the warning if necessary. +fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) { + if args.is_empty() { + // When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg. + return; + } + + let ident = path_segment.ident.as_str(); + let method_arg_kind = &args[0].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 { + // 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); + } +} + +/// 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; + if let ExprKind::Path(qpath) = &func.kind { + if let QPath::TypeRelative(_, _) = qpath { + // String::from(...) or String::try_from(...) + if let QPath::TypeRelative(ty, path_seg) = qpath && + [sym::from, sym::try_from].contains(&path_seg.ident.name) && + let TyKind::Path(qpath) = &ty.kind && + let QPath::Resolved(_, path) = qpath && + let [path_seg] = path.segments && + path_seg.ident.name == sym::String && + 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) && ( + (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) + ) + { + warn_then_suggest(cx, span); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/map_clone.rs b/src/tools/clippy/clippy_lints/src/map_clone.rs deleted file mode 100644 index 95c312f1fe2..00000000000 --- a/src/tools/clippy/clippy_lints/src/map_clone.rs +++ /dev/null @@ -1,167 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::Mutability; -use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; -use rustc_semver::RustcVersion; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Ident; -use rustc_span::{sym, Span}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of `map(|x| x.clone())` or - /// dereferencing closures for `Copy` types, on `Iterator` or `Option`, - /// and suggests `cloned()` or `copied()` instead - /// - /// ### Why is this bad? - /// Readability, this can be written more concisely - /// - /// ### Example - /// ```rust - /// let x = vec![42, 43]; - /// let y = x.iter(); - /// let z = y.map(|i| *i); - /// ``` - /// - /// The correct use would be: - /// - /// ```rust - /// let x = vec![42, 43]; - /// let y = x.iter(); - /// let z = y.cloned(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub MAP_CLONE, - style, - "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types" -} - -pub struct MapClone { - msrv: Option<RustcVersion>, -} - -impl_lint_pass!(MapClone => [MAP_CLONE]); - -impl MapClone { - pub fn new(msrv: Option<RustcVersion>) -> Self { - Self { msrv } - } -} - -impl<'tcx> LateLintPass<'tcx> for MapClone { - fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { - if e.span.from_expansion() { - return; - } - - if_chain! { - if let hir::ExprKind::MethodCall(method, args, _) = e.kind; - if args.len() == 2; - if method.ident.name == sym::map; - let ty = cx.typeck_results().expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator); - if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind; - then { - let closure_body = cx.tcx.hir().body(body); - let closure_expr = peel_blocks(&closure_body.value); - match closure_body.params[0].pat.kind { - hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( - hir::BindingAnnotation::Unannotated, .., name, None - ) = inner.kind { - if ident_eq(name, closure_expr) { - self.lint_explicit_closure(cx, e.span, args[0].span, true); - } - }, - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { - match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { - if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { - self.lint_explicit_closure(cx, e.span, args[0].span, true); - } - } - }, - hir::ExprKind::MethodCall(method, [obj], _) => if_chain! { - if ident_eq(name, obj) && method.ident.name == sym::clone; - if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); - if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); - if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id); - // no autoderefs - if !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); - then { - let obj_ty = cx.typeck_results().expr_ty(obj); - if let ty::Ref(_, ty, mutability) = obj_ty.kind() { - if matches!(mutability, Mutability::Not) { - let copy = is_copy(cx, *ty); - self.lint_explicit_closure(cx, e.span, args[0].span, copy); - } - } else { - lint_needless_cloning(cx, e.span, args[0].span); - } - } - }, - _ => {}, - } - }, - _ => {}, - } - } - } - } - - extract_msrv_attr!(LateContext); -} - -fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind { - path.segments.len() == 1 && path.segments[0].ident == name - } else { - false - } -} - -fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { - span_lint_and_sugg( - cx, - MAP_CLONE, - root.trim_start(receiver).unwrap(), - "you are needlessly cloning iterator elements", - "remove the `map` call", - String::new(), - Applicability::MachineApplicable, - ); -} - -impl MapClone { - fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { - let mut applicability = Applicability::MachineApplicable; - - let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) { - ("you are using an explicit closure for copying elements", "copied") - } else { - ("you are using an explicit closure for cloning elements", "cloned") - }; - - span_lint_and_sugg( - cx, - MAP_CLONE, - replace, - message, - &format!("consider calling the dedicated `{}` method", sugg_method), - format!( - "{}.{}()", - snippet_with_applicability(cx, root, "..", &mut applicability), - sugg_method, - ), - applicability, - ); - } -} diff --git a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/map_err_ignore.rs deleted file mode 100644 index 1e542447c96..00000000000 --- a/src/tools/clippy/clippy_lints/src/map_err_ignore.rs +++ /dev/null @@ -1,154 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for instances of `map_err(|_| Some::Enum)` - /// - /// ### Why is this bad? - /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error - /// - /// ### Example - /// Before: - /// ```rust - /// use std::fmt; - /// - /// #[derive(Debug)] - /// enum Error { - /// Indivisible, - /// Remainder(u8), - /// } - /// - /// impl fmt::Display for Error { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// match self { - /// Error::Indivisible => write!(f, "could not divide input by three"), - /// Error::Remainder(remainder) => write!( - /// f, - /// "input is not divisible by three, remainder = {}", - /// remainder - /// ), - /// } - /// } - /// } - /// - /// impl std::error::Error for Error {} - /// - /// fn divisible_by_3(input: &str) -> Result<(), Error> { - /// input - /// .parse::<i32>() - /// .map_err(|_| Error::Indivisible) - /// .map(|v| v % 3) - /// .and_then(|remainder| { - /// if remainder == 0 { - /// Ok(()) - /// } else { - /// Err(Error::Remainder(remainder as u8)) - /// } - /// }) - /// } - /// ``` - /// - /// After: - /// ```rust - /// use std::{fmt, num::ParseIntError}; - /// - /// #[derive(Debug)] - /// enum Error { - /// Indivisible(ParseIntError), - /// Remainder(u8), - /// } - /// - /// impl fmt::Display for Error { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// match self { - /// Error::Indivisible(_) => write!(f, "could not divide input by three"), - /// Error::Remainder(remainder) => write!( - /// f, - /// "input is not divisible by three, remainder = {}", - /// remainder - /// ), - /// } - /// } - /// } - /// - /// impl std::error::Error for Error { - /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - /// match self { - /// Error::Indivisible(source) => Some(source), - /// _ => None, - /// } - /// } - /// } - /// - /// fn divisible_by_3(input: &str) -> Result<(), Error> { - /// input - /// .parse::<i32>() - /// .map_err(Error::Indivisible) - /// .map(|v| v % 3) - /// .and_then(|remainder| { - /// if remainder == 0 { - /// Ok(()) - /// } else { - /// Err(Error::Remainder(remainder as u8)) - /// } - /// }) - /// } - /// ``` - #[clippy::version = "1.48.0"] - pub MAP_ERR_IGNORE, - restriction, - "`map_err` should not ignore the original error" -} - -declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]); - -impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { - // do not try to lint if this is from a macro or desugaring - fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { - if e.span.from_expansion() { - return; - } - - // check if this is a method call (e.g. x.foo()) - if let ExprKind::MethodCall(method, [_, arg], _) = e.kind { - // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] - // Enum::Variant[2])) - if method.ident.name == sym!(map_err) { - // make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span - // fields - if let ExprKind::Closure(&Closure { - capture_clause, - body, - fn_decl_span, - .. - }) = arg.kind - { - // check if this is by Reference (meaning there's no move statement) - if capture_clause == CaptureBy::Ref { - // Get the closure body to check the parameters and values - let closure_body = cx.tcx.hir().body(body); - // make sure there's only one parameter (`|_|`) - if closure_body.params.len() == 1 { - // make sure that parameter is the wild token (`_`) - if let PatKind::Wild = closure_body.params[0].pat.kind { - // span the area of the closure capture and warn that the - // original error will be thrown away - span_lint_and_help( - cx, - MAP_ERR_IGNORE, - fn_decl_span, - "`map_err(|_|...` wildcard pattern discards the original error", - None, - "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", - ); - } - } - } - } - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 0da4833f1df..34cc082687e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::span_contains_comment; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; @@ -76,6 +77,7 @@ where >, { if_chain! { + if !span_contains_comment(cx.sess().source_map(), expr.span); if iter.len() >= 2; if cx.typeck_results().expr_ty(expr).is_bool(); if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back(); diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index fa19cddd35e..6f037339ec7 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::sym; use rustc_typeck::hir_ty_to_ty; @@ -65,8 +65,26 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool { for arm in arms { let arm_expr = peel_blocks_with_stmt(arm.body); + + if let Some(guard_expr) = &arm.guard { + match guard_expr { + // gives up if `pat if expr` can have side effects + Guard::If(if_cond) => { + if if_cond.can_have_side_effects() { + return false; + } + }, + // gives up `pat if let ...` arm + Guard::IfLet(_) => { + return false; + }, + }; + } + if let PatKind::Wild = arm.pat.kind { - return eq_expr_value(cx, match_expr, strip_return(arm_expr)); + if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) { + return false; + } } else if !pat_same_as_expr(arm.pat, arm_expr) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs new file mode 100644 index 00000000000..6a7c63d76f7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs @@ -0,0 +1,70 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::match_type; +use clippy_utils::visitors::is_local_used; +use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, UintTy}; +use rustc_span::sym; + +use super::NAIVE_BYTECOUNT; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + filter_recv: &'tcx Expr<'_>, + filter_arg: &'tcx Expr<'_>, +) { + if_chain! { + if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind; + let body = cx.tcx.hir().body(body); + if let [param] = body.params; + if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind; + if let ExprKind::Binary(ref op, l, r) = body.value.kind; + if op.node == BinOpKind::Eq; + if match_type(cx, + cx.typeck_results().expr_ty(filter_recv).peel_refs(), + &paths::SLICE_ITER); + let operand_is_arg = |expr| { + let expr = peel_ref_operators(cx, peel_blocks(expr)); + path_to_local_id(expr, arg_id) + }; + let needle = if operand_is_arg(l) { + r + } else if operand_is_arg(r) { + l + } else { + return; + }; + if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); + if !is_local_used(cx, needle, arg_id); + then { + let haystack = if let ExprKind::MethodCall(path, args, _) = + filter_recv.kind { + let p = path.ident.name; + if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { + &args[0] + } else { + filter_recv + } + } else { + filter_recv + }; + let mut applicability = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + NAIVE_BYTECOUNT, + expr.span, + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", + format!("bytecount::count({}, {})", + snippet_with_applicability(cx, haystack.span, "..", &mut applicability), + snippet_with_applicability(cx, needle.span, "..", &mut applicability)), + applicability, + ); + } + }; +} diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs new file mode 100644 index 00000000000..fcfc25b523d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::BYTES_COUNT_TO_LEN; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + count_recv: &'tcx hir::Expr<'_>, + bytes_recv: &'tcx hir::Expr<'_>, +) { + if_chain! { + if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); + if cx.tcx.type_of(impl_id).is_str(); + let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); + if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)), + applicability + ); + } + }; +} diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs new file mode 100644 index 00000000000..b3c2c7c9a2d --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::{source_map::Spanned, symbol::sym, Span}; + +use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + call_span: Span, + recv: &'tcx Expr<'_>, + arg: &'tcx Expr<'_>, +) { + if_chain! { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if cx.tcx.type_of(impl_id).is_str(); + if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind; + if (2..=6).contains(&ext_literal.as_str().len()); + let ext_str = ext_literal.as_str(); + if ext_str.starts_with('.'); + if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); + let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String); + then { + span_lint_and_help( + cx, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + call_span, + "case-sensitive file extension comparison", + None, + "consider using a case-insensitive comparison instead", + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs new file mode 100644 index 00000000000..561033be5b6 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -0,0 +1,96 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{eq_expr_value, get_parent_expr}; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use std::collections::VecDeque; + +use super::method_call; +use super::COLLAPSIBLE_STR_REPLACE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + from: &'tcx hir::Expr<'tcx>, + to: &'tcx hir::Expr<'tcx>, +) { + let replace_methods = collect_replace_calls(cx, expr, to); + if replace_methods.methods.len() > 1 { + let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind(); + // If the parent node's `to` argument is the same as the `to` argument + // of the last replace call in the current chain, don't lint as it was already linted + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent) + && eq_expr_value(cx, to, current_to) + && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() + { + return; + } + + check_consecutive_replace_calls(cx, expr, &replace_methods, to); + } +} + +struct ReplaceMethods<'tcx> { + methods: VecDeque<&'tcx hir::Expr<'tcx>>, + from_args: VecDeque<&'tcx hir::Expr<'tcx>>, +} + +fn collect_replace_calls<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + to_arg: &'tcx hir::Expr<'tcx>, +) -> ReplaceMethods<'tcx> { + let mut methods = VecDeque::new(); + let mut from_args = VecDeque::new(); + + let _: Option<()> = for_each_expr(expr, |e| { + if let Some(("replace", [_, from, to], _)) = method_call(e) { + if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { + methods.push_front(e); + from_args.push_front(from); + ControlFlow::Continue(()) + } else { + ControlFlow::BREAK + } + } else { + ControlFlow::Continue(()) + } + }); + + ReplaceMethods { methods, from_args } +} + +/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint. +fn check_consecutive_replace_calls<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + replace_methods: &ReplaceMethods<'tcx>, + to_arg: &'tcx hir::Expr<'tcx>, +) { + let from_args = &replace_methods.from_args; + let from_arg_reprs: Vec<String> = from_args + .iter() + .map(|from_arg| snippet(cx, from_arg.span, "..").to_string()) + .collect(); + let app = Applicability::MachineApplicable; + let earliest_replace_call = replace_methods.methods.front().unwrap(); + if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_STR_REPLACE, + expr.span.with_lo(span_lo.lo()), + "used consecutive `str::replace` call", + "replace with", + format!( + "replace([{}], {})", + from_arg_reprs.join(", "), + snippet(cx, to_arg.span, ".."), + ), + app, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs index 5ef08ca6290..d59fefa1ddc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs @@ -7,18 +7,26 @@ use rustc_span::sym; use super::EXPECT_USED; -/// lint use of `expect()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) { +/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`. +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + is_err: bool, + allow_expect_in_tests: bool, +) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { Some((EXPECT_USED, "an Option", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a Result", "Err", "an ")) + Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; + let method = if is_err { "expect_err" } else { "expect" }; + if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { return; } @@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr cx, lint, expr.span, - &format!("used `expect()` on `{kind}` value"), + &format!("used `{method}()` on `{kind}` value"), None, &format!("if this value is {none_prefix}`{none_value}`, it will panic"), ); diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs new file mode 100644 index 00000000000..4de77de7404 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_slice_of_primitives; +use clippy_utils::source::snippet_with_applicability; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::GET_FIRST; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + recv: &'tcx hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, +) { + if_chain! { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if cx.tcx.type_of(impl_id).is_slice(); + if let Some(_) = is_slice_of_primitives(cx, recv); + if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind; + then { + let mut app = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{0}.get(0)`", slice_name), + "try", + format!("{}.first()", slice_name), + app, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs new file mode 100644 index 00000000000..cea7b0d82ff --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -0,0 +1,107 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate}; + +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{Expr, ExprKind, Node}; +use rustc_lint::LateContext; + +use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS}; + +enum IterType { + Iter, + IterMut, + IntoIter, +} + +impl IterType { + fn ref_prefix(&self) -> &'static str { + match self { + Self::Iter => "&", + Self::IterMut => "&mut ", + Self::IntoIter => "", + } + } +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { + let item = match &recv.kind { + ExprKind::Array(v) if v.len() <= 1 => v.first(), + ExprKind::Path(p) => { + if is_lang_ctor(cx, p, OptionNone) { + None + } else { + return; + } + }, + ExprKind::Call(f, some_args) if some_args.len() == 1 => { + if let ExprKind::Path(p) = &f.kind { + if is_lang_ctor(cx, p, OptionSome) { + Some(&some_args[0]) + } else { + return; + } + } else { + return; + } + }, + _ => return, + }; + let iter_type = match method_name { + "iter" => IterType::Iter, + "iter_mut" => IterType::IterMut, + "into_iter" => IterType::IntoIter, + _ => return, + }; + + let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) { + Some((Node::Expr(parent), child_id)) => match parent.kind { + ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false, + ExprKind::If(_, _, _) + | ExprKind::Match(_, _, _) + | ExprKind::Closure(_) + | ExprKind::Ret(_) + | ExprKind::Break(_, _) => true, + _ => false, + }, + Some((Node::Stmt(_) | Node::Local(_), _)) => false, + _ => true, + }; + + if is_unified { + return; + } + + if let Some(i) = item { + let sugg = format!( + "{}::iter::once({}{})", + if is_no_std_crate(cx) { "core" } else { "std" }, + iter_type.ref_prefix(), + snippet(cx, i.span, "...") + ); + span_lint_and_sugg( + cx, + ITER_ON_SINGLE_ITEMS, + expr.span, + &format!("`{method_name}` call on a collection with only one item"), + "try", + sugg, + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_sugg( + cx, + ITER_ON_EMPTY_COLLECTIONS, + expr.span, + &format!("`{method_name}` call on an empty collection"), + "try", + if is_no_std_crate(cx) { + "core::iter::empty()".to_string() + } else { + "std::iter::empty()".to_string() + }, + Applicability::MaybeIncorrect, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs new file mode 100644 index 00000000000..ffd2f4a38b8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -0,0 +1,64 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_lang_ctor, path_to_local_id}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{ResultErr, ResultOk}; +use rustc_hir::{Closure, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::MANUAL_OK_OR; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'_>, + or_expr: &'tcx Expr<'_>, + map_expr: &'tcx Expr<'_>, +) { + if_chain! { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option); + if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind; + if is_lang_ctor(cx, err_path, ResultErr); + if is_ok_wrapping(cx, map_expr); + if let Some(recv_snippet) = snippet_opt(cx, recv.span); + if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span); + if let Some(indent) = indent_of(cx, expr.span); + then { + let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + span_lint_and_sugg( + cx, + MANUAL_OK_OR, + expr.span, + "this pattern reimplements `Option::ok_or`", + "replace with", + format!( + "{}.ok_or({})", + recv_snippet, + reindented_err_arg_snippet + ), + Applicability::MachineApplicable, + ); + } + } +} + +fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { + if let ExprKind::Path(ref qpath) = map_expr.kind { + if is_lang_ctor(cx, qpath, ResultOk) { + return true; + } + } + if_chain! { + if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind; + let body = cx.tcx.hir().body(body); + if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; + if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; + if is_lang_ctor(cx, ok_path, ResultOk); + then { path_to_local_id(ok_arg, param_id) } else { false } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs new file mode 100644 index 00000000000..ffedda95ff8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::mir::Mutability; +use rustc_middle::ty; +use rustc_middle::ty::adjustment::Adjust; +use rustc_semver::RustcVersion; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span}; + +use super::MAP_CLONE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'_>, + e: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &'tcx hir::Expr<'_>, + msrv: Option<RustcVersion>, +) { + if_chain! { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); + if cx.tcx.impl_of_method(method_id) + .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option)) + || is_diag_trait_item(cx, method_id, sym::Iterator); + if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind; + then { + let closure_body = cx.tcx.hir().body(body); + let closure_expr = peel_blocks(&closure_body.value); + match closure_body.params[0].pat.kind { + hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( + hir::BindingAnnotation::Unannotated, .., name, None + ) = inner.kind { + if ident_eq(name, closure_expr) { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); + } + }, + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { + match closure_expr.kind { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { + if ident_eq(name, inner) { + if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); + } + } + }, + hir::ExprKind::MethodCall(method, [obj], _) => if_chain! { + if ident_eq(name, obj) && method.ident.name == sym::clone; + if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); + if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); + if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id); + // no autoderefs + if !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); + then { + let obj_ty = cx.typeck_results().expr_ty(obj); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, *ty); + lint_explicit_closure(cx, e.span, recv.span, copy, msrv); + } + } else { + lint_needless_cloning(cx, e.span, recv.span); + } + } + }, + _ => {}, + } + }, + _ => {}, + } + } + } +} + +fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind { + path.segments.len() == 1 && path.segments[0].ident == name + } else { + false + } +} + +fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { + span_lint_and_sugg( + cx, + MAP_CLONE, + root.trim_start(receiver).unwrap(), + "you are needlessly cloning iterator elements", + "remove the `map` call", + String::new(), + Applicability::MachineApplicable, + ); +} + +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) { + let mut applicability = Applicability::MachineApplicable; + + let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + ("you are using an explicit closure for copying elements", "copied") + } else { + ("you are using an explicit closure for cloning elements", "cloned") + }; + + span_lint_and_sugg( + cx, + MAP_CLONE, + replace, + message, + &format!("consider calling the dedicated `{}` method", sugg_method), + format!( + "{}.{}()", + snippet_with_applicability(cx, root, "..", &mut applicability), + sugg_method, + ), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs new file mode 100644 index 00000000000..1fb6617145e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MAP_ERR_IGNORE; + +pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result) + && let ExprKind::Closure(&Closure { + capture_clause: CaptureBy::Ref, + body, + fn_decl_span, + .. + }) = arg.kind + && let closure_body = cx.tcx.hir().body(body) + && let [param] = closure_body.params + && let PatKind::Wild = param.pat.kind + { + // span the area of the closure capture and warn that the + // original error will be thrown away + span_lint_and_help( + cx, + MAP_ERR_IGNORE, + fn_decl_span, + "`map_err(|_|...` wildcard pattern discards the original error", + None, + "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 5ac6b09f0aa..a0d190a58af 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1,5 +1,8 @@ mod bind_instead_of_map; +mod bytecount; +mod bytes_count_to_len; mod bytes_nth; +mod case_sensitive_file_extension_comparisons; mod chars_cmp; mod chars_cmp_with_unwrap; mod chars_last_cmp; @@ -9,6 +12,7 @@ mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; mod cloned_instead_of_copied; +mod collapsible_str_replace; mod err_expect; mod expect_fun_call; mod expect_used; @@ -21,6 +25,7 @@ mod filter_next; mod flat_map_identity; mod flat_map_option; mod from_iter_instead_of_collect; +mod get_first; mod get_last_with_len; mod get_unwrap; mod implicit_clone; @@ -33,55 +38,72 @@ mod iter_count; mod iter_next_slice; mod iter_nth; mod iter_nth_zero; +mod iter_on_single_or_empty_collections; mod iter_overeager_cloned; mod iter_skip_next; mod iter_with_drain; mod iterator_step_by_zero; +mod manual_ok_or; mod manual_saturating_arithmetic; mod manual_str_repeat; +mod map_clone; mod map_collect_result_unit; +mod map_err_ignore; mod map_flatten; mod map_identity; mod map_unwrap_or; +mod mut_mutex_lock; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; +mod open_options; mod option_as_ref_deref; mod option_map_or_none; mod option_map_unwrap_or; mod or_fun_call; mod or_then_unwrap; +mod path_buf_push_overwrite; +mod range_zip_with_len; +mod repeat_once; mod search_is_some; mod single_char_add_str; mod single_char_insert_string; mod single_char_pattern; mod single_char_push_string; mod skip_while_next; +mod stable_sort_primitive; mod str_splitn; mod string_extend_chars; mod suspicious_map; mod suspicious_splitn; +mod suspicious_to_owned; mod uninit_assumed_init; +mod unit_hash; mod unnecessary_filter_map; mod unnecessary_fold; mod unnecessary_iter_cloned; mod unnecessary_join; mod unnecessary_lazy_eval; +mod unnecessary_sort_by; mod unnecessary_to_owned; mod unwrap_or_else_default; mod unwrap_used; mod useless_asref; mod utils; +mod vec_resize_to_zero; +mod verbose_file_reads; mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty}; +use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{ + contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty, +}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -119,6 +141,32 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for consecutive calls to `str::replace` (2 or more) + /// that can be collapsed into a single call. + /// + /// ### Why is this bad? + /// Consecutive `str::replace` calls scan the string multiple times + /// with repetitive code. + /// + /// ### Example + /// ```rust + /// let hello = "hesuo worpd" + /// .replace('s', "l") + /// .replace("u", "l") + /// .replace('p', "l"); + /// ``` + /// Use instead: + /// ```rust + /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); + /// ``` + #[clippy::version = "1.64.0"] + pub COLLAPSIBLE_STR_REPLACE, + perf, + "collapse consecutive calls to str::replace (2 or more) into a single call" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed. /// /// ### Why is this bad? @@ -173,7 +221,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `.unwrap()` calls on `Option`s and on `Result`s. + /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s. /// /// ### Why is this bad? /// It is better to handle the `None` or `Err` case, @@ -223,7 +271,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `.expect()` calls on `Option`s and `Result`s. + /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s. /// /// ### Why is this bad? /// Usually it is better to handle the `None` or `Err` case. @@ -2008,6 +2056,55 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`. + /// + /// ### Why is this bad? + /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow` + /// itself, without taking ownership of the `Cow` contents (i.e. + /// it's equivalent to calling `Cow::clone`). + /// The similarly named `into_owned` method, on the other hand, + /// clones the `Cow` contents, effectively turning any `Cow::Borrowed` + /// into a `Cow::Owned`. + /// + /// Given the potential ambiguity, consider replacing `to_owned` + /// with `clone` for better readability or, if getting a `Cow::Owned` + /// was the original intent, using `into_owned` instead. + /// + /// ### Example + /// ```rust + /// # use std::borrow::Cow; + /// let s = "Hello world!"; + /// let cow = Cow::Borrowed(s); + /// + /// let data = cow.to_owned(); + /// assert!(matches!(data, Cow::Borrowed(_))) + /// ``` + /// Use instead: + /// ```rust + /// # use std::borrow::Cow; + /// let s = "Hello world!"; + /// let cow = Cow::Borrowed(s); + /// + /// let data = cow.clone(); + /// assert!(matches!(data, Cow::Borrowed(_))) + /// ``` + /// or + /// ```rust + /// # use std::borrow::Cow; + /// let s = "Hello world!"; + /// let cow = Cow::Borrowed(s); + /// + /// let data = cow.into_owned(); + /// assert!(matches!(data, String)) + /// ``` + #[clippy::version = "1.65.0"] + pub SUSPICIOUS_TO_OWNED, + suspicious, + "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected" +} + +declare_clippy_lint! { + /// ### What it does /// Checks for calls to [`splitn`] /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and /// related functions with either zero or one splits. @@ -2269,7 +2366,7 @@ declare_clippy_lint! { /// "1234".replace("12", "12"); /// "1234".replacen("12", "12", 1); /// ``` - #[clippy::version = "1.62.0"] + #[clippy::version = "1.63.0"] pub NO_EFFECT_REPLACE, suspicious, "replace with no effect" @@ -2304,6 +2401,640 @@ declare_clippy_lint! { more clearly with `if .. else ..`" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item + /// + /// ### Why is this bad? + /// + /// It is simpler to use the once function from the standard library: + /// + /// ### Example + /// + /// ```rust + /// let a = [123].iter(); + /// let b = Some(123).into_iter(); + /// ``` + /// Use instead: + /// ```rust + /// use std::iter; + /// let a = iter::once(&123); + /// let b = iter::once(123); + /// ``` + /// + /// ### Known problems + /// + /// The type of the resulting iterator might become incompatible with its usage + #[clippy::version = "1.64.0"] + pub ITER_ON_SINGLE_ITEMS, + nursery, + "Iterator for array of length 1" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections + /// + /// ### Why is this bad? + /// + /// It is simpler to use the empty function from the standard library: + /// + /// ### Example + /// + /// ```rust + /// use std::{slice, option}; + /// let a: slice::Iter<i32> = [].iter(); + /// let f: option::IntoIter<i32> = None.into_iter(); + /// ``` + /// Use instead: + /// ```rust + /// use std::iter; + /// let a: iter::Empty<i32> = iter::empty(); + /// let b: iter::Empty<i32> = iter::empty(); + /// ``` + /// + /// ### Known problems + /// + /// The type of the resulting iterator might become incompatible with its usage + #[clippy::version = "1.64.0"] + pub ITER_ON_EMPTY_COLLECTIONS, + nursery, + "Iterator for empty array" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for naive byte counts + /// + /// ### Why is this bad? + /// The [`bytecount`](https://crates.io/crates/bytecount) + /// crate has methods to count your bytes faster, especially for large slices. + /// + /// ### Known problems + /// If you have predominantly small slices, the + /// `bytecount::count(..)` method may actually be slower. However, if you can + /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be + /// faster in those cases. + /// + /// ### Example + /// ```rust + /// # let vec = vec![1_u8]; + /// let count = vec.iter().filter(|x| **x == 0u8).count(); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// # let vec = vec![1_u8]; + /// let count = bytecount::count(&vec, 0u8); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NAIVE_BYTECOUNT, + pedantic, + "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values" +} + +declare_clippy_lint! { + /// ### What it does + /// It checks for `str::bytes().count()` and suggests replacing it with + /// `str::len()`. + /// + /// ### Why is this bad? + /// `str::bytes().count()` is longer and may not be as performant as using + /// `str::len()`. + /// + /// ### Example + /// ```rust + /// "hello".bytes().count(); + /// String::from("hello").bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// String::from("hello").len(); + /// ``` + #[clippy::version = "1.62.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using `bytes().count()` when `len()` performs the same functionality" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `ends_with` with possible file extensions + /// and suggests to use a case-insensitive approach instead. + /// + /// ### Why is this bad? + /// `ends_with` is case-sensitive and may not detect files with a valid extension. + /// + /// ### Example + /// ```rust + /// fn is_rust_file(filename: &str) -> bool { + /// filename.ends_with(".rs") + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn is_rust_file(filename: &str) -> bool { + /// let filename = std::path::Path::new(filename); + /// filename.extension() + /// .map_or(false, |ext| ext.eq_ignore_ascii_case("rs")) + /// } + /// ``` + #[clippy::version = "1.51.0"] + pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + pedantic, + "Checks for calls to ends_with with case-sensitive file extensions" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(0)` instead of + /// `x.first()`. + /// + /// ### Why is this bad? + /// Using `x.first()` is easier to read and has the same + /// result. + /// + /// ### Example + /// ```rust + /// let x = vec![2, 3, 5]; + /// let first_element = x.get(0); + /// ``` + /// + /// Use instead: + /// ```rust + /// let x = vec![2, 3, 5]; + /// let first_element = x.first(); + /// ``` + #[clippy::version = "1.63.0"] + pub GET_FIRST, + style, + "Using `x.get(0)` when `x.first()` is simpler" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Finds patterns that reimplement `Option::ok_or`. + /// + /// ### Why is this bad? + /// + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// ### Examples + /// ```rust + /// let foo: Option<i32> = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option<i32> = None; + /// foo.ok_or("error"); + /// ``` + #[clippy::version = "1.49.0"] + pub MANUAL_OK_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::ok_or`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `map(|x| x.clone())` or + /// dereferencing closures for `Copy` types, on `Iterator` or `Option`, + /// and suggests `cloned()` or `copied()` instead + /// + /// ### Why is this bad? + /// Readability, this can be written more concisely + /// + /// ### Example + /// ```rust + /// let x = vec![42, 43]; + /// let y = x.iter(); + /// let z = y.map(|i| *i); + /// ``` + /// + /// The correct use would be: + /// + /// ```rust + /// let x = vec![42, 43]; + /// let y = x.iter(); + /// let z = y.cloned(); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub MAP_CLONE, + style, + "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for instances of `map_err(|_| Some::Enum)` + /// + /// ### Why is this bad? + /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error + /// + /// ### Example + /// Before: + /// ```rust + /// use std::fmt; + /// + /// #[derive(Debug)] + /// enum Error { + /// Indivisible, + /// Remainder(u8), + /// } + /// + /// impl fmt::Display for Error { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// match self { + /// Error::Indivisible => write!(f, "could not divide input by three"), + /// Error::Remainder(remainder) => write!( + /// f, + /// "input is not divisible by three, remainder = {}", + /// remainder + /// ), + /// } + /// } + /// } + /// + /// impl std::error::Error for Error {} + /// + /// fn divisible_by_3(input: &str) -> Result<(), Error> { + /// input + /// .parse::<i32>() + /// .map_err(|_| Error::Indivisible) + /// .map(|v| v % 3) + /// .and_then(|remainder| { + /// if remainder == 0 { + /// Ok(()) + /// } else { + /// Err(Error::Remainder(remainder as u8)) + /// } + /// }) + /// } + /// ``` + /// + /// After: + /// ```rust + /// use std::{fmt, num::ParseIntError}; + /// + /// #[derive(Debug)] + /// enum Error { + /// Indivisible(ParseIntError), + /// Remainder(u8), + /// } + /// + /// impl fmt::Display for Error { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// match self { + /// Error::Indivisible(_) => write!(f, "could not divide input by three"), + /// Error::Remainder(remainder) => write!( + /// f, + /// "input is not divisible by three, remainder = {}", + /// remainder + /// ), + /// } + /// } + /// } + /// + /// impl std::error::Error for Error { + /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + /// match self { + /// Error::Indivisible(source) => Some(source), + /// _ => None, + /// } + /// } + /// } + /// + /// fn divisible_by_3(input: &str) -> Result<(), Error> { + /// input + /// .parse::<i32>() + /// .map_err(Error::Indivisible) + /// .map(|v| v % 3) + /// .and_then(|remainder| { + /// if remainder == 0 { + /// Ok(()) + /// } else { + /// Err(Error::Remainder(remainder as u8)) + /// } + /// }) + /// } + /// ``` + #[clippy::version = "1.48.0"] + pub MAP_ERR_IGNORE, + restriction, + "`map_err` should not ignore the original error" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `&mut Mutex::lock` calls + /// + /// ### Why is this bad? + /// `Mutex::lock` is less efficient than + /// calling `Mutex::get_mut`. In addition you also have a statically + /// guarantee that the mutex isn't locked, instead of just a runtime + /// guarantee. + /// + /// ### Example + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let mut value = value_mutex.lock().unwrap(); + /// *value += 1; + /// ``` + /// Use instead: + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.get_mut().unwrap(); + /// *value += 1; + /// ``` + #[clippy::version = "1.49.0"] + pub MUT_MUTEX_LOCK, + style, + "`&mut Mutex::lock` does unnecessary locking" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for duplicate open options as well as combinations + /// that make no sense. + /// + /// ### Why is this bad? + /// In the best case, the code will be harder to read than + /// necessary. I don't know the worst case. + /// + /// ### Example + /// ```rust + /// use std::fs::OpenOptions; + /// + /// OpenOptions::new().read(true).truncate(true); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NONSENSICAL_OPEN_OPTIONS, + correctness, + "nonsensical combination of options for opening a file" +} + +declare_clippy_lint! { + /// ### What it does + ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) + /// calls on `PathBuf` that can cause overwrites. + /// + /// ### Why is this bad? + /// Calling `push` with a root path at the start can overwrite the + /// previous defined path. + /// + /// ### Example + /// ```rust + /// use std::path::PathBuf; + /// + /// let mut x = PathBuf::from("/foo"); + /// x.push("/bar"); + /// assert_eq!(x, PathBuf::from("/bar")); + /// ``` + /// Could be written: + /// + /// ```rust + /// use std::path::PathBuf; + /// + /// let mut x = PathBuf::from("/foo"); + /// x.push("bar"); + /// assert_eq!(x, PathBuf::from("/foo/bar")); + /// ``` + #[clippy::version = "1.36.0"] + pub PATH_BUF_PUSH_OVERWRITE, + nursery, + "calling `push` with file system root on `PathBuf` can overwrite it" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for zipping a collection with the range of + /// `0.._.len()`. + /// + /// ### Why is this bad? + /// The code is better expressed with `.enumerate()`. + /// + /// ### Example + /// ```rust + /// # let x = vec![1]; + /// let _ = x.iter().zip(0..x.len()); + /// ``` + /// + /// Use instead: + /// ```rust + /// # let x = vec![1]; + /// let _ = x.iter().enumerate(); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub RANGE_ZIP_WITH_LEN, + complexity, + "zipping iterator with a range when `enumerate()` would do" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.repeat(1)` and suggest the following method for each types. + /// - `.to_string()` for `str` + /// - `.clone()` for `String` + /// - `.to_vec()` for `slice` + /// + /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if + /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306)) + /// + /// ### Why is this bad? + /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning + /// the string is the intention behind this, `clone()` should be used. + /// + /// ### Example + /// ```rust + /// fn main() { + /// let x = String::from("hello world").repeat(1); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// let x = String::from("hello world").clone(); + /// } + /// ``` + #[clippy::version = "1.47.0"] + pub REPEAT_ONCE, + complexity, + "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` " +} + +declare_clippy_lint! { + /// ### What it does + /// When sorting primitive values (integers, bools, chars, as well + /// as arrays, slices, and tuples of such items), it is typically better to + /// use an unstable sort than a stable sort. + /// + /// ### Why is this bad? + /// Typically, using a stable sort consumes more memory and cpu cycles. + /// Because values which compare equal are identical, preserving their + /// relative order (the guarantee that a stable sort provides) means + /// nothing, while the extra costs still apply. + /// + /// ### Known problems + /// + /// As pointed out in + /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), + /// a stable sort can instead be significantly faster for certain scenarios + /// (eg. when a sorted vector is extended with new data and resorted). + /// + /// For more information and benchmarking results, please refer to the + /// issue linked above. + /// + /// ### Example + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort(); + /// ``` + /// Use instead: + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort_unstable(); + /// ``` + #[clippy::version = "1.47.0"] + pub STABLE_SORT_PRIMITIVE, + pedantic, + "use of sort() when sort_unstable() is equivalent" +} + +declare_clippy_lint! { + /// ### What it does + /// Detects `().hash(_)`. + /// + /// ### Why is this bad? + /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op. + /// + /// ### Example + /// ```rust + /// # use std::hash::Hash; + /// # use std::collections::hash_map::DefaultHasher; + /// # enum Foo { Empty, WithValue(u8) } + /// # use Foo::*; + /// # let mut state = DefaultHasher::new(); + /// # let my_enum = Foo::Empty; + /// match my_enum { + /// Empty => ().hash(&mut state), + /// WithValue(x) => x.hash(&mut state), + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::hash::Hash; + /// # use std::collections::hash_map::DefaultHasher; + /// # enum Foo { Empty, WithValue(u8) } + /// # use Foo::*; + /// # let mut state = DefaultHasher::new(); + /// # let my_enum = Foo::Empty; + /// match my_enum { + /// Empty => 0_u8.hash(&mut state), + /// WithValue(x) => x.hash(&mut state), + /// } + /// ``` + #[clippy::version = "1.58.0"] + pub UNIT_HASH, + correctness, + "hashing a unit value, which does nothing" +} + +declare_clippy_lint! { + /// ### What it does + /// Detects uses of `Vec::sort_by` passing in a closure + /// which compares the two arguments, either directly or indirectly. + /// + /// ### Why is this bad? + /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if + /// possible) than to use `Vec::sort_by` and a more complicated + /// closure. + /// + /// ### Known problems + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already + /// imported by a use statement, then it will need to be added manually. + /// + /// ### Example + /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec<A> = Vec::new(); + /// vec.sort_by(|a, b| a.foo().cmp(&b.foo())); + /// ``` + /// Use instead: + /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec<A> = Vec::new(); + /// vec.sort_by_key(|a| a.foo()); + /// ``` + #[clippy::version = "1.46.0"] + pub UNNECESSARY_SORT_BY, + complexity, + "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer" +} + +declare_clippy_lint! { + /// ### What it does + /// Finds occurrences of `Vec::resize(0, an_int)` + /// + /// ### Why is this bad? + /// This is probably an argument inversion mistake. + /// + /// ### Example + /// ```rust + /// vec!(1, 2, 3, 4, 5).resize(0, 5) + /// ``` + /// + /// Use instead: + /// ```rust + /// vec!(1, 2, 3, 4, 5).clear() + /// ``` + #[clippy::version = "1.46.0"] + pub VEC_RESIZE_TO_ZERO, + correctness, + "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for use of File::read_to_end and File::read_to_string. + /// + /// ### Why is this bad? + /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. + /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) + /// + /// ### Example + /// ```rust,no_run + /// # use std::io::Read; + /// # use std::fs::File; + /// let mut f = File::open("foo.txt").unwrap(); + /// let mut bytes = Vec::new(); + /// f.read_to_end(&mut bytes).unwrap(); + /// ``` + /// Can be written more concisely as + /// ```rust,no_run + /// # use std::fs; + /// let mut bytes = fs::read("foo.txt").unwrap(); + /// ``` + #[clippy::version = "1.44.0"] + pub VERBOSE_FILE_READS, + restriction, + "use of `File::read_to_end` or `File::read_to_string`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>, @@ -2347,6 +3078,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, + COLLAPSIBLE_STR_REPLACE, ITER_OVEREAGER_CLONED, CLONED_INSTEAD_OF_COPIED, FLAT_MAP_OPTION, @@ -2393,6 +3125,7 @@ impl_lint_pass!(Methods => [ FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, IMPLICIT_CLONE, + SUSPICIOUS_TO_OWNED, SUSPICIOUS_SPLITN, MANUAL_STR_REPEAT, EXTEND_WITH_DRAIN, @@ -2406,6 +3139,25 @@ impl_lint_pass!(Methods => [ NEEDLESS_OPTION_TAKE, NO_EFFECT_REPLACE, OBFUSCATED_IF_ELSE, + ITER_ON_SINGLE_ITEMS, + ITER_ON_EMPTY_COLLECTIONS, + NAIVE_BYTECOUNT, + BYTES_COUNT_TO_LEN, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + GET_FIRST, + MANUAL_OK_OR, + MAP_CLONE, + MAP_ERR_IGNORE, + MUT_MUTEX_LOCK, + NONSENSICAL_OPEN_OPTIONS, + PATH_BUF_PUSH_OVERWRITE, + RANGE_ZIP_WITH_LEN, + REPEAT_ONCE, + STABLE_SORT_PRIMITIVE, + UNIT_HASH, + UNNECESSARY_SORT_BY, + VEC_RESIZE_TO_ZERO, + VERBOSE_FILE_READS, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2541,7 +3293,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if contains_adt_constructor(ret_ty, self_adt) { return; } - } else if contains_ty(ret_ty, self_ty) { + } else if ret_ty.contains(self_ty) { return; } @@ -2559,7 +3311,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if contains_adt_constructor(assoc_ty, self_adt) { return; } - } else if contains_ty(assoc_ty, self_ty) { + } else if assoc_ty.contains(self_ty) { return; } } @@ -2608,7 +3360,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id()); let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder(); - if !contains_ty(ret_ty, self_ty); + if !ret_ty.contains(self_ty); then { span_lint( @@ -2660,22 +3412,30 @@ impl Methods { }, _ => {}, }, - ("count", []) => match method_call(recv) { + ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { iter_count::check(cx, expr, recv2, name2); }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), + Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => { iter_with_drain::check(cx, expr, recv, span, arg); }, + ("ends_with", [arg]) => { + if let ExprKind::MethodCall(_, _, span) = expr.kind { + case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg); + } + }, ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), - _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests), + _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, + ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); extend_with_drain::check(cx, expr, recv, arg); @@ -2702,12 +3462,21 @@ impl Methods { inspect_for_each::check(cx, expr, span2); } }, - ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg), + ("get", [arg]) => { + get_first::check(cx, expr, recv, arg); + get_last_with_len::check(cx, expr, recv, arg); + }, ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("hash", [arg]) => { + unit_hash::check(cx, expr, recv, arg); + }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("iter" | "iter_mut" | "into_iter", []) => { + iter_on_single_or_empty_collections::check(cx, expr, name, recv); + }, ("join", [join_arg]) => { if let Some(("collect", _, span)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); @@ -2720,7 +3489,15 @@ impl Methods { } } }, + ("lock", []) => { + mut_mutex_lock::check(cx, expr, recv, span); + }, (name @ ("map" | "map_err"), [m_arg]) => { + if name == "map" { + map_clone::check(cx, expr, recv, m_arg, self.msrv); + } else { + map_err_ignore::check(cx, expr, m_arg); + } if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), @@ -2736,7 +3513,10 @@ impl Methods { } map_identity::check(cx, expr, recv, m_arg, name, span); }, - ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), + ("map_or", [def, map]) => { + option_map_or_none::check(cx, expr, recv, def, map); + manual_ok_or::check(cx, expr, recv, def, map); + }, ("next", []) => { if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { match (name2, args2) { @@ -2758,11 +3538,46 @@ impl Methods { _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("open", [_]) => { + open_options::check(cx, expr, recv); + }, ("or_else", [arg]) => { if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, + ("push", [arg]) => { + path_buf_push_overwrite::check(cx, expr, arg); + }, + ("read_to_end", [_]) => { + verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG); + }, + ("read_to_string", [_]) => { + verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG); + }, + ("repeat", [arg]) => { + repeat_once::check(cx, expr, recv, arg); + }, + (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => { + no_effect_replace::check(cx, expr, arg1, arg2); + + // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint + if name == "replace" && let Some(("replace", ..)) = method_call(recv) { + collapsible_str_replace::check(cx, expr, arg1, arg2); + } + }, + ("resize", [count_arg, default_arg]) => { + vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); + }, + ("sort", []) => { + stable_sort_primitive::check(cx, expr, recv); + }, + ("sort_by", [arg]) => { + unnecessary_sort_by::check(cx, expr, recv, arg, false); + }, + ("sort_unstable_by", [arg]) => { + unnecessary_sort_by::check(cx, expr, recv, arg, true); + }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); @@ -2789,7 +3604,12 @@ impl Methods { } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); }, - ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { + ("to_owned", []) => { + if !suspicious_to_owned::check(cx, expr, recv) { + implicit_clone::check(cx, name, expr, recv); + } + }, + ("to_os_string" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, ("unwrap", []) => { @@ -2805,8 +3625,9 @@ impl Methods { }, _ => {}, } - unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests); + unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests); }, + ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_or", [u_arg]) => match method_call(recv) { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); @@ -2827,8 +3648,12 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); }, }, - ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => { - no_effect_replace::check(cx, expr, arg1, arg2); + ("zip", [arg]) => { + if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind + && name.ident.name == sym::iter + { + range_zip_with_len::check(cx, expr, iter_recv, arg); + } }, _ => {}, } diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs new file mode 100644 index 00000000000..bd8458a222e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs @@ -0,0 +1,30 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{sym, Span}; + +use super::MUT_MUTEX_LOCK; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { + if_chain! { + if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind(); + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex); + then { + span_lint_and_sugg( + cx, + MUT_MUTEX_LOCK, + name_span, + "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", + "change this to", + "get_mut".to_owned(), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 5a0b5042018..c3112823e34 100644 --- a/src/tools/clippy/clippy_lints/src/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -3,43 +3,19 @@ use clippy_utils::paths; use clippy_utils::ty::match_type; use rustc_ast::ast::LitKind; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::LateContext; use rustc_span::source_map::{Span, Spanned}; -declare_clippy_lint! { - /// ### What it does - /// Checks for duplicate open options as well as combinations - /// that make no sense. - /// - /// ### Why is this bad? - /// In the best case, the code will be harder to read than - /// necessary. I don't know the worst case. - /// - /// ### Example - /// ```rust - /// use std::fs::OpenOptions; - /// - /// OpenOptions::new().read(true).truncate(true); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub NONSENSICAL_OPEN_OPTIONS, - correctness, - "nonsensical combination of options for opening a file" -} - -declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); +use super::NONSENSICAL_OPEN_OPTIONS; -impl<'tcx> LateLintPass<'tcx> for OpenOptions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(path, [self_arg, ..], _) = &e.kind { - let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { - let mut options = Vec::new(); - get_open_options(cx, self_arg, &mut options); - check_open_options(cx, &options, e.span); - } - } +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS) + { + let mut options = Vec::new(); + get_open_options(cx, recv, &mut options); + check_open_options(cx, &options, e.span); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 6c641af59f9..3c4002a3aef 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -78,7 +78,7 @@ pub(super) fn check<'tcx>( map_span, String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }), ), - (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")), + (expr.span.with_lo(unwrap_recv.span.hi()), String::new()), ]; if !unwrap_snippet_none { diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs new file mode 100644 index 00000000000..0cc28c0dcb3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; +use std::path::{Component, Path}; + +use super::PATH_BUF_PUSH_OVERWRITE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { + if_chain! { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf); + if let ExprKind::Lit(ref lit) = arg.kind; + if let LitKind::Str(ref path_lit, _) = lit.node; + if let pushed_path = Path::new(path_lit.as_str()); + if let Some(pushed_path_lit) = pushed_path.to_str(); + if pushed_path.has_root(); + if let Some(root) = pushed_path.components().next(); + if root == Component::RootDir; + then { + span_lint_and_sugg( + cx, + PATH_BUF_PUSH_OVERWRITE, + lit.span, + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "try", + format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs new file mode 100644 index 00000000000..00a2a0d14d1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::source::snippet; +use clippy_utils::{higher, SpanlessEq}; +use clippy_utils::{is_integer_const, is_trait_method}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::RANGE_ZIP_WITH_LEN; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) { + if_chain! { + if is_trait_method(cx, expr, sym::Iterator); + // range expression in `.zip()` call: `0..x.len()` + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg); + if is_integer_const(cx, start, 0); + // `.len()` call + if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind; + if len_path.ident.name == sym::len; + // `.iter()` and `.len()` called on same `Path` + if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind; + if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind; + if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); + then { + span_lint(cx, + RANGE_ZIP_WITH_LEN, + expr.span, + &format!("it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, recv.span, "_")) + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs new file mode 100644 index 00000000000..0a14f9216ab --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/repeat_once.rs @@ -0,0 +1,52 @@ +use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::REPEAT_ONCE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + repeat_arg: &'tcx Expr<'_>, +) { + if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) { + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if ty.is_str() { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on str", + "consider using `.to_string()` instead", + format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if ty.builtin_index().is_some() { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on slice", + "consider using `.to_vec()` instead", + format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_type_diagnostic_item(cx, ty, sym::String) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on a string literal", + "consider using `.clone()` instead", + format!("{}.clone()", snippet(cx, recv.span, r#""...""#)), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs new file mode 100644 index 00000000000..91951c65bb3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs @@ -0,0 +1,31 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_slice_of_primitives; +use clippy_utils::source::snippet_with_context; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; + +use super::STABLE_SORT_PRIMITIVE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && cx.tcx.type_of(impl_id).is_slice() + && let Some(slice_type) = is_slice_of_primitives(cx, recv) + { + span_lint_and_then( + cx, + STABLE_SORT_PRIMITIVE, + e.span, + &format!("used `sort` on primitive type `{}`", slice_type), + |diag| { + let mut app = Applicability::MachineApplicable; + let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0; + diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app); + diag.note( + "an unstable sort typically performs faster without any observable difference for this data type", + ); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs new file mode 100644 index 00000000000..6b306fbf008 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs @@ -0,0 +1,36 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_diag_trait_item; +use clippy_utils::source::snippet_with_context; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::sym; + +use super::SUSPICIOUS_TO_OWNED; + +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool { + if_chain! { + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if is_diag_trait_item(cx, method_def_id, sym::ToOwned); + let input_type = cx.typeck_results().expr_ty(expr); + if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind(); + if cx.tcx.is_diagnostic_item(sym::Cow, adt.did()); + then { + let mut app = Applicability::MaybeIncorrect; + let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; + span_lint_and_sugg( + cx, + SUSPICIOUS_TO_OWNED, + expr.span, + &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type), + "consider using, depending on intent", + format!("{0}.clone()` or `{0}.into_owned()", recv_snip), + app, + ); + return true; + } + } + false +} 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 77d21f1d373..a1c6294737c 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 @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty}; +use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if_chain! { if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); - if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit); + if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit); if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr)); then { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs new file mode 100644 index 00000000000..3c7955bc469 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unit_hash.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::UNIT_HASH; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { + if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() { + span_lint_and_then( + cx, + UNIT_HASH, + expr.span, + "this call to `hash` on the unit type will do nothing", + |diag| { + diag.span_suggestion( + expr.span, + "remove the call to `hash` or consider using", + format!("0_u8.hash({})", snippet(cx, arg.span, ".."),), + Applicability::MaybeIncorrect, + ); + diag.note("the implementation of `Hash` for `()` is a no-op"); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index ea5aadbbca1..1966990bd77 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -1,51 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::ty::implements_trait; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; use rustc_span::symbol::Ident; use std::iter; -declare_clippy_lint! { - /// ### What it does - /// Detects uses of `Vec::sort_by` passing in a closure - /// which compares the two arguments, either directly or indirectly. - /// - /// ### Why is this bad? - /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if - /// possible) than to use `Vec::sort_by` and a more complicated - /// closure. - /// - /// ### Known problems - /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already - /// imported by a use statement, then it will need to be added manually. - /// - /// ### Example - /// ```rust - /// # struct A; - /// # impl A { fn foo(&self) {} } - /// # let mut vec: Vec<A> = Vec::new(); - /// vec.sort_by(|a, b| a.foo().cmp(&b.foo())); - /// ``` - /// Use instead: - /// ```rust - /// # struct A; - /// # impl A { fn foo(&self) {} } - /// # let mut vec: Vec<A> = Vec::new(); - /// vec.sort_by_key(|a| a.foo()); - /// ``` - #[clippy::version = "1.46.0"] - pub UNNECESSARY_SORT_BY, - complexity, - "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer" -} - -declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]); +use super::UNNECESSARY_SORT_BY; enum LintTrigger { Sort(SortDetection), @@ -54,7 +20,6 @@ enum LintTrigger { struct SortDetection { vec_name: String, - unstable: bool, } struct SortByKeyDetection { @@ -62,7 +27,6 @@ struct SortByKeyDetection { closure_arg: String, closure_body: String, reverse: bool, - unstable: bool, } /// Detect if the two expressions are mirrored (identical, except one @@ -150,20 +114,20 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident } } -fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { +fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> { if_chain! { - if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind; - if let name = name_ident.ident.name.to_ident_string(); - if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec); - if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if cx.tcx.type_of(impl_id).is_slice(); + if let ExprKind::Closure(&Closure { body, .. }) = arg.kind; + if let closure_body = cx.tcx.hir().body(body); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } ] = &closure_body.params; - if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind; + if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind; if method_path.ident.name == sym::cmp; + if is_trait_method(cx, &closure_body.value, sym::Ord); then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( left_expr, @@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { } else { return None; }; - let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); - let unstable = name == "sort_unstable_by"; + let vec_name = Sugg::hir(cx, recv, "..").to_string(); if_chain! { - if let ExprKind::Path(QPath::Resolved(_, Path { - segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind; - if left_name == left_ident; - if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) - }); + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind; + if left_name == left_ident; + if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]) + }); then { - return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); + return Some(LintTrigger::Sort(SortDetection { vec_name })); } } @@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { closure_arg, closure_body, reverse, - unstable, })); } } @@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } -impl LateLintPass<'_> for UnnecessarySortBy { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - match detect_lint(cx, expr) { - Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg( - cx, - UNNECESSARY_SORT_BY, - expr.span, - "use Vec::sort_by_key here instead", - "try", - format!( - "{}.sort{}_by_key(|{}| {})", - trigger.vec_name, - if trigger.unstable { "_unstable" } else { "" }, - trigger.closure_arg, - if trigger.reverse { - format!("std::cmp::Reverse({})", trigger.closure_body) - } else { - trigger.closure_body.to_string() - }, - ), +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + arg: &'tcx Expr<'_>, + is_unstable: bool, +) { + match detect_lint(cx, expr, recv, arg) { + Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|{}| {})", + trigger.vec_name, + if is_unstable { "_unstable" } else { "" }, + trigger.closure_arg, if trigger.reverse { - Applicability::MaybeIncorrect + format!("std::cmp::Reverse({})", trigger.closure_body) } else { - Applicability::MachineApplicable + trigger.closure_body.to_string() }, ), - Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg( - cx, - UNNECESSARY_SORT_BY, - expr.span, - "use Vec::sort here instead", - "try", - format!( - "{}.sort{}()", - trigger.vec_name, - if trigger.unstable { "_unstable" } else { "" }, - ), - Applicability::MachineApplicable, + if trigger.reverse { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, + ), + Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort here instead", + "try", + format!( + "{}.sort{}()", + trigger.vec_name, + if is_unstable { "_unstable" } else { "" }, ), - None => {}, - } + Applicability::MachineApplicable, + ), + None => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index b3276f1394e..44bf8435294 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,11 +3,10 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{ - contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, + get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs, }; -use clippy_utils::{meets_msrv, msrvs}; - use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; +use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -279,7 +278,19 @@ fn check_other_call_arg<'tcx>( &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..], call_substs, ); - implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs) + // if `expr` is a `String` and generic target is [u8], skip + // (https://github.com/rust-lang/rust-clippy/issues/9317). + if let [subst] = composed_substs[..] + && let GenericArgKind::Type(arg_ty) = subst.unpack() + && arg_ty.is_slice() + && let inner_ty = arg_ty.builtin_index().unwrap() + && let ty::Uint(ty::UintTy::U8) = inner_ty.kind() + && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs() + && is_type_diagnostic_item(cx, self_ty, sym::String) { + false + } else { + implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs) + } } else { false }; @@ -292,7 +303,7 @@ fn check_other_call_arg<'tcx>( // (https://github.com/rust-lang/rust-clippy/issues/8507). if (n_refs == 0 && !receiver_ty.is_ref()) || trait_predicate.def_id() != as_ref_trait_id - || !contains_ty(fn_sig.output(), input); + || !fn_sig.output().contains(input); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -360,25 +371,15 @@ fn get_input_traits_and_projections<'tcx>( ) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) { let mut trait_predicates = Vec::new(); let mut projection_predicates = Vec::new(); - for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() { - // `substs` should have 1 + n elements. The first is the type on the left hand side of an - // `as`. The remaining n are trait parameters. - let is_input_substs = |substs: SubstsRef<'tcx>| { - if_chain! { - if let Some(arg) = substs.iter().next(); - if let GenericArgKind::Type(arg_ty) = arg.unpack(); - if arg_ty == input; - then { true } else { false } - } - }; + for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() { match predicate.kind().skip_binder() { PredicateKind::Trait(trait_predicate) => { - if is_input_substs(trait_predicate.trait_ref.substs) { + if trait_predicate.trait_ref.self_ty() == input { trait_predicates.push(trait_predicate); } }, PredicateKind::Projection(projection_predicate) => { - if is_input_substs(projection_predicate.projection_ty.substs) { + if projection_predicate.projection_ty.self_ty() == input { projection_predicates.push(projection_predicate); } }, diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs index ce1a52e5480..ee17f2d7889 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs @@ -7,18 +7,26 @@ use rustc_span::sym; use super::{EXPECT_USED, UNWRAP_USED}; -/// lint use of `unwrap()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) { +/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`. +pub(super) fn check( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + is_err: bool, + allow_unwrap_in_tests: bool, +) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { + let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { Some((UNWRAP_USED, "an Option", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a Result", "Err", "an ")) + Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; + let method_suffix = if is_err { "_err" } else { "" }; + if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { return; } @@ -27,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) { format!( "if you don't want to handle the `{none_value}` case gracefully, consider \ - using `expect()` to provide a better panic message" + using `expect{method_suffix}()` to provide a better panic message" ) } else { format!("if this value is {none_prefix}`{none_value}`, it will panic") @@ -37,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr cx, lint, expr.span, - &format!("used `unwrap()` on `{kind}` value"), + &format!("used `unwrap{method_suffix}()` on `{kind}` value"), None, &help, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs new file mode 100644 index 00000000000..02d8364cb29 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; +use rustc_span::{sym, Span}; + +use super::VEC_RESIZE_TO_ZERO; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + count_arg: &'tcx Expr<'_>, + default_arg: &'tcx Expr<'_>, + name_span: Span, +) { + if_chain! { + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(method_id); + if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec); + if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind; + if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind; + then { + let method_call_span = expr.span.with_lo(name_span.lo()); + span_lint_and_then( + cx, + VEC_RESIZE_TO_ZERO, + expr.span, + "emptying a vector with `resize`", + |db| { + db.help("the arguments may be inverted..."); + db.span_suggestion( + method_call_span, + "...or you can empty the vector with", + "clear()".to_string(), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs new file mode 100644 index 00000000000..2fe5ae9a9ad --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs @@ -0,0 +1,28 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_trait_method; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::VERBOSE_FILE_READS; + +pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead"); +pub(super) const READ_TO_STRING_MSG: (&str, &str) = ( + "use of `File::read_to_string`", + "consider using `fs::read_to_string` instead", +); + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + (msg, help): (&str, &str), +) { + if is_trait_method(cx, expr, sym::IoRead) + && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _))) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File) + { + span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help); + } +} diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs index df044538fe1..7c4ae746e90 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs @@ -46,7 +46,7 @@ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) { "these patterns are unneeded as the `..` pattern can match those elements" }, if only_one { "remove it" } else { "remove them" }, - "".to_string(), + String::new(), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index f763e0d24c9..020efeaebf0 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { /// } /// impl<A, B> Foo<A, B> {} /// ``` - #[clippy::version = "1.62.0"] + #[clippy::version = "1.63.0"] pub MISMATCHING_TYPE_PARAM_ORDER, pedantic, "type parameter positioned inconsistently between type def and impl block" diff --git a/src/tools/clippy/clippy_lints/src/multi_assignments.rs b/src/tools/clippy/clippy_lints/src/multi_assignments.rs new file mode 100644 index 00000000000..81eb1a085ae --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/multi_assignments.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for nested assignments. + /// + /// ### Why is this bad? + /// While this is in most cases already a type mismatch, + /// the result of an assignment being `()` can throw off people coming from languages like python or C, + /// where such assignments return a copy of the assigned value. + /// + /// ### Example + /// ```rust + ///# let (a, b); + /// a = b = 42; + /// ``` + /// Use instead: + /// ```rust + ///# let (a, b); + /// b = 42; + /// a = b; + /// ``` + #[clippy::version = "1.65.0"] + pub MULTI_ASSIGNMENTS, + suspicious, + "instead of using `a = b = c;` use `a = c; b = c;`" +} + +declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]); + +fn strip_paren_blocks(expr: &Expr) -> &Expr { + match &expr.kind { + ExprKind::Paren(e) => strip_paren_blocks(e), + ExprKind::Block(b, _) => { + if let [ + Stmt { + kind: StmtKind::Expr(e), + .. + }, + ] = &b.stmts[..] + { + strip_paren_blocks(e) + } else { + expr + } + }, + _ => expr, + } +} + +impl EarlyLintPass for MultiAssignments { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::Assign(target, source, _) = &expr.kind { + if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind { + span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); + }; + if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind { + span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); + } + }; + } +} diff --git a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs deleted file mode 100644 index b7f981faa2d..00000000000 --- a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs +++ /dev/null @@ -1,70 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `&mut Mutex::lock` calls - /// - /// ### Why is this bad? - /// `Mutex::lock` is less efficient than - /// calling `Mutex::get_mut`. In addition you also have a statically - /// guarantee that the mutex isn't locked, instead of just a runtime - /// guarantee. - /// - /// ### Example - /// ```rust - /// use std::sync::{Arc, Mutex}; - /// - /// let mut value_rc = Arc::new(Mutex::new(42_u8)); - /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - /// - /// let mut value = value_mutex.lock().unwrap(); - /// *value += 1; - /// ``` - /// Use instead: - /// ```rust - /// use std::sync::{Arc, Mutex}; - /// - /// let mut value_rc = Arc::new(Mutex::new(42_u8)); - /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - /// - /// let value = value_mutex.get_mut().unwrap(); - /// *value += 1; - /// ``` - #[clippy::version = "1.49.0"] - pub MUT_MUTEX_LOCK, - style, - "`&mut Mutex::lock` does unnecessary locking" -} - -declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); - -impl<'tcx> LateLintPass<'tcx> for MutMutexLock { - fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(path, [self_arg, ..], _) = &ex.kind; - if path.ident.name == sym!(lock); - let ty = cx.typeck_results().expr_ty(self_arg); - if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); - if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex); - then { - span_lint_and_sugg( - cx, - MUT_MUTEX_LOCK, - path.ident.span, - "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", - "change this to", - "get_mut".to_owned(), - Applicability::MaybeIncorrect, - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 413a740be25..774a3540d1e 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -1,25 +1,16 @@ -use std::collections::VecDeque; - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lint_allowed; -use itertools::{izip, Itertools}; -use rustc_ast::{walk_list, Label, Mutability}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id}; +use core::cell::Cell; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor}; -use rustc_hir::{ - Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, - PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp, -}; +use rustc_hir::hir_id::HirIdMap; +use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_middle::ty::{Ty, TyCtxt, TypeckResults}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::kw; -use rustc_span::symbol::Ident; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, ConstKind}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; declare_clippy_lint! { @@ -89,572 +80,323 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.61.0"] pub ONLY_USED_IN_RECURSION, - nursery, + complexity, "arguments that is only used in recursion can be removed" } -declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); - -impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - decl: &'tcx rustc_hir::FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - _: Span, - id: HirId, - ) { - if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) { - return; - } - if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind { - let def_id = id.owner.to_def_id(); - let data = cx.tcx.def_path(def_id).data; - - if data.len() > 1 { - match data.get(data.len() - 2) { - Some(DisambiguatedDefPathData { - data: DefPathData::Impl, - disambiguator, - }) if *disambiguator != 0 => return, - _ => {}, - } - } - - let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None); - - let ty_res = cx.typeck_results(); - let param_span = body - .params - .iter() - .flat_map(|param| { - let mut v = Vec::new(); - param.pat.each_binding(|_, hir_id, span, ident| { - v.push((hir_id, span, ident)); - }); - v - }) - .skip(if has_self { 1 } else { 0 }) - .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_')) - .collect_vec(); - - let params = body.params.iter().map(|param| param.pat).collect(); - - let mut visitor = SideEffectVisit { - graph: FxHashMap::default(), - has_side_effect: FxHashSet::default(), - ret_vars: Vec::new(), - contains_side_effect: false, - break_vars: FxHashMap::default(), - params, - fn_ident: ident, - fn_def_id: def_id, - is_method: matches!(kind, FnKind::Method(..)), - has_self, - ty_res, - tcx: cx.tcx, - visited_exprs: FxHashSet::default(), - }; - - visitor.visit_expr(&body.value); - let vars = std::mem::take(&mut visitor.ret_vars); - // this would set the return variables to side effect - visitor.add_side_effect(vars); - - let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>(); - - // a simple BFS to check all the variables that have side effect - while let Some(id) = queue.pop_front() { - if let Some(next) = visitor.graph.get(&id) { - for i in next { - if !visitor.has_side_effect.contains(i) { - visitor.has_side_effect.insert(*i); - queue.push_back(*i); - } - } - } - } - - for (id, span, ident) in param_span { - // if the variable is not used in recursion, it would be marked as unused - if !visitor.has_side_effect.contains(&id) { - let mut queue = VecDeque::new(); - let mut visited = FxHashSet::default(); - - queue.push_back(id); - - // a simple BFS to check the graph can reach to itself - // if it can't, it means the variable is never used in recursion - while let Some(id) = queue.pop_front() { - if let Some(next) = visitor.graph.get(&id) { - for i in next { - if !visited.contains(i) { - visited.insert(id); - queue.push_back(*i); - } - } - } - } +impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); + +#[derive(Clone, Copy)] +enum FnKind { + Fn, + TraitFn, + // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`. + // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for + // equality. + ImplTraitFn(usize), +} - if visited.contains(&id) { - span_lint_and_sugg( - cx, - ONLY_USED_IN_RECURSION, - span, - "parameter is only used in recursion", - "if this is intentional, prefix with an underscore", - format!("_{}", ident.name.as_str()), - Applicability::MaybeIncorrect, - ); - } - } - } +struct Param { + /// The function this is a parameter for. + fn_id: DefId, + fn_kind: FnKind, + /// The index of this parameter. + idx: usize, + ident: Ident, + /// Whether this parameter should be linted. Set by `Params::flag_for_linting`. + apply_lint: Cell<bool>, + /// All the uses of this parameter. + uses: Vec<Usage>, +} +impl Param { + fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self { + Self { + fn_id, + fn_kind, + idx, + ident, + apply_lint: Cell::new(true), + uses: Vec::new(), } } } -pub fn is_primitive(ty: Ty<'_>) -> bool { - let ty = ty.peel_refs(); - ty.is_primitive() || ty.is_str() +#[derive(Debug)] +struct Usage { + span: Span, + idx: usize, } - -pub fn is_array(ty: Ty<'_>) -> bool { - let ty = ty.peel_refs(); - ty.is_array() || ty.is_array_slice() +impl Usage { + fn new(span: Span, idx: usize) -> Self { + Self { span, idx } + } } -/// This builds the graph of side effect. -/// The edge `a -> b` means if `a` has side effect, `b` will have side effect. -/// -/// There are some example in following code: -/// ```rust, ignore -/// let b = 1; -/// let a = b; // a -> b -/// let (c, d) = (a, b); // c -> b, d -> b -/// -/// let e = if a == 0 { // e -> a -/// c // e -> c -/// } else { -/// d // e -> d -/// }; -/// ``` -pub struct SideEffectVisit<'tcx> { - graph: FxHashMap<HirId, FxHashSet<HirId>>, - has_side_effect: FxHashSet<HirId>, - // bool for if the variable was dereferenced from mutable reference - ret_vars: Vec<(HirId, bool)>, - contains_side_effect: bool, - // break label - break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>, - params: Vec<&'tcx Pat<'tcx>>, - fn_ident: Ident, - fn_def_id: DefId, - is_method: bool, - has_self: bool, - ty_res: &'tcx TypeckResults<'tcx>, - tcx: TyCtxt<'tcx>, - visited_exprs: FxHashSet<HirId>, +/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the +/// `DefId` of the function paired with the parameter's index. +#[derive(Default)] +struct Params { + params: Vec<Param>, + by_id: HirIdMap<usize>, + by_fn: FxHashMap<(DefId, usize), usize>, } - -impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { - fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { - match s.kind { - StmtKind::Local(Local { - pat, init: Some(init), .. - }) => { - self.visit_pat_expr(pat, init, false); - }, - StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => { - walk_stmt(self, s); - }, - StmtKind::Local(_) => {}, - } - self.ret_vars.clear(); +impl Params { + fn insert(&mut self, param: Param, id: HirId) { + let idx = self.params.len(); + self.by_id.insert(id, idx); + self.by_fn.insert((param.fn_id, param.idx), idx); + self.params.push(param); } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if !self.visited_exprs.insert(ex.hir_id) { - return; - } - match ex.kind { - ExprKind::Array(exprs) | ExprKind::Tup(exprs) => { - self.ret_vars = exprs - .iter() - .flat_map(|expr| { - self.visit_expr(expr); - std::mem::take(&mut self.ret_vars) - }) - .collect(); - }, - ExprKind::Call(callee, args) => self.visit_fn(callee, args), - ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args), - ExprKind::Binary(_, lhs, rhs) => { - self.visit_bin_op(lhs, rhs); - }, - ExprKind::Unary(op, expr) => self.visit_un_op(op, expr), - ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false), - ExprKind::If(bind, then_expr, else_expr) => { - self.visit_if(bind, then_expr, else_expr); - }, - ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms), - // since analysing the closure is not easy, just set all variables in it to side-effect - ExprKind::Closure(&Closure { body, .. }) => { - let body = self.tcx.hir().body(body); - self.visit_body(body); - let vars = std::mem::take(&mut self.ret_vars); - self.add_side_effect(vars); - }, - ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => { - self.visit_block_label(block, label); - }, - ExprKind::Assign(bind, expr, _) => { - self.visit_assign(bind, expr); - }, - ExprKind::AssignOp(_, bind, expr) => { - self.visit_assign(bind, expr); - self.visit_bin_op(bind, expr); - }, - ExprKind::Field(expr, _) => { - self.visit_expr(expr); - if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) { - self.ret_vars.iter_mut().for_each(|(_, b)| *b = true); - } - }, - ExprKind::Index(expr, index) => { - self.visit_expr(expr); - let mut vars = std::mem::take(&mut self.ret_vars); - self.visit_expr(index); - self.ret_vars.append(&mut vars); - - if !is_array(self.ty_res.expr_ty(expr)) { - self.add_side_effect(self.ret_vars.clone()); - } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) { - self.ret_vars.iter_mut().for_each(|(_, b)| *b = true); - } - }, - ExprKind::Break(dest, Some(expr)) => { - self.visit_expr(expr); - if let Some(label) = dest.label { - self.break_vars - .entry(label.ident) - .or_insert(Vec::new()) - .append(&mut self.ret_vars); - } - self.contains_side_effect = true; - }, - ExprKind::Ret(Some(expr)) => { - self.visit_expr(expr); - let vars = std::mem::take(&mut self.ret_vars); - self.add_side_effect(vars); - self.contains_side_effect = true; - }, - ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => { - self.contains_side_effect = true; - }, - ExprKind::Struct(_, exprs, expr) => { - let mut ret_vars = exprs - .iter() - .flat_map(|field| { - self.visit_expr(field.expr); - std::mem::take(&mut self.ret_vars) - }) - .collect(); - - walk_list!(self, visit_expr, expr); - self.ret_vars.append(&mut ret_vars); - }, - _ => walk_expr(self, ex), + fn remove_by_id(&mut self, id: HirId) { + if let Some(param) = self.get_by_id_mut(id) { + param.uses = Vec::new(); + let key = (param.fn_id, param.idx); + self.by_fn.remove(&key); + self.by_id.remove(&id); } } - fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) { - if let Res::Local(id) = path.res { - self.ret_vars.push((id, false)); - } + fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> { + self.params.get_mut(*self.by_id.get(&id)?) } -} -impl<'tcx> SideEffectVisit<'tcx> { - fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) { - // Just support array and tuple unwrapping for now. - // - // ex) `(a, b) = (c, d);` - // The graph would look like this: - // a -> c - // b -> d - // - // This would minimize the connection of the side-effect graph. - match (&lhs.kind, &rhs.kind) { - (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => { - // if not, it is a compile error - debug_assert!(lhs.len() == rhs.len()); - izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs)); - }, - // in other assigns, we have to connect all each other - // because they can be connected somehow - _ => { - self.visit_expr(lhs); - let lhs_vars = std::mem::take(&mut self.ret_vars); - self.visit_expr(rhs); - let rhs_vars = std::mem::take(&mut self.ret_vars); - self.connect_assign(&lhs_vars, &rhs_vars, false); - }, - } + fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> { + self.params.get(*self.by_fn.get(&(id, idx))?) } - fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) { - self.visit_block(block); - let _ = label.and_then(|label| { - self.break_vars - .remove(&label.ident) - .map(|mut break_vars| self.ret_vars.append(&mut break_vars)) - }); - } - - fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) { - self.visit_expr(lhs); - let mut ret_vars = std::mem::take(&mut self.ret_vars); - self.visit_expr(rhs); - self.ret_vars.append(&mut ret_vars); - - // the binary operation between non primitive values are overloaded operators - // so they can have side-effects - if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) { - self.ret_vars.iter().for_each(|id| { - self.has_side_effect.insert(id.0); - }); - self.contains_side_effect = true; - } + fn clear(&mut self) { + self.params.clear(); + self.by_id.clear(); + self.by_fn.clear(); } - fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) { - self.visit_expr(expr); - let ty = self.ty_res.expr_ty(expr); - // dereferencing a reference has no side-effect - if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) { - self.add_side_effect(self.ret_vars.clone()); - } - - if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) { - self.ret_vars.iter_mut().for_each(|(_, b)| *b = true); + /// Sets the `apply_lint` flag on each parameter. + fn flag_for_linting(&mut self) { + // Stores the list of parameters currently being resolved. Needed to avoid cycles. + let mut eval_stack = Vec::new(); + for param in &self.params { + self.try_disable_lint_for_param(param, &mut eval_stack); } } - fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) { - match (&pat.kind, &expr.kind) { - (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => { - self.ret_vars = izip!(*pats, *exprs) - .flat_map(|(pat, expr)| { - self.visit_pat_expr(pat, expr, connect_self); - std::mem::take(&mut self.ret_vars) - }) - .collect(); - }, - (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => { - let mut vars = izip!(*front_exprs, *exprs) - .flat_map(|(pat, expr)| { - self.visit_pat_expr(pat, expr, connect_self); - std::mem::take(&mut self.ret_vars) - }) - .collect(); - self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev()) - .flat_map(|(pat, expr)| { - self.visit_pat_expr(pat, expr, connect_self); - std::mem::take(&mut self.ret_vars) - }) - .collect(); - self.ret_vars.append(&mut vars); - }, - _ => { - let mut lhs_vars = Vec::new(); - pat.each_binding(|_, id, _, _| lhs_vars.push((id, false))); - self.visit_expr(expr); - let rhs_vars = std::mem::take(&mut self.ret_vars); - self.connect_assign(&lhs_vars, &rhs_vars, connect_self); - self.ret_vars = rhs_vars; - }, + // Use by calling `flag_for_linting`. + fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool { + if !param.apply_lint.get() { + true + } else if param.uses.is_empty() { + // Don't lint on unused parameters. + param.apply_lint.set(false); + true + } else if eval_stack.contains(¶m.idx) { + // Already on the evaluation stack. Returning false will continue to evaluate other dependencies. + false + } else { + eval_stack.push(param.idx); + // Check all cases when used at a different parameter index. + // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }` + for usage in param.uses.iter().filter(|u| u.idx != param.idx) { + if self + .get_by_fn(param.fn_id, usage.idx) + // If the parameter can't be found, then it's used for more than just recursion. + .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack)) + { + param.apply_lint.set(false); + eval_stack.pop(); + return true; + } + } + eval_stack.pop(); + false } } +} - fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) { - self.visit_expr(callee); - let mut ret_vars = std::mem::take(&mut self.ret_vars); - self.add_side_effect(ret_vars.clone()); - - let mut is_recursive = false; - - if_chain! { - if !self.has_self; - if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind; - if let Res::Def(DefKind::Fn, def_id) = path.res; - if self.fn_def_id == def_id; - then { - is_recursive = true; - } - } +#[derive(Default)] +pub struct OnlyUsedInRecursion { + /// Track the top-level body entered. Needed to delay reporting when entering nested bodies. + entered_body: Option<HirId>, + params: Params, +} - if_chain! { - if !self.has_self && self.is_method; - if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind; - if segment.ident == self.fn_ident; - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - if let Res::SelfTy{ .. } = path.res; - then { - is_recursive = true; - } +impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) { + if body.value.span.from_expansion() { + return; } - - if is_recursive { - izip!(self.params.clone(), args).for_each(|(pat, expr)| { - self.visit_pat_expr(pat, expr, true); - self.ret_vars.clear(); - }); - } else { - // This would set arguments used in closure that does not have side-effect. - // Closure itself can be detected whether there is a side-effect, but the - // value of variable that is holding closure can change. - // So, we just check the variables. - self.ret_vars = args - .iter() - .flat_map(|expr| { - self.visit_expr(expr); - std::mem::take(&mut self.ret_vars) - }) - .collect_vec() - .into_iter() - .map(|id| { - self.has_side_effect.insert(id.0); - id - }) - .collect(); - self.contains_side_effect = true; + // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions. + // It can't be renamed, and it can't be removed without removing it from multiple functions. + let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) { + Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0), + Some(Node::TraitItem(&TraitItem { + kind: TraitItemKind::Fn(ref sig, _), + def_id, + .. + })) => ( + def_id.to_def_id(), + FnKind::TraitFn, + if sig.decl.implicit_self.has_implicit_self() { + 1 + } else { + 0 + }, + ), + Some(Node::ImplItem(&ImplItem { + kind: ImplItemKind::Fn(ref sig, _), + def_id, + .. + })) => { + #[allow(trivial_casts)] + if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id)) + && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id) + && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id + { + ( + trait_item_id, + FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize), + if sig.decl.implicit_self.has_implicit_self() { + 1 + } else { + 0 + }, + ) + } else { + (def_id.to_def_id(), FnKind::Fn, 0) + } + }, + _ => return, + }; + body.params + .iter() + .enumerate() + .skip(skip_params) + .filter_map(|(idx, p)| match p.pat.kind { + PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => { + Some((id, Param::new(fn_id, fn_kind, idx, ident))) + }, + _ => None, + }) + .for_each(|(id, param)| self.params.insert(param, id)); + if self.entered_body.is_none() { + self.entered_body = Some(body.value.hir_id); } - - self.ret_vars.append(&mut ret_vars); } - fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) { - if_chain! { - if self.is_method; - if path.ident == self.fn_ident; - if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind; - if let Res::Local(..) = path.res; - let ident = path.segments.last().unwrap().ident; - if ident.name == kw::SelfLower; - then { - izip!(self.params.clone(), args.iter()) - .for_each(|(pat, expr)| { - self.visit_pat_expr(pat, expr, true); - self.ret_vars.clear(); - }); - } else { - self.ret_vars = args - .iter() - .flat_map(|expr| { - self.visit_expr(expr); - std::mem::take(&mut self.ret_vars) - }) - .collect_vec() - .into_iter() - .map(|a| { - self.has_side_effect.insert(a.0); - a - }) - .collect(); - self.contains_side_effect = true; + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) { + if let Some(id) = path_to_local(e) + && let Some(param) = self.params.get_by_id_mut(id) + { + let typeck = cx.typeck_results(); + let span = e.span; + let mut e = e; + loop { + match get_expr_use_or_unification_node(cx.tcx, e) { + None | Some((Node::Stmt(_), _)) => return, + Some((Node::Expr(parent), child_id)) => match parent.kind { + // Recursive call. Track which index the parameter is used in. + ExprKind::Call(callee, args) + if path_def_id(cx, callee).map_or(false, |id| { + id == param.fn_id + && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id)) + }) => + { + if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) { + param.uses.push(Usage::new(span, idx)); + } + return; + }, + ExprKind::MethodCall(_, args, _) + if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| { + id == param.fn_id + && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id)) + }) => + { + if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) { + param.uses.push(Usage::new(span, idx)); + } + return; + }, + // Assignment to a parameter is fine. + ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => { + return; + }, + // Parameter update e.g. `x = x + 1` + ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) + if rhs.hir_id == child_id && path_to_local_id(lhs, id) => + { + return; + }, + // Side-effect free expressions. Walk to the parent expression. + ExprKind::Binary(_, lhs, rhs) + if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() => + { + e = parent; + continue; + }, + ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => { + e = parent; + continue; + }, + ExprKind::AddrOf(..) | ExprKind::Cast(..) => { + e = parent; + continue; + }, + // Only allow field accesses without auto-deref + ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => { + e = parent; + continue + } + _ => (), + }, + _ => (), + } + self.params.remove_by_id(id); + return; } } } - fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) { - let contains_side_effect = self.contains_side_effect; - self.contains_side_effect = false; - self.visit_expr(bind); - let mut vars = std::mem::take(&mut self.ret_vars); - self.visit_expr(then_expr); - let mut then_vars = std::mem::take(&mut self.ret_vars); - walk_list!(self, visit_expr, else_expr); - if self.contains_side_effect { - self.add_side_effect(vars.clone()); - } - self.contains_side_effect |= contains_side_effect; - self.ret_vars.append(&mut vars); - self.ret_vars.append(&mut then_vars); - } - - fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) { - self.visit_expr(expr); - let mut expr_vars = std::mem::take(&mut self.ret_vars); - self.ret_vars = arms - .iter() - .flat_map(|arm| { - let contains_side_effect = self.contains_side_effect; - self.contains_side_effect = false; - // this would visit `expr` multiple times - // but couldn't think of a better way - self.visit_pat_expr(arm.pat, expr, false); - let mut vars = std::mem::take(&mut self.ret_vars); - let _ = arm.guard.as_ref().map(|guard| { - self.visit_expr(match guard { - Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr, - }); - vars.append(&mut self.ret_vars); - }); - self.visit_expr(arm.body); - if self.contains_side_effect { - self.add_side_effect(vars.clone()); - self.add_side_effect(expr_vars.clone()); + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) { + if self.entered_body == Some(body.value.hir_id) { + self.entered_body = None; + self.params.flag_for_linting(); + for param in &self.params.params { + if param.apply_lint.get() { + span_lint_and_then( + cx, + ONLY_USED_IN_RECURSION, + param.ident.span, + "parameter is only used in recursion", + |diag| { + if param.ident.name != kw::SelfLower { + diag.span_suggestion( + param.ident.span, + "if this is intentional, prefix it with an underscore", + format!("_{}", param.ident.name), + Applicability::MaybeIncorrect, + ); + } + diag.span_note( + param.uses.iter().map(|x| x.span).collect::<Vec<_>>(), + "parameter used here", + ); + }, + ); } - self.contains_side_effect |= contains_side_effect; - vars.append(&mut self.ret_vars); - vars - }) - .collect(); - self.ret_vars.append(&mut expr_vars); - } - - fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) { - // if mutable dereference is on assignment it can have side-effect - // (this can lead to parameter mutable dereference and change the original value) - // too hard to detect whether this value is from parameter, so this would all - // check mutable dereference assignment to side effect - lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| { - self.has_side_effect.insert(*id); - self.contains_side_effect = true; - }); - - // there is no connection - if lhs.is_empty() || rhs.is_empty() { - return; - } - - // by connected rhs in cycle, the connections would decrease - // from `n * m` to `n + m` - // where `n` and `m` are length of `lhs` and `rhs`. - - // unwrap is possible since rhs is not empty - let rhs_first = rhs.first().unwrap(); - for (id, _) in lhs.iter() { - if connect_self || *id != rhs_first.0 { - self.graph - .entry(*id) - .or_insert_with(FxHashSet::default) - .insert(rhs_first.0); } + self.params.clear(); } - - let rhs = rhs.iter(); - izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| { - if connect_self || from.0 != to.0 { - self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0); - } - }); } +} - fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) { - for (id, _) in v { - self.has_side_effect.insert(id); - self.contains_side_effect = true; - } +fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool { + match kind { + FnKind::Fn => true, + FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() { + GenericArgKind::Lifetime(_) => true, + GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx), + GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx), + }), + #[allow(trivial_casts)] + FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs, } } diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 44f153cffac..9602d0d1d2e 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -1,21 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks, peel_hir_expr_while, CaptureKind, }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::OptionSome; -use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp}; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// Lints usage of `if let Some(v) = ... { y } else { x }` and + /// `match .. { Some(v) => y, None/_ => x }` which are more /// idiomatically done with `Option::map_or` (if the else bit is a pure /// expression) or `Option::map_or_else` (if the else bit is an impure /// expression). @@ -39,6 +40,10 @@ declare_clippy_lint! { /// } else { /// 5 /// }; + /// let _ = match optional { + /// Some(val) => val + 1, + /// None => 5 + /// }; /// let _ = if let Some(foo) = optional { /// foo /// } else { @@ -53,11 +58,14 @@ declare_clippy_lint! { /// # let optional: Option<u32> = Some(0); /// # fn do_complicated_function() -> u32 { 5 }; /// let _ = optional.map_or(5, |foo| foo); + /// let _ = optional.map_or(5, |val| val + 1); /// let _ = optional.map_or_else(||{ /// let y = do_complicated_function(); /// y*y /// }, |foo| foo); /// ``` + // FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also + // covers matches and `Result`. #[clippy::version = "1.47.0"] pub OPTION_IF_LET_ELSE, nursery, @@ -66,19 +74,21 @@ declare_clippy_lint! { declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); -/// Returns true iff the given expression is the result of calling `Result::ok` -fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { - if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind { - path.ident.name.as_str() == "ok" - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result) - } else { - false - } -} - -/// A struct containing information about occurrences of the -/// `if let Some(..) = .. else` construct that this lint detects. -struct OptionIfLetElseOccurrence { +/// A struct containing information about occurrences of construct that this lint detects +/// +/// Such as: +/// +/// ```ignore +/// if let Some(..) = {..} else {..} +/// ``` +/// or +/// ```ignore +/// match x { +/// Some(..) => {..}, +/// None/_ => {..} +/// } +/// ``` +struct OptionOccurence { option: String, method_sugg: String, some_expr: String, @@ -99,43 +109,38 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo ) } -/// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionIfLetElseOccurrence` struct with details if -/// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> { +fn try_get_option_occurence<'tcx>( + cx: &LateContext<'tcx>, + pat: &Pat<'tcx>, + expr: &Expr<'_>, + if_then: &'tcx Expr<'_>, + if_else: &'tcx Expr<'_>, +) -> Option<OptionOccurence> { + let cond_expr = match expr.kind { + ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr, + _ => expr, + }; + let inner_pat = try_get_inner_pat(cx, pat)?; if_chain! { - if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly - if !in_constant(cx, expr.hir_id); - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) - = higher::IfLet::hir(cx, expr); - if !is_else_clause(cx.tcx, expr); - if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already - if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind; - if is_lang_ctor(cx, struct_qpath, OptionSome); - if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind; + if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind; if let Some(some_captures) = can_move_expr_to_closure(cx, if_then); if let Some(none_captures) = can_move_expr_to_closure(cx, if_else); if some_captures .iter() .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2))) .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()); - then { - let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; + let capture_mut = if bind_annotation == BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = peel_blocks(if_then); let none_body = peel_blocks(if_else); let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); - let (as_ref, as_mut) = match &let_expr.kind { + let (as_ref, as_mut) = match &expr.kind { ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), - _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), - }; - let cond_expr = match let_expr.kind { - // Pointer dereferencing happens automatically, so we can omit it in the suggestion - ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr, - _ => let_expr, + _ => (bind_annotation == BindingAnnotation::Ref, bind_annotation == BindingAnnotation::RefMut), }; + // Check if captures the closure will need conflict with borrows made in the scrutinee. // TODO: check all the references made in the scrutinee expression. This will require interacting // with the borrow checker. Currently only `<local>[.<field>]*` is checked for. @@ -154,30 +159,100 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> } } } - Some(OptionIfLetElseOccurrence { + + return Some(OptionOccurence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")), - }) + }); + } + } + + None +} + +fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> { + if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) { + return Some(inner_pat); + } + } + None +} + +/// If this expression is the option if let/else construct we're detecting, then +/// this function returns an `OptionOccurence` struct with details if +/// this construct is found, or None if this construct is not found. +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> { + if let Some(higher::IfLet { + let_pat, + let_expr, + if_then, + if_else: Some(if_else), + }) = higher::IfLet::hir(cx, expr) + { + if !is_else_clause(cx.tcx, expr) { + return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else); + } + } + None +} + +fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> { + if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { + if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) { + return try_get_option_occurence(cx, let_pat, ex, if_then, if_else); + } + } + None +} + +fn try_convert_match<'tcx>( + cx: &LateContext<'tcx>, + arms: &[Arm<'tcx>], +) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if arms.len() == 2 { + return if is_none_or_err_arm(cx, &arms[1]) { + Some((arms[0].pat, arms[0].body, arms[1].body)) + } else if is_none_or_err_arm(cx, &arms[0]) { + Some((arms[1].pat, arms[1].body, arms[0].body)) } else { None - } + }; + } + None +} + +fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + match arm.pat.kind { + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, [first_pat], _) => { + is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild) + }, + PatKind::Wild => true, + _ => false, } } impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if let Some(detection) = detect_option_if_let_else(cx, expr) { + // Don't lint macros and constants + if expr.span.from_expansion() || in_constant(cx, expr.hir_id) { + return; + } + + let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr)); + if let Some(det) = detection { span_lint_and_sugg( cx, OPTION_IF_LET_ELSE, expr.span, - format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), + format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(), "try", format!( "{}.{}({}, {})", - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + det.option, det.method_sugg, det.none_expr, det.some_expr ), Applicability::MaybeIncorrect, ); diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs index eee7642068d..000b0ba7a14 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs @@ -53,7 +53,8 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { // If the expression is a literal `Option::None` let is_none_ctor = |expr: &Expr<'_>| { - matches!(&peel_hir_expr_refs(expr).0.kind, + !expr.span.from_expansion() + && matches!(&peel_hir_expr_refs(expr).0.kind, ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone)) }; diff --git a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs deleted file mode 100644 index bc6a918f703..00000000000 --- a/src/tools/clippy/clippy_lints/src/path_buf_push_overwrite.rs +++ /dev/null @@ -1,72 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; -use std::path::{Component, Path}; - -declare_clippy_lint! { - /// ### What it does - ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) - /// calls on `PathBuf` that can cause overwrites. - /// - /// ### Why is this bad? - /// Calling `push` with a root path at the start can overwrite the - /// previous defined path. - /// - /// ### Example - /// ```rust - /// use std::path::PathBuf; - /// - /// let mut x = PathBuf::from("/foo"); - /// x.push("/bar"); - /// assert_eq!(x, PathBuf::from("/bar")); - /// ``` - /// Could be written: - /// - /// ```rust - /// use std::path::PathBuf; - /// - /// let mut x = PathBuf::from("/foo"); - /// x.push("bar"); - /// assert_eq!(x, PathBuf::from("/foo/bar")); - /// ``` - #[clippy::version = "1.36.0"] - pub PATH_BUF_PUSH_OVERWRITE, - nursery, - "calling `push` with file system root on `PathBuf` can overwrite it" -} - -declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]); - -impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind; - if path.ident.name == sym!(push); - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf); - if let ExprKind::Lit(ref lit) = get_index_arg.kind; - if let LitKind::Str(ref path_lit, _) = lit.node; - if let pushed_path = Path::new(path_lit.as_str()); - if let Some(pushed_path_lit) = pushed_path.to_str(); - if pushed_path.has_root(); - if let Some(root) = pushed_path.components().next(); - if root == Component::RootDir; - then { - span_lint_and_sugg( - cx, - PATH_BUF_PUSH_OVERWRITE, - lit.span, - "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", - "try", - format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), - Applicability::MachineApplicable, - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 964a057f00d..b432ccb1ee3 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -123,7 +123,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind; - if let PatKind::Binding(annot, bind_id, ident, _) = field.kind; + if let PatKind::Binding(annot, bind_id, ident, None) = field.kind; let caller_ty = cx.typeck_results().expr_ty(let_expr); let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else); if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id)) diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index fbf842c339e..490f345d297 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -1,48 +1,22 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local}; -use clippy_utils::{higher, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::sym; use std::cmp::Ordering; declare_clippy_lint! { /// ### What it does - /// Checks for zipping a collection with the range of - /// `0.._.len()`. - /// - /// ### Why is this bad? - /// The code is better expressed with `.enumerate()`. - /// - /// ### Example - /// ```rust - /// # let x = vec![1]; - /// let _ = x.iter().zip(0..x.len()); - /// ``` - /// - /// Use instead: - /// ```rust - /// # let x = vec![1]; - /// let _ = x.iter().enumerate(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub RANGE_ZIP_WITH_LEN, - complexity, - "zipping iterator with a range when `enumerate()` would do" -} - -declare_clippy_lint! { - /// ### What it does /// Checks for exclusive ranges where 1 is added to the /// upper bound, e.g., `x..(y+1)`. /// @@ -198,7 +172,6 @@ impl Ranges { } impl_lint_pass!(Ranges => [ - RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, REVERSED_EMPTY_RANGES, @@ -207,16 +180,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - match expr.kind { - ExprKind::MethodCall(path, args, _) => { - check_range_zip_with_len(cx, path, args, expr.span); - }, - ExprKind::Binary(ref op, l, r) => { - if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr, expr.span); - } - }, - _ => {}, + if let ExprKind::Binary(ref op, l, r) = expr.kind { + if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); + } } check_exclusive_range_plus_one(cx, expr); @@ -380,34 +347,6 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R None } -fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) { - if_chain! { - if path.ident.as_str() == "zip"; - if let [iter, zip_arg] = args; - // `.iter()` call - if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind; - if iter_path.ident.name == sym::iter; - // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg); - if is_integer_const(cx, start, 0); - // `.len()` call - if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind; - if len_path.ident.name == sym::len; - // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind; - if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, iter_caller.span, "_")) - ); - } - } -} - // exclusive range plus one: `x..(y+1)` fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { 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 8db8c4e9b78..e82aa3a7b98 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 @@ -41,7 +41,7 @@ declare_clippy_lint! { /// let data = std::rc::Rc::new("some data".to_string()); /// let v = vec![data; 100]; /// ``` - #[clippy::version = "1.62.0"] + #[clippy::version = "1.63.0"] pub RC_CLONE_IN_VEC_INIT, suspicious, "initializing reference-counted pointer in `vec![elem; len]`" diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index db6c97f3739..8693ca9af83 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -11,6 +11,8 @@ use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::subst::GenericArg; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use std::iter; + declare_clippy_lint! { /// ### What it does /// Checks for redundant slicing expressions which use the full range, and @@ -134,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { } else if let Some(target_id) = cx.tcx.lang_items().deref_target() { if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions( cx.param_env, - cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())), + cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(iter::once(GenericArg::from(indexed_ty)))), ) { if deref_ty == expr_ty { let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index f8801f769e8..2d751c27467 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -48,15 +48,15 @@ impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); impl RedundantStaticLifetimes { // Recursively visit types - fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { + fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { match ty.kind { // Be careful of nested structures (arrays and tuples) TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => { - self.visit_type(ty, cx, reason); + Self::visit_type(ty, cx, reason); }, TyKind::Tup(ref tup) => { for tup_ty in tup { - self.visit_type(tup_ty, cx, reason); + Self::visit_type(tup_ty, cx, reason); } }, // This is what we are looking for ! @@ -87,7 +87,7 @@ impl RedundantStaticLifetimes { _ => {}, } } - self.visit_type(&borrow_type.ty, cx, reason); + Self::visit_type(&borrow_type.ty, cx, reason); }, _ => {}, } @@ -102,13 +102,13 @@ impl EarlyLintPass for RedundantStaticLifetimes { if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { - self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); + Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime"); // Don't check associated consts because `'static` cannot be elided on those (issue // #2438) } if let ItemKind::Static(ref var_type, _, _) = item.kind { - self.visit_type(var_type, cx, "statics have by default a `'static` lifetime"); + Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime"); } } } diff --git a/src/tools/clippy/clippy_lints/src/repeat_once.rs b/src/tools/clippy/clippy_lints/src/repeat_once.rs deleted file mode 100644 index 898c70ace66..00000000000 --- a/src/tools/clippy/clippy_lints/src/repeat_once.rs +++ /dev/null @@ -1,89 +0,0 @@ -use clippy_utils::consts::{constant_context, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of `.repeat(1)` and suggest the following method for each types. - /// - `.to_string()` for `str` - /// - `.clone()` for `String` - /// - `.to_vec()` for `slice` - /// - /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if - /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306)) - /// - /// ### Why is this bad? - /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning - /// the string is the intention behind this, `clone()` should be used. - /// - /// ### Example - /// ```rust - /// fn main() { - /// let x = String::from("hello world").repeat(1); - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn main() { - /// let x = String::from("hello world").clone(); - /// } - /// ``` - #[clippy::version = "1.47.0"] - pub REPEAT_ONCE, - complexity, - "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` " -} - -declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); - -impl<'tcx> LateLintPass<'tcx> for RepeatOnce { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, [receiver, count], _) = &expr.kind; - if path.ident.name == sym!(repeat); - if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1)); - if !receiver.span.from_expansion(); - then { - let ty = cx.typeck_results().expr_ty(receiver).peel_refs(); - if ty.is_str() { - span_lint_and_sugg( - cx, - REPEAT_ONCE, - expr.span, - "calling `repeat(1)` on str", - "consider using `.to_string()` instead", - format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)), - Applicability::MachineApplicable, - ); - } else if ty.builtin_index().is_some() { - span_lint_and_sugg( - cx, - REPEAT_ONCE, - expr.span, - "calling `repeat(1)` on slice", - "consider using `.to_vec()` instead", - format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), - Applicability::MachineApplicable, - ); - } else if is_type_diagnostic_item(cx, ty, sym::String) { - span_lint_and_sugg( - cx, - REPEAT_ONCE, - expr.span, - "calling `repeat(1)` on a string literal", - "consider using `.clone()` instead", - format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)), - Applicability::MachineApplicable, - ); - } - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 1d9a2abf706..1926661c596 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::{fn_def_id, path_to_local_id}; use if_chain::if_chain; -use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; @@ -11,7 +10,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -152,10 +150,6 @@ impl<'tcx> LateLintPass<'tcx> for Return { } } -fn attr_is_cfg(attr: &Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym::cfg) -} - fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { if let Some(expr) = block.expr { check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); @@ -178,9 +172,7 @@ fn check_final_expr<'tcx>( match expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - let attrs = cx.tcx.hir().attrs(expr.hir_id); - if !attrs.iter().any(attr_is_cfg) { + if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { emit_return_lint( diff --git a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs index d07c26d7c89..9cea4d88067 100644 --- a/src/tools/clippy/clippy_lints/src/self_named_constructors.rs +++ b/src/tools/clippy/clippy_lints/src/self_named_constructors.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::return_ty; -use clippy_utils::ty::{contains_adt_constructor, contains_ty}; +use clippy_utils::ty::contains_adt_constructor; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { if !contains_adt_constructor(ret_ty, self_adt) { return; } - } else if !contains_ty(ret_ty, self_ty) { + } else if !ret_ty.contains(self_ty) { return; } diff --git a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs deleted file mode 100644 index 6d54935f81a..00000000000 --- a/src/tools/clippy/clippy_lints/src/stable_sort_primitive.rs +++ /dev/null @@ -1,144 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_slice_of_primitives, sugg::Sugg}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// When sorting primitive values (integers, bools, chars, as well - /// as arrays, slices, and tuples of such items), it is typically better to - /// use an unstable sort than a stable sort. - /// - /// ### Why is this bad? - /// Typically, using a stable sort consumes more memory and cpu cycles. - /// Because values which compare equal are identical, preserving their - /// relative order (the guarantee that a stable sort provides) means - /// nothing, while the extra costs still apply. - /// - /// ### Known problems - /// - /// As pointed out in - /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), - /// a stable sort can instead be significantly faster for certain scenarios - /// (eg. when a sorted vector is extended with new data and resorted). - /// - /// For more information and benchmarking results, please refer to the - /// issue linked above. - /// - /// ### Example - /// ```rust - /// let mut vec = vec![2, 1, 3]; - /// vec.sort(); - /// ``` - /// Use instead: - /// ```rust - /// let mut vec = vec![2, 1, 3]; - /// vec.sort_unstable(); - /// ``` - #[clippy::version = "1.47.0"] - pub STABLE_SORT_PRIMITIVE, - pedantic, - "use of sort() when sort_unstable() is equivalent" -} - -declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); - -/// The three "kinds" of sorts -enum SortingKind { - Vanilla, - /* The other kinds of lint are currently commented out because they - * can map distinct values to equal ones. If the key function is - * provably one-to-one, or if the Cmp function conserves equality, - * then they could be linted on, but I don't know if we can check - * for that. */ - - /* ByKey, - * ByCmp, */ -} -impl SortingKind { - /// The name of the stable version of this kind of sort - fn stable_name(&self) -> &str { - match self { - SortingKind::Vanilla => "sort", - /* SortingKind::ByKey => "sort_by_key", - * SortingKind::ByCmp => "sort_by", */ - } - } - /// The name of the unstable version of this kind of sort - fn unstable_name(&self) -> &str { - match self { - SortingKind::Vanilla => "sort_unstable", - /* SortingKind::ByKey => "sort_unstable_by_key", - * SortingKind::ByCmp => "sort_unstable_by", */ - } - } - /// Takes the name of a function call and returns the kind of sort - /// that corresponds to that function name (or None if it isn't) - fn from_stable_name(name: &str) -> Option<SortingKind> { - match name { - "sort" => Some(SortingKind::Vanilla), - // "sort_by" => Some(SortingKind::ByCmp), - // "sort_by_key" => Some(SortingKind::ByKey), - _ => None, - } - } -} - -/// A detected instance of this lint -struct LintDetection { - slice_name: String, - method: SortingKind, - method_args: String, - slice_type: String, -} - -fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> { - if_chain! { - if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind; - if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str()); - if let Some(slice_type) = is_slice_of_primitives(cx, slice); - then { - let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", "); - Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type }) - } else { - None - } - } -} - -impl LateLintPass<'_> for StableSortPrimitive { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let Some(detection) = detect_stable_sort_primitive(cx, expr) { - span_lint_and_then( - cx, - STABLE_SORT_PRIMITIVE, - expr.span, - format!( - "used `{}` on primitive type `{}`", - detection.method.stable_name(), - detection.slice_type, - ) - .as_str(), - |diag| { - diag.span_suggestion( - expr.span, - "try", - format!( - "{}.{}({})", - detection.slice_name, - detection.method.unstable_name(), - detection.method_args, - ), - Applicability::MachineApplicable, - ); - diag.note( - "an unstable sort typically performs faster without any observable difference for this data type", - ); - }, - ); - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 0a42a31fb8c..2ffa022b04f 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash}; use core::hash::{Hash, Hasher}; use if_chain::if_chain; use itertools::Itertools; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -15,6 +15,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Span}; +use std::collections::hash_map::Entry; declare_clippy_lint! { /// ### What it does @@ -103,7 +104,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { self.check_type_repetition(cx, gen); check_trait_bound_duplication(cx, gen); - check_bounds_or_where_duplication(cx, gen); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { @@ -234,35 +234,61 @@ impl TraitBounds { } fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() { + if gen.span.from_expansion() { return; } - let mut map = FxHashMap::<_, Vec<_>>::default(); - for predicate in gen.predicates { + // Explanation: + // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) + // where T: Clone + Default, { unimplemented!(); } + // ^^^^^^^^^^^^^^^^^^ + // | + // collects each of these where clauses into a set keyed by generic name and comparable trait + // eg. (T, Clone) + let where_predicates = gen + .predicates + .iter() + .filter_map(|pred| { + if_chain! { + if pred.in_where_clause(); + if let WherePredicate::BoundPredicate(bound_predicate) = pred; + if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind; + then { + return Some( + rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements") + .into_iter().map(|(trait_ref, _)| (path.res, trait_ref))) + } + } + None + }) + .flatten() + .collect::<FxHashSet<_>>(); + + // Explanation: + // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ... + // ^^^^^^^^^^^^^^^^^^ ^^^^^^^ + // | + // compare trait bounds keyed by generic name and comparable trait to collected where + // predicates eg. (T, Clone) + for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) { if_chain! { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if let WherePredicate::BoundPredicate(bound_predicate) = predicate; if bound_predicate.origin != PredicateOrigin::ImplTrait; if !bound_predicate.span.from_expansion(); - if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; - if let Some(segment) = segments.first(); + if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind; then { - for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { - let trait_resolutions_direct = map.entry(segment.ident).or_default(); - if let Some((_, span_direct)) = trait_resolutions_direct - .iter() - .find(|(res_direct, _)| *res_direct == res_where) { + let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); + for (trait_ref, span) in traits { + let key = (path.res, trait_ref); + if where_predicates.contains(&key) { span_lint_and_help( cx, TRAIT_DUPLICATION_IN_BOUNDS, - *span_direct, + span, "this trait bound is already specified in the where clause", None, "consider removing this trait bound", - ); - } - else { - trait_resolutions_direct.push((res_where, span_where)); + ); } } } @@ -270,23 +296,11 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { } } -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] struct ComparableTraitRef(Res, Vec<Res>); - -fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if gen.span.from_expansion() { - return; - } - - for predicate in gen.predicates { - if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate { - let msg = if predicate.in_where_clause() { - "these where clauses contain repeated elements" - } else { - "these bounds contain repeated elements" - }; - rollup_traits(cx, bound_predicate.bounds, msg); - } +impl Default for ComparableTraitRef { + fn default() -> Self { + Self(Res::Err, Vec::new()) } } @@ -331,7 +345,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef { ) } -fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) { +fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> Vec<(ComparableTraitRef, Span)> { let mut map = FxHashMap::default(); let mut repeated_res = false; @@ -343,23 +357,33 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) { } }; + let mut i = 0usize; for bound in bounds.iter().filter_map(only_comparable_trait_refs) { let (comparable_bound, span_direct) = bound; - if map.insert(comparable_bound, span_direct).is_some() { - repeated_res = true; + match map.entry(comparable_bound) { + Entry::Occupied(_) => repeated_res = true, + Entry::Vacant(e) => { + e.insert((span_direct, i)); + i += 1; + }, } } + // 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); + } + if_chain! { if repeated_res; if let [first_trait, .., last_trait] = bounds; then { let all_trait_span = first_trait.span().to(last_trait.span()); - let mut traits = map.values() - .filter_map(|span| snippet_opt(cx, *span)) + let traits = comparable_bounds.iter() + .filter_map(|&(_, span)| snippet_opt(cx, span)) .collect::<Vec<_>>(); - traits.sort_unstable(); let traits = traits.join(" + "); span_lint_and_sugg( @@ -373,4 +397,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) { ); } } + + comparable_bounds } diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 5f3e98144f4..424a6e9264e 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -9,6 +9,7 @@ mod transmute_ptr_to_ref; mod transmute_ref_to_ref; mod transmute_undefined_repr; mod transmutes_expressible_as_ptr_casts; +mod transmuting_null; mod unsound_collection_transmute; mod useless_transmute; mod utils; @@ -386,6 +387,28 @@ declare_clippy_lint! { "transmute to or from a type with an undefined representation" } +declare_clippy_lint! { + /// ### What it does + /// Checks for transmute calls which would receive a null pointer. + /// + /// ### Why is this bad? + /// Transmuting a null pointer is undefined behavior. + /// + /// ### Known problems + /// Not all cases can be detected at the moment of this writing. + /// For example, variables which hold a null pointer and are then fed to a `transmute` + /// call, aren't detectable yet. + /// + /// ### Example + /// ```rust + /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; + /// ``` + #[clippy::version = "1.35.0"] + pub TRANSMUTING_NULL, + correctness, + "transmutes from a null pointer to a reference, which is undefined behavior" +} + pub struct Transmute { msrv: Option<RustcVersion>, } @@ -404,6 +427,7 @@ impl_lint_pass!(Transmute => [ UNSOUND_COLLECTION_TRANSMUTE, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, + TRANSMUTING_NULL, ]); impl Transmute { #[must_use] @@ -436,6 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) + | transmuting_null::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 20b348fc14f..b6d7d9f5b42 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -5,9 +5,9 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; -use rustc_span::Span; +use rustc_span::DUMMY_SP; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, @@ -18,116 +18,89 @@ pub(super) fn check<'tcx>( let mut to_ty = cx.tcx.erase_regions(to_ty_orig); while from_ty != to_ty { - match reduce_refs(cx, e.span, from_ty, to_ty) { - ReducedTys::FromFatPtr { - unsized_ty, - to_ty: to_sub_ty, - } => match reduce_ty(cx, to_sub_ty) { - ReducedTy::TypeErasure => break, - ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, - ReducedTy::Ref(to_sub_ty) => { - from_ty = unsized_ty; - to_ty = to_sub_ty; - continue; - }, - _ => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), - |diag| { - if from_ty_orig.peel_refs() != unsized_ty { - diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty)); - } - }, - ); - return true; - }, + let reduced_tys = reduce_refs(cx, from_ty, to_ty); + match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) { + // Various forms of type erasure. + (ReducedTy::TypeErasure { raw_ptr_only: false }, _) + | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false, + (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false, + (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false, + + // `Repr(C)` <-> unordered type. + // If the first field of the `Repr(C)` type matches then the transmute is ok + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty)) + | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::ToFatPtr { - unsized_ty, - from_ty: from_sub_ty, - } => match reduce_ty(cx, from_sub_ty) { - ReducedTy::TypeErasure => break, - ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break, - ReducedTy::Ref(from_sub_ty) => { - from_ty = from_sub_ty; - to_ty = unsized_ty; - continue; - }, - _ => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), - |diag| { - if to_ty_orig.peel_refs() != unsized_ty { - diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty)); - } - }, - ); - return true; - }, + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::ToPtr { - from_ty: from_sub_ty, - to_ty: to_sub_ty, - } => match reduce_ty(cx, from_sub_ty) { - ReducedTy::UnorderedFields(from_ty) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), - |diag| { - if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); - } - }, - ); - return true; - }, - ReducedTy::Ref(from_sub_ty) => { - from_ty = from_sub_ty; - to_ty = to_sub_ty; - continue; - }, - _ => break, + (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) + if reduced_tys.from_fat_ptr => + { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::FromPtr { - from_ty: from_sub_ty, - to_ty: to_sub_ty, - } => match reduce_ty(cx, to_sub_ty) { - ReducedTy::UnorderedFields(to_ty) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute to `{}` which has an undefined layout", to_ty_orig), - |diag| { - if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); - } - }, - ); - return true; - }, - ReducedTy::Ref(to_sub_ty) => { - from_ty = from_sub_ty; - to_ty = to_sub_ty; - continue; - }, - _ => break, + + // ptr <-> ptr + (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty)) + if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) + && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) => + { + from_ty = from_sub_ty; + to_ty = to_sub_ty; + continue; }, - ReducedTys::Other { - from_ty: from_sub_ty, - to_ty: to_sub_ty, - } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) { - (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false, - (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { - let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) + + // fat ptr <-> (*size, *size) + (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty)) + if reduced_tys.from_fat_ptr && is_size_pair(to_ty) => + { + return false; + }, + (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_)) + if reduced_tys.to_fat_ptr && is_size_pair(from_ty) => + { + return false; + }, + + // fat ptr -> some struct | some struct -> fat ptr + (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + |diag| { + if from_ty_orig.peel_refs() != from_ty.peel_refs() { + diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + } + }, + ); + return true; + }, + (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute to `{}` which has an undefined layout", to_ty_orig), + |diag| { + if to_ty_orig.peel_refs() != to_ty.peel_refs() { + diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + } + }, + ); + return true; + }, + + (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { + let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) = (from_ty.kind(), to_ty.kind()) && from_def == to_def { @@ -138,79 +111,72 @@ pub(super) fn check<'tcx>( } else { None }; - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!( - "transmute from `{}` to `{}`, both of which have an undefined layout", - from_ty_orig, to_ty_orig - ), - |diag| { - if let Some(same_adt_did) = same_adt_did { - diag.note(&format!( - "two instances of the same generic type (`{}`) may have different layouts", - cx.tcx.item_name(same_adt_did) - )); - } else { - if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); - } - if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); - } - } - }, - ); - return true; - }, - ( - ReducedTy::UnorderedFields(from_ty), - ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_), - ) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute from `{}` which has an undefined layout", from_ty_orig), - |diag| { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!( + "transmute from `{}` to `{}`, both of which have an undefined layout", + from_ty_orig, to_ty_orig + ), + |diag| { + if let Some(same_adt_did) = same_adt_did { + diag.note(&format!( + "two instances of the same generic type (`{}`) may have different layouts", + cx.tcx.item_name(same_adt_did) + )); + } else { if from_ty_orig.peel_refs() != from_ty { diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); } - }, - ); - return true; - }, - ( - ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_), - ReducedTy::UnorderedFields(to_ty), - ) => { - span_lint_and_then( - cx, - TRANSMUTE_UNDEFINED_REPR, - e.span, - &format!("transmute into `{}` which has an undefined layout", to_ty_orig), - |diag| { if to_ty_orig.peel_refs() != to_ty { diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); } - }, - ); - return true; - }, - (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => { - from_ty = from_sub_ty; - to_ty = to_sub_ty; - continue; - }, - ( - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, - ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param, - ) - | ( - ReducedTy::UnorderedFields(_) | ReducedTy::Param, - ReducedTy::UnorderedFields(_) | ReducedTy::Param, - ) => break, + } + }, + ); + return true; + }, + ( + ReducedTy::UnorderedFields(from_ty), + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ) => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute from `{}` which has an undefined layout", from_ty_orig), + |diag| { + if from_ty_orig.peel_refs() != from_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", from_ty)); + } + }, + ); + return true; + }, + ( + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::UnorderedFields(to_ty), + ) => { + span_lint_and_then( + cx, + TRANSMUTE_UNDEFINED_REPR, + e.span, + &format!("transmute into `{}` which has an undefined layout", to_ty_orig), + |diag| { + if to_ty_orig.peel_refs() != to_ty { + diag.note(&format!("the contained type `{}` has an undefined layout", to_ty)); + } + }, + ); + return true; + }, + ( + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ) + | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => { + break; }, } } @@ -218,64 +184,64 @@ pub(super) fn check<'tcx>( false } -enum ReducedTys<'tcx> { - FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, - ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> }, - ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, - FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, - Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> }, +#[expect(clippy::struct_excessive_bools)] +struct ReducedTys<'tcx> { + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + from_raw_ptr: bool, + to_raw_ptr: bool, + from_fat_ptr: bool, + to_fat_ptr: bool, } /// Remove references so long as both types are references. -fn reduce_refs<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - mut from_ty: Ty<'tcx>, - mut to_ty: Ty<'tcx>, -) -> ReducedTys<'tcx> { - loop { - return match (from_ty.kind(), to_ty.kind()) { +fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> { + let mut from_raw_ptr = false; + let mut to_raw_ptr = false; + let (from_fat_ptr, to_fat_ptr) = loop { + break match (from_ty.kind(), to_ty.kind()) { ( &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), ) => { + from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); from_ty = from_sub_ty; + to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); to_ty = to_sub_ty; continue; }, (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _) - if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) => + if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) => { - ReducedTys::FromFatPtr { unsized_ty, to_ty } + (true, false) }, (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))) - if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) => + if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) => { - ReducedTys::ToFatPtr { unsized_ty, from_ty } + (false, true) }, - (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => { - ReducedTys::FromPtr { from_ty, to_ty } - }, - (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => { - ReducedTys::ToPtr { from_ty, to_ty } - }, - _ => ReducedTys::Other { from_ty, to_ty }, + _ => (false, false), }; + }; + ReducedTys { + from_ty, + to_ty, + from_raw_ptr, + to_raw_ptr, + from_fat_ptr, + to_fat_ptr, } } enum ReducedTy<'tcx> { /// The type can be used for type erasure. - TypeErasure, + TypeErasure { raw_ptr_only: bool }, /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero /// sized fields with a defined order. - OrderedFields(Ty<'tcx>), + /// The second value is the first non-zero sized type. + OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>), /// The type is a struct containing multiple non-zero sized fields with no defined order. UnorderedFields(Ty<'tcx>), - /// The type is a reference to the contained type. - Ref(Ty<'tcx>), - /// The type is a generic parameter. - Param, /// Any other type. Other(Ty<'tcx>), } @@ -285,16 +251,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> loop { ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty); return match *ty.kind() { - ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure, + ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => { + ReducedTy::TypeErasure { raw_ptr_only: false } + }, ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { ty = sub_ty; continue; }, - ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure, + ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false }, ty::Tuple(args) => { let mut iter = args.iter(); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { - return ReducedTy::OrderedFields(ty); + return ReducedTy::OrderedFields(ty, None); }; if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; @@ -309,27 +277,25 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .iter() .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { - return ReducedTy::TypeErasure; + return ReducedTy::TypeErasure { raw_ptr_only: false }; }; if iter.all(|ty| is_zero_sized_ty(cx, ty)) { ty = sized_ty; continue; } if def.repr().inhibit_struct_field_reordering_opt() { - ReducedTy::OrderedFields(ty) + ReducedTy::OrderedFields(ty, Some(sized_ty)) } else { ReducedTy::UnorderedFields(ty) } }, ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => { - ReducedTy::TypeErasure + ReducedTy::TypeErasure { raw_ptr_only: false } }, // TODO: Check if the conversion to or from at least one of a union's fields is valid. - ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure, - ty::Foreign(_) => ReducedTy::TypeErasure, - ty::Ref(_, ty, _) => ReducedTy::Ref(ty), - ty::RawPtr(ty) => ReducedTy::Ref(ty.ty), - ty::Param(_) => ReducedTy::Param, + ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false }, + ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false }, + ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true }, _ => ReducedTy::Other(ty), }; } diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs new file mode 100644 index 00000000000..d8e349af7af --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs @@ -0,0 +1,61 @@ +use clippy_utils::consts::{constant_context, Constant}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_path_diagnostic_item; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::symbol::sym; + +use super::TRANSMUTING_NULL; + +const LINT_MSG: &str = "transmuting a known null pointer into a reference"; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool { + if !to_ty.is_ref() { + return false; + } + + // Catching transmute over constants that resolve to `null`. + let mut const_eval_context = constant_context(cx, cx.typeck_results()); + if_chain! { + if let ExprKind::Path(ref _qpath) = arg.kind; + if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); + if x == 0; + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; + } + } + + // Catching: + // `std::mem::transmute(0 as *const i32)` + if_chain! { + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; + if let ExprKind::Lit(ref lit) = inner_expr.kind; + if let LitKind::Int(0, _) = lit.node; + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; + } + } + + // Catching: + // `std::mem::transmute(std::ptr::null::<i32>())` + if_chain! { + if let ExprKind::Call(func1, []) = arg.kind; + if is_path_diagnostic_item(cx, func1, sym::ptr_null); + then { + span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG); + return true; + } + } + + // FIXME: + // Also catch transmutations of variables which are known nulls. + // To do this, MIR const propagation seems to be the better tool. + // Whenever MIR const prop routines are more developed, this will + // become available. As of this writing (25/03/19) it is not yet. + false +} diff --git a/src/tools/clippy/clippy_lints/src/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmuting_null.rs deleted file mode 100644 index 7939dfedc3a..00000000000 --- a/src/tools/clippy/clippy_lints/src/transmuting_null.rs +++ /dev/null @@ -1,89 +0,0 @@ -use clippy_utils::consts::{constant_context, Constant}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_expr_diagnostic_item; -use if_chain::if_chain; -use rustc_ast::LitKind; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for transmute calls which would receive a null pointer. - /// - /// ### Why is this bad? - /// Transmuting a null pointer is undefined behavior. - /// - /// ### Known problems - /// Not all cases can be detected at the moment of this writing. - /// For example, variables which hold a null pointer and are then fed to a `transmute` - /// call, aren't detectable yet. - /// - /// ### Example - /// ```rust - /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) }; - /// ``` - #[clippy::version = "1.35.0"] - pub TRANSMUTING_NULL, - correctness, - "transmutes from a null pointer to a reference, which is undefined behavior" -} - -declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]); - -const LINT_MSG: &str = "transmuting a known null pointer into a reference"; - -impl<'tcx> LateLintPass<'tcx> for TransmutingNull { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if_chain! { - if let ExprKind::Call(func, [arg]) = expr.kind; - if is_expr_diagnostic_item(cx, func, sym::transmute); - - then { - // Catching transmute over constants that resolve to `null`. - let mut const_eval_context = constant_context(cx, cx.typeck_results()); - if_chain! { - if let ExprKind::Path(ref _qpath) = arg.kind; - if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg); - if x == 0; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) - } - } - - // Catching: - // `std::mem::transmute(0 as *const i32)` - if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; - if let ExprKind::Lit(ref lit) = inner_expr.kind; - if let LitKind::Int(0, _) = lit.node; - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) - } - } - - // Catching: - // `std::mem::transmute(std::ptr::null::<i32>())` - if_chain! { - if let ExprKind::Call(func1, []) = arg.kind; - if is_expr_diagnostic_item(cx, func1, sym::ptr_null); - then { - span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) - } - } - - // FIXME: - // Also catch transmutations of variables which are known nulls. - // To do this, MIR const propagation seems to be the better tool. - // Whenever MIR const prop routines are more developed, this will - // become available. As of this writing (25/03/19) it is not yet. - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs index cc64d17be05..8980283e5c8 100644 --- a/src/tools/clippy/clippy_lints/src/unicode.rs +++ b/src/tools/clippy/clippy_lints/src/unicode.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_lint_allowed; +use clippy_utils::macros::span_is_local; use clippy_utils::source::snippet; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -98,6 +99,10 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String { } fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { + if !span_is_local(span) { + return; + } + let string = snippet(cx, span, ""); if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { span_lint_and_sugg( @@ -113,6 +118,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { Applicability::MachineApplicable, ); } + if string.chars().any(|c| c as u32 > 0x7F) { span_lint_and_sugg( cx, @@ -128,6 +134,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { Applicability::MachineApplicable, ); } + if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 9f4c5555f11..9a41603f2f4 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { /// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000); /// vec.set_len(1000); // `MaybeUninit` can be uninitialized /// ``` - /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available: + /// 3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available: /// ```rust,ignore /// let mut vec: Vec<u8> = Vec::with_capacity(1000); /// let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit<u8>]` diff --git a/src/tools/clippy/clippy_lints/src/unit_hash.rs b/src/tools/clippy/clippy_lints/src/unit_hash.rs deleted file mode 100644 index 88ca0cb20a1..00000000000 --- a/src/tools/clippy/clippy_lints/src/unit_hash.rs +++ /dev/null @@ -1,78 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Detects `().hash(_)`. - /// - /// ### Why is this bad? - /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op. - /// - /// ### Example - /// ```rust - /// # use std::hash::Hash; - /// # use std::collections::hash_map::DefaultHasher; - /// # enum Foo { Empty, WithValue(u8) } - /// # use Foo::*; - /// # let mut state = DefaultHasher::new(); - /// # let my_enum = Foo::Empty; - /// match my_enum { - /// Empty => ().hash(&mut state), - /// WithValue(x) => x.hash(&mut state), - /// } - /// ``` - /// Use instead: - /// ```rust - /// # use std::hash::Hash; - /// # use std::collections::hash_map::DefaultHasher; - /// # enum Foo { Empty, WithValue(u8) } - /// # use Foo::*; - /// # let mut state = DefaultHasher::new(); - /// # let my_enum = Foo::Empty; - /// match my_enum { - /// Empty => 0_u8.hash(&mut state), - /// WithValue(x) => x.hash(&mut state), - /// } - /// ``` - #[clippy::version = "1.58.0"] - pub UNIT_HASH, - correctness, - "hashing a unit value, which does nothing" -} -declare_lint_pass!(UnitHash => [UNIT_HASH]); - -impl<'tcx> LateLintPass<'tcx> for UnitHash { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind; - if name_ident.ident.name == sym::hash; - if let [recv, state_param] = args; - if cx.typeck_results().expr_ty(recv).is_unit(); - then { - span_lint_and_then( - cx, - UNIT_HASH, - expr.span, - "this call to `hash` on the unit type will do nothing", - |diag| { - diag.span_suggestion( - expr.span, - "remove the call to `hash` or consider using", - format!( - "0_u8.hash({})", - snippet(cx, state_param.span, ".."), - ), - Applicability::MaybeIncorrect, - ); - diag.note("the implementation of `Hash` for `()` is a no-op"); - } - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index f4f5a4336a3..a5afbb8ff9d 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ( ret_expr.span, if inner_type.is_unit() { - "".to_string() + String::new() } else { snippet(cx, arg.span.source_callsite(), "..").to_string() } diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs new file mode 100644 index 00000000000..ac73173697e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -0,0 +1,225 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable}; +use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators}; +use rustc_ast::Mutability; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the creation of a `peekable` iterator that is never `.peek()`ed + /// + /// ### Why is this bad? + /// Creating a peekable iterator without using any of its methods is likely a mistake, + /// or just a leftover after a refactor. + /// + /// ### Example + /// ```rust + /// let collection = vec![1, 2, 3]; + /// let iter = collection.iter().peekable(); + /// + /// for item in iter { + /// // ... + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// let collection = vec![1, 2, 3]; + /// let iter = collection.iter(); + /// + /// for item in iter { + /// // ... + /// } + /// ``` + #[clippy::version = "1.64.0"] + pub UNUSED_PEEKABLE, + suspicious, + "creating a peekable iterator without using any of its methods" +} + +declare_lint_pass!(UnusedPeekable => [UNUSED_PEEKABLE]); + +impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + // Don't lint `Peekable`s returned from a block + if let Some(expr) = block.expr + && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr)) + && match_type(cx, ty, &paths::PEEKABLE) + { + return; + } + + for (idx, stmt) in block.stmts.iter().enumerate() { + if !stmt.span.from_expansion() + && let StmtKind::Local(local) = stmt.kind + && let PatKind::Binding(_, binding, ident, _) = local.pat.kind + && let Some(init) = local.init + && !init.span.from_expansion() + && let Some(ty) = cx.typeck_results().expr_ty_opt(init) + && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && match_type(cx, ty, &paths::PEEKABLE) + { + let mut vis = PeekableVisitor::new(cx, binding); + + if idx + 1 == block.stmts.len() && block.expr.is_none() { + return; + } + + for stmt in &block.stmts[idx..] { + vis.visit_stmt(stmt); + } + + if let Some(expr) = block.expr { + vis.visit_expr(expr); + } + + if !vis.found_peek_call { + span_lint_and_help( + cx, + UNUSED_PEEKABLE, + ident.span, + "`peek` never called on `Peekable` iterator", + None, + "consider removing the call to `peekable`" + ); + } + } + } + } +} + +struct PeekableVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + expected_hir_id: HirId, + found_peek_call: bool, +} + +impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self { + Self { + cx, + expected_hir_id, + found_peek_call: false, + } + } +} + +impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> { + fn visit_expr(&mut self, ex: &'_ Expr<'_>) { + if self.found_peek_call { + return; + } + + if path_to_local_id(ex, self.expected_hir_id) { + for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) { + match node { + Node::Expr(expr) => { + match expr.kind { + // some_function(peekable) + // + // If the Peekable is passed to a function, stop + ExprKind::Call(_, args) => { + if let Some(func_did) = fn_def_id(self.cx, expr) + && let Ok(into_iter_did) = self + .cx + .tcx + .lang_items() + .require(LangItem::IntoIterIntoIter) + && func_did == into_iter_did + { + // Probably a for loop desugar, stop searching + return; + } + + if args.iter().any(|arg| { + matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg) + }) { + self.found_peek_call = true; + return; + } + }, + // Catch anything taking a Peekable mutably + ExprKind::MethodCall( + PathSegment { + ident: method_name_ident, + .. + }, + [self_arg, remaining_args @ ..], + _, + ) => { + let method_name = method_name_ident.name.as_str(); + + // `Peekable` methods + if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq") + && arg_is_mut_peekable(self.cx, self_arg) + { + self.found_peek_call = true; + return; + } + + // foo.some_method() excluding Iterator methods + if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) + && !is_trait_method(self.cx, expr, sym::Iterator) + { + self.found_peek_call = true; + return; + } + + // foo.by_ref(), keep checking for `peek` + if method_name == "by_ref" { + continue; + } + + return; + }, + ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => { + }, + ExprKind::AddrOf(_, Mutability::Not, _) => return, + _ => { + self.found_peek_call = true; + return; + }, + } + }, + Node::Local(Local { init: Some(init), .. }) => { + if arg_is_mut_peekable(self.cx, init) { + self.found_peek_call = true; + return; + } + + break; + }, + Node::Stmt(stmt) => match stmt.kind { + StmtKind::Expr(_) | StmtKind::Semi(_) => {}, + _ => { + self.found_peek_call = true; + return; + }, + }, + Node::Block(_) | Node::ExprField(_) => {}, + _ => { + break; + }, + } + } + } + + walk_expr(self, ex); + } +} + +fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { + if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) + && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && match_type(cx, ty, &paths::PEEKABLE) + { + true + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/unused_rounding.rs b/src/tools/clippy/clippy_lints/src/unused_rounding.rs index e1ec357838d..b8a5d4ea8c9 100644 --- a/src/tools/clippy/clippy_lints/src/unused_rounding.rs +++ b/src/tools/clippy/clippy_lints/src/unused_rounding.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```rust /// let x = 1f32; /// ``` - #[clippy::version = "1.62.0"] + #[clippy::version = "1.63.0"] pub UNUSED_ROUNDING, nursery, "Uselessly rounding a whole number floating-point literal" diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index 3faae9ac0d2..84e65d5fa0b 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -350,7 +350,7 @@ define_Conf! { /// Lint: DISALLOWED_SCRIPT_IDENTS. /// /// The list of unicode scripts allowed to be used in the scope. - (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()), + (allowed_scripts: Vec<String> = vec!["Latin".to_string()]), /// Lint: NON_SEND_FIELDS_IN_SEND_TY. /// /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. @@ -379,6 +379,10 @@ define_Conf! { /// /// Whether `dbg!` should be allowed in test functions (allow_dbg_in_tests: bool = false), + /// Lint: RESULT_LARGE_ERR + /// + /// The maximum size of the `Err`-variant in a `Result` returned from a function + (large_error_threshold: u64 = 128), } /// Search for the configuration file. diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs index 5dcacd604be..eb34085a2ab 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs @@ -593,8 +593,8 @@ fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Opt attrs.iter().find_map(|attr| { if_chain! { // Identify attribute - if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind; - if let [tool_name, attr_name] = &attr_kind.path.segments[..]; + if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind; + if let [tool_name, attr_name] = &attr_kind.item.path.segments[..]; if tool_name.ident.name == sym::clippy; if attr_name.ident.name == sym::version; if let Some(version) = attr.value_str(); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 92cf42c7ad4..b1148bccc2a 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -797,7 +797,7 @@ fn get_lint_group_and_level_or_lint( let result = cx.lint_store.check_lint_name( lint_name, Some(sym::clippy), - &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(), + &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(), ); if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { if let Some(group) = get_lint_group(cx, lint_lst[0]) { diff --git a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs deleted file mode 100644 index 0fee3e812d2..00000000000 --- a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs +++ /dev/null @@ -1,64 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; -use rustc_ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Finds occurrences of `Vec::resize(0, an_int)` - /// - /// ### Why is this bad? - /// This is probably an argument inversion mistake. - /// - /// ### Example - /// ```rust - /// vec!(1, 2, 3, 4, 5).resize(0, 5) - /// ``` - /// - /// Use instead: - /// ```rust - /// vec!(1, 2, 3, 4, 5).clear() - /// ``` - #[clippy::version = "1.46.0"] - pub VEC_RESIZE_TO_ZERO, - correctness, - "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake" -} - -declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); - -impl<'tcx> LateLintPass<'tcx> for VecResizeToZero { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let hir::ExprKind::MethodCall(path_segment, args, _) = expr.kind; - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3; - if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; - if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind; - then { - let method_call_span = expr.span.with_lo(path_segment.ident.span.lo()); - span_lint_and_then( - cx, - VEC_RESIZE_TO_ZERO, - expr.span, - "emptying a vector with `resize`", - |db| { - db.help("the arguments may be inverted..."); - db.span_suggestion( - method_call_span, - "...or you can empty the vector with", - "clear()".to_string(), - Applicability::MaybeIncorrect, - ); - }, - ); - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs b/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs deleted file mode 100644 index afd0077a658..00000000000 --- a/src/tools/clippy/clippy_lints/src/verbose_file_reads.rs +++ /dev/null @@ -1,88 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::paths; -use clippy_utils::ty::match_type; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for use of File::read_to_end and File::read_to_string. - /// - /// ### Why is this bad? - /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values. - /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) - /// - /// ### Example - /// ```rust,no_run - /// # use std::io::Read; - /// # use std::fs::File; - /// let mut f = File::open("foo.txt").unwrap(); - /// let mut bytes = Vec::new(); - /// f.read_to_end(&mut bytes).unwrap(); - /// ``` - /// Can be written more concisely as - /// ```rust,no_run - /// # use std::fs; - /// let mut bytes = fs::read("foo.txt").unwrap(); - /// ``` - #[clippy::version = "1.44.0"] - pub VERBOSE_FILE_READS, - restriction, - "use of `File::read_to_end` or `File::read_to_string`" -} - -declare_lint_pass!(VerboseFileReads => [VERBOSE_FILE_READS]); - -impl<'tcx> LateLintPass<'tcx> for VerboseFileReads { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if is_file_read_to_end(cx, expr) { - span_lint_and_help( - cx, - VERBOSE_FILE_READS, - expr.span, - "use of `File::read_to_end`", - None, - "consider using `fs::read` instead", - ); - } else if is_file_read_to_string(cx, expr) { - span_lint_and_help( - cx, - VERBOSE_FILE_READS, - expr.span, - "use of `File::read_to_string`", - None, - "consider using `fs::read_to_string` instead", - ); - } - } -} - -fn is_file_read_to_end<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - if_chain! { - if let ExprKind::MethodCall(method_name, [recv, ..], _) = expr.kind; - if method_name.ident.as_str() == "read_to_end"; - if let ExprKind::Path(QPath::Resolved(None, _)) = &recv.kind; - let ty = cx.typeck_results().expr_ty(recv); - if match_type(cx, ty, &paths::FILE); - then { - return true - } - } - false -} - -fn is_file_read_to_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - if_chain! { - if let ExprKind::MethodCall(method_name, exprs, _) = expr.kind; - if method_name.ident.as_str() == "read_to_string"; - if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind; - let ty = cx.typeck_results().expr_ty(&exprs[0]); - if match_type(cx, ty, &paths::FILE); - then { - return true - } - } - false -} diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index fa2383066f3..347165d9704 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -3,8 +3,9 @@ use std::iter; use std::ops::{Deref, Range}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; +use rustc_ast::ptr::P; use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::{Applicability, DiagnosticBuilder}; @@ -256,6 +257,28 @@ declare_clippy_lint! { "writing a literal with a format string" } +declare_clippy_lint! { + /// ### What it does + /// This lint warns when a named parameter in a format string is used as a positional one. + /// + /// ### Why is this bad? + /// It may be confused for an assignment and obfuscates which parameter is being used. + /// + /// ### Example + /// ```rust + /// println!("{}", x = 10); + /// ``` + /// + /// Use instead: + /// ```rust + /// println!("{x}", x = 10); + /// ``` + #[clippy::version = "1.63.0"] + pub POSITIONAL_NAMED_FORMAT_PARAMETERS, + suspicious, + "named parameter in a format string is used positionally" +} + #[derive(Default)] pub struct Write { in_debug_impl: bool, @@ -270,7 +293,8 @@ impl_lint_pass!(Write => [ PRINT_LITERAL, WRITE_WITH_NEWLINE, WRITELN_EMPTY_STRING, - WRITE_LITERAL + WRITE_LITERAL, + POSITIONAL_NAMED_FORMAT_PARAMETERS, ]); impl EarlyLintPass for Write { @@ -408,6 +432,7 @@ fn newline_span(fmtstr: &StrLit) -> (Span, bool) { #[derive(Default)] struct SimpleFormatArgs { unnamed: Vec<Vec<Span>>, + complex_unnamed: Vec<Vec<Span>>, named: Vec<(Symbol, Vec<Span>)>, } impl SimpleFormatArgs { @@ -419,6 +444,10 @@ impl SimpleFormatArgs { }) } + fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> { + self.complex_unnamed.iter().map(Vec::as_slice) + } + fn get_named(&self, n: &Path) -> &[Span] { self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice()) } @@ -479,6 +508,61 @@ impl SimpleFormatArgs { }, }; } + + fn push_to_complex(&mut self, span: Span, position: usize) { + if self.complex_unnamed.len() <= position { + self.complex_unnamed.resize_with(position, Vec::new); + self.complex_unnamed.push(vec![span]); + } else { + let args: &mut Vec<Span> = &mut self.complex_unnamed[position]; + args.push(span); + } + } + + fn push_complex( + &mut self, + cx: &EarlyContext<'_>, + arg: rustc_parse_format::Argument<'_>, + str_lit_span: Span, + fmt_span: Span, + ) { + use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam, CountIsStar}; + + let snippet = snippet_opt(cx, fmt_span); + + let end = snippet + .as_ref() + .and_then(|s| s.find(':')) + .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize)); + + if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) { + let span = fmt_span.from_inner(InnerSpan::new(1, end)); + self.push_to_complex(span, n); + }; + + if let (CountIsParam(n) | CountIsStar(n), Some(span)) = (arg.format.precision, arg.format.precision_span) { + // We need to do this hack as precision spans should be converted from .* to .foo$ + let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() { + 0 + } else { + 1 + }; + + let span = str_lit_span.from_inner(InnerSpan { + start: span.start + 1, + end: span.end - hack, + }); + self.push_to_complex(span, n); + }; + + if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) { + let span = str_lit_span.from_inner(InnerSpan { + start: span.start, + end: span.end - 1, + }); + self.push_to_complex(span, n); + }; + } } impl Write { @@ -511,8 +595,8 @@ impl Write { // FIXME: modify rustc's fmt string parser to give us the current span span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); } - args.push(arg, span); + args.push_complex(cx, arg, str_lit.span, span); } parser.errors.is_empty().then_some(args) @@ -566,6 +650,7 @@ impl Write { let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL }; let mut unnamed_args = args.get_unnamed(); + let mut complex_unnamed_args = args.get_complex_unnamed(); loop { if !parser.eat(&token::Comma) { return (Some(fmtstr), expr); @@ -577,11 +662,20 @@ impl Write { } else { return (Some(fmtstr), None); }; + let complex_unnamed_arg = complex_unnamed_args.next(); + let (fmt_spans, lit) = match &token_expr.kind { ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit), - ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) { - (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit), - _ => continue, + ExprKind::Assign(lhs, rhs, _) => { + if let Some(span) = complex_unnamed_arg { + for x in span { + Self::report_positional_named_param(cx, *x, lhs, rhs); + } + } + match (&lhs.kind, &rhs.kind) { + (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit), + _ => continue, + } }, _ => { unnamed_args.next(); @@ -637,6 +731,29 @@ impl Write { } } + fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) { + if let ExprKind::Path(_, _p) = &lhs.kind { + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability); + // We need to do this hack as precision spans should be converted from .* to .foo$ + let hack = snippet(cx, span, "").contains('*'); + + span_lint_and_sugg( + cx, + POSITIONAL_NAMED_FORMAT_PARAMETERS, + span, + &format!("named parameter {} is used as a positional parameter", name), + "replace it with", + if hack { + format!("{}$", name) + } else { + format!("{}", name) + }, + applicability, + ); + }; + } + fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == kw::Empty { diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index a688050f63a..c36bca06507 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] arrayvec = { version = "0.7", default-features = false } if_chain = "1.0" +itertools = "0.10.1" rustc-semver = "1.1" [features] diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 313f1f1d9a6..997e773b5da 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -28,6 +28,7 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; +extern crate rustc_parse_format; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; @@ -87,6 +88,7 @@ use rustc_hir::{ Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; +use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; @@ -104,6 +106,7 @@ use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; +use rustc_span::source_map::SourceMap; use rustc_span::sym; use rustc_span::symbol::{kw, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -372,15 +375,19 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. /// -/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item. +/// Please use `is_path_diagnostic_item` if the target is a diagnostic item. pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments)) } -/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given -/// diagnostic item. -pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { - path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id)) +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if +/// it matches the given diagnostic item. +pub fn is_path_diagnostic_item<'tcx>( + cx: &LateContext<'_>, + maybe_path: &impl MaybePath<'tcx>, + diag_item: Symbol, +) -> bool { + path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id)) } /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the @@ -2274,6 +2281,18 @@ pub fn walk_to_expr_usage<'tcx, T>( None } +/// Checks whether a given span has any comment token +/// This checks for all types of comment: line "//", block "/**", doc "///" "//!" +pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { + let Ok(snippet) = sm.span_to_snippet(span) else { return false }; + return tokenize(&snippet).any(|token| { + matches!( + token.kind, + TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } + ) + }); +} + macro_rules! op_utils { ($($name:ident $assign:ident)*) => { /// Binary operation traits like `LangItem::Add` diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index a268e339bb1..43e53f3feeb 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -1,16 +1,21 @@ #![allow(clippy::similar_names)] // `expr` and `expn` +use crate::is_path_diagnostic_item; +use crate::source::snippet_opt; use crate::visitors::expr_visitor_no_bodies; use arrayvec::ArrayVec; -use if_chain::if_chain; +use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; +use rustc_lexer::unescape::unescape_literal; +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use rustc_lint::LateContext; +use rustc_parse_format::{self as rpf, Alignment}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; -use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol}; +use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -332,121 +337,495 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } -/// A parsed `format_args!` expansion +/// The format string doesn't exist in the HIR, so we reassemble it from source code #[derive(Debug)] -pub struct FormatArgsExpn<'tcx> { - /// Span of the first argument, the format string - pub format_string_span: Span, - /// The format string split by formatted args like `{..}` - pub format_string_parts: Vec<Symbol>, - /// Values passed after the format string - pub value_args: Vec<&'tcx Expr<'tcx>>, - /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`) - pub formatters: Vec<(usize, Symbol)>, - /// List of `fmt::v1::Argument { .. }` expressions. If this is empty, - /// then `formatters` represents the format args (`{..}`). - /// If this is non-empty, it represents the format args, and the `position` - /// parameters within the struct expressions are indexes of `formatters`. - pub specs: Vec<&'tcx Expr<'tcx>>, +pub struct FormatString { + /// Span of the whole format string literal, including `[r#]"`. + pub span: Span, + /// Snippet of the whole format string literal, including `[r#]"`. + pub snippet: String, + /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side. + pub style: Option<usize>, + /// The unescaped value of the format string, e.g. `"val – {}"` for the literal + /// `"val \u{2013} {}"`. + pub unescaped: String, + /// The format string split by format args like `{..}`. + pub parts: Vec<Symbol>, } -impl<'tcx> FormatArgsExpn<'tcx> { - /// Parses an expanded `format_args!` or `format_args_nl!` invocation - pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> { - macro_backtrace(expr.span).find(|macro_call| { - matches!( - cx.tcx.item_name(macro_call.def_id), - sym::const_format_args | sym::format_args | sym::format_args_nl - ) - })?; - let mut format_string_span: Option<Span> = None; - let mut format_string_parts: Vec<Symbol> = Vec::new(); - let mut value_args: Vec<&Expr<'_>> = Vec::new(); - let mut formatters: Vec<(usize, Symbol)> = Vec::new(); - let mut specs: Vec<&Expr<'_>> = Vec::new(); - expr_visitor_no_bodies(|e| { - // if we're still inside of the macro definition... - if e.span.ctxt() == expr.span.ctxt() { - // ArgumentV1::new_<format_trait>(<value>) - if_chain! { - if let ExprKind::Call(callee, [val]) = e.kind; - if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; - if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind; - if path.segments.last().unwrap().ident.name == sym::ArgumentV1; - if seg.ident.name.as_str().starts_with("new_"); - then { - let val_idx = if_chain! { - if val.span.ctxt() == expr.span.ctxt(); - if let ExprKind::Field(_, field) = val.kind; - if let Ok(idx) = field.name.as_str().parse(); - then { - // tuple index - idx - } else { - // assume the value expression is passed directly - formatters.len() - } - }; - let fmt_trait = match seg.ident.name.as_str() { - "new_display" => "Display", - "new_debug" => "Debug", - "new_lower_exp" => "LowerExp", - "new_upper_exp" => "UpperExp", - "new_octal" => "Octal", - "new_pointer" => "Pointer", - "new_binary" => "Binary", - "new_lower_hex" => "LowerHex", - "new_upper_hex" => "UpperHex", - _ => unreachable!(), - }; - formatters.push((val_idx, Symbol::intern(fmt_trait))); - } - } - if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind { - if path.segments.last().unwrap().ident.name == sym::Argument { - specs.push(e); - } +impl FormatString { + fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> { + // format_args!(r"a {} b \", 1); + // + // expands to + // + // ::core::fmt::Arguments::new_v1(&["a ", " b \\"], + // &[::core::fmt::ArgumentV1::new_display(&1)]); + // + // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"` + let span = pieces.span; + let snippet = snippet_opt(cx, span)?; + + let (inner, style) = match tokenize(&snippet).next()?.kind { + TokenKind::Literal { kind, .. } => { + let style = match kind { + LiteralKind::Str { .. } => None, + LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()), + _ => return None, + }; + + let start = style.map_or(1, |n| 2 + n); + let end = snippet.len() - style.map_or(1, |n| 1 + n); + + (&snippet[start..end], style) + }, + _ => return None, + }; + + let mode = if style.is_some() { + unescape::Mode::RawStr + } else { + unescape::Mode::Str + }; + + let mut unescaped = String::with_capacity(inner.len()); + unescape_literal(inner, mode, &mut |_, ch| { + unescaped.push(ch.unwrap()); + }); + + let mut parts = Vec::new(); + expr_visitor_no_bodies(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(symbol, _) = lit.node { + parts.push(symbol); } - // walk through the macro expansion - return true; } - // assume that the first expr with a differing context represents - // (and has the span of) the format string - if format_string_span.is_none() { - format_string_span = Some(e.span); - let span = e.span; - // walk the expr and collect string literals which are format string parts - expr_visitor_no_bodies(|e| { - if e.span.ctxt() != span.ctxt() { - // defensive check, probably doesn't happen - return false; - } - if let ExprKind::Lit(lit) = &e.kind { - if let LitKind::Str(symbol, _s) = lit.node { - format_string_parts.push(symbol); - } - } - true - }) - .visit_expr(e); + + true + }) + .visit_expr(pieces); + + Some(Self { + span, + snippet, + style, + unescaped, + parts, + }) + } +} + +struct FormatArgsValues<'tcx> { + /// See `FormatArgsExpn::value_args` + value_args: Vec<&'tcx Expr<'tcx>>, + /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in + /// `value_args` + pos_to_value_index: Vec<usize>, + /// Used to check if a value is declared inline & to resolve `InnerSpan`s. + format_string_span: SpanData, +} + +impl<'tcx> FormatArgsValues<'tcx> { + fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self { + let mut pos_to_value_index = Vec::new(); + let mut value_args = Vec::new(); + expr_visitor_no_bodies(|expr| { + if expr.span.ctxt() == args.span.ctxt() { + // ArgumentV1::new_<format_trait>(<val>) + // ArgumentV1::from_usize(<val>) + if let ExprKind::Call(callee, [val]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind + && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind + && path.segments.last().unwrap().ident.name == sym::ArgumentV1 + { + let val_idx = if val.span.ctxt() == expr.span.ctxt() + && let ExprKind::Field(_, field) = val.kind + && let Ok(idx) = field.name.as_str().parse() + { + // tuple index + idx + } else { + // assume the value expression is passed directly + pos_to_value_index.len() + }; + + pos_to_value_index.push(val_idx); + } + + true } else { - // assume that any further exprs with a differing context are value args - value_args.push(e); + // assume that any expr with a differing span is a value + value_args.push(expr); + + false } - // don't walk anything not from the macro expansion (e.a. inputs) - false }) - .visit_expr(expr); - Some(FormatArgsExpn { - format_string_span: format_string_span?, - format_string_parts, + .visit_expr(args); + + Self { value_args, - formatters, - specs, + pos_to_value_index, + format_string_span, + } + } +} + +/// The positions of a format argument's value, precision and width +/// +/// A position is an index into the second argument of `Arguments::new_v1[_formatted]` +#[derive(Debug, Default, Copy, Clone)] +struct ParamPosition { + /// The position stored in `rt::v1::Argument::position`. + value: usize, + /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`. + width: Option<usize>, + /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`. + precision: Option<usize>, +} + +/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)` +fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> { + fn parse_count(expr: &Expr<'_>) -> Option<usize> { + // ::core::fmt::rt::v1::Count::Param(1usize), + if let ExprKind::Call(ctor, [val]) = expr.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind + && path.segments.last()?.ident.name == sym::Param + && let ExprKind::Lit(lit) = &val.kind + && let LitKind::Int(pos, _) = lit.node + { + Some(pos as usize) + } else { + None + } + } + + if let ExprKind::AddrOf(.., array) = fmt_arg.kind + && let ExprKind::Array(specs) = array.kind + { + Some(specs.iter().map(|spec| { + let mut position = ParamPosition::default(); + + // ::core::fmt::rt::v1::Argument { + // position: 0usize, + // format: ::core::fmt::rt::v1::FormatSpec { + // .. + // precision: ::core::fmt::rt::v1::Count::Implied, + // width: ::core::fmt::rt::v1::Count::Implied, + // }, + // } + + // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field` + if let ExprKind::Struct(_, fields, _) = spec.kind { + for field in fields { + match (field.ident.name, &field.expr.kind) { + (sym::position, ExprKind::Lit(lit)) => { + if let LitKind::Int(pos, _) = lit.node { + position.value = pos as usize; + } + }, + (sym::format, &ExprKind::Struct(_, spec_fields, _)) => { + for spec_field in spec_fields { + match spec_field.ident.name { + sym::precision => { + position.precision = parse_count(spec_field.expr); + }, + sym::width => { + position.width = parse_count(spec_field.expr); + }, + _ => {}, + } + } + }, + _ => {}, + } + } + } + + position + })) + } else { + None + } +} + +/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan` +fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span { + Span::new( + base.lo + BytePos::from_usize(inner.start), + base.lo + BytePos::from_usize(inner.end), + base.ctxt, + base.parent, + ) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FormatParamKind { + /// An implicit parameter , such as `{}` or `{:?}`. + Implicit, + /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`, + /// `{:.0$}` or `{:.*}`. + Numbered, + /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`. + Named(Symbol), + /// An implicit named parameter, such as the `y` in `format!("{y}")`. + NamedInline(Symbol), +} + +/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g. +/// +/// ``` +/// let precision = 2; +/// format!("{:.precision$}", 0.1234); +/// ``` +/// +/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234` +/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2` +#[derive(Debug, Copy, Clone)] +pub struct FormatParam<'tcx> { + /// The expression this parameter refers to. + pub value: &'tcx Expr<'tcx>, + /// How this parameter refers to its `value`. + pub kind: FormatParamKind, + /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters. + /// + /// ```text + /// format!("{}, { }, {0}, {name}", ...); + /// ^ ~~ ~ ~~~~ + /// ``` + pub span: Span, +} + +impl<'tcx> FormatParam<'tcx> { + fn new( + mut kind: FormatParamKind, + position: usize, + inner: rpf::InnerSpan, + values: &FormatArgsValues<'tcx>, + ) -> Option<Self> { + let value_index = *values.pos_to_value_index.get(position)?; + let value = *values.value_args.get(value_index)?; + let span = span_from_inner(values.format_string_span, inner); + + // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points + // into the format string + if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) { + kind = FormatParamKind::NamedInline(name); + } + + Some(Self { value, kind, span }) + } +} + +/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and +/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. +#[derive(Debug, Copy, Clone)] +pub enum Count<'tcx> { + /// Specified with a literal number, stores the value. + Is(usize, Span), + /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be + /// `FormatParamKind::Numbered`. + Param(FormatParam<'tcx>), + /// Not specified. + Implied, +} + +impl<'tcx> Count<'tcx> { + fn new( + count: rpf::Count<'_>, + position: Option<usize>, + inner: Option<rpf::InnerSpan>, + values: &FormatArgsValues<'tcx>, + ) -> Option<Self> { + Some(match count { + rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)), + rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new( + FormatParamKind::Named(Symbol::intern(name)), + position?, + span, + values, + )?), + rpf::Count::CountIsParam(_) | rpf::Count::CountIsStar(_) => { + Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?) + }, + rpf::Count::CountImplied => Self::Implied, + }) + } + + pub fn is_implied(self) -> bool { + matches!(self, Count::Implied) + } + + pub fn param(self) -> Option<FormatParam<'tcx>> { + match self { + Count::Param(param) => Some(param), + _ => None, + } + } +} + +/// Specification for the formatting of an argument in the format string. See +/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings. +#[derive(Debug)] +pub struct FormatSpec<'tcx> { + /// Optionally specified character to fill alignment with. + pub fill: Option<char>, + /// Optionally specified alignment. + pub align: Alignment, + /// Packed version of various flags provided, see [`rustc_parse_format::Flag`]. + pub flags: u32, + /// Represents either the maximum width or the integer precision. + pub precision: Count<'tcx>, + /// The minimum width, will be padded according to `width`/`align` + pub width: Count<'tcx>, + /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for + /// `{:?}`. + pub r#trait: Symbol, + pub trait_span: Option<Span>, +} + +impl<'tcx> FormatSpec<'tcx> { + fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> { + Some(Self { + fill: spec.fill, + align: spec.align, + flags: spec.flags, + precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?, + width: Count::new(spec.width, positions.width, spec.width_span, values)?, + r#trait: match spec.ty { + "" => sym::Display, + "?" => sym::Debug, + "o" => sym!(Octal), + "x" => sym!(LowerHex), + "X" => sym!(UpperHex), + "p" => sym::Pointer, + "b" => sym!(Binary), + "e" => sym!(LowerExp), + "E" => sym!(UpperExp), + _ => return None, + }, + trait_span: spec + .ty_span + .map(|span| span_from_inner(values.format_string_span, span)), }) } - /// Finds a nested call to `format_args!` within a `format!`-like macro call + /// Returns true if this format spec would change the contents of a string when formatted + pub fn has_string_formatting(&self) -> bool { + self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied() + } +} + +/// A format argument, such as `{}`, `{foo:?}`. +#[derive(Debug)] +pub struct FormatArg<'tcx> { + /// The parameter the argument refers to. + pub param: FormatParam<'tcx>, + /// How to format `param`. + pub format: FormatSpec<'tcx>, + /// span of the whole argument, `{..}`. + pub span: Span, +} + +/// A parsed `format_args!` expansion. +#[derive(Debug)] +pub struct FormatArgsExpn<'tcx> { + /// The format string literal. + pub format_string: FormatString, + // The format arguments, such as `{:?}`. + pub args: Vec<FormatArg<'tcx>>, + /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will + /// include this added newline. + pub newline: bool, + /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for + /// `format!("{x} {} {y}", 1, z + 2)`. + value_args: Vec<&'tcx Expr<'tcx>>, +} + +impl<'tcx> FormatArgsExpn<'tcx> { + pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> { + let macro_name = macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?; + let newline = macro_name == sym::format_args_nl; + + // ::core::fmt::Arguments::new_v1(pieces, args) + // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg) + if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind + && is_path_diagnostic_item(cx, ty, sym::Arguments) + && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted") + { + let format_string = FormatString::new(cx, pieces)?; + + let mut parser = rpf::Parser::new( + &format_string.unescaped, + format_string.style, + Some(format_string.snippet.clone()), + // `format_string.unescaped` does not contain the appended newline + false, + rpf::ParseMode::Format, + ); + + let parsed_args = parser + .by_ref() + .filter_map(|piece| match piece { + rpf::Piece::NextArgument(a) => Some(a), + rpf::Piece::String(_) => None, + }) + .collect_vec(); + if !parser.errors.is_empty() { + return None; + } + + let positions = if let Some(fmt_arg) = rest.first() { + // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse + // them. + + Either::Left(parse_rt_fmt(fmt_arg)?) + } else { + // If no format specs are given, the positions are in the given order and there are + // no `precision`/`width`s to consider. + + Either::Right((0..).map(|n| ParamPosition { + value: n, + width: None, + precision: None, + })) + }; + + let values = FormatArgsValues::new(args, format_string.span.data()); + + let args = izip!(positions, parsed_args, parser.arg_places) + .map(|(position, parsed_arg, arg_span)| { + Some(FormatArg { + param: FormatParam::new( + match parsed_arg.position { + rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit, + rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered, + // NamedInline is handled by `FormatParam::new()` + rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)), + }, + position.value, + parsed_arg.position_span, + &values, + )?, + format: FormatSpec::new(parsed_arg.format, position, &values)?, + span: span_from_inner(values.format_string_span, arg_span), + }) + }) + .collect::<Option<Vec<_>>>()?; + + Some(Self { + format_string, + args, + value_args: values.value_args, + newline, + }) + } else { + None + } + } + pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> { let mut format_args = None; expr_visitor_no_bodies(|e| { @@ -466,88 +845,23 @@ impl<'tcx> FormatArgsExpn<'tcx> { format_args } - /// Returns a vector of `FormatArgsArg`. - pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> { - if self.specs.is_empty() { - let args = std::iter::zip(&self.value_args, &self.formatters) - .map(|(value, &(_, format_trait))| FormatArgsArg { - value, - format_trait, - spec: None, - }) - .collect(); - return Some(args); - } - self.specs - .iter() - .map(|spec| { - if_chain! { - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = spec.kind; - if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); - if let ExprKind::Lit(lit) = &position_field.expr.kind; - if let LitKind::Int(position, _) = lit.node; - if let Ok(i) = usize::try_from(position); - if let Some(&(j, format_trait)) = self.formatters.get(i); - then { - Some(FormatArgsArg { - value: self.value_args[j], - format_trait, - spec: Some(spec), - }) - } else { - None - } - } - }) - .collect() - } - /// Source callsite span of all inputs pub fn inputs_span(&self) -> Span { match *self.value_args { - [] => self.format_string_span, + [] => self.format_string.span, [.., last] => self - .format_string_span - .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())), + .format_string + .span + .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())), } } -} -/// Type representing a `FormatArgsExpn`'s format arguments -pub struct FormatArgsArg<'tcx> { - /// An element of `value_args` according to `position` - pub value: &'tcx Expr<'tcx>, - /// An element of `args` according to `position` - pub format_trait: Symbol, - /// An element of `specs` - pub spec: Option<&'tcx Expr<'tcx>>, -} - -impl<'tcx> FormatArgsArg<'tcx> { - /// Returns true if any formatting parameters are used that would have an effect on strings, - /// like `{:+2}` instead of just `{}`. - pub fn has_string_formatting(&self) -> bool { - self.spec.map_or(false, |spec| { - // `!` because these conditions check that `self` is unformatted. - !if_chain! { - // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, fields, _) = spec.kind; - if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format); - // struct `core::fmt::rt::v1::FormatSpec` - if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind; - if subfields.iter().all(|field| match field.ident.name { - sym::precision | sym::width => match field.expr.kind { - ExprKind::Path(QPath::Resolved(_, path)) => { - path.segments.last().unwrap().ident.name == sym::Implied - } - _ => false, - } - _ => true, - }); - then { true } else { false } - } - }) + /// Iterator of all format params, both values and those referenced by `width`/`precision`s. + pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> { + self.args + .iter() + .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()]) + .flatten() } } diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 9e238c6f1ac..62020e21c81 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -13,7 +13,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,62,0 { BOOL_THEN_SOME } - 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN } + 1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR } 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS } 1,50,0 { BOOL_THEN } @@ -32,8 +32,8 @@ msrv_aliases! { 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } + 1,24,0 { IS_ASCII_DIGIT } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } - 1,24,0 { IS_ASCII_DIGIT } } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 8d697a301c4..fb0d34e02ee 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -71,7 +71,6 @@ pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -96,6 +95,7 @@ pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwL pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"]; pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; #[cfg_attr(not(unix), allow(clippy::invalid_paths))] pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index e7d670766a0..5a7f9568441 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -43,14 +43,6 @@ pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool } } -/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty` -pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool { - ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => other_ty == inner_ty, - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }) -} - /// Walks into `ty` and returns `true` if any inner type is an instance of the given adt /// constructor. pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool { @@ -410,7 +402,7 @@ pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { peel(ty, 0) } -/// Peels off all references on the type.Returns the underlying type, the number of references +/// Peels off all references on the type. Returns the underlying type, the number of references /// removed, and whether the pointer is ultimately mutable or not. pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { @@ -839,3 +831,53 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc }) .unwrap_or(false) } + +/// Comes up with an "at least" guesstimate for the type's size, not taking into +/// account the layout of type parameters. +pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 { + use rustc_middle::ty::layout::LayoutOf; + if !is_normalizable(cx, cx.param_env, ty) { + return 0; + } + match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) { + (Ok(size), _) => size, + (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(), + (Err(_), ty::Array(t, n)) => { + n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t) + }, + (Err(_), ty::Adt(def, subst)) if def.is_struct() => def + .variants() + .iter() + .map(|v| { + v.fields + .iter() + .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst))) + .sum::<u64>() + }) + .sum(), + (Err(_), ty::Adt(def, subst)) if def.is_enum() => def + .variants() + .iter() + .map(|v| { + v.fields + .iter() + .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst))) + .sum::<u64>() + }) + .max() + .unwrap_or_default(), + (Err(_), ty::Adt(def, subst)) if def.is_union() => def + .variants() + .iter() + .map(|v| { + v.fields + .iter() + .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst))) + .max() + .unwrap_or_default() + }) + .max() + .unwrap_or_default(), + (Err(_), _) => 0, + } +} diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 7e14df4feea..85b60fefd60 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-08-11" +channel = "nightly-2022-08-27" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/rustc_tools_util/src/lib.rs b/src/tools/clippy/rustc_tools_util/src/lib.rs index 5f289918a7c..429dddc42ea 100644 --- a/src/tools/clippy/rustc_tools_util/src/lib.rs +++ b/src/tools/clippy/rustc_tools_util/src/lib.rs @@ -84,7 +84,7 @@ impl std::fmt::Debug for VersionInfo { #[must_use] pub fn get_commit_hash() -> Option<String> { std::process::Command::new("git") - .args(&["rev-parse", "--short", "HEAD"]) + .args(["rev-parse", "--short", "HEAD"]) .output() .ok() .and_then(|r| String::from_utf8(r.stdout).ok()) @@ -93,7 +93,7 @@ pub fn get_commit_hash() -> Option<String> { #[must_use] pub fn get_commit_date() -> Option<String> { std::process::Command::new("git") - .args(&["log", "-1", "--date=short", "--pretty=format:%cd"]) + .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output() .ok() .and_then(|r| String::from_utf8(r.stdout).ok()) diff --git a/src/tools/clippy/tests/check-fmt.rs b/src/tools/clippy/tests/check-fmt.rs index 0defd45b68b..e106583de4a 100644 --- a/src/tools/clippy/tests/check-fmt.rs +++ b/src/tools/clippy/tests/check-fmt.rs @@ -13,7 +13,7 @@ fn fmt() { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let output = Command::new("cargo") .current_dir(root_dir) - .args(&["dev", "fmt", "--check"]) + .args(["dev", "fmt", "--check"]) .output() .unwrap(); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 92ac1a2be56..ba6186e599e 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -393,8 +393,8 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ "search_is_some.rs", "single_component_path_imports_nested_first.rs", "string_add.rs", + "suspicious_to_owned.rs", "toplevel_ref_arg_non_rustfix.rs", - "trait_duplication_in_bounds.rs", "unit_arg.rs", "unnecessary_clone.rs", "unnecessary_lazy_eval_unfixable.rs", @@ -404,16 +404,23 @@ const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ ]; fn check_rustfix_coverage() { - let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt"); + let missing_coverage_path = Path::new("debug/test/ui/rustfix_missing_coverage.txt"); + let missing_coverage_path = if let Ok(target_dir) = std::env::var("CARGO_TARGET_DIR") { + PathBuf::from(target_dir).join(missing_coverage_path) + } else { + missing_coverage_path.to_path_buf() + }; if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) { assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new)); - for rs_path in missing_coverage_contents.lines() { - if Path::new(rs_path).starts_with("tests/ui/crashes") { + for rs_file in missing_coverage_contents.lines() { + let rs_path = Path::new(rs_file); + if rs_path.starts_with("tests/ui/crashes") { continue; } - let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap(); + assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file); + let filename = rs_path.strip_prefix("tests/ui/").unwrap(); assert!( RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS .binary_search_by_key(&filename, Path::new) @@ -421,7 +428,7 @@ fn check_rustfix_coverage() { "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ Please either add `// run-rustfix` at the top of the file or add the file to \ `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", - rs_path, + rs_file, ); } } diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 5697e8680cd..961525bbd91 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -87,11 +87,11 @@ fn run_clippy_for_package(project: &str, args: &[&str]) { if cfg!(feature = "internal") { // internal lints only exist if we build with the internal feature - command.args(&["-D", "clippy::internal"]); + command.args(["-D", "clippy::internal"]); } else { // running a clippy built without internal lints on the clippy source // that contains e.g. `allow(clippy::invalid_paths)` - command.args(&["-A", "unknown_lints"]); + command.args(["-A", "unknown_lints"]); } let output = command.output().unwrap(); diff --git a/src/tools/clippy/tests/integration.rs b/src/tools/clippy/tests/integration.rs index c64425fa01a..23a9bef3ccc 100644 --- a/src/tools/clippy/tests/integration.rs +++ b/src/tools/clippy/tests/integration.rs @@ -19,7 +19,7 @@ fn integration_test() { repo_dir.push(crate_name); let st = Command::new("git") - .args(&[ + .args([ OsStr::new("clone"), OsStr::new("--depth=1"), OsStr::new(&repo_url), @@ -37,7 +37,7 @@ fn integration_test() { .current_dir(repo_dir) .env("RUST_BACKTRACE", "full") .env("CARGO_TARGET_DIR", target_dir) - .args(&[ + .args([ "clippy", "--all-targets", "--all-features", diff --git a/src/tools/clippy/tests/lint_message_convention.rs b/src/tools/clippy/tests/lint_message_convention.rs index c3aae1a9aa2..2e0f4e76075 100644 --- a/src/tools/clippy/tests/lint_message_convention.rs +++ b/src/tools/clippy/tests/lint_message_convention.rs @@ -19,7 +19,7 @@ impl Message { // we don't want the first letter after "error: ", "help: " ... to be capitalized // also no punctuation (except for "?" ?) at the end of a line static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| { - RegexSet::new(&[ + RegexSet::new([ r"error: [A-Z]", r"help: [A-Z]", r"warning: [A-Z]", @@ -37,7 +37,7 @@ impl Message { // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or // we want to ask a question ending in "?" static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| { - RegexSet::new(&[ + RegexSet::new([ r"\.\.\.$", r".*C-like enum variant discriminant is not portable to 32-bit targets", r".*Intel x86 assembly syntax used", diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 9f8e778b3b9..a52a0b5289f 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -19,6 +19,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold + large-error-threshold literal-representation-threshold max-fn-params-bools max-include-file-size diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs index 0d65071af15..6f0485b5279 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -14,31 +14,31 @@ fn is_rust_file(filename: &str) -> bool { fn main() { // std::string::String and &str should trigger the lint failure with .ext12 - let _ = String::from("").ends_with(".ext12"); + let _ = String::new().ends_with(".ext12"); let _ = "str".ends_with(".ext12"); // The test struct should not trigger the lint failure with .ext12 TestStruct {}.ends_with(".ext12"); // std::string::String and &str should trigger the lint failure with .EXT12 - let _ = String::from("").ends_with(".EXT12"); + let _ = String::new().ends_with(".EXT12"); let _ = "str".ends_with(".EXT12"); // The test struct should not trigger the lint failure with .EXT12 TestStruct {}.ends_with(".EXT12"); // Should not trigger the lint failure with .eXT12 - let _ = String::from("").ends_with(".eXT12"); + let _ = String::new().ends_with(".eXT12"); let _ = "str".ends_with(".eXT12"); TestStruct {}.ends_with(".eXT12"); // Should not trigger the lint failure with .EXT123 (too long) - let _ = String::from("").ends_with(".EXT123"); + let _ = String::new().ends_with(".EXT123"); let _ = "str".ends_with(".EXT123"); TestStruct {}.ends_with(".EXT123"); // Shouldn't fail if it doesn't start with a dot - let _ = String::from("").ends_with("a.ext"); + let _ = String::new().ends_with("a.ext"); let _ = "str".ends_with("a.extA"); TestStruct {}.ends_with("a.ext"); } diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr index 05b98169f2d..5d9a043edb9 100644 --- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr +++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -8,10 +8,10 @@ LL | filename.ends_with(".rs") = help: consider using a case-insensitive comparison instead error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30 + --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27 | -LL | let _ = String::from("").ends_with(".ext12"); - | ^^^^^^^^^^^^^^^^^^^ +LL | let _ = String::new().ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead @@ -24,10 +24,10 @@ LL | let _ = "str".ends_with(".ext12"); = help: consider using a case-insensitive comparison instead error: case-sensitive file extension comparison - --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30 + --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27 | -LL | let _ = String::from("").ends_with(".EXT12"); - | ^^^^^^^^^^^^^^^^^^^ +LL | let _ = String::new().ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^ | = help: consider using a case-insensitive comparison instead diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed new file mode 100644 index 00000000000..b70c1912951 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::cast_slice_from_raw_parts)] + +#[allow(unused_imports, unused_unsafe)] +fn main() { + let mut vec = vec![0u8; 1]; + let ptr: *const u8 = vec.as_ptr(); + let mptr = vec.as_mut_ptr(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + } + { + use std::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + } +} diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs new file mode 100644 index 00000000000..c1b316765c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::cast_slice_from_raw_parts)] + +#[allow(unused_imports, unused_unsafe)] +fn main() { + let mut vec = vec![0u8; 1]; + let ptr: *const u8 = vec.as_ptr(); + let mptr = vec.as_mut_ptr(); + let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] }; + let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8]; + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + } + { + use std::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + } +} diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr new file mode 100644 index 00000000000..f07801c197c --- /dev/null +++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr @@ -0,0 +1,46 @@ +error: casting the result of `from_raw_parts` to *const [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:9:35 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | + = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` + +error: casting the result of `from_raw_parts_mut` to *mut [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:10:35 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:11:26 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:14:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:16:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:20:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> $DIR/cast_raw_slice_pointer_cast.rs:22:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/collapsible_str_replace.fixed b/src/tools/clippy/tests/ui/collapsible_str_replace.fixed new file mode 100644 index 00000000000..49fc9a9629e --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_str_replace.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::collapsible_str_replace)] + +fn get_filter() -> char { + 'u' +} + +fn main() { + let d = 'd'; + let p = 'p'; + let s = 's'; + let u = 'u'; + let l = "l"; + + let mut iter = ["l", "z"].iter(); + + // LINT CASES + let _ = "hesuo worpd".replace(['s', 'u'], "l"); + + let _ = "hesuo worpd".replace(['s', 'u'], l); + + let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l"); + + let _ = "hesuo worpd" + .replace(['s', 'u', 'p', 'd'], "l"); + + let _ = "hesuo world".replace([s, 'u'], "l"); + + let _ = "hesuo worpd".replace([s, 'u', 'p'], "l"); + + let _ = "hesuo worpd".replace([s, u, 'p'], "l"); + + let _ = "hesuo worpd".replace([s, u, p], "l"); + + let _ = "hesuo worlp".replace(['s', 'u'], "l").replace('p', "d"); + + let _ = "hesuo worpd".replace('s', "x").replace(['u', 'p'], "l"); + + // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")` + let _ = "hesudo worpd".replace("su", "l").replace(['d', 'p'], "l"); + + let _ = "hesudo worpd".replace([d, 'p'], "l").replace("su", "l"); + + let _ = "hesuo world".replace([get_filter(), 's'], "l"); + + // NO LINT CASES + let _ = "hesuo world".replace('s', "l").replace('u', "p"); + + let _ = "hesuo worpd".replace('s', "l").replace('p', l); + + let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l"); + + // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]` + let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l"); + + let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l"); + + let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l"); + + let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l"); + + let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l); + + let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l"); + + let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l"); + + // Regression test + let _ = "hesuo worpd" + .replace('u', iter.next().unwrap()) + .replace('s', iter.next().unwrap()); +} diff --git a/src/tools/clippy/tests/ui/collapsible_str_replace.rs b/src/tools/clippy/tests/ui/collapsible_str_replace.rs new file mode 100644 index 00000000000..e3e25c4146f --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_str_replace.rs @@ -0,0 +1,76 @@ +// run-rustfix + +#![warn(clippy::collapsible_str_replace)] + +fn get_filter() -> char { + 'u' +} + +fn main() { + let d = 'd'; + let p = 'p'; + let s = 's'; + let u = 'u'; + let l = "l"; + + let mut iter = ["l", "z"].iter(); + + // LINT CASES + let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); + + let _ = "hesuo worpd".replace('s', l).replace('u', l); + + let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); + + let _ = "hesuo worpd" + .replace('s', "l") + .replace('u', "l") + .replace('p', "l") + .replace('d', "l"); + + let _ = "hesuo world".replace(s, "l").replace('u', "l"); + + let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); + + let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); + + let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); + + let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); + + let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); + + // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")` + let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); + + let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); + + let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); + + // NO LINT CASES + let _ = "hesuo world".replace('s', "l").replace('u', "p"); + + let _ = "hesuo worpd".replace('s', "l").replace('p', l); + + let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l"); + + // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]` + let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l"); + + let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l"); + + let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l"); + + let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l"); + + let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l); + + let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l"); + + let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l"); + + // Regression test + let _ = "hesuo worpd" + .replace('u', iter.next().unwrap()) + .replace('s', iter.next().unwrap()); +} diff --git a/src/tools/clippy/tests/ui/collapsible_str_replace.stderr b/src/tools/clippy/tests/ui/collapsible_str_replace.stderr new file mode 100644 index 00000000000..8e3daf3b898 --- /dev/null +++ b/src/tools/clippy/tests/ui/collapsible_str_replace.stderr @@ -0,0 +1,86 @@ +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:19:27 + | +LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` + | + = note: `-D clippy::collapsible-str-replace` implied by `-D warnings` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:21:27 + | +LL | let _ = "hesuo worpd".replace('s', l).replace('u', l); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:23:27 + | +LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:26:10 + | +LL | .replace('s', "l") + | __________^ +LL | | .replace('u', "l") +LL | | .replace('p', "l") +LL | | .replace('d', "l"); + | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:31:27 + | +LL | let _ = "hesuo world".replace(s, "l").replace('u', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:33:27 + | +LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:35:27 + | +LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:37:27 + | +LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:39:27 + | +LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:41:45 + | +LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:44:47 + | +LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:46:28 + | +LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:48:27 + | +LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")` + +error: aborting due to 13 previous errors + diff --git a/src/tools/clippy/tests/ui/expect.rs b/src/tools/clippy/tests/ui/expect.rs index 1073acf6f0c..d742595e14d 100644 --- a/src/tools/clippy/tests/ui/expect.rs +++ b/src/tools/clippy/tests/ui/expect.rs @@ -6,8 +6,9 @@ fn expect_option() { } fn expect_result() { - let res: Result<u8, ()> = Ok(0); + let res: Result<u8, u8> = Ok(0); let _ = res.expect(""); + let _ = res.expect_err(""); } fn main() { diff --git a/src/tools/clippy/tests/ui/expect.stderr b/src/tools/clippy/tests/ui/expect.stderr index ab28aac4556..904c0904645 100644 --- a/src/tools/clippy/tests/ui/expect.stderr +++ b/src/tools/clippy/tests/ui/expect.stderr @@ -15,5 +15,13 @@ LL | let _ = res.expect(""); | = help: if this value is an `Err`, it will panic -error: aborting due to 2 previous errors +error: used `expect_err()` on `a Result` value + --> $DIR/expect.rs:11:13 + | +LL | let _ = res.expect_err(""); + | ^^^^^^^^^^^^^^^^^^ + | + = help: if this value is an `Ok`, it will panic + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_exp.fixed b/src/tools/clippy/tests/ui/floating_point_exp.fixed index ae7805fdf01..c86a502d15f 100644 --- a/src/tools/clippy/tests/ui/floating_point_exp.fixed +++ b/src/tools/clippy/tests/ui/floating_point_exp.fixed @@ -5,6 +5,7 @@ fn main() { let x = 2f32; let _ = x.exp_m1(); let _ = x.exp_m1() + 2.0; + let _ = (x as f32).exp_m1() + 2.0; // Cases where the lint shouldn't be applied let _ = x.exp() - 2.0; let _ = x.exp() - 1.0 * 2.0; diff --git a/src/tools/clippy/tests/ui/floating_point_exp.rs b/src/tools/clippy/tests/ui/floating_point_exp.rs index 27e0b9bcbc9..e59589f912a 100644 --- a/src/tools/clippy/tests/ui/floating_point_exp.rs +++ b/src/tools/clippy/tests/ui/floating_point_exp.rs @@ -5,6 +5,7 @@ fn main() { let x = 2f32; let _ = x.exp() - 1.0; let _ = x.exp() - 1.0 + 2.0; + let _ = (x as f32).exp() - 1.0 + 2.0; // Cases where the lint shouldn't be applied let _ = x.exp() - 2.0; let _ = x.exp() - 1.0 * 2.0; diff --git a/src/tools/clippy/tests/ui/floating_point_exp.stderr b/src/tools/clippy/tests/ui/floating_point_exp.stderr index 5cd999ad47c..f84eede1987 100644 --- a/src/tools/clippy/tests/ui/floating_point_exp.stderr +++ b/src/tools/clippy/tests/ui/floating_point_exp.stderr @@ -13,16 +13,22 @@ LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:13:13 + --> $DIR/floating_point_exp.rs:8:13 + | +LL | let _ = (x as f32).exp() - 1.0 + 2.0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()` + +error: (e.pow(x) - 1) can be computed more accurately + --> $DIR/floating_point_exp.rs:14:13 | LL | let _ = x.exp() - 1.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` error: (e.pow(x) - 1) can be computed more accurately - --> $DIR/floating_point_exp.rs:14:13 + --> $DIR/floating_point_exp.rs:15:13 | LL | let _ = x.exp() - 1.0 + 2.0; | ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_log.fixed b/src/tools/clippy/tests/ui/floating_point_log.fixed index 5b487bb8fcf..4def9300bb7 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.fixed +++ b/src/tools/clippy/tests/ui/floating_point_log.fixed @@ -12,6 +12,7 @@ fn check_log_base() { let _ = x.ln(); let _ = x.log2(); let _ = x.ln(); + let _ = (x as f32).log2(); let x = 1f64; let _ = x.log2(); diff --git a/src/tools/clippy/tests/ui/floating_point_log.rs b/src/tools/clippy/tests/ui/floating_point_log.rs index 01181484e7d..1e04caa7d2a 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.rs +++ b/src/tools/clippy/tests/ui/floating_point_log.rs @@ -12,6 +12,7 @@ fn check_log_base() { let _ = x.log(std::f32::consts::E); let _ = x.log(TWO); let _ = x.log(E); + let _ = (x as f32).log(2f32); let x = 1f64; let _ = x.log(2f64); diff --git a/src/tools/clippy/tests/ui/floating_point_log.stderr b/src/tools/clippy/tests/ui/floating_point_log.stderr index 96e5a154441..89800a13a6e 100644 --- a/src/tools/clippy/tests/ui/floating_point_log.stderr +++ b/src/tools/clippy/tests/ui/floating_point_log.stderr @@ -31,25 +31,31 @@ LL | let _ = x.log(E); | ^^^^^^^^ help: consider using: `x.ln()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> $DIR/floating_point_log.rs:17:13 + --> $DIR/floating_point_log.rs:15:13 + | +LL | let _ = (x as f32).log(2f32); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()` + +error: logarithm for bases 2, 10 and e can be computed more accurately + --> $DIR/floating_point_log.rs:18:13 | LL | let _ = x.log(2f64); | ^^^^^^^^^^^ help: consider using: `x.log2()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> $DIR/floating_point_log.rs:18:13 + --> $DIR/floating_point_log.rs:19:13 | LL | let _ = x.log(10f64); | ^^^^^^^^^^^^ help: consider using: `x.log10()` error: logarithm for bases 2, 10 and e can be computed more accurately - --> $DIR/floating_point_log.rs:19:13 + --> $DIR/floating_point_log.rs:20:13 | LL | let _ = x.log(std::f64::consts::E); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:24:13 + --> $DIR/floating_point_log.rs:25:13 | LL | let _ = (1f32 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` @@ -57,118 +63,118 @@ LL | let _ = (1f32 + 2.).ln(); = note: `-D clippy::imprecise-flops` implied by `-D warnings` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:25:13 + --> $DIR/floating_point_log.rs:26:13 | LL | let _ = (1f32 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:26:13 + --> $DIR/floating_point_log.rs:27:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:27:13 + --> $DIR/floating_point_log.rs:28:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:28:13 + --> $DIR/floating_point_log.rs:29:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:29:13 + --> $DIR/floating_point_log.rs:30:13 | LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:30:13 + --> $DIR/floating_point_log.rs:31:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:31:13 + --> $DIR/floating_point_log.rs:32:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:32:13 + --> $DIR/floating_point_log.rs:33:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:33:13 + --> $DIR/floating_point_log.rs:34:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:34:13 + --> $DIR/floating_point_log.rs:35:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:42:13 + --> $DIR/floating_point_log.rs:43:13 | LL | let _ = (1f64 + 2.).ln(); | ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:43:13 + --> $DIR/floating_point_log.rs:44:13 | LL | let _ = (1f64 + 2.0).ln(); | ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:44:13 + --> $DIR/floating_point_log.rs:45:13 | LL | let _ = (1.0 + x).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:45:13 + --> $DIR/floating_point_log.rs:46:13 | LL | let _ = (1.0 + x / 2.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:46:13 + --> $DIR/floating_point_log.rs:47:13 | LL | let _ = (1.0 + x.powi(3)).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:47:13 + --> $DIR/floating_point_log.rs:48:13 | LL | let _ = (x + 1.0).ln(); | ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:48:13 + --> $DIR/floating_point_log.rs:49:13 | LL | let _ = (x.powi(3) + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:49:13 + --> $DIR/floating_point_log.rs:50:13 | LL | let _ = (x + 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately - --> $DIR/floating_point_log.rs:50:13 + --> $DIR/floating_point_log.rs:51:13 | LL | let _ = (x / 2.0 + 1.0).ln(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()` -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.fixed b/src/tools/clippy/tests/ui/floating_point_logbase.fixed index 13962a272d4..936462f9406 100644 --- a/src/tools/clippy/tests/ui/floating_point_logbase.fixed +++ b/src/tools/clippy/tests/ui/floating_point_logbase.fixed @@ -5,6 +5,7 @@ fn main() { let x = 3f32; let y = 5f32; let _ = x.log(y); + let _ = (x as f32).log(y); let _ = x.log(y); let _ = x.log(y); let _ = x.log(y); diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.rs b/src/tools/clippy/tests/ui/floating_point_logbase.rs index 26bc20d5370..0b56fa8fa41 100644 --- a/src/tools/clippy/tests/ui/floating_point_logbase.rs +++ b/src/tools/clippy/tests/ui/floating_point_logbase.rs @@ -5,6 +5,7 @@ fn main() { let x = 3f32; let y = 5f32; let _ = x.ln() / y.ln(); + let _ = (x as f32).ln() / y.ln(); let _ = x.log2() / y.log2(); let _ = x.log10() / y.log10(); let _ = x.log(5f32) / y.log(5f32); diff --git a/src/tools/clippy/tests/ui/floating_point_logbase.stderr b/src/tools/clippy/tests/ui/floating_point_logbase.stderr index 78354c2f62d..384e3554cbb 100644 --- a/src/tools/clippy/tests/ui/floating_point_logbase.stderr +++ b/src/tools/clippy/tests/ui/floating_point_logbase.stderr @@ -9,20 +9,26 @@ LL | let _ = x.ln() / y.ln(); error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:8:13 | +LL | let _ = (x as f32).ln() / y.ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)` + +error: log base can be expressed more clearly + --> $DIR/floating_point_logbase.rs:9:13 + | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:9:13 + --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` error: log base can be expressed more clearly - --> $DIR/floating_point_logbase.rs:10:13 + --> $DIR/floating_point_logbase.rs:11:13 | LL | let _ = x.log(5f32) / y.log(5f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_powf.fixed b/src/tools/clippy/tests/ui/floating_point_powf.fixed index b0641a100cd..7efe10a10f9 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.fixed +++ b/src/tools/clippy/tests/ui/floating_point_powf.fixed @@ -11,10 +11,13 @@ fn main() { let _ = (-3.1f32).exp(); let _ = x.sqrt(); let _ = x.cbrt(); + let _ = (x as f32).cbrt(); let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(16_777_215); let _ = x.powi(-16_777_215); + let _ = (x as f32).powi(-16_777_215); + let _ = (x as f32).powi(3); // Cases where the lint shouldn't be applied let _ = x.powf(2.1); let _ = x.powf(-2.1); diff --git a/src/tools/clippy/tests/ui/floating_point_powf.rs b/src/tools/clippy/tests/ui/floating_point_powf.rs index a0a2c973900..445080417f2 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.rs +++ b/src/tools/clippy/tests/ui/floating_point_powf.rs @@ -11,10 +11,13 @@ fn main() { let _ = std::f32::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); + let _ = (x as f32).powf(1.0 / 3.0); let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(16_777_215.0); let _ = x.powf(-16_777_215.0); + let _ = (x as f32).powf(-16_777_215.0); + let _ = (x as f32).powf(3.0); // Cases where the lint shouldn't be applied let _ = x.powf(2.1); let _ = x.powf(-2.1); diff --git a/src/tools/clippy/tests/ui/floating_point_powf.stderr b/src/tools/clippy/tests/ui/floating_point_powf.stderr index 2422eb911e9..6ee696e6ada 100644 --- a/src/tools/clippy/tests/ui/floating_point_powf.stderr +++ b/src/tools/clippy/tests/ui/floating_point_powf.stderr @@ -50,101 +50,119 @@ LL | let _ = x.powf(1.0 / 3.0); | = note: `-D clippy::imprecise-flops` implied by `-D warnings` -error: exponentiation with integer powers can be computed more efficiently +error: cube-root of a number can be computed more accurately --> $DIR/floating_point_powf.rs:14:13 | +LL | let _ = (x as f32).powf(1.0 / 3.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:15:13 + | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:15:13 + --> $DIR/floating_point_powf.rs:16:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:16:13 + --> $DIR/floating_point_powf.rs:17:13 | LL | let _ = x.powf(16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:17:13 + --> $DIR/floating_point_powf.rs:18:13 | LL | let _ = x.powf(-16_777_215.0); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)` +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:19:13 + | +LL | let _ = (x as f32).powf(-16_777_215.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)` + +error: exponentiation with integer powers can be computed more efficiently + --> $DIR/floating_point_powf.rs:20:13 + | +LL | let _ = (x as f32).powf(3.0); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)` + error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:25:13 + --> $DIR/floating_point_powf.rs:28:13 | LL | let _ = 2f64.powf(x); | ^^^^^^^^^^^^ help: consider using: `x.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:26:13 + --> $DIR/floating_point_powf.rs:29:13 | LL | let _ = 2f64.powf(3.1); | ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:27:13 + --> $DIR/floating_point_powf.rs:30:13 | LL | let _ = 2f64.powf(-3.1); | ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:28:13 + --> $DIR/floating_point_powf.rs:31:13 | LL | let _ = std::f64::consts::E.powf(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:29:13 + --> $DIR/floating_point_powf.rs:32:13 | LL | let _ = std::f64::consts::E.powf(3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()` error: exponent for bases 2 and e can be computed more accurately - --> $DIR/floating_point_powf.rs:30:13 + --> $DIR/floating_point_powf.rs:33:13 | LL | let _ = std::f64::consts::E.powf(-3.1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()` error: square-root of a number can be computed more efficiently and accurately - --> $DIR/floating_point_powf.rs:31:13 + --> $DIR/floating_point_powf.rs:34:13 | LL | let _ = x.powf(1.0 / 2.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()` error: cube-root of a number can be computed more accurately - --> $DIR/floating_point_powf.rs:32:13 + --> $DIR/floating_point_powf.rs:35:13 | LL | let _ = x.powf(1.0 / 3.0); | ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:33:13 + --> $DIR/floating_point_powf.rs:36:13 | LL | let _ = x.powf(3.0); | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:34:13 + --> $DIR/floating_point_powf.rs:37:13 | LL | let _ = x.powf(-2.0); | ^^^^^^^^^^^^ help: consider using: `x.powi(-2)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:35:13 + --> $DIR/floating_point_powf.rs:38:13 | LL | let _ = x.powf(-2_147_483_648.0); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)` error: exponentiation with integer powers can be computed more efficiently - --> $DIR/floating_point_powf.rs:36:13 + --> $DIR/floating_point_powf.rs:39:13 | LL | let _ = x.powf(2_147_483_647.0); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)` -error: aborting due to 24 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_powi.fixed b/src/tools/clippy/tests/ui/floating_point_powi.fixed index 85f7c531e39..5758db7c6c8 100644 --- a/src/tools/clippy/tests/ui/floating_point_powi.fixed +++ b/src/tools/clippy/tests/ui/floating_point_powi.fixed @@ -8,6 +8,7 @@ fn main() { let y = 4f32; let _ = x.mul_add(x, y); let _ = y.mul_add(y, x); + let _ = (y as f32).mul_add(y as f32, x); let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); // Cases where the lint shouldn't be applied diff --git a/src/tools/clippy/tests/ui/floating_point_powi.rs b/src/tools/clippy/tests/ui/floating_point_powi.rs index ece61d1bec4..5926bf1b000 100644 --- a/src/tools/clippy/tests/ui/floating_point_powi.rs +++ b/src/tools/clippy/tests/ui/floating_point_powi.rs @@ -8,6 +8,7 @@ fn main() { let y = 4f32; let _ = x.powi(2) + y; let _ = x + y.powi(2); + let _ = x + (y as f32).powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied diff --git a/src/tools/clippy/tests/ui/floating_point_powi.stderr b/src/tools/clippy/tests/ui/floating_point_powi.stderr index 37d840988bb..a3c74544212 100644 --- a/src/tools/clippy/tests/ui/floating_point_powi.stderr +++ b/src/tools/clippy/tests/ui/floating_point_powi.stderr @@ -15,14 +15,20 @@ LL | let _ = x + y.powi(2); error: multiply and add expressions can be calculated more efficiently and accurately --> $DIR/floating_point_powi.rs:11:13 | +LL | let _ = x + (y as f32).powi(2); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 + | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_powi.rs:12:13 + --> $DIR/floating_point_powi.rs:13:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/floating_point_rad.fixed b/src/tools/clippy/tests/ui/floating_point_rad.fixed index ce91fe176c6..27674b8a455 100644 --- a/src/tools/clippy/tests/ui/floating_point_rad.fixed +++ b/src/tools/clippy/tests/ui/floating_point_rad.fixed @@ -8,6 +8,11 @@ pub const fn const_context() { let _ = x * 180f32 / std::f32::consts::PI; } +pub fn issue9391(degrees: i64) { + let _ = (degrees as f64).to_radians(); + let _ = (degrees as f64).to_degrees(); +} + fn main() { let x = 3f32; let _ = x.to_degrees(); diff --git a/src/tools/clippy/tests/ui/floating_point_rad.rs b/src/tools/clippy/tests/ui/floating_point_rad.rs index 8f323498614..f1ea73df398 100644 --- a/src/tools/clippy/tests/ui/floating_point_rad.rs +++ b/src/tools/clippy/tests/ui/floating_point_rad.rs @@ -8,6 +8,11 @@ pub const fn const_context() { let _ = x * 180f32 / std::f32::consts::PI; } +pub fn issue9391(degrees: i64) { + let _ = degrees as f64 * std::f64::consts::PI / 180.0; + let _ = degrees as f64 * 180.0 / std::f64::consts::PI; +} + fn main() { let x = 3f32; let _ = x * 180f32 / std::f32::consts::PI; diff --git a/src/tools/clippy/tests/ui/floating_point_rad.stderr b/src/tools/clippy/tests/ui/floating_point_rad.stderr index f12d3d23f3a..979442f2c24 100644 --- a/src/tools/clippy/tests/ui/floating_point_rad.stderr +++ b/src/tools/clippy/tests/ui/floating_point_rad.stderr @@ -1,40 +1,52 @@ +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:12:13 + | +LL | let _ = degrees as f64 * std::f64::consts::PI / 180.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_radians()` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + error: conversion to degrees can be done more accurately --> $DIR/floating_point_rad.rs:13:13 | +LL | let _ = degrees as f64 * 180.0 / std::f64::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_degrees()` + +error: conversion to degrees can be done more accurately + --> $DIR/floating_point_rad.rs:18:13 + | LL | let _ = x * 180f32 / std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` - | - = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: conversion to degrees can be done more accurately - --> $DIR/floating_point_rad.rs:14:13 + --> $DIR/floating_point_rad.rs:19:13 | LL | let _ = 90. * 180f64 / std::f64::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()` error: conversion to degrees can be done more accurately - --> $DIR/floating_point_rad.rs:15:13 + --> $DIR/floating_point_rad.rs:20:13 | LL | let _ = 90.5 * 180f64 / std::f64::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()` error: conversion to radians can be done more accurately - --> $DIR/floating_point_rad.rs:16:13 + --> $DIR/floating_point_rad.rs:21:13 | LL | let _ = x * std::f32::consts::PI / 180f32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()` error: conversion to radians can be done more accurately - --> $DIR/floating_point_rad.rs:17:13 + --> $DIR/floating_point_rad.rs:22:13 | LL | let _ = 90. * std::f32::consts::PI / 180f32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()` error: conversion to radians can be done more accurately - --> $DIR/floating_point_rad.rs:18:13 + --> $DIR/floating_point_rad.rs:23:13 | LL | let _ = 90.5 * std::f32::consts::PI / 180f32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()` -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/format.fixed b/src/tools/clippy/tests/ui/format.fixed index 6b754f3bd71..b56d6aec508 100644 --- a/src/tools/clippy/tests/ui/format.fixed +++ b/src/tools/clippy/tests/ui/format.fixed @@ -33,7 +33,7 @@ fn main() { format!("foo {}", "bar"); format!("{} bar", "foo"); - let arg: String = "".to_owned(); + let arg = String::new(); arg.to_string(); format!("{:?}", arg); // Don't warn about debug. format!("{:8}", arg); diff --git a/src/tools/clippy/tests/ui/format.rs b/src/tools/clippy/tests/ui/format.rs index ca9826b356e..4c1a3a840ed 100644 --- a/src/tools/clippy/tests/ui/format.rs +++ b/src/tools/clippy/tests/ui/format.rs @@ -35,7 +35,7 @@ fn main() { format!("foo {}", "bar"); format!("{} bar", "foo"); - let arg: String = "".to_owned(); + let arg = String::new(); format!("{}", arg); format!("{:?}", arg); // Don't warn about debug. format!("{:8}", arg); diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed index 69b5e1c722e..e1c2d4d70be 100644 --- a/src/tools/clippy/tests/ui/format_args.fixed +++ b/src/tools/clippy/tests/ui/format_args.fixed @@ -1,8 +1,6 @@ // run-rustfix -#![allow(unreachable_code)] -#![allow(unused_macros)] -#![allow(unused_variables)] +#![allow(unused)] #![allow(clippy::assertions_on_constants)] #![allow(clippy::eq_op)] #![allow(clippy::print_literal)] @@ -115,3 +113,50 @@ fn main() { // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); } + +fn issue8643(vendor_id: usize, product_id: usize, name: &str) { + println!( + "{:<9} {:<10} {}", + format!("0x{:x}", vendor_id), + format!("0x{:x}", product_id), + name + ); +} + +// https://github.com/rust-lang/rust-clippy/issues/8855 +mod issue_8855 { + #![allow(dead_code)] + + struct A {} + + impl std::fmt::Display for A { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "test") + } + } + + fn main() { + let a = A {}; + let b = A {}; + + let x = format!("{} {}", a, b); + dbg!(x); + + let x = format!("{:>6} {:>6}", a, b.to_string()); + dbg!(x); + } +} + +// https://github.com/rust-lang/rust-clippy/issues/9256 +mod issue_9256 { + #![allow(dead_code)] + + fn print_substring(original: &str) { + assert!(original.len() > 10); + println!("{}", &original[..10]); + } + + fn main() { + print_substring("Hello, world!"); + } +} diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs index 3a434c5bf00..b9a4d66c28a 100644 --- a/src/tools/clippy/tests/ui/format_args.rs +++ b/src/tools/clippy/tests/ui/format_args.rs @@ -1,8 +1,6 @@ // run-rustfix -#![allow(unreachable_code)] -#![allow(unused_macros)] -#![allow(unused_variables)] +#![allow(unused)] #![allow(clippy::assertions_on_constants)] #![allow(clippy::eq_op)] #![allow(clippy::print_literal)] @@ -115,3 +113,50 @@ fn main() { // https://github.com/rust-lang/rust-clippy/issues/7903 println!("{foo}{foo:?}", foo = "foo".to_string()); } + +fn issue8643(vendor_id: usize, product_id: usize, name: &str) { + println!( + "{:<9} {:<10} {}", + format!("0x{:x}", vendor_id), + format!("0x{:x}", product_id), + name + ); +} + +// https://github.com/rust-lang/rust-clippy/issues/8855 +mod issue_8855 { + #![allow(dead_code)] + + struct A {} + + impl std::fmt::Display for A { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "test") + } + } + + fn main() { + let a = A {}; + let b = A {}; + + let x = format!("{} {}", a, b.to_string()); + dbg!(x); + + let x = format!("{:>6} {:>6}", a, b.to_string()); + dbg!(x); + } +} + +// https://github.com/rust-lang/rust-clippy/issues/9256 +mod issue_9256 { + #![allow(dead_code)] + + fn print_substring(original: &str) { + assert!(original.len() > 10); + println!("{}", original[..10].to_string()); + } + + fn main() { + print_substring("Hello, world!"); + } +} diff --git a/src/tools/clippy/tests/ui/format_args.stderr b/src/tools/clippy/tests/ui/format_args.stderr index c0cbca50795..aa6e3659b43 100644 --- a/src/tools/clippy/tests/ui/format_args.stderr +++ b/src/tools/clippy/tests/ui/format_args.stderr @@ -1,5 +1,5 @@ error: `to_string` applied to a type that implements `Display` in `format!` args - --> $DIR/format_args.rs:76:72 + --> $DIR/format_args.rs:74:72 | LL | let _ = format!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this @@ -7,124 +7,136 @@ LL | let _ = format!("error: something failed at {}", Location::caller().to_ = note: `-D clippy::to-string-in-format-args` implied by `-D warnings` error: `to_string` applied to a type that implements `Display` in `write!` args - --> $DIR/format_args.rs:80:27 + --> $DIR/format_args.rs:78:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `writeln!` args - --> $DIR/format_args.rs:85:27 + --> $DIR/format_args.rs:83:27 | LL | Location::caller().to_string() | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `print!` args - --> $DIR/format_args.rs:87:63 + --> $DIR/format_args.rs:85:63 | LL | print!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:88:65 + --> $DIR/format_args.rs:86:65 | LL | println!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprint!` args - --> $DIR/format_args.rs:89:64 + --> $DIR/format_args.rs:87:64 | LL | eprint!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `eprintln!` args - --> $DIR/format_args.rs:90:66 + --> $DIR/format_args.rs:88:66 | LL | eprintln!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `format_args!` args - --> $DIR/format_args.rs:91:77 + --> $DIR/format_args.rs:89:77 | LL | let _ = format_args!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert!` args - --> $DIR/format_args.rs:92:70 + --> $DIR/format_args.rs:90:70 | LL | assert!(true, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_eq!` args - --> $DIR/format_args.rs:93:73 + --> $DIR/format_args.rs:91:73 | LL | assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `assert_ne!` args - --> $DIR/format_args.rs:94:73 + --> $DIR/format_args.rs:92:73 | LL | assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `panic!` args - --> $DIR/format_args.rs:95:63 + --> $DIR/format_args.rs:93:63 | LL | panic!("error: something failed at {}", Location::caller().to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:96:20 + --> $DIR/format_args.rs:94:20 | LL | println!("{}", X(1).to_string()); | ^^^^^^^^^^^^^^^^ help: use this: `*X(1)` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:97:20 + --> $DIR/format_args.rs:95:20 | LL | println!("{}", Y(&X(1)).to_string()); | ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:98:24 + --> $DIR/format_args.rs:96:24 | LL | println!("{}", Z(1).to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:99:20 + --> $DIR/format_args.rs:97:20 | LL | println!("{}", x.to_string()); | ^^^^^^^^^^^^^ help: use this: `**x` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:100:20 + --> $DIR/format_args.rs:98:20 | LL | println!("{}", x_ref.to_string()); | ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref` error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:102:39 + --> $DIR/format_args.rs:100:39 | LL | println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:103:52 + --> $DIR/format_args.rs:101:52 | LL | println!("{foo}{bar}", foo = "foo", bar = "bar".to_string()); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:104:39 + --> $DIR/format_args.rs:102:39 | LL | println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo"); | ^^^^^^^^^^^^ help: remove this error: `to_string` applied to a type that implements `Display` in `println!` args - --> $DIR/format_args.rs:105:52 + --> $DIR/format_args.rs:103:52 | LL | println!("{foo}{bar}", bar = "bar", foo = "foo".to_string()); | ^^^^^^^^^^^^ help: remove this -error: aborting due to 21 previous errors +error: `to_string` applied to a type that implements `Display` in `format!` args + --> $DIR/format_args.rs:142:38 + | +LL | let x = format!("{} {}", a, b.to_string()); + | ^^^^^^^^^^^^ help: remove this + +error: `to_string` applied to a type that implements `Display` in `println!` args + --> $DIR/format_args.rs:156:24 + | +LL | println!("{}", original[..10].to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]` + +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/identity_op.fixed b/src/tools/clippy/tests/ui/identity_op.fixed index 5f9cebe212a..fa564e23cd2 100644 --- a/src/tools/clippy/tests/ui/identity_op.fixed +++ b/src/tools/clippy/tests/ui/identity_op.fixed @@ -68,7 +68,7 @@ fn main() { &x; x; - let mut a = A("".into()); + let mut a = A(String::new()); let b = a << 0; // no error: non-integer 1 * Meter; // no error: non-integer diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs index ca799c9cfac..3d06d2a73b6 100644 --- a/src/tools/clippy/tests/ui/identity_op.rs +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -68,7 +68,7 @@ fn main() { &x >> 0; x >> &0; - let mut a = A("".into()); + let mut a = A(String::new()); let b = a << 0; // no error: non-integer 1 * Meter; // no error: non-integer diff --git a/src/tools/clippy/tests/ui/if_let_mutex.rs b/src/tools/clippy/tests/ui/if_let_mutex.rs index 6cbfafbb38b..321feb0224e 100644 --- a/src/tools/clippy/tests/ui/if_let_mutex.rs +++ b/src/tools/clippy/tests/ui/if_let_mutex.rs @@ -39,4 +39,12 @@ fn if_let_different_mutex() { }; } +fn mutex_ref(mutex: &Mutex<i32>) { + if let Ok(i) = mutex.lock() { + do_stuff(i); + } else { + let _x = mutex.lock(); + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/if_let_mutex.stderr b/src/tools/clippy/tests/ui/if_let_mutex.stderr index e9c4d916332..8a4d5dbac59 100644 --- a/src/tools/clippy/tests/ui/if_let_mutex.stderr +++ b/src/tools/clippy/tests/ui/if_let_mutex.stderr @@ -1,10 +1,14 @@ error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock --> $DIR/if_let_mutex.rs:10:5 | -LL | / if let Err(locked) = m.lock() { +LL | if let Err(locked) = m.lock() { + | ^ - this Mutex will remain locked for the entire `if let`-block... + | _____| + | | LL | | do_stuff(locked); LL | | } else { LL | | let lock = m.lock().unwrap(); + | | - ... and is tried to lock again here, which will always deadlock. LL | | do_stuff(lock); LL | | }; | |_____^ @@ -15,15 +19,35 @@ LL | | }; error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock --> $DIR/if_let_mutex.rs:22:5 | -LL | / if let Some(locked) = m.lock().unwrap().deref() { +LL | if let Some(locked) = m.lock().unwrap().deref() { + | ^ - this Mutex will remain locked for the entire `if let`-block... + | _____| + | | LL | | do_stuff(locked); LL | | } else { LL | | let lock = m.lock().unwrap(); + | | - ... and is tried to lock again here, which will always deadlock. LL | | do_stuff(lock); LL | | }; | |_____^ | = help: move the lock call outside of the `if let ...` expression -error: aborting due to 2 previous errors +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock + --> $DIR/if_let_mutex.rs:43:5 + | +LL | if let Ok(i) = mutex.lock() { + | ^ ----- this Mutex will remain locked for the entire `if let`-block... + | _____| + | | +LL | | do_stuff(i); +LL | | } else { +LL | | let _x = mutex.lock(); + | | ----- ... and is tried to lock again here, which will always deadlock. +LL | | }; + | |_____^ + | + = help: move the lock call outside of the `if let ...` expression + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr index 8cb22d569f4..c22ace30d2d 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr @@ -27,21 +27,21 @@ LL | | }; | = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` -error: this could be simplified with `bool::then` +error: this could be simplified with `bool::then_some` --> $DIR/if_then_some_else_none.rs:23:28 | LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider using `bool::then` like: `(o < 32).then(|| o)` + = help: consider using `bool::then_some` like: `(o < 32).then_some(o)` -error: this could be simplified with `bool::then` +error: this could be simplified with `bool::then_some` --> $DIR/if_then_some_else_none.rs:27:13 | LL | let _ = if !x { Some(0) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider using `bool::then` like: `(!x).then(|| 0)` + = help: consider using `bool::then_some` like: `(!x).then_some(0)` error: this could be simplified with `bool::then` --> $DIR/if_then_some_else_none.rs:82:13 diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed new file mode 100644 index 00000000000..bd9b07aefbf --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::iter_on_empty_collections)] +#![allow(clippy::iter_next_slice, clippy::redundant_clone)] + +fn array() { + assert_eq!(std::iter::empty().next(), Option::<i32>::None); + assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None); + assert_eq!(std::iter::empty().next(), Option::<&i32>::None); + assert_eq!(std::iter::empty().next(), Option::<i32>::None); + assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None); + assert_eq!(std::iter::empty().next(), Option::<&i32>::None); + + // Don't trigger on non-iter methods + let _: Option<String> = None.clone(); + let _: [String; 0] = [].clone(); + + // Don't trigger on match or if branches + let _ = match 123 { + 123 => [].iter(), + _ => ["test"].iter(), + }; + + let _ = if false { ["test"].iter() } else { [].iter() }; +} + +macro_rules! in_macros { + () => { + assert_eq!([].into_iter().next(), Option::<i32>::None); + assert_eq!([].iter_mut().next(), Option::<&mut i32>::None); + assert_eq!([].iter().next(), Option::<&i32>::None); + assert_eq!(None.into_iter().next(), Option::<i32>::None); + assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None); + assert_eq!(None.iter().next(), Option::<&i32>::None); + }; +} + +// Don't trigger on a `None` that isn't std's option +mod custom_option { + #[allow(unused)] + enum CustomOption { + Some(i32), + None, + } + + impl CustomOption { + fn iter(&self) {} + fn iter_mut(&mut self) {} + fn into_iter(self) {} + } + use CustomOption::*; + + pub fn custom_option() { + None.iter(); + None.iter_mut(); + None.into_iter(); + } +} + +fn main() { + array(); + custom_option::custom_option(); + in_macros!(); +} diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.rs b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs new file mode 100644 index 00000000000..e15ba94bd46 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::iter_on_empty_collections)] +#![allow(clippy::iter_next_slice, clippy::redundant_clone)] + +fn array() { + assert_eq!([].into_iter().next(), Option::<i32>::None); + assert_eq!([].iter_mut().next(), Option::<&mut i32>::None); + assert_eq!([].iter().next(), Option::<&i32>::None); + assert_eq!(None.into_iter().next(), Option::<i32>::None); + assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None); + assert_eq!(None.iter().next(), Option::<&i32>::None); + + // Don't trigger on non-iter methods + let _: Option<String> = None.clone(); + let _: [String; 0] = [].clone(); + + // Don't trigger on match or if branches + let _ = match 123 { + 123 => [].iter(), + _ => ["test"].iter(), + }; + + let _ = if false { ["test"].iter() } else { [].iter() }; +} + +macro_rules! in_macros { + () => { + assert_eq!([].into_iter().next(), Option::<i32>::None); + assert_eq!([].iter_mut().next(), Option::<&mut i32>::None); + assert_eq!([].iter().next(), Option::<&i32>::None); + assert_eq!(None.into_iter().next(), Option::<i32>::None); + assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None); + assert_eq!(None.iter().next(), Option::<&i32>::None); + }; +} + +// Don't trigger on a `None` that isn't std's option +mod custom_option { + #[allow(unused)] + enum CustomOption { + Some(i32), + None, + } + + impl CustomOption { + fn iter(&self) {} + fn iter_mut(&mut self) {} + fn into_iter(self) {} + } + use CustomOption::*; + + pub fn custom_option() { + None.iter(); + None.iter_mut(); + None.into_iter(); + } +} + +fn main() { + array(); + custom_option::custom_option(); + in_macros!(); +} diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr new file mode 100644 index 00000000000..cbd61176956 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr @@ -0,0 +1,40 @@ +error: `into_iter` call on an empty collection + --> $DIR/iter_on_empty_collections.rs:6:16 + | +LL | assert_eq!([].into_iter().next(), Option::<i32>::None); + | ^^^^^^^^^^^^^^ help: try: `std::iter::empty()` + | + = note: `-D clippy::iter-on-empty-collections` implied by `-D warnings` + +error: `iter_mut` call on an empty collection + --> $DIR/iter_on_empty_collections.rs:7:16 + | +LL | assert_eq!([].iter_mut().next(), Option::<&mut i32>::None); + | ^^^^^^^^^^^^^ help: try: `std::iter::empty()` + +error: `iter` call on an empty collection + --> $DIR/iter_on_empty_collections.rs:8:16 + | +LL | assert_eq!([].iter().next(), Option::<&i32>::None); + | ^^^^^^^^^ help: try: `std::iter::empty()` + +error: `into_iter` call on an empty collection + --> $DIR/iter_on_empty_collections.rs:9:16 + | +LL | assert_eq!(None.into_iter().next(), Option::<i32>::None); + | ^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()` + +error: `iter_mut` call on an empty collection + --> $DIR/iter_on_empty_collections.rs:10:16 + | +LL | assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None); + | ^^^^^^^^^^^^^^^ help: try: `std::iter::empty()` + +error: `iter` call on an empty collection + --> $DIR/iter_on_empty_collections.rs:11:16 + | +LL | assert_eq!(None.iter().next(), Option::<&i32>::None); + | ^^^^^^^^^^^ help: try: `std::iter::empty()` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_on_single_items.fixed b/src/tools/clippy/tests/ui/iter_on_single_items.fixed new file mode 100644 index 00000000000..1fa4b03641b --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_on_single_items.fixed @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::iter_on_single_items)] +#![allow(clippy::iter_next_slice, clippy::redundant_clone)] + +fn array() { + assert_eq!(std::iter::once(123).next(), Some(123)); + assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123)); + assert_eq!(std::iter::once(&123).next(), Some(&123)); + assert_eq!(std::iter::once(123).next(), Some(123)); + assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123)); + assert_eq!(std::iter::once(&123).next(), Some(&123)); + + // Don't trigger on non-iter methods + let _: Option<String> = Some("test".to_string()).clone(); + let _: [String; 1] = ["test".to_string()].clone(); + + // Don't trigger on match or if branches + let _ = match 123 { + 123 => [].iter(), + _ => ["test"].iter(), + }; + + let _ = if false { ["test"].iter() } else { [].iter() }; +} + +macro_rules! in_macros { + () => { + assert_eq!([123].into_iter().next(), Some(123)); + assert_eq!([123].iter_mut().next(), Some(&mut 123)); + assert_eq!([123].iter().next(), Some(&123)); + assert_eq!(Some(123).into_iter().next(), Some(123)); + assert_eq!(Some(123).iter_mut().next(), Some(&mut 123)); + assert_eq!(Some(123).iter().next(), Some(&123)); + }; +} + +// Don't trigger on a `Some` that isn't std's option +mod custom_option { + #[allow(unused)] + enum CustomOption { + Some(i32), + None, + } + + impl CustomOption { + fn iter(&self) {} + fn iter_mut(&mut self) {} + fn into_iter(self) {} + } + use CustomOption::*; + + pub fn custom_option() { + Some(3).iter(); + Some(3).iter_mut(); + Some(3).into_iter(); + } +} + +fn main() { + array(); + custom_option::custom_option(); + in_macros!(); +} diff --git a/src/tools/clippy/tests/ui/iter_on_single_items.rs b/src/tools/clippy/tests/ui/iter_on_single_items.rs new file mode 100644 index 00000000000..ea96d8066c5 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_on_single_items.rs @@ -0,0 +1,63 @@ +// run-rustfix +#![warn(clippy::iter_on_single_items)] +#![allow(clippy::iter_next_slice, clippy::redundant_clone)] + +fn array() { + assert_eq!([123].into_iter().next(), Some(123)); + assert_eq!([123].iter_mut().next(), Some(&mut 123)); + assert_eq!([123].iter().next(), Some(&123)); + assert_eq!(Some(123).into_iter().next(), Some(123)); + assert_eq!(Some(123).iter_mut().next(), Some(&mut 123)); + assert_eq!(Some(123).iter().next(), Some(&123)); + + // Don't trigger on non-iter methods + let _: Option<String> = Some("test".to_string()).clone(); + let _: [String; 1] = ["test".to_string()].clone(); + + // Don't trigger on match or if branches + let _ = match 123 { + 123 => [].iter(), + _ => ["test"].iter(), + }; + + let _ = if false { ["test"].iter() } else { [].iter() }; +} + +macro_rules! in_macros { + () => { + assert_eq!([123].into_iter().next(), Some(123)); + assert_eq!([123].iter_mut().next(), Some(&mut 123)); + assert_eq!([123].iter().next(), Some(&123)); + assert_eq!(Some(123).into_iter().next(), Some(123)); + assert_eq!(Some(123).iter_mut().next(), Some(&mut 123)); + assert_eq!(Some(123).iter().next(), Some(&123)); + }; +} + +// Don't trigger on a `Some` that isn't std's option +mod custom_option { + #[allow(unused)] + enum CustomOption { + Some(i32), + None, + } + + impl CustomOption { + fn iter(&self) {} + fn iter_mut(&mut self) {} + fn into_iter(self) {} + } + use CustomOption::*; + + pub fn custom_option() { + Some(3).iter(); + Some(3).iter_mut(); + Some(3).into_iter(); + } +} + +fn main() { + array(); + custom_option::custom_option(); + in_macros!(); +} diff --git a/src/tools/clippy/tests/ui/iter_on_single_items.stderr b/src/tools/clippy/tests/ui/iter_on_single_items.stderr new file mode 100644 index 00000000000..d6c54711636 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_on_single_items.stderr @@ -0,0 +1,40 @@ +error: `into_iter` call on a collection with only one item + --> $DIR/iter_on_single_items.rs:6:16 + | +LL | assert_eq!([123].into_iter().next(), Some(123)); + | ^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)` + | + = note: `-D clippy::iter-on-single-items` implied by `-D warnings` + +error: `iter_mut` call on a collection with only one item + --> $DIR/iter_on_single_items.rs:7:16 + | +LL | assert_eq!([123].iter_mut().next(), Some(&mut 123)); + | ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)` + +error: `iter` call on a collection with only one item + --> $DIR/iter_on_single_items.rs:8:16 + | +LL | assert_eq!([123].iter().next(), Some(&123)); + | ^^^^^^^^^^^^ help: try: `std::iter::once(&123)` + +error: `into_iter` call on a collection with only one item + --> $DIR/iter_on_single_items.rs:9:16 + | +LL | assert_eq!(Some(123).into_iter().next(), Some(123)); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)` + +error: `iter_mut` call on a collection with only one item + --> $DIR/iter_on_single_items.rs:10:16 + | +LL | assert_eq!(Some(123).iter_mut().next(), Some(&mut 123)); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)` + +error: `iter` call on a collection with only one item + --> $DIR/iter_on_single_items.rs:11:16 + | +LL | assert_eq!(Some(123).iter().next(), Some(&123)); + | ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&123)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_string_new.fixed b/src/tools/clippy/tests/ui/manual_string_new.fixed new file mode 100644 index 00000000000..a376411bfbc --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_string_new.fixed @@ -0,0 +1,63 @@ +// run-rustfix + +#![warn(clippy::manual_string_new)] + +macro_rules! create_strings_from_macro { + // When inside a macro, nothing should warn to prevent false positives. + ($some_str:expr) => { + let _: String = $some_str.into(); + let _ = $some_str.to_string(); + }; +} + +fn main() { + // Method calls + let _ = String::new(); + let _ = "no warning".to_string(); + + let _ = String::new(); + let _ = "no warning".to_owned(); + + let _: String = String::new(); + let _: String = "no warning".into(); + + let _: SomeOtherStruct = "no warning".into(); + let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String. + + // Calls + let _ = String::new(); + let _ = String::new(); + let _ = String::from("no warning"); + let _ = SomeOtherStruct::from("no warning"); + let _ = SomeOtherStruct::from(""); // Again: no warning. + + let _ = String::new(); + let _ = String::try_from("no warning").unwrap(); + let _ = String::try_from("no warning").expect("this should not warn"); + let _ = SomeOtherStruct::try_from("no warning").unwrap(); + let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning. + + let _: String = String::new(); + let _: String = From::from("no warning"); + let _: SomeOtherStruct = From::from("no warning"); + let _: SomeOtherStruct = From::from(""); // Again: no warning. + + let _: String = String::new(); + let _: String = TryFrom::try_from("no warning").unwrap(); + let _: String = TryFrom::try_from("no warning").expect("this should not warn"); + let _: String = String::new(); + let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap(); + let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning. + + // Macros (never warn) + create_strings_from_macro!(""); + create_strings_from_macro!("Hey"); +} + +struct SomeOtherStruct {} + +impl From<&str> for SomeOtherStruct { + fn from(_value: &str) -> Self { + Self {} + } +} diff --git a/src/tools/clippy/tests/ui/manual_string_new.rs b/src/tools/clippy/tests/ui/manual_string_new.rs new file mode 100644 index 00000000000..6bfc52fb1bc --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_string_new.rs @@ -0,0 +1,63 @@ +// run-rustfix + +#![warn(clippy::manual_string_new)] + +macro_rules! create_strings_from_macro { + // When inside a macro, nothing should warn to prevent false positives. + ($some_str:expr) => { + let _: String = $some_str.into(); + let _ = $some_str.to_string(); + }; +} + +fn main() { + // Method calls + let _ = "".to_string(); + let _ = "no warning".to_string(); + + let _ = "".to_owned(); + let _ = "no warning".to_owned(); + + let _: String = "".into(); + let _: String = "no warning".into(); + + let _: SomeOtherStruct = "no warning".into(); + let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String. + + // Calls + let _ = String::from(""); + let _ = <String>::from(""); + let _ = String::from("no warning"); + let _ = SomeOtherStruct::from("no warning"); + let _ = SomeOtherStruct::from(""); // Again: no warning. + + let _ = String::try_from("").unwrap(); + let _ = String::try_from("no warning").unwrap(); + let _ = String::try_from("no warning").expect("this should not warn"); + let _ = SomeOtherStruct::try_from("no warning").unwrap(); + let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning. + + let _: String = From::from(""); + let _: String = From::from("no warning"); + let _: SomeOtherStruct = From::from("no warning"); + let _: SomeOtherStruct = From::from(""); // Again: no warning. + + let _: String = TryFrom::try_from("").unwrap(); + let _: String = TryFrom::try_from("no warning").unwrap(); + let _: String = TryFrom::try_from("no warning").expect("this should not warn"); + let _: String = TryFrom::try_from("").expect("this should warn"); + let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap(); + let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning. + + // Macros (never warn) + create_strings_from_macro!(""); + create_strings_from_macro!("Hey"); +} + +struct SomeOtherStruct {} + +impl From<&str> for SomeOtherStruct { + fn from(_value: &str) -> Self { + Self {} + } +} diff --git a/src/tools/clippy/tests/ui/manual_string_new.stderr b/src/tools/clippy/tests/ui/manual_string_new.stderr new file mode 100644 index 00000000000..e5ecfc61947 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_string_new.stderr @@ -0,0 +1,58 @@ +error: empty String is being created manually + --> $DIR/manual_string_new.rs:15:13 + | +LL | let _ = "".to_string(); + | ^^^^^^^^^^^^^^ help: consider using: `String::new()` + | + = note: `-D clippy::manual-string-new` implied by `-D warnings` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:18:13 + | +LL | let _ = "".to_owned(); + | ^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:21:21 + | +LL | let _: String = "".into(); + | ^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:28:13 + | +LL | let _ = String::from(""); + | ^^^^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:29:13 + | +LL | let _ = <String>::from(""); + | ^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:34:13 + | +LL | let _ = String::try_from("").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:40:21 + | +LL | let _: String = From::from(""); + | ^^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:45:21 + | +LL | let _: String = TryFrom::try_from("").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: empty String is being created manually + --> $DIR/manual_string_new.rs:48:21 + | +LL | let _: String = TryFrom::try_from("").expect("this should warn"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()` + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed index 1ccbfda64b7..95ca571d07b 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed @@ -167,4 +167,29 @@ fn main() { _ => false, }; } + + let x = ' '; + // ignore if match block contains comment + let _line_comments = match x { + // numbers are bad! + '1' | '2' | '3' => true, + // spaces are very important to be true. + ' ' => true, + // as are dots + '.' => true, + _ => false, + }; + + let _block_comments = match x { + /* numbers are bad! + */ + '1' | '2' | '3' => true, + /* spaces are very important to be true. + */ + ' ' => true, + /* as are dots + */ + '.' => true, + _ => false, + }; } diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs index a49991f5941..3b9c8cadadc 100644 --- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs +++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs @@ -208,4 +208,29 @@ fn main() { _ => false, }; } + + let x = ' '; + // ignore if match block contains comment + let _line_comments = match x { + // numbers are bad! + '1' | '2' | '3' => true, + // spaces are very important to be true. + ' ' => true, + // as are dots + '.' => true, + _ => false, + }; + + let _block_comments = match x { + /* numbers are bad! + */ + '1' | '2' | '3' => true, + /* spaces are very important to be true. + */ + ' ' => true, + /* as are dots + */ + '.' => true, + _ => false, + }; } diff --git a/src/tools/clippy/tests/ui/multi_assignments.rs b/src/tools/clippy/tests/ui/multi_assignments.rs new file mode 100644 index 00000000000..b186bf8bbdb --- /dev/null +++ b/src/tools/clippy/tests/ui/multi_assignments.rs @@ -0,0 +1,9 @@ +#![warn(clippy::multi_assignments)] +fn main() { + let (mut a, mut b, mut c, mut d) = ((), (), (), ()); + a = b = c; + a = b = c = d; + a = b = { c }; + a = { b = c }; + a = (b = c); +} diff --git a/src/tools/clippy/tests/ui/multi_assignments.stderr b/src/tools/clippy/tests/ui/multi_assignments.stderr new file mode 100644 index 00000000000..d6c42bb698c --- /dev/null +++ b/src/tools/clippy/tests/ui/multi_assignments.stderr @@ -0,0 +1,40 @@ +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:4:5 + | +LL | a = b = c; + | ^^^^^^^^^ + | + = note: `-D clippy::multi-assignments` implied by `-D warnings` + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:5:5 + | +LL | a = b = c = d; + | ^^^^^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:5:9 + | +LL | a = b = c = d; + | ^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:6:5 + | +LL | a = b = { c }; + | ^^^^^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:7:5 + | +LL | a = { b = c }; + | ^^^^^^^^^^^^^ + +error: assignments don't nest intuitively + --> $DIR/multi_assignments.rs:8:5 + | +LL | a = (b = c); + | ^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed index bfd2725ecaa..8cf93bd2481 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.fixed +++ b/src/tools/clippy/tests/ui/needless_borrow.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables, clippy::unnecessary_mut_passed)] @@ -127,6 +127,20 @@ fn main() { 0 } } + + let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); + let _ = std::path::Path::new(".").join("."); + deref_target_is_x(X); + multiple_constraints([[""]]); + multiple_constraints_normalizes_to_same(X, X); + let _ = Some("").unwrap_or(""); + + only_sized(&""); // Don't lint. `Sized` is only bound + let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -183,3 +197,104 @@ mod issue9160 { } } } + +#[derive(Clone, Copy)] +struct X; + +impl std::ops::Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } +} + +fn deref_target_is_x<T>(_: T) +where + T: std::ops::Deref<Target = X>, +{ +} + +fn multiple_constraints<T, U, V, X, Y>(_: T) +where + T: IntoIterator<Item = U> + IntoIterator<Item = X>, + U: IntoIterator<Item = V>, + V: AsRef<str>, + X: IntoIterator<Item = Y>, + Y: AsRef<std::ffi::OsStr>, +{ +} + +fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V) +where + T: std::ops::Deref<Target = U>, + U: std::ops::Deref<Target = V>, +{ +} + +fn only_sized<T>(_: T) {} + +fn ref_as_ref_path<T: 'static>(_: &'static T) +where + &'static T: AsRef<std::path::Path>, +{ +} + +trait RefsOnly { + type Referent; +} + +impl<T> RefsOnly for &T { + type Referent = T; +} + +fn refs_only<T, U>(_: T) +where + T: RefsOnly<Referent = U>, +{ +} + +fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U) +where + T: IntoIterator<Item = U>, + U: IntoIterator<Item = V>, + V: AsRef<str>, +{ +} + +// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 +#[allow(dead_code)] +mod copyable_iterator { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + fn warn(mut x: &mut Iter) { + takes_iter(&mut x) + } +} + +mod under_msrv { + #![allow(dead_code)] + #![clippy::msrv = "1.52.0"] + + fn foo() { + let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + } +} + +mod meets_msrv { + #![allow(dead_code)] + #![clippy::msrv = "1.53.0"] + + fn foo() { + let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs index c457d8c5471..fd9b2a11df9 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.rs +++ b/src/tools/clippy/tests/ui/needless_borrow.rs @@ -1,6 +1,6 @@ // run-rustfix -#![feature(lint_reasons)] +#![feature(custom_inner_attributes, lint_reasons)] #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables, clippy::unnecessary_mut_passed)] @@ -127,6 +127,20 @@ fn main() { 0 } } + + let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + let _ = std::path::Path::new(".").join(&&"."); + deref_target_is_x(&X); + multiple_constraints(&[[""]]); + multiple_constraints_normalizes_to_same(&X, X); + let _ = Some("").unwrap_or(&""); + + only_sized(&""); // Don't lint. `Sized` is only bound + let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound + let _ = Box::new(&""); // Don't lint. Type parameter appears in return type + ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter + refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't + multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments } #[allow(clippy::needless_borrowed_reference)] @@ -183,3 +197,104 @@ mod issue9160 { } } } + +#[derive(Clone, Copy)] +struct X; + +impl std::ops::Deref for X { + type Target = X; + fn deref(&self) -> &Self::Target { + self + } +} + +fn deref_target_is_x<T>(_: T) +where + T: std::ops::Deref<Target = X>, +{ +} + +fn multiple_constraints<T, U, V, X, Y>(_: T) +where + T: IntoIterator<Item = U> + IntoIterator<Item = X>, + U: IntoIterator<Item = V>, + V: AsRef<str>, + X: IntoIterator<Item = Y>, + Y: AsRef<std::ffi::OsStr>, +{ +} + +fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V) +where + T: std::ops::Deref<Target = U>, + U: std::ops::Deref<Target = V>, +{ +} + +fn only_sized<T>(_: T) {} + +fn ref_as_ref_path<T: 'static>(_: &'static T) +where + &'static T: AsRef<std::path::Path>, +{ +} + +trait RefsOnly { + type Referent; +} + +impl<T> RefsOnly for &T { + type Referent = T; +} + +fn refs_only<T, U>(_: T) +where + T: RefsOnly<Referent = U>, +{ +} + +fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U) +where + T: IntoIterator<Item = U>, + U: IntoIterator<Item = V>, + V: AsRef<str>, +{ +} + +// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321 +#[allow(dead_code)] +mod copyable_iterator { + #[derive(Clone, Copy)] + struct Iter; + impl Iterator for Iter { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { + None + } + } + fn takes_iter(_: impl Iterator) {} + fn dont_warn(mut x: Iter) { + takes_iter(&mut x); + } + fn warn(mut x: &mut Iter) { + takes_iter(&mut x) + } +} + +mod under_msrv { + #![allow(dead_code)] + #![clippy::msrv = "1.52.0"] + + fn foo() { + let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + } +} + +mod meets_msrv { + #![allow(dead_code)] + #![clippy::msrv = "1.53.0"] + + fn foo() { + let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr index 66588689d81..5af68706d4b 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.stderr +++ b/src/tools/clippy/tests/ui/needless_borrow.stderr @@ -120,17 +120,59 @@ error: this expression creates a reference which is immediately dereferenced by LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:131:51 + | +LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:132:44 + | +LL | let _ = std::path::Path::new(".").join(&&"."); + | ^^^^^ help: change this to: `"."` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:133:23 + | +LL | deref_target_is_x(&X); + | ^^ help: change this to: `X` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:134:26 + | +LL | multiple_constraints(&[[""]]); + | ^^^^^^^ help: change this to: `[[""]]` + +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:135:45 + | +LL | multiple_constraints_normalizes_to_same(&X, X); + | ^^ help: change this to: `X` + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> $DIR/needless_borrow.rs:136:32 + | +LL | let _ = Some("").unwrap_or(&""); + | ^^^ help: change this to: `""` + error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:173:13 + --> $DIR/needless_borrow.rs:187:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:182:13 + --> $DIR/needless_borrow.rs:196:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` -error: aborting due to 22 previous errors +error: the borrowed expression implements the required traits + --> $DIR/needless_borrow.rs:298:55 + | +LL | let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap(); + | ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]` + +error: aborting due to 29 previous errors diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs index 1f11d1f8d56..12a9ace1ee6 100644 --- a/src/tools/clippy/tests/ui/needless_collect_indirect.rs +++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs @@ -112,3 +112,192 @@ fn allow_test() { let v = [1].iter().collect::<Vec<_>>(); v.into_iter().collect::<HashSet<_>>(); } + +mod issue_8553 { + fn test_for() { + let vec = vec![1, 2]; + let w: Vec<usize> = vec.iter().map(|i| i * i).collect(); + + for i in 0..2 { + // Do not lint, because this method call is in the loop + w.contains(&i); + } + + for i in 0..2 { + let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); + // Do lint + y.contains(&i); + for j in 0..2 { + // Do not lint, because this method call is in the loop + z.contains(&j); + } + } + + // Do not lint, because this variable is used. + w.contains(&0); + } + + fn test_while() { + let vec = vec![1, 2]; + let x: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let mut n = 0; + while n > 1 { + // Do not lint, because this method call is in the loop + x.contains(&n); + n += 1; + } + + while n > 2 { + let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); + // Do lint + y.contains(&n); + n += 1; + while n > 4 { + // Do not lint, because this method call is in the loop + z.contains(&n); + n += 1; + } + } + } + + fn test_loop() { + let vec = vec![1, 2]; + let x: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let mut n = 0; + loop { + if n < 1 { + // Do not lint, because this method call is in the loop + x.contains(&n); + n += 1; + } else { + break; + } + } + + loop { + if n < 2 { + let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); + // Do lint + y.contains(&n); + n += 1; + loop { + if n < 4 { + // Do not lint, because this method call is in the loop + z.contains(&n); + n += 1; + } else { + break; + } + } + } else { + break; + } + } + } + + fn test_while_let() { + let vec = vec![1, 2]; + let x: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let optional = Some(0); + let mut n = 0; + while let Some(value) = optional { + if n < 1 { + // Do not lint, because this method call is in the loop + x.contains(&n); + n += 1; + } else { + break; + } + } + + while let Some(value) = optional { + let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); + if n < 2 { + // Do lint + y.contains(&n); + n += 1; + } else { + break; + } + + while let Some(value) = optional { + if n < 4 { + // Do not lint, because this method call is in the loop + z.contains(&n); + n += 1; + } else { + break; + } + } + } + } + + fn test_if_cond() { + let vec = vec![1, 2]; + let v: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let w = v.iter().collect::<Vec<_>>(); + // Do lint + for _ in 0..w.len() { + todo!(); + } + } + + fn test_if_cond_false_case() { + let vec = vec![1, 2]; + let v: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let w = v.iter().collect::<Vec<_>>(); + // Do not lint, because w is used. + for _ in 0..w.len() { + todo!(); + } + + w.len(); + } + + fn test_while_cond() { + let mut vec = vec![1, 2]; + let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let mut w = v.iter().collect::<Vec<_>>(); + // Do lint + while 1 == w.len() { + todo!(); + } + } + + fn test_while_cond_false_case() { + let mut vec = vec![1, 2]; + let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let mut w = v.iter().collect::<Vec<_>>(); + // Do not lint, because w is used. + while 1 == w.len() { + todo!(); + } + + w.len(); + } + + fn test_while_let_cond() { + let mut vec = vec![1, 2]; + let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let mut w = v.iter().collect::<Vec<_>>(); + // Do lint + while let Some(i) = Some(w.len()) { + todo!(); + } + } + + fn test_while_let_cond_false_case() { + let mut vec = vec![1, 2]; + let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect(); + let mut w = v.iter().collect::<Vec<_>>(); + // Do not lint, because w is used. + while let Some(i) = Some(w.len()) { + todo!(); + } + w.len(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr index 0f5e78f9119..9f0880cc606 100644 --- a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr +++ b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr @@ -125,5 +125,122 @@ LL ~ LL ~ sample.iter().count() | -error: aborting due to 9 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:127:59 + | +LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + | ^^^^^^^ +... +LL | y.contains(&i); + | -------------- the iterator could be used here instead + | +help: check if the original Iterator contains an element instead of collecting then checking + | +LL ~ +LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); +LL | // Do lint +LL ~ vec.iter().map(|k| k * k).any(|x| x == i); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:152:59 + | +LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + | ^^^^^^^ +... +LL | y.contains(&n); + | -------------- the iterator could be used here instead + | +help: check if the original Iterator contains an element instead of collecting then checking + | +LL ~ +LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); +LL | // Do lint +LL ~ vec.iter().map(|k| k * k).any(|x| x == n); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:181:63 + | +LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + | ^^^^^^^ +... +LL | y.contains(&n); + | -------------- the iterator could be used here instead + | +help: check if the original Iterator contains an element instead of collecting then checking + | +LL ~ +LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); +LL | // Do lint +LL ~ vec.iter().map(|k| k * k).any(|x| x == n); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:217:59 + | +LL | let y: Vec<usize> = vec.iter().map(|k| k * k).collect(); + | ^^^^^^^ +... +LL | y.contains(&n); + | -------------- the iterator could be used here instead + | +help: check if the original Iterator contains an element instead of collecting then checking + | +LL ~ +LL | let z: Vec<usize> = vec.iter().map(|k| k * k).collect(); +LL | if n < 2 { +LL | // Do lint +LL ~ vec.iter().map(|k| k * k).any(|x| x == n); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:242:26 + | +LL | let w = v.iter().collect::<Vec<_>>(); + | ^^^^^^^ +LL | // Do lint +LL | for _ in 0..w.len() { + | ------- the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL ~ +LL | // Do lint +LL ~ for _ in 0..v.iter().count() { + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:264:30 + | +LL | let mut w = v.iter().collect::<Vec<_>>(); + | ^^^^^^^ +LL | // Do lint +LL | while 1 == w.len() { + | ------- the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL ~ +LL | // Do lint +LL ~ while 1 == v.iter().count() { + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:286:30 + | +LL | let mut w = v.iter().collect::<Vec<_>>(); + | ^^^^^^^ +LL | // Do lint +LL | while let Some(i) = Some(w.len()) { + | ------- the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL ~ +LL | // Do lint +LL ~ while let Some(i) = Some(v.iter().count()) { + | + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/needless_match.fixed b/src/tools/clippy/tests/ui/needless_match.fixed index 0c9178fb85e..7e47406798c 100644 --- a/src/tools/clippy/tests/ui/needless_match.fixed +++ b/src/tools/clippy/tests/ui/needless_match.fixed @@ -207,4 +207,43 @@ impl Tr for Result<i32, i32> { } } +mod issue9084 { + fn wildcard_if() { + let mut some_bool = true; + let e = Some(1); + + // should lint + let _ = e; + + // should lint + let _ = e; + + // should not lint + let _ = match e { + _ if some_bool => e, + _ => Some(2), + }; + + // should not lint + let _ = match e { + Some(i) => Some(i + 1), + _ if some_bool => e, + _ => e, + }; + + // should not lint (guard has side effects) + let _ = match e { + Some(i) => Some(i), + _ if { + some_bool = false; + some_bool + } => + { + e + }, + _ => e, + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.rs b/src/tools/clippy/tests/ui/needless_match.rs index f66f01d7cca..809c694bf40 100644 --- a/src/tools/clippy/tests/ui/needless_match.rs +++ b/src/tools/clippy/tests/ui/needless_match.rs @@ -244,4 +244,50 @@ impl Tr for Result<i32, i32> { } } +mod issue9084 { + fn wildcard_if() { + let mut some_bool = true; + let e = Some(1); + + // should lint + let _ = match e { + _ if some_bool => e, + _ => e, + }; + + // should lint + let _ = match e { + Some(i) => Some(i), + _ if some_bool => e, + _ => e, + }; + + // should not lint + let _ = match e { + _ if some_bool => e, + _ => Some(2), + }; + + // should not lint + let _ = match e { + Some(i) => Some(i + 1), + _ if some_bool => e, + _ => e, + }; + + // should not lint (guard has side effects) + let _ = match e { + Some(i) => Some(i), + _ if { + some_bool = false; + some_bool + } => + { + e + }, + _ => e, + }; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_match.stderr b/src/tools/clippy/tests/ui/needless_match.stderr index 5bc79800a1a..28e78441c25 100644 --- a/src/tools/clippy/tests/ui/needless_match.stderr +++ b/src/tools/clippy/tests/ui/needless_match.stderr @@ -109,5 +109,26 @@ LL | | Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB( LL | | }; | |_________^ help: replace it with: `ce` -error: aborting due to 11 previous errors +error: this match expression is unnecessary + --> $DIR/needless_match.rs:253:17 + | +LL | let _ = match e { + | _________________^ +LL | | _ if some_bool => e, +LL | | _ => e, +LL | | }; + | |_________^ help: replace it with: `e` + +error: this match expression is unnecessary + --> $DIR/needless_match.rs:259:17 + | +LL | let _ = match e { + | _________________^ +LL | | Some(i) => Some(i), +LL | | _ if some_bool => e, +LL | | _ => e, +LL | | }; + | |_________^ help: replace it with: `e` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index 0bc0d0011ef..87c8fc03b3c 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -228,13 +228,9 @@ fn needless_return_macro() -> String { format!("Hello {}", "world!") } -fn check_expect() -> bool { - if true { - // no error! - return true; - } - #[expect(clippy::needless_return)] - return true; +fn issue_9361() -> i32 { + #[allow(clippy::integer_arithmetic)] + return 1 + 2; } fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index eb9f72e8e78..5a86e656255 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -228,13 +228,9 @@ fn needless_return_macro() -> String { return format!("Hello {}", "world!"); } -fn check_expect() -> bool { - if true { - // no error! - return true; - } - #[expect(clippy::needless_return)] - return true; +fn issue_9361() -> i32 { + #[allow(clippy::integer_arithmetic)] + return 1 + 2; } fn main() {} diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion.rs b/src/tools/clippy/tests/ui/only_used_in_recursion.rs index 5768434f988..f71e8ead519 100644 --- a/src/tools/clippy/tests/ui/only_used_in_recursion.rs +++ b/src/tools/clippy/tests/ui/only_used_in_recursion.rs @@ -1,122 +1,113 @@ #![warn(clippy::only_used_in_recursion)] -fn simple(a: usize, b: usize) -> usize { - if a == 0 { 1 } else { simple(a - 1, b) } +fn _simple(x: u32) -> u32 { + x } -fn with_calc(a: usize, b: isize) -> usize { - if a == 0 { 1 } else { with_calc(a - 1, -b + 1) } +fn _simple2(x: u32) -> u32 { + _simple(x) } -fn tuple((a, b): (usize, usize)) -> usize { - if a == 0 { 1 } else { tuple((a - 1, b + 1)) } +fn _one_unused(flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { _one_unused(flag - 1, a) } } -fn let_tuple(a: usize, b: usize) -> usize { - let (c, d) = (a, b); - if c == 0 { 1 } else { let_tuple(c - 1, d + 1) } +fn _two_unused(flag: u32, a: u32, b: i32) -> usize { + if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } } -fn array([a, b]: [usize; 2]) -> usize { - if a == 0 { 1 } else { array([a - 1, b + 1]) } -} - -fn index(a: usize, mut b: &[usize], c: usize) -> usize { - if a == 0 { 1 } else { index(a - 1, b, c + b[0]) } -} - -fn break_(a: usize, mut b: usize, mut c: usize) -> usize { - let c = loop { - b += 1; - c += 1; - if c == 10 { - break b; - } - }; - - if a == 0 { 1 } else { break_(a - 1, c, c) } +fn _with_calc(flag: u32, a: i64) -> usize { + if flag == 0 { + 0 + } else { + _with_calc(flag - 1, (-a + 10) * 5) + } } -// this has a side effect -fn mut_ref(a: usize, b: &mut usize) -> usize { - *b = 1; - if a == 0 { 1 } else { mut_ref(a - 1, b) } +// Don't lint +fn _used_with_flag(flag: u32, a: u32) -> usize { + if flag == 0 { 0 } else { _used_with_flag(flag ^ a, a - 1) } } -fn mut_ref2(a: usize, b: &mut usize) -> usize { - let mut c = *b; - if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) } +fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { + if flag == 0 { + 0 + } else { + _used_with_unused(flag - 1, -a, a + b) + } } -fn not_primitive(a: usize, b: String) -> usize { - if a == 0 { 1 } else { not_primitive(a - 1, b) } +fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { + if flag == 0 { + 0 + } else { + _codependent_unused(flag - 1, a * b, a + b) + } } -// this doesn't have a side effect, -// but `String` is not primitive. -fn not_primitive_op(a: usize, b: String, c: &str) -> usize { - if a == 1 { 1 } else { not_primitive_op(a, b + c, c) } +fn _not_primitive(flag: u32, b: String) -> usize { + if flag == 0 { 0 } else { _not_primitive(flag - 1, b) } } struct A; impl A { - fn method(a: usize, b: usize) -> usize { - if a == 0 { 1 } else { A::method(a - 1, b - 1) } + fn _method(flag: usize, a: usize) -> usize { + if flag == 0 { 0 } else { Self::_method(flag - 1, a) } } - fn method2(&self, a: usize, b: usize) -> usize { - if a == 0 { 1 } else { self.method2(a - 1, b + 1) } + fn _method_self(&self, flag: usize, a: usize) -> usize { + if flag == 0 { 0 } else { self._method_self(flag - 1, a) } } } trait B { - fn hello(a: usize, b: usize) -> usize; - - fn hello2(&self, a: usize, b: usize) -> usize; + fn method(flag: u32, a: usize) -> usize; + fn method_self(&self, flag: u32, a: usize) -> usize; } impl B for A { - fn hello(a: usize, b: usize) -> usize { - if a == 0 { 1 } else { A::hello(a - 1, b + 1) } + fn method(flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { Self::method(flag - 1, a) } } - fn hello2(&self, a: usize, b: usize) -> usize { - if a == 0 { 1 } else { self.hello2(a - 1, b + 1) } + fn method_self(&self, flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { self.method_self(flag - 1, a) } } } -trait C { - fn hello(a: usize, b: usize) -> usize { - if a == 0 { 1 } else { Self::hello(a - 1, b + 1) } +impl B for () { + fn method(flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { a } } - fn hello2(&self, a: usize, b: usize) -> usize { - if a == 0 { 1 } else { self.hello2(a - 1, b + 1) } + fn method_self(&self, flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { a } } } -fn ignore(a: usize, _: usize) -> usize { - if a == 1 { 1 } else { ignore(a - 1, 0) } -} +impl B for u32 { + fn method(flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { <() as B>::method(flag, a) } + } -fn ignore2(a: usize, _b: usize) -> usize { - if a == 1 { 1 } else { ignore2(a - 1, _b) } + fn method_self(&self, flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { ().method_self(flag, a) } + } } -fn f1(a: u32) -> u32 { - a -} +trait C { + fn method(flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { Self::method(flag - 1, a) } + } -fn f2(a: u32) -> u32 { - f1(a) + fn method_self(&self, flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { self.method_self(flag - 1, a) } + } } -fn inner_fn(a: u32) -> u32 { - fn inner_fn(a: u32) -> u32 { - a - } - inner_fn(a) +fn _ignore(flag: usize, _a: usize) -> usize { + if flag == 0 { 0 } else { _ignore(flag - 1, _a) } } fn main() {} diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion.stderr b/src/tools/clippy/tests/ui/only_used_in_recursion.stderr index 6fe9361bf5f..74057ddcfda 100644 --- a/src/tools/clippy/tests/ui/only_used_in_recursion.stderr +++ b/src/tools/clippy/tests/ui/only_used_in_recursion.stderr @@ -1,82 +1,195 @@ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:3:21 + --> $DIR/only_used_in_recursion.rs:11:27 | -LL | fn simple(a: usize, b: usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | fn _one_unused(flag: u32, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` | = note: `-D clippy::only-used-in-recursion` implied by `-D warnings` +note: parameter used here + --> $DIR/only_used_in_recursion.rs:12:53 + | +LL | if flag == 0 { 0 } else { _one_unused(flag - 1, a) } + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion.rs:15:27 + | +LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:16:53 + | +LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion.rs:15:35 + | +LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:16:56 + | +LL | if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) } + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion.rs:19:26 + | +LL | fn _with_calc(flag: u32, a: i64) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:23:32 + | +LL | _with_calc(flag - 1, (-a + 10) * 5) + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:7:24 + --> $DIR/only_used_in_recursion.rs:32:33 + | +LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:36:38 | -LL | fn with_calc(a: usize, b: isize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | _used_with_unused(flag - 1, -a, a + b) + | ^ ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:11:14 + --> $DIR/only_used_in_recursion.rs:32:41 + | +LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:36:45 | -LL | fn tuple((a, b): (usize, usize)) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | _used_with_unused(flag - 1, -a, a + b) + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:15:24 + --> $DIR/only_used_in_recursion.rs:40:35 | -LL | fn let_tuple(a: usize, b: usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:44:39 + | +LL | _codependent_unused(flag - 1, a * b, a + b) + | ^ ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:20:14 + --> $DIR/only_used_in_recursion.rs:40:43 + | +LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` | -LL | fn array([a, b]: [usize; 2]) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +note: parameter used here + --> $DIR/only_used_in_recursion.rs:44:43 + | +LL | _codependent_unused(flag - 1, a * b, a + b) + | ^ ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:24:20 + --> $DIR/only_used_in_recursion.rs:48:30 + | +LL | fn _not_primitive(flag: u32, b: String) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` | -LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize { - | ^^^^^ help: if this is intentional, prefix with an underscore: `_b` +note: parameter used here + --> $DIR/only_used_in_recursion.rs:49:56 + | +LL | if flag == 0 { 0 } else { _not_primitive(flag - 1, b) } + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:24:37 + --> $DIR/only_used_in_recursion.rs:55:29 + | +LL | fn _method(flag: usize, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:56:59 | -LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_c` +LL | if flag == 0 { 0 } else { Self::_method(flag - 1, a) } + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:28:21 + --> $DIR/only_used_in_recursion.rs:59:22 + | +LL | fn _method_self(&self, flag: usize, a: usize) -> usize { + | ^^^^ + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:60:35 | -LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize { - | ^^^^^ help: if this is intentional, prefix with an underscore: `_b` +LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } + | ^^^^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:46:23 + --> $DIR/only_used_in_recursion.rs:59:41 | -LL | fn mut_ref2(a: usize, b: &mut usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | fn _method_self(&self, flag: usize, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:60:63 + | +LL | if flag == 0 { 0 } else { self._method_self(flag - 1, a) } + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:51:28 + --> $DIR/only_used_in_recursion.rs:70:26 + | +LL | fn method(flag: u32, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` | -LL | fn not_primitive(a: usize, b: String) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +note: parameter used here + --> $DIR/only_used_in_recursion.rs:71:58 + | +LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:68:33 + --> $DIR/only_used_in_recursion.rs:74:38 + | +LL | fn method_self(&self, flag: u32, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` | -LL | fn method2(&self, a: usize, b: usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +note: parameter used here + --> $DIR/only_used_in_recursion.rs:75:62 + | +LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:90:24 + --> $DIR/only_used_in_recursion.rs:100:26 + | +LL | fn method(flag: u32, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:101:58 | -LL | fn hello(a: usize, b: usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | if flag == 0 { 0 } else { Self::method(flag - 1, a) } + | ^ error: parameter is only used in recursion - --> $DIR/only_used_in_recursion.rs:94:32 + --> $DIR/only_used_in_recursion.rs:104:38 + | +LL | fn method_self(&self, flag: u32, a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion.rs:105:62 | -LL | fn hello2(&self, a: usize, b: usize) -> usize { - | ^ help: if this is intentional, prefix with an underscore: `_b` +LL | if flag == 0 { 0 } else { self.method_self(flag - 1, a) } + | ^ -error: aborting due to 13 previous errors +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion2.rs b/src/tools/clippy/tests/ui/only_used_in_recursion2.rs new file mode 100644 index 00000000000..45dd0553f58 --- /dev/null +++ b/src/tools/clippy/tests/ui/only_used_in_recursion2.rs @@ -0,0 +1,91 @@ +#![warn(clippy::only_used_in_recursion)] + +fn _with_inner(flag: u32, a: u32, b: u32) -> usize { + fn inner(flag: u32, a: u32) -> u32 { + if flag == 0 { 0 } else { inner(flag, a) } + } + + let x = inner(flag, a); + if flag == 0 { 0 } else { _with_inner(flag, a, b + x) } +} + +fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 { + if let Some(x) = a.and_then(|x| f(x, x)) { + _with_closure(Some(x), b, f) + } else { + 0 + } +} + +// Issue #8560 +trait D { + fn foo(&mut self, arg: u32) -> u32; +} + +mod m { + pub struct S(u32); + impl S { + pub fn foo(&mut self, arg: u32) -> u32 { + arg + self.0 + } + } +} + +impl D for m::S { + fn foo(&mut self, arg: u32) -> u32 { + self.foo(arg) + } +} + +// Issue #8782 +fn only_let(x: u32) { + let y = 10u32; + let _z = x * y; +} + +trait E<T: E<()>> { + fn method(flag: u32, a: usize) -> usize { + if flag == 0 { + 0 + } else { + <T as E<()>>::method(flag - 1, a) + } + } +} + +impl E<()> for () { + fn method(flag: u32, a: usize) -> usize { + if flag == 0 { 0 } else { a } + } +} + +fn overwritten_param(flag: u32, mut a: usize) -> usize { + if flag == 0 { + return 0; + } else if flag > 5 { + a += flag as usize; + } else { + a = 5; + } + overwritten_param(flag, a) +} + +fn field_direct(flag: u32, mut a: (usize,)) -> usize { + if flag == 0 { + 0 + } else { + a.0 += 5; + field_direct(flag - 1, a) + } +} + +fn field_deref(flag: u32, a: &mut Box<(usize,)>) -> usize { + if flag == 0 { + 0 + } else { + a.0 += 5; + field_deref(flag - 1, a) + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/only_used_in_recursion2.stderr b/src/tools/clippy/tests/ui/only_used_in_recursion2.stderr new file mode 100644 index 00000000000..23f6ffd30c9 --- /dev/null +++ b/src/tools/clippy/tests/ui/only_used_in_recursion2.stderr @@ -0,0 +1,63 @@ +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion2.rs:3:35 + | +LL | fn _with_inner(flag: u32, a: u32, b: u32) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + | + = note: `-D clippy::only-used-in-recursion` implied by `-D warnings` +note: parameter used here + --> $DIR/only_used_in_recursion2.rs:9:52 + | +LL | if flag == 0 { 0 } else { _with_inner(flag, a, b + x) } + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion2.rs:4:25 + | +LL | fn inner(flag: u32, a: u32) -> u32 { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion2.rs:5:47 + | +LL | if flag == 0 { 0 } else { inner(flag, a) } + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion2.rs:12:34 + | +LL | fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 { + | ^ help: if this is intentional, prefix it with an underscore: `_b` + | +note: parameter used here + --> $DIR/only_used_in_recursion2.rs:14:32 + | +LL | _with_closure(Some(x), b, f) + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion2.rs:62:37 + | +LL | fn overwritten_param(flag: u32, mut a: usize) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion2.rs:70:29 + | +LL | overwritten_param(flag, a) + | ^ + +error: parameter is only used in recursion + --> $DIR/only_used_in_recursion2.rs:73:32 + | +LL | fn field_direct(flag: u32, mut a: (usize,)) -> usize { + | ^ help: if this is intentional, prefix it with an underscore: `_a` + | +note: parameter used here + --> $DIR/only_used_in_recursion2.rs:78:32 + | +LL | field_direct(flag - 1, a) + | ^ + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index b6d5e106f05..f15ac551bb3 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -179,4 +179,13 @@ fn main() { let _ = pattern_to_vec("hello world"); let _ = complex_subpat(); + + // issue #8492 + let _ = s.map_or(1, |string| string.len()); + let _ = Some(10).map_or(5, |a| a + 1); + + let res: Result<i32, i32> = Ok(5); + let _ = res.map_or(1, |a| a + 1); + let _ = res.map_or(1, |a| a + 1); + let _ = res.map_or(5, |a| a + 1); } diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 35bae159343..9eeaea12d3b 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -208,4 +208,25 @@ fn main() { let _ = pattern_to_vec("hello world"); let _ = complex_subpat(); + + // issue #8492 + let _ = match s { + Some(string) => string.len(), + None => 1, + }; + let _ = match Some(10) { + Some(a) => a + 1, + None => 5, + }; + + let res: Result<i32, i32> = Ok(5); + let _ = match res { + Ok(a) => a + 1, + _ => 1, + }; + let _ = match res { + Err(_) => 1, + Ok(a) => a + 1, + }; + let _ = if let Ok(a) = res { a + 1 } else { 5 }; } diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index daba606004e..a5dbf6e1f22 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -206,5 +206,51 @@ LL + s.len() + x LL ~ }); | -error: aborting due to 15 previous errors +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:213:13 + | +LL | let _ = match s { + | _____________^ +LL | | Some(string) => string.len(), +LL | | None => 1, +LL | | }; + | |_____^ help: try: `s.map_or(1, |string| string.len())` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:217:13 + | +LL | let _ = match Some(10) { + | _____________^ +LL | | Some(a) => a + 1, +LL | | None => 5, +LL | | }; + | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:223:13 + | +LL | let _ = match res { + | _____________^ +LL | | Ok(a) => a + 1, +LL | | _ => 1, +LL | | }; + | |_____^ help: try: `res.map_or(1, |a| a + 1)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:227:13 + | +LL | let _ = match res { + | _____________^ +LL | | Err(_) => 1, +LL | | Ok(a) => a + 1, +LL | | }; + | |_____^ help: try: `res.map_or(1, |a| a + 1)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:231:13 + | +LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` + +error: aborting due to 20 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 fdb08d953ff..18ea4e55029 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -90,8 +90,8 @@ fn or_fun_call() { let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new(); btree_vec.entry(42).or_insert(vec![]); - let stringy = Some(String::from("")); - let _ = stringy.unwrap_or_else(|| "".to_owned()); + let stringy = Some(String::new()); + let _ = stringy.unwrap_or_default(); let opt = Some(1); let hello = "Hello"; diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index 57ab5f03ee2..c353b41e449 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -90,8 +90,8 @@ fn or_fun_call() { let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new(); btree_vec.entry(42).or_insert(vec![]); - let stringy = Some(String::from("")); - let _ = stringy.unwrap_or("".to_owned()); + let stringy = Some(String::new()); + let _ = stringy.unwrap_or(String::new()); let opt = Some(1); let hello = "Hello"; diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 4c5938ab88b..887f23ac976 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -66,11 +66,11 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` -error: use of `unwrap_or` followed by a function call +error: use of `unwrap_or` followed by a call to `new` --> $DIR/or_fun_call.rs:94:21 | -LL | let _ = stringy.unwrap_or("".to_owned()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` +LL | let _ = stringy.unwrap_or(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()` error: use of `unwrap_or` followed by a function call --> $DIR/or_fun_call.rs:102:21 diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.fixed b/src/tools/clippy/tests/ui/partialeq_to_none.fixed index f3e4c58d694..4644ea8f51d 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.fixed +++ b/src/tools/clippy/tests/ui/partialeq_to_none.fixed @@ -26,6 +26,18 @@ fn optref() -> &'static &'static Option<()> { &&None } +pub fn macro_expansion() { + macro_rules! foo { + () => { + None::<()> + }; + } + + let _ = foobar() == foo!(); + let _ = foo!() == foobar(); + let _ = foo!() == foo!(); +} + fn main() { let x = Some(0); diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.rs b/src/tools/clippy/tests/ui/partialeq_to_none.rs index 767b2a38bcc..61011b3a8c5 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.rs +++ b/src/tools/clippy/tests/ui/partialeq_to_none.rs @@ -26,6 +26,18 @@ fn optref() -> &'static &'static Option<()> { &&None } +pub fn macro_expansion() { + macro_rules! foo { + () => { + None::<()> + }; + } + + let _ = foobar() == foo!(); + let _ = foo!() == foobar(); + let _ = foo!() == foo!(); +} + fn main() { let x = Some(0); diff --git a/src/tools/clippy/tests/ui/partialeq_to_none.stderr b/src/tools/clippy/tests/ui/partialeq_to_none.stderr index de15a9f7baa..d06ab7aee55 100644 --- a/src/tools/clippy/tests/ui/partialeq_to_none.stderr +++ b/src/tools/clippy/tests/ui/partialeq_to_none.stderr @@ -7,55 +7,55 @@ LL | if f != None { "yay" } else { "nay" } = note: `-D clippy::partialeq-to-none` implied by `-D warnings` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:32:13 + --> $DIR/partialeq_to_none.rs:44:13 | LL | let _ = x == None; | ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:33:13 + --> $DIR/partialeq_to_none.rs:45:13 | LL | let _ = x != None; | ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:34:13 + --> $DIR/partialeq_to_none.rs:46:13 | LL | let _ = None == x; | ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:35:13 + --> $DIR/partialeq_to_none.rs:47:13 | LL | let _ = None != x; | ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:37:8 + --> $DIR/partialeq_to_none.rs:49:8 | LL | if foobar() == None {} | ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:39:8 + --> $DIR/partialeq_to_none.rs:51:8 | LL | if bar().ok() != None {} | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:41:13 + --> $DIR/partialeq_to_none.rs:53:13 | LL | let _ = Some(1 + 2) != None; | ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:43:13 + --> $DIR/partialeq_to_none.rs:55:13 | LL | let _ = { Some(0) } == None; | ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:45:13 + --> $DIR/partialeq_to_none.rs:57:13 | LL | let _ = { | _____________^ @@ -77,31 +77,31 @@ LL ~ }.is_some(); | error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:55:13 + --> $DIR/partialeq_to_none.rs:67:13 | LL | let _ = optref() == &&None; | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:56:13 + --> $DIR/partialeq_to_none.rs:68:13 | LL | let _ = &&None != optref(); | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:57:13 + --> $DIR/partialeq_to_none.rs:69:13 | LL | let _ = **optref() == None; | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:58:13 + --> $DIR/partialeq_to_none.rs:70:13 | LL | let _ = &None != *optref(); | ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()` error: binary comparison to literal `Option::None` - --> $DIR/partialeq_to_none.rs:61:13 + --> $DIR/partialeq_to_none.rs:73:13 | LL | let _ = None != *x; | ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()` diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed b/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed new file mode 100644 index 00000000000..4170e109820 --- /dev/null +++ b/src/tools/clippy/tests/ui/positional_named_format_parameters.fixed @@ -0,0 +1,56 @@ +// run-rustfix +#![allow(unused_must_use)] +#![allow(named_arguments_used_positionally)] // Unstable at time of writing. +#![warn(clippy::positional_named_format_parameters)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + let hello = "Hello"; + + println!("{hello:.foo$}", foo = 2); + writeln!(v, "{hello:.foo$}", foo = 2); + + // Warnings + println!("{zero} {one:?}", zero = 0, one = 1); + println!("This is a test {zero} {one:?}", zero = 0, one = 1); + println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01); + println!("Hello {one:zero$}!", zero = 5, one = 1); + println!("Hello {zero:one$}!", zero = 4, one = 1); + println!("Hello {zero:0one$}!", zero = 4, one = 1); + println!("Hello is {one:.zero$}", zero = 5, one = 0.01); + println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01); + println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello); + println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01); + println!("Hello {world} {world}!", world = 5); + + writeln!(v, "{zero} {one:?}", zero = 0, one = 1); + writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1); + writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01); + writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1); + writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1); + writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1); + writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01); + writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01); + writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello); + writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01); + writeln!(v, "Hello {world} {world}!", world = 0); + + // Tests from other files + println!("{w:w$}", w = 1); + println!("{p:.p$}", p = 1); + println!("{v}", v = 1); + println!("{v:v$}", v = 1); + println!("{v:v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{v:v$.v$}", v = 1); + println!("{w:w$}", w = 1); + println!("{p:.p$}", p = 1); + println!("{:p$.w$}", 1, w = 1, p = 1); +} diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.rs b/src/tools/clippy/tests/ui/positional_named_format_parameters.rs new file mode 100644 index 00000000000..553d8494ecc --- /dev/null +++ b/src/tools/clippy/tests/ui/positional_named_format_parameters.rs @@ -0,0 +1,56 @@ +// run-rustfix +#![allow(unused_must_use)] +#![allow(named_arguments_used_positionally)] // Unstable at time of writing. +#![warn(clippy::positional_named_format_parameters)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + let hello = "Hello"; + + println!("{hello:.foo$}", foo = 2); + writeln!(v, "{hello:.foo$}", foo = 2); + + // Warnings + println!("{} {1:?}", zero = 0, one = 1); + println!("This is a test { } {000001:?}", zero = 0, one = 1); + println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + println!("Hello {1:0$}!", zero = 5, one = 1); + println!("Hello {0:1$}!", zero = 4, one = 1); + println!("Hello {0:01$}!", zero = 4, one = 1); + println!("Hello is {1:.*}", zero = 5, one = 0.01); + println!("Hello is {:<6.*}", zero = 5, one = 0.01); + println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); + println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + println!("Hello {world} {}!", world = 5); + + writeln!(v, "{} {1:?}", zero = 0, one = 1); + writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1); + writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + writeln!(v, "Hello {1:0$}!", zero = 4, one = 1); + writeln!(v, "Hello {0:1$}!", zero = 4, one = 1); + writeln!(v, "Hello {0:01$}!", zero = 4, one = 1); + writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01); + writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01); + writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); + writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); + writeln!(v, "Hello {world} {}!", world = 0); + + // Tests from other files + println!("{:w$}", w = 1); + println!("{:.p$}", p = 1); + println!("{}", v = 1); + println!("{:0$}", v = 1); + println!("{0:0$}", v = 1); + println!("{:0$.0$}", v = 1); + println!("{0:0$.0$}", v = 1); + println!("{0:0$.v$}", v = 1); + println!("{0:v$.0$}", v = 1); + println!("{v:0$.0$}", v = 1); + println!("{v:v$.0$}", v = 1); + println!("{v:0$.v$}", v = 1); + println!("{:w$}", w = 1); + println!("{:.p$}", p = 1); + println!("{:p$.w$}", 1, w = 1, p = 1); +} diff --git a/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr b/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr new file mode 100644 index 00000000000..48ddb6d67ad --- /dev/null +++ b/src/tools/clippy/tests/ui/positional_named_format_parameters.stderr @@ -0,0 +1,418 @@ +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:16:16 + | +LL | println!("{} {1:?}", zero = 0, one = 1); + | ^ help: replace it with: `zero` + | + = note: `-D clippy::positional-named-format-parameters` implied by `-D warnings` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:16:19 + | +LL | println!("{} {1:?}", zero = 0, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:17:31 + | +LL | println!("This is a test { } {000001:?}", zero = 0, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:17:35 + | +LL | println!("This is a test { } {000001:?}", zero = 0, one = 1); + | ^^^^^^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:18:32 + | +LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:18:22 + | +LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `one` + +error: named parameter two is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:18:29 + | +LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `two` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:19:24 + | +LL | println!("Hello {1:0$}!", zero = 5, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:19:22 + | +LL | println!("Hello {1:0$}!", zero = 5, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:20:22 + | +LL | println!("Hello {0:1$}!", zero = 4, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:20:24 + | +LL | println!("Hello {0:1$}!", zero = 4, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:21:22 + | +LL | println!("Hello {0:01$}!", zero = 4, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:21:25 + | +LL | println!("Hello {0:01$}!", zero = 4, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:22:28 + | +LL | println!("Hello is {1:.*}", zero = 5, one = 0.01); + | ^ help: replace it with: `zero$` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:22:25 + | +LL | println!("Hello is {1:.*}", zero = 5, one = 0.01); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:23:29 + | +LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01); + | ^ help: replace it with: `zero$` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:23:25 + | +LL | println!("Hello is {:<6.*}", zero = 5, one = 0.01); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:24:16 + | +LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:24:28 + | +LL | println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); + | ^ help: replace it with: `one$` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:25:32 + | +LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:25:22 + | +LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `one` + +error: named parameter two is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:25:29 + | +LL | println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `two` + +error: named parameter world is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:26:30 + | +LL | println!("Hello {world} {}!", world = 5); + | ^ help: replace it with: `world` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:28:19 + | +LL | writeln!(v, "{} {1:?}", zero = 0, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:28:22 + | +LL | writeln!(v, "{} {1:?}", zero = 0, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:29:34 + | +LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:29:38 + | +LL | writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1); + | ^^^^^^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:30:35 + | +LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:30:25 + | +LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `one` + +error: named parameter two is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:30:32 + | +LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01); + | ^ help: replace it with: `two` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:31:27 + | +LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:31:25 + | +LL | writeln!(v, "Hello {1:0$}!", zero = 4, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:32:25 + | +LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:32:27 + | +LL | writeln!(v, "Hello {0:1$}!", zero = 4, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:33:25 + | +LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:33:28 + | +LL | writeln!(v, "Hello {0:01$}!", zero = 4, one = 1); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:34:31 + | +LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01); + | ^ help: replace it with: `zero$` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:34:28 + | +LL | writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:35:32 + | +LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01); + | ^ help: replace it with: `zero$` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:35:28 + | +LL | writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01); + | ^ help: replace it with: `one` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:36:19 + | +LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:36:31 + | +LL | writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello); + | ^ help: replace it with: `one$` + +error: named parameter zero is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:37:35 + | +LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); + | ^ help: replace it with: `zero` + +error: named parameter one is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:37:25 + | +LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); + | ^ help: replace it with: `one` + +error: named parameter two is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:37:32 + | +LL | writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01); + | ^ help: replace it with: `two` + +error: named parameter world is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:38:33 + | +LL | writeln!(v, "Hello {world} {}!", world = 0); + | ^ help: replace it with: `world` + +error: named parameter w is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:41:16 + | +LL | println!("{:w$}", w = 1); + | ^ help: replace it with: `w` + +error: named parameter p is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:42:16 + | +LL | println!("{:.p$}", p = 1); + | ^ help: replace it with: `p` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:43:16 + | +LL | println!("{}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:44:16 + | +LL | println!("{:0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:44:17 + | +LL | println!("{:0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:45:16 + | +LL | println!("{0:0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:45:18 + | +LL | println!("{0:0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:46:16 + | +LL | println!("{:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:46:20 + | +LL | println!("{:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:46:17 + | +LL | println!("{:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:47:16 + | +LL | println!("{0:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:47:21 + | +LL | println!("{0:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:47:18 + | +LL | println!("{0:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:48:16 + | +LL | println!("{0:0$.v$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:48:18 + | +LL | println!("{0:0$.v$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:49:16 + | +LL | println!("{0:v$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:49:21 + | +LL | println!("{0:v$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:50:21 + | +LL | println!("{v:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:50:18 + | +LL | println!("{v:0$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:51:21 + | +LL | println!("{v:v$.0$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter v is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:52:18 + | +LL | println!("{v:0$.v$}", v = 1); + | ^ help: replace it with: `v` + +error: named parameter w is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:53:16 + | +LL | println!("{:w$}", w = 1); + | ^ help: replace it with: `w` + +error: named parameter p is used as a positional parameter + --> $DIR/positional_named_format_parameters.rs:54:16 + | +LL | println!("{:.p$}", p = 1); + | ^ help: replace it with: `p` + +error: aborting due to 69 previous errors + diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index c4c9c821433..57f23bd1916 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -207,4 +207,19 @@ fn option_map() -> Option<bool> { } } +pub struct PatternedError { + flag: bool, +} + +// No warning +fn pattern() -> Result<(), PatternedError> { + let res = Ok(()); + + if let Err(err @ PatternedError { flag: true }) = res { + return Err(err); + } + + res +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index cdbc7b1606f..436f027c215 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -243,4 +243,19 @@ fn option_map() -> Option<bool> { } } +pub struct PatternedError { + flag: bool, +} + +// No warning +fn pattern() -> Result<(), PatternedError> { + let res = Ok(()); + + if let Err(err @ PatternedError { flag: true }) = res { + return Err(err); + } + + res +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index f7f3b195ccc..f0e1a8128d7 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![allow(unused, clippy::needless_borrow)] #![warn(clippy::invalid_regex, clippy::trivial_regex)] extern crate regex; diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs new file mode 100644 index 00000000000..78d8f76fe66 --- /dev/null +++ b/src/tools/clippy/tests/ui/result_large_err.rs @@ -0,0 +1,98 @@ +#![warn(clippy::result_large_err)] + +pub fn small_err() -> Result<(), u128> { + Ok(()) +} + +pub fn large_err() -> Result<(), [u8; 512]> { + Ok(()) +} + +pub struct FullyDefinedLargeError { + _foo: u128, + _bar: [u8; 100], + _foobar: [u8; 120], +} + +impl FullyDefinedLargeError { + pub fn ret() -> Result<(), Self> { + Ok(()) + } +} + +pub fn struct_error() -> Result<(), FullyDefinedLargeError> { + Ok(()) +} + +type Fdlr<T> = std::result::Result<T, FullyDefinedLargeError>; +pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> { + Ok(x) +} + +pub fn param_small_error<R>() -> Result<(), (R, u128)> { + Ok(()) +} + +pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> { + Ok(()) +} + +pub enum LargeErrorVariants<T> { + _Small(u8), + _Omg([u8; 512]), + _Param(T), +} + +impl LargeErrorVariants<()> { + pub fn large_enum_error() -> Result<(), Self> { + Ok(()) + } +} + +trait TraitForcesLargeError { + fn large_error() -> Result<(), [u8; 512]> { + Ok(()) + } +} + +struct TraitImpl; + +impl TraitForcesLargeError for TraitImpl { + // Should not lint + fn large_error() -> Result<(), [u8; 512]> { + Ok(()) + } +} + +pub union FullyDefinedUnionError { + _maybe: u8, + _or_even: [[u8; 16]; 32], +} + +pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { + Ok(()) +} + +pub union UnionError<T: Copy> { + _maybe: T, + _or_perhaps_even: (T, [u8; 512]), +} + +pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> { + Ok(()) +} + +pub struct ArrayError<T, U> { + _large_array: [T; 32], + _other_stuff: U, +} + +pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> { + Ok(()) +} + +pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> { + Ok(()) +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/result_large_err.stderr b/src/tools/clippy/tests/ui/result_large_err.stderr new file mode 100644 index 00000000000..0f1f39d72cb --- /dev/null +++ b/src/tools/clippy/tests/ui/result_large_err.stderr @@ -0,0 +1,91 @@ +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:7:23 + | +LL | pub fn large_err() -> Result<(), [u8; 512]> { + | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = note: `-D clippy::result-large-err` implied by `-D warnings` + = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:18:21 + | +LL | pub fn ret() -> Result<(), Self> { + | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes + | + = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:23:26 + | +LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes + | + = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:28:45 + | +LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> { + | ^^^^^^^ the `Err`-variant is at least 240 bytes + | + = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:36:34 + | +LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes + | + = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:47:34 + | +LL | pub fn large_enum_error() -> Result<(), Self> { + | ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes + | + = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:53:25 + | +LL | fn large_error() -> Result<(), [u8; 512]> { + | ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:72:29 + | +LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:81:40 + | +LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes + | + = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:90:34 + | +LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes + | + = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>` + +error: the `Err`-variant returned from this function is very large + --> $DIR/result_large_err.rs:94:31 + | +LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes + | + = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>` + +error: aborting due to 11 previous errors + diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs index 99964f0de07..af01a8df71b 100644 --- a/src/tools/clippy/tests/ui/same_item_push.rs +++ b/src/tools/clippy/tests/ui/same_item_push.rs @@ -151,6 +151,7 @@ fn main() { // Fix #6987 let mut vec = Vec::new(); + #[allow(clippy::needless_borrow)] for _ in 0..10 { vec.push(1); vec.extend(&[2]); diff --git a/src/tools/clippy/tests/ui/string_add.rs b/src/tools/clippy/tests/ui/string_add.rs index 30fd17c59e5..16673c01e63 100644 --- a/src/tools/clippy/tests/ui/string_add.rs +++ b/src/tools/clippy/tests/ui/string_add.rs @@ -7,13 +7,13 @@ extern crate macro_rules; #[allow(clippy::string_add_assign, unused)] fn main() { // ignores assignment distinction - let mut x = "".to_owned(); + let mut x = String::new(); for _ in 1..3 { x = x + "."; } - let y = "".to_owned(); + let y = String::new(); let z = y + "..."; assert_eq!(&x, &z); diff --git a/src/tools/clippy/tests/ui/string_add_assign.fixed b/src/tools/clippy/tests/ui/string_add_assign.fixed index db71bab1e52..b687f43b254 100644 --- a/src/tools/clippy/tests/ui/string_add_assign.fixed +++ b/src/tools/clippy/tests/ui/string_add_assign.fixed @@ -4,13 +4,13 @@ #[warn(clippy::string_add_assign)] fn main() { // ignores assignment distinction - let mut x = "".to_owned(); + let mut x = String::new(); for _ in 1..3 { x += "."; } - let y = "".to_owned(); + let y = String::new(); let z = y + "..."; assert_eq!(&x, &z); diff --git a/src/tools/clippy/tests/ui/string_add_assign.rs b/src/tools/clippy/tests/ui/string_add_assign.rs index 644991945cb..e5dbde108fb 100644 --- a/src/tools/clippy/tests/ui/string_add_assign.rs +++ b/src/tools/clippy/tests/ui/string_add_assign.rs @@ -4,13 +4,13 @@ #[warn(clippy::string_add_assign)] fn main() { // ignores assignment distinction - let mut x = "".to_owned(); + let mut x = String::new(); for _ in 1..3 { x = x + "."; } - let y = "".to_owned(); + let y = String::new(); let z = y + "..."; assert_eq!(&x, &z); diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.rs b/src/tools/clippy/tests/ui/suspicious_to_owned.rs new file mode 100644 index 00000000000..cba21bf4a93 --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_to_owned.rs @@ -0,0 +1,62 @@ +#![warn(clippy::suspicious_to_owned)] +#![warn(clippy::implicit_clone)] +#![allow(clippy::redundant_clone)] +use std::borrow::Cow; +use std::ffi::{c_char, CStr}; + +fn main() { + let moo = "Moooo"; + let c_moo = b"Moooo\0"; + let c_moo_ptr = c_moo.as_ptr() as *const c_char; + let moos = ['M', 'o', 'o']; + let moos_vec = moos.to_vec(); + + // we expect this to be linted + let cow = Cow::Borrowed(moo); + let _ = cow.to_owned(); + // we expect no lints for this + let cow = Cow::Borrowed(moo); + let _ = cow.into_owned(); + // we expect no lints for this + let cow = Cow::Borrowed(moo); + let _ = cow.clone(); + + // we expect this to be linted + let cow = Cow::Borrowed(&moos); + let _ = cow.to_owned(); + // we expect no lints for this + let cow = Cow::Borrowed(&moos); + let _ = cow.into_owned(); + // we expect no lints for this + let cow = Cow::Borrowed(&moos); + let _ = cow.clone(); + + // we expect this to be linted + let cow = Cow::Borrowed(&moos_vec); + let _ = cow.to_owned(); + // we expect no lints for this + let cow = Cow::Borrowed(&moos_vec); + let _ = cow.into_owned(); + // we expect no lints for this + let cow = Cow::Borrowed(&moos_vec); + let _ = cow.clone(); + + // we expect this to be linted + let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy(); + let _ = cow.to_owned(); + // we expect no lints for this + let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy(); + let _ = cow.into_owned(); + // we expect no lints for this + let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy(); + let _ = cow.clone(); + + // we expect no lints for these + let _ = moo.to_owned(); + let _ = c_moo.to_owned(); + let _ = moos.to_owned(); + + // we expect implicit_clone lints for these + let _ = String::from(moo).to_owned(); + let _ = moos_vec.to_owned(); +} diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.stderr b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr new file mode 100644 index 00000000000..92e1024bf1f --- /dev/null +++ b/src/tools/clippy/tests/ui/suspicious_to_owned.stderr @@ -0,0 +1,42 @@ +error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned + --> $DIR/suspicious_to_owned.rs:16:13 + | +LL | let _ = cow.to_owned(); + | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + | + = note: `-D clippy::suspicious-to-owned` implied by `-D warnings` + +error: this `to_owned` call clones the std::borrow::Cow<[char; 3]> itself and does not cause the std::borrow::Cow<[char; 3]> contents to become owned + --> $DIR/suspicious_to_owned.rs:26:13 + | +LL | let _ = cow.to_owned(); + | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + +error: this `to_owned` call clones the std::borrow::Cow<std::vec::Vec<char>> itself and does not cause the std::borrow::Cow<std::vec::Vec<char>> contents to become owned + --> $DIR/suspicious_to_owned.rs:36:13 + | +LL | let _ = cow.to_owned(); + | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + +error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned + --> $DIR/suspicious_to_owned.rs:46:13 + | +LL | let _ = cow.to_owned(); + | ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()` + +error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type + --> $DIR/suspicious_to_owned.rs:60:13 + | +LL | let _ = String::from(moo).to_owned(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::from(moo).clone()` + | + = note: `-D clippy::implicit-clone` implied by `-D warnings` + +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type + --> $DIR/suspicious_to_owned.rs:61:13 + | +LL | let _ = moos_vec.to_owned(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `moos_vec.clone()` + +error: aborting due to 6 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 new file mode 100644 index 00000000000..4ce5d421782 --- /dev/null +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed @@ -0,0 +1,112 @@ +// run-rustfix +#![deny(clippy::trait_duplication_in_bounds)] +#![allow(unused)] + +fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { + unimplemented!(); +} + +fn bad_bar<T, U>(arg0: T, arg1: U) +where + T: Clone + Copy, + U: Clone + Copy, +{ + unimplemented!(); +} + +fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) { + unimplemented!(); +} + +fn good_foo<T, U>(arg0: T, arg1: U) +where + T: Clone + Copy, + U: Clone + Copy, +{ + unimplemented!(); +} + +trait GoodSelfTraitBound: Clone + Copy { + fn f(); +} + +trait GoodSelfWhereClause { + fn f() + where + Self: Clone + Copy; +} + +trait BadSelfTraitBound: Clone { + fn f(); +} + +trait BadSelfWhereClause { + fn f() + where + Self: Clone; +} + +trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> { + fn f(); +} + +trait GoodWhereClause<T, U> { + fn f() + where + T: Clone + Copy, + U: Clone + Copy; +} + +trait BadTraitBound<T: Clone + Copy, U: Clone + Copy> { + fn f(); +} + +trait BadWhereClause<T, U> { + fn f() + where + T: Clone + Copy, + U: Clone + Copy; +} + +struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> { + t: T, + u: U, +} + +impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> { + // this should not warn + fn f() {} +} + +struct GoodStructWhereClause; + +impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause +where + T: Clone + Copy, + U: Clone + Copy, +{ + // this should not warn + fn f() {} +} + +fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {} + +trait GenericTrait<T> {} + +fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) { + unimplemented!(); +} + +fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) { + unimplemented!(); +} + +mod foo { + pub trait Clone {} +} + +fn qualified_path<T: std::clone::Clone + foo::Clone>(arg0: T) { + unimplemented!(); +} + +fn main() {} 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 a5751c58aab..7f2e96a22e6 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs @@ -1,212 +1,112 @@ +// run-rustfix #![deny(clippy::trait_duplication_in_bounds)] #![allow(unused)] -use std::collections::BTreeMap; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; +fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { + unimplemented!(); +} -fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) +fn bad_bar<T, U>(arg0: T, arg1: U) where - T: Clone, - T: Default, + T: Clone + Clone + Clone + Copy, + U: Clone + Copy, { unimplemented!(); } -fn good_bar<T: Clone + Default>(arg: T) { +fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) { unimplemented!(); } -fn good_foo<T>(arg: T) +fn good_foo<T, U>(arg0: T, arg1: U) where - T: Clone + Default, + T: Clone + Copy, + U: Clone + Copy, { unimplemented!(); } -fn good_foobar<T: Default>(arg: T) -where - T: Clone, -{ - unimplemented!(); +trait GoodSelfTraitBound: Clone + Copy { + fn f(); } -trait T: Default { +trait GoodSelfWhereClause { fn f() where - Self: Default; + Self: Clone + Copy; } -trait U: Default { +trait BadSelfTraitBound: Clone + Clone + Clone { + fn f(); +} + +trait BadSelfWhereClause { fn f() where - Self: Clone; + Self: Clone + Clone + Clone; +} + +trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> { + fn f(); } -trait ZZ: Default { - fn g(); - fn h(); +trait GoodWhereClause<T, U> { fn f() where - Self: Default + Clone; + T: Clone + Copy, + U: Clone + Copy; } -trait BadTrait: Default + Clone { +trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { + fn f(); +} + +trait BadWhereClause<T, U> { fn f() where - Self: Default + Clone; - fn g() - where - Self: Default; - fn h() - where - Self: Copy; + T: Clone + Clone + Clone + Copy, + U: Clone + Copy; } -#[derive(Default, Clone)] -struct Life; +struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> { + t: T, + u: U, +} -impl T for Life { +impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> { // this should not warn fn f() {} } -impl U for Life { +struct GoodStructWhereClause; + +impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause +where + T: Clone + Copy, + U: Clone + Copy, +{ // this should not warn fn f() {} } -// should not warn -trait Iter: Iterator { - fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>> - where - Self: Iterator<Item = (K, V)> + Sized, - K: Ord + Eq, - { - unimplemented!(); - } -} +fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {} -struct Foo; +trait GenericTrait<T> {} -trait FooIter: Iterator<Item = Foo> { - fn bar() - where - Self: Iterator<Item = Foo>, - { - } +fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) { + unimplemented!(); } -// This should not lint -fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {} - -mod repeated_where_clauses_or_trait_bounds { - fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { - unimplemented!(); - } - - fn bad_bar<T, U>(arg0: T, arg1: U) - where - T: Clone + Clone + Clone + Copy, - U: Clone + Copy, - { - unimplemented!(); - } - - fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) { - unimplemented!(); - } - - fn good_foo<T, U>(arg0: T, arg1: U) - where - T: Clone + Copy, - U: Clone + Copy, - { - unimplemented!(); - } - - trait GoodSelfTraitBound: Clone + Copy { - fn f(); - } - - trait GoodSelfWhereClause { - fn f() - where - Self: Clone + Copy; - } - - trait BadSelfTraitBound: Clone + Clone + Clone { - fn f(); - } - - trait BadSelfWhereClause { - fn f() - where - Self: Clone + Clone + Clone; - } - - trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> { - fn f(); - } - - trait GoodWhereClause<T, U> { - fn f() - where - T: Clone + Copy, - U: Clone + Copy; - } - - trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { - fn f(); - } - - trait BadWhereClause<T, U> { - fn f() - where - T: Clone + Clone + Clone + Copy, - U: Clone + Copy; - } - - struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> { - t: T, - u: U, - } - - impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> { - // this should not warn - fn f() {} - } - - struct GoodStructWhereClause; - - impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause - where - T: Clone + Copy, - U: Clone + Copy, - { - // this should not warn - fn f() {} - } - - fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {} - - trait GenericTrait<T> {} - - // This should not warn but currently does see #8757 - fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) { - unimplemented!(); - } - - fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { - unimplemented!(); - } +fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { + unimplemented!(); +} - mod foo { - pub trait Clone {} - } +mod foo { + pub trait Clone {} +} - fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { - unimplemented!(); - } +fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { + unimplemented!(); } fn main() {} 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 7ef04e52708..af800ba7888 100644 --- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr @@ -1,167 +1,56 @@ -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:7:15 +error: these bounds contain repeated elements + --> $DIR/trait_duplication_in_bounds.rs:5:15 | -LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) - | ^^^^^ +LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` | note: the lint level is defined here - --> $DIR/trait_duplication_in_bounds.rs:1:9 + --> $DIR/trait_duplication_in_bounds.rs:2:9 | LL | #![deny(clippy::trait_duplication_in_bounds)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: consider removing this trait bound - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:7:23 - | -LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) - | ^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:36:15 - | -LL | Self: Default; - | ^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:50:15 - | -LL | Self: Default + Clone; - | ^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:56:15 - | -LL | Self: Default + Clone; - | ^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:56:25 - | -LL | Self: Default + Clone; - | ^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:59:15 - | -LL | Self: Default; - | ^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in trait declaration - --> $DIR/trait_duplication_in_bounds.rs:94:15 - | -LL | Self: Iterator<Item = Foo>, - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:103:19 - | -LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { - | ^^^^^ - | - = help: consider removing this trait bound - -error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:103:19 - | -LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:109:12 - | -LL | T: Clone + Clone + Clone + Copy, - | ^^^^^ - | - = help: consider removing this trait bound error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:109:12 + --> $DIR/trait_duplication_in_bounds.rs:11:8 | -LL | T: Clone + Clone + Clone + Copy, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` +LL | T: Clone + Clone + Clone + Copy, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:137:30 + --> $DIR/trait_duplication_in_bounds.rs:39:26 | -LL | trait BadSelfTraitBound: Clone + Clone + Clone { - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` +LL | trait BadSelfTraitBound: Clone + Clone + Clone { + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:144:19 - | -LL | Self: Clone + Clone + Clone; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:158:28 + --> $DIR/trait_duplication_in_bounds.rs:46:15 | -LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { - | ^^^^^ - | - = help: consider removing this trait bound +LL | Self: Clone + Clone + Clone; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:158:28 + --> $DIR/trait_duplication_in_bounds.rs:60:24 | -LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` +LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these where clauses contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:165:16 - | -LL | T: Clone + Clone + Clone + Copy, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:195:24 - | -LL | fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) { - | ^^^^^^^^^^^^^^^^^ - | - = help: consider removing this trait bound - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:199:23 + --> $DIR/trait_duplication_in_bounds.rs:67:12 | -LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { - | ^^^^^^^^^^^^^^^^^ - | - = help: consider removing this trait bound +LL | T: Clone + Clone + Clone + Copy, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy` error: these bounds contain repeated elements - --> $DIR/trait_duplication_in_bounds.rs:199:23 - | -LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>` - -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:207:26 - | -LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { - | ^^^^^^^^^^^^^^^^^ + --> $DIR/trait_duplication_in_bounds.rs:100:19 | - = help: consider removing this trait bound +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 - --> $DIR/trait_duplication_in_bounds.rs:207:26 + --> $DIR/trait_duplication_in_bounds.rs:108:22 | -LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone` +LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone` -error: aborting due to 22 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.rs new file mode 100644 index 00000000000..5630a0345ad --- /dev/null +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.rs @@ -0,0 +1,166 @@ +#![deny(clippy::trait_duplication_in_bounds)] + +use std::collections::BTreeMap; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) +where + T: Clone, + T: Default, +{ + unimplemented!(); +} + +fn good_bar<T: Clone + Default>(arg: T) { + unimplemented!(); +} + +fn good_foo<T>(arg: T) +where + T: Clone + Default, +{ + unimplemented!(); +} + +fn good_foobar<T: Default>(arg: T) +where + T: Clone, +{ + unimplemented!(); +} + +trait T: Default { + fn f() + where + Self: Default; +} + +trait U: Default { + fn f() + where + Self: Clone; +} + +trait ZZ: Default { + fn g(); + fn h(); + fn f() + where + Self: Default + Clone; +} + +trait BadTrait: Default + Clone { + fn f() + where + Self: Default + Clone; + fn g() + where + Self: Default; + fn h() + where + Self: Copy; +} + +#[derive(Default, Clone)] +struct Life; + +impl T for Life { + // this should not warn + fn f() {} +} + +impl U for Life { + // this should not warn + fn f() {} +} + +// should not warn +trait Iter: Iterator { + fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>> + where + Self: Iterator<Item = (K, V)> + Sized, + K: Ord + Eq, + { + unimplemented!(); + } +} + +struct Foo; + +trait FooIter: Iterator<Item = Foo> { + fn bar() + where + Self: Iterator<Item = Foo>, + { + } +} + +// The below should not lint and exist to guard against false positives +fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {} + +pub mod one { + #[derive(Clone, Debug)] + struct MultiProductIter<I> + where + I: Iterator + Clone, + I::Item: Clone, + { + _marker: I, + } + + pub struct MultiProduct<I>(Vec<MultiProductIter<I>>) + where + I: Iterator + Clone, + I::Item: Clone; + + pub fn multi_cartesian_product<H>(_: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter> + where + H: Iterator, + H::Item: IntoIterator, + <H::Item as IntoIterator>::IntoIter: Clone, + <H::Item as IntoIterator>::Item: Clone, + { + todo!() + } +} + +pub mod two { + use std::iter::Peekable; + + pub struct MergeBy<I, J, F> + where + I: Iterator, + J: Iterator<Item = I::Item>, + { + _i: Peekable<I>, + _j: Peekable<J>, + _f: F, + } + + impl<I, J, F> Clone for MergeBy<I, J, F> + where + I: Iterator, + J: Iterator<Item = I::Item>, + std::iter::Peekable<I>: Clone, + std::iter::Peekable<J>: Clone, + F: Clone, + { + fn clone(&self) -> Self { + Self { + _i: self._i.clone(), + _j: self._j.clone(), + _f: self._f.clone(), + } + } + } +} + +pub trait Trait {} + +pub fn f(_a: impl Trait, _b: impl Trait) {} + +pub trait ImplTrait<T> {} + +impl<A, B> ImplTrait<(A, B)> for Foo where Foo: ImplTrait<A> + ImplTrait<B> {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr new file mode 100644 index 00000000000..fbd9abb005f --- /dev/null +++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr @@ -0,0 +1,71 @@ +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:15 + | +LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/trait_duplication_in_bounds_unfixable.rs:1:9 + | +LL | #![deny(clippy::trait_duplication_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:23 + | +LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds_unfixable.rs:35:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds_unfixable.rs:49:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:15 + | +LL | Self: Default + Clone; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:25 + | +LL | Self: Default + Clone; + | ^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds_unfixable.rs:58:15 + | +LL | Self: Default; + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: this trait bound is already specified in trait declaration + --> $DIR/trait_duplication_in_bounds_unfixable.rs:93:15 + | +LL | Self: Iterator<Item = Foo>, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs index ebcaa7a84cf..5aad0b44270 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs @@ -4,6 +4,7 @@ use core::any::TypeId; use core::ffi::c_void; use core::mem::{size_of, transmute, MaybeUninit}; +use core::ptr::NonNull; fn value<T>() -> T { unimplemented!() @@ -109,6 +110,17 @@ fn main() { let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok + + let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<Ty2<u32, u32>, u32>>()); // Ok + let _: *const Ty2C<Ty2<u32, u32>, u32> = transmute(value::<*const Ty2<u32, u32>>()); // Ok + let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<(), Ty2<u32, u32>>>()); // Ok + let _: *const Ty2C<(), Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Ok + + let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err + let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err + + let _: NonNull<u8> = transmute(value::<NonNull<(String, String)>>()); // Ok + let _: NonNull<(String, String)> = transmute(value::<NonNull<u8>>()); // Ok } } diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr index 28bfba6c757..e50a773290e 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.stderr @@ -1,5 +1,5 @@ error: transmute from `Ty2<u32, i32>` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:27:33 + --> $DIR/transmute_undefined_repr.rs:28:33 | LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lin = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings` error: transmute into `Ty2<u32, i32>` which has an undefined layout - --> $DIR/transmute_undefined_repr.rs:28:32 + --> $DIR/transmute_undefined_repr.rs:29:32 | LL | let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:33:32 + --> $DIR/transmute_undefined_repr.rs:34:32 | LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:34:36 + --> $DIR/transmute_undefined_repr.rs:35:36 | LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:39:33 + --> $DIR/transmute_undefined_repr.rs:40:33 | LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); / = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:40:37 + --> $DIR/transmute_undefined_repr.rs:41:37 | LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); / = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:57:45 + --> $DIR/transmute_undefined_repr.rs:58:45 | LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,15 +53,31 @@ LL | let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, = note: two instances of the same generic type (`Ty2`) may have different layouts error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:58:37 + --> $DIR/transmute_undefined_repr.rs:59:37 | LL | let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: two instances of the same generic type (`Ty2`) may have different layouts +error: transmute into `*const Ty2<u32, u32>` which has an undefined layout + --> $DIR/transmute_undefined_repr.rs:119:39 + | +LL | let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the contained type `Ty2<u32, u32>` has an undefined layout + +error: transmute from `*const Ty2<u32, u32>` which has an undefined layout + --> $DIR/transmute_undefined_repr.rs:120:50 + | +LL | let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the contained type `Ty2<u32, u32>` has an undefined layout + error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:138:35 + --> $DIR/transmute_undefined_repr.rs:150:35 | LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,12 +85,12 @@ LL | let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); / = note: two instances of the same generic type (`Vec`) may have different layouts error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout - --> $DIR/transmute_undefined_repr.rs:139:35 + --> $DIR/transmute_undefined_repr.rs:151:35 | LL | let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: two instances of the same generic type (`Vec`) may have different layouts -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/unicode.fixed b/src/tools/clippy/tests/ui/unicode.fixed index 328cda369e1..94b4723452f 100644 --- a/src/tools/clippy/tests/ui/unicode.fixed +++ b/src/tools/clippy/tests/ui/unicode.fixed @@ -1,4 +1,7 @@ // run-rustfix +// compile-flags: --test +#![allow(dead_code)] + #[warn(clippy::invisible_characters)] fn zero() { print!("Here >\u{200B}< is a ZWS, and \u{200B}another"); @@ -15,22 +18,43 @@ fn canon() { print!("a\u{0300}h?"); // also ok } -#[warn(clippy::non_ascii_literal)] -fn uni() { - print!("\u{dc}ben!"); - print!("\u{DC}ben!"); // this is ok -} +mod non_ascii_literal { + #![deny(clippy::non_ascii_literal)] + + fn uni() { + print!("\u{dc}ben!"); + print!("\u{DC}ben!"); // this is ok + } + + // issue 8013 + fn single_quote() { + const _EMPTY_BLOCK: char = '\u{25b1}'; + const _FULL_BLOCK: char = '\u{25b0}'; + } + + #[test] + pub fn issue_7739() { + // Ryū crate: https://github.com/dtolnay/ryu + } + + mod issue_8263 { + #![deny(clippy::non_ascii_literal)] + + // Re-allow for a single test + #[test] + #[allow(clippy::non_ascii_literal)] + fn allowed() { + let _ = "悲しいかな、ここに日本語を書くことはできない。"; + } -// issue 8013 -#[warn(clippy::non_ascii_literal)] -fn single_quote() { - const _EMPTY_BLOCK: char = '\u{25b1}'; - const _FULL_BLOCK: char = '\u{25b0}'; + #[test] + fn denied() { + let _ = "\u{60b2}\u{3057}\u{3044}\u{304b}\u{306a}\u{3001}\u{3053}\u{3053}\u{306b}\u{65e5}\u{672c}\u{8a9e}\u{3092}\u{66f8}\u{304f}\u{3053}\u{3068}\u{306f}\u{3067}\u{304d}\u{306a}\u{3044}\u{3002}"; + } + } } fn main() { zero(); - uni(); canon(); - single_quote(); } diff --git a/src/tools/clippy/tests/ui/unicode.rs b/src/tools/clippy/tests/ui/unicode.rs index 7828d6bcbea..6ad0b255b94 100644 --- a/src/tools/clippy/tests/ui/unicode.rs +++ b/src/tools/clippy/tests/ui/unicode.rs @@ -1,4 +1,7 @@ // run-rustfix +// compile-flags: --test +#![allow(dead_code)] + #[warn(clippy::invisible_characters)] fn zero() { print!("Here >< is a ZWS, and another"); @@ -15,22 +18,43 @@ fn canon() { print!("a\u{0300}h?"); // also ok } -#[warn(clippy::non_ascii_literal)] -fn uni() { - print!("Üben!"); - print!("\u{DC}ben!"); // this is ok -} +mod non_ascii_literal { + #![deny(clippy::non_ascii_literal)] + + fn uni() { + print!("Üben!"); + print!("\u{DC}ben!"); // this is ok + } + + // issue 8013 + fn single_quote() { + const _EMPTY_BLOCK: char = '▱'; + const _FULL_BLOCK: char = '▰'; + } + + #[test] + pub fn issue_7739() { + // Ryū crate: https://github.com/dtolnay/ryu + } + + mod issue_8263 { + #![deny(clippy::non_ascii_literal)] + + // Re-allow for a single test + #[test] + #[allow(clippy::non_ascii_literal)] + fn allowed() { + let _ = "悲しいかな、ここに日本語を書くことはできない。"; + } -// issue 8013 -#[warn(clippy::non_ascii_literal)] -fn single_quote() { - const _EMPTY_BLOCK: char = '▱'; - const _FULL_BLOCK: char = '▰'; + #[test] + fn denied() { + let _ = "悲しいかな、ここに日本語を書くことはできない。"; + } + } } fn main() { zero(); - uni(); canon(); - single_quote(); } diff --git a/src/tools/clippy/tests/ui/unicode.stderr b/src/tools/clippy/tests/ui/unicode.stderr index 01d3f3c0296..ea74a81451e 100644 --- a/src/tools/clippy/tests/ui/unicode.stderr +++ b/src/tools/clippy/tests/ui/unicode.stderr @@ -1,5 +1,5 @@ error: invisible character detected - --> $DIR/unicode.rs:4:12 + --> $DIR/unicode.rs:7:12 | LL | print!("Here >< is a ZWS, and another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` @@ -7,19 +7,19 @@ LL | print!("Here >< is a ZWS, and another"); = note: `-D clippy::invisible-characters` implied by `-D warnings` error: invisible character detected - --> $DIR/unicode.rs:6:12 + --> $DIR/unicode.rs:9:12 | LL | print!("Here >< is a SHY, and another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` error: invisible character detected - --> $DIR/unicode.rs:8:12 + --> $DIR/unicode.rs:11:12 | LL | print!("Here >< is a WJ, and another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:14:12 + --> $DIR/unicode.rs:17:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -27,24 +27,40 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:20:12 + --> $DIR/unicode.rs:25:16 | -LL | print!("Üben!"); - | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` +LL | print!("Üben!"); + | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | - = note: `-D clippy::non-ascii-literal` implied by `-D warnings` +note: the lint level is defined here + --> $DIR/unicode.rs:22:13 + | +LL | #![deny(clippy::non_ascii_literal)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: literal non-ASCII character detected + --> $DIR/unicode.rs:31:36 + | +LL | const _EMPTY_BLOCK: char = '▱'; + | ^^^ help: consider replacing the string with: `'/u{25b1}'` error: literal non-ASCII character detected - --> $DIR/unicode.rs:27:32 + --> $DIR/unicode.rs:32:35 | -LL | const _EMPTY_BLOCK: char = '▱'; - | ^^^ help: consider replacing the string with: `'/u{25b1}'` +LL | const _FULL_BLOCK: char = '▰'; + | ^^^ help: consider replacing the string with: `'/u{25b0}'` error: literal non-ASCII character detected - --> $DIR/unicode.rs:28:31 + --> $DIR/unicode.rs:52:21 + | +LL | let _ = "悲しいかな、ここに日本語を書くことはできない。"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"/u{60b2}/u{3057}/u{3044}/u{304b}/u{306a}/u{3001}/u{3053}/u{3053}/u{306b}/u{65e5}/u{672c}/u{8a9e}/u{3092}/u{66f8}/u{304f}/u{3053}/u{3068}/u{306f}/u{3067}/u{304d}/u{306a}/u{3044}/u{3002}"` + | +note: the lint level is defined here + --> $DIR/unicode.rs:41:17 | -LL | const _FULL_BLOCK: char = '▰'; - | ^^^ help: consider replacing the string with: `'/u{25b0}'` +LL | #![deny(clippy::non_ascii_literal)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index b352b285c86..ee9f157342d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -88,4 +88,13 @@ mod fixable { } type I32Alias = i32; + + fn issue_9380() { + let _: i32 = -1_i32; + let _: f32 = -(1) as f32; + let _: i64 = -1_i64; + let _: i64 = -(1.0) as i64; + + let _ = -(1 + 1) as i64; + } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index 6c8cc3effe8..5b70412424c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -88,4 +88,13 @@ mod fixable { } type I32Alias = i32; + + fn issue_9380() { + let _: i32 = -(1) as i32; + let _: f32 = -(1) as f32; + let _: i64 = -(1) as i64; + let _: i64 = -(1.0) as i64; + + let _ = -(1 + 1) as i64; + } } diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index bad45f0025b..f7829ff3b0e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -150,5 +150,17 @@ error: casting float literal to `f32` is unnecessary LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 25 previous errors +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:93:22 + | +LL | let _: i32 = -(1) as i32; + | ^^^^^^^^^^^ help: try: `-1_i32` + +error: casting integer literal to `i64` is unnecessary + --> $DIR/unnecessary_cast.rs:95:22 + | +LL | let _: i64 = -(1) as i64; + | ^^^^^^^^^^^ help: try: `-1_i64` + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed index f95f91329a2..40052c41039 100644 --- a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed @@ -12,6 +12,7 @@ fn main() { ref_str_argument(""); // should be linted + #[allow(clippy::manual_string_new)] ref_str_argument(""); // should not be linted diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs index 0cbdc151ed9..2304dff5192 100644 --- a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs +++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs @@ -12,6 +12,7 @@ fn main() { ref_str_argument(&String::new()); // should be linted + #[allow(clippy::manual_string_new)] ref_str_argument(&String::from("")); // should not be linted diff --git a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr index 46bc4597b33..1eb198a8675 100644 --- a/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr @@ -7,7 +7,7 @@ LL | ref_str_argument(&String::new()); = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings` error: usage of `&String::from("")` for a function expecting a `&str` argument - --> $DIR/unnecessary_owned_empty_strings.rs:15:22 + --> $DIR/unnecessary_owned_empty_strings.rs:16:22 | LL | ref_str_argument(&String::from("")); | ^^^^^^^^^^^^^^^^^ help: try: `""` diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index f4f76cd3dd4..9cd5bc73b1e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -329,3 +329,31 @@ mod issue_8759_variant { rw.set_view(&rw.default_view().to_owned()); } } + +mod issue_9317 { + #![allow(dead_code)] + + struct Bytes {} + + impl ToString for Bytes { + fn to_string(&self) -> String { + "123".to_string() + } + } + + impl AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + &[1, 2, 3] + } + } + + fn consume<C: AsRef<[u8]>>(c: C) { + let _ = c; + } + + pub fn main() { + let b = Bytes {}; + // Should not lint. + consume(b.to_string()); + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index fe09a489ab0..7f62ba3ab5d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -329,3 +329,31 @@ mod issue_8759_variant { rw.set_view(&rw.default_view().to_owned()); } } + +mod issue_9317 { + #![allow(dead_code)] + + struct Bytes {} + + impl ToString for Bytes { + fn to_string(&self) -> String { + "123".to_string() + } + } + + impl AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + &[1, 2, 3] + } + } + + fn consume<C: AsRef<[u8]>>(c: C) { + let _ = c; + } + + pub fn main() { + let b = Bytes {}; + // Should not lint. + consume(b.to_string()); + } +} diff --git a/src/tools/clippy/tests/ui/unused_peekable.rs b/src/tools/clippy/tests/ui/unused_peekable.rs new file mode 100644 index 00000000000..153457e3671 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_peekable.rs @@ -0,0 +1,144 @@ +#![warn(clippy::unused_peekable)] +#![allow(clippy::no_effect)] + +use std::iter::Empty; +use std::iter::Peekable; + +fn main() { + invalid(); + valid(); +} + +#[allow(clippy::unused_unit)] +fn invalid() { + let peekable = std::iter::empty::<u32>().peekable(); + + // Only lint `new_local` + let old_local = std::iter::empty::<u32>().peekable(); + let new_local = old_local; + + // Behind mut ref + let mut by_mut_ref_test = std::iter::empty::<u32>().peekable(); + let by_mut_ref = &mut by_mut_ref_test; + + // Explicitly returns `Peekable` + fn returns_peekable() -> Peekable<Empty<u32>> { + std::iter::empty().peekable() + } + + let peekable_from_fn = returns_peekable(); + + // Using a method not exclusive to `Peekable` + let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable(); + peekable_using_iterator_method.next(); + + // Passed by ref to another function + fn takes_ref(_peek: &Peekable<Empty<u32>>) {} + let passed_along_ref = std::iter::empty::<u32>().peekable(); + takes_ref(&passed_along_ref); + + // `by_ref` without `peek` + let mut by_ref_test = std::iter::empty::<u32>().peekable(); + let _by_ref = by_ref_test.by_ref(); + + let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable(); + for x in peekable_in_for_loop {} +} + +fn valid() { + fn takes_peekable(_peek: Peekable<Empty<u32>>) {} + + // Passed to another function + let passed_along = std::iter::empty::<u32>().peekable(); + takes_peekable(passed_along); + + // Passed to another method + struct PeekableConsumer; + impl PeekableConsumer { + fn consume(&self, _: Peekable<Empty<u32>>) {} + fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {} + } + + let peekable_consumer = PeekableConsumer; + let mut passed_along_to_method = std::iter::empty::<u32>().peekable(); + peekable_consumer.consume_mut_ref(&mut passed_along_to_method); + peekable_consumer.consume(passed_along_to_method); + + // `peek` called in another block + let mut peekable_in_block = std::iter::empty::<u32>().peekable(); + { + peekable_in_block.peek(); + } + + // Check the other `Peekable` methods :) + { + let mut peekable_with_peek_mut = std::iter::empty::<u32>().peekable(); + peekable_with_peek_mut.peek_mut(); + + let mut peekable_with_next_if = std::iter::empty::<u32>().peekable(); + peekable_with_next_if.next_if(|_| true); + + let mut peekable_with_next_if_eq = std::iter::empty::<u32>().peekable(); + peekable_with_next_if_eq.next_if_eq(&3); + } + + let mut peekable_in_closure = std::iter::empty::<u32>().peekable(); + let call_peek = |p: &mut Peekable<Empty<u32>>| { + p.peek(); + }; + call_peek(&mut peekable_in_closure); + + // From a macro + macro_rules! make_me_a_peekable_please { + () => { + std::iter::empty::<u32>().peekable() + }; + } + + let _unsuspecting_macro_user = make_me_a_peekable_please!(); + + // Generic Iterator returned + fn return_an_iter() -> impl Iterator<Item = u32> { + std::iter::empty::<u32>().peekable() + } + + let _unsuspecting_user = return_an_iter(); + + // Call `peek` in a macro + macro_rules! peek_iter { + ($iter:ident) => { + $iter.peek(); + }; + } + + let mut peek_in_macro = std::iter::empty::<u32>().peekable(); + peek_iter!(peek_in_macro); + + // Behind mut ref + let mut by_mut_ref_test = std::iter::empty::<u32>().peekable(); + let by_mut_ref = &mut by_mut_ref_test; + by_mut_ref.peek(); + + // Behind ref + let mut by_ref_test = std::iter::empty::<u32>().peekable(); + let by_ref = &by_ref_test; + by_ref_test.peek(); + + // In struct + struct PeekableWrapper { + f: Peekable<Empty<u32>>, + } + + let struct_test = std::iter::empty::<u32>().peekable(); + PeekableWrapper { f: struct_test }; + + // `by_ref` before `peek` + let mut by_ref_test = std::iter::empty::<u32>().peekable(); + let peeked_val = by_ref_test.by_ref().peek(); + + // `peek` called in another block as the last expression + let mut peekable_last_expr = std::iter::empty::<u32>().peekable(); + { + peekable_last_expr.peek(); + } +} diff --git a/src/tools/clippy/tests/ui/unused_peekable.stderr b/src/tools/clippy/tests/ui/unused_peekable.stderr new file mode 100644 index 00000000000..d557f54179d --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_peekable.stderr @@ -0,0 +1,67 @@ +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:14:9 + | +LL | let peekable = std::iter::empty::<u32>().peekable(); + | ^^^^^^^^ + | + = note: `-D clippy::unused-peekable` implied by `-D warnings` + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:18:9 + | +LL | let new_local = old_local; + | ^^^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:22:9 + | +LL | let by_mut_ref = &mut by_mut_ref_test; + | ^^^^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:29:9 + | +LL | let peekable_from_fn = returns_peekable(); + | ^^^^^^^^^^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:32:13 + | +LL | let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:37:9 + | +LL | let passed_along_ref = std::iter::empty::<u32>().peekable(); + | ^^^^^^^^^^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:42:9 + | +LL | let _by_ref = by_ref_test.by_ref(); + | ^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: `peek` never called on `Peekable` iterator + --> $DIR/unused_peekable.rs:44:13 + | +LL | let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing the call to `peekable` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/unwrap.rs b/src/tools/clippy/tests/ui/unwrap.rs index a4a3cd1d379..d9fd402e7cf 100644 --- a/src/tools/clippy/tests/ui/unwrap.rs +++ b/src/tools/clippy/tests/ui/unwrap.rs @@ -6,8 +6,9 @@ fn unwrap_option() { } fn unwrap_result() { - let res: Result<u8, ()> = Ok(0); + let res: Result<u8, u8> = Ok(0); let _ = res.unwrap(); + let _ = res.unwrap_err(); } fn main() { diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr index 4f0858005f6..78422757819 100644 --- a/src/tools/clippy/tests/ui/unwrap.stderr +++ b/src/tools/clippy/tests/ui/unwrap.stderr @@ -15,5 +15,13 @@ LL | let _ = res.unwrap(); | = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message -error: aborting due to 2 previous errors +error: used `unwrap_err()` on `a Result` value + --> $DIR/unwrap.rs:11:13 + | +LL | let _ = res.unwrap_err(); + | ^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `Ok` case gracefully, consider using `expect_err()` to provide a better panic message + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs index 0d4a0504a6e..9f27fef8249 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs @@ -1,10 +1,35 @@ #![warn(clippy::unwrap_used, clippy::expect_used)] +trait OptionExt { + type Item; + + fn unwrap_err(self) -> Self::Item; + + fn expect_err(self, msg: &str) -> Self::Item; +} + +impl<T> OptionExt for Option<T> { + type Item = T; + fn unwrap_err(self) -> T { + panic!(); + } + + fn expect_err(self, msg: &str) -> T { + panic!(); + } +} + fn main() { Some(3).unwrap(); Some(3).expect("Hello world!"); + // Don't trigger on unwrap_err on an option + Some(3).unwrap_err(); + Some(3).expect_err("Hellow none!"); + let a: Result<i32, i32> = Ok(3); a.unwrap(); a.expect("Hello world!"); + a.unwrap_err(); + a.expect_err("Hello error!"); } diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr index f54bfd617c4..1a19459b2c1 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr @@ -1,5 +1,5 @@ error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_expect_used.rs:4:5 + --> $DIR/unwrap_expect_used.rs:23:5 | LL | Some(3).unwrap(); | ^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | Some(3).unwrap(); = help: if this value is `None`, it will panic error: used `expect()` on `an Option` value - --> $DIR/unwrap_expect_used.rs:5:5 + --> $DIR/unwrap_expect_used.rs:24:5 | LL | Some(3).expect("Hello world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | Some(3).expect("Hello world!"); = help: if this value is `None`, it will panic error: used `unwrap()` on `a Result` value - --> $DIR/unwrap_expect_used.rs:8:5 + --> $DIR/unwrap_expect_used.rs:31:5 | LL | a.unwrap(); | ^^^^^^^^^^ @@ -25,12 +25,28 @@ LL | a.unwrap(); = help: if this value is an `Err`, it will panic error: used `expect()` on `a Result` value - --> $DIR/unwrap_expect_used.rs:9:5 + --> $DIR/unwrap_expect_used.rs:32:5 | LL | a.expect("Hello world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: if this value is an `Err`, it will panic -error: aborting due to 4 previous errors +error: used `unwrap_err()` on `a Result` value + --> $DIR/unwrap_expect_used.rs:33:5 + | +LL | a.unwrap_err(); + | ^^^^^^^^^^^^^^ + | + = help: if this value is an `Ok`, it will panic + +error: used `expect_err()` on `a Result` value + --> $DIR/unwrap_expect_used.rs:34:5 + | +LL | a.expect_err("Hello error!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if this value is an `Ok`, it will panic + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.rs b/src/tools/clippy/tests/ui/useless_conversion_try.rs index 39f54c27bee..4acf5b5fa2d 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.rs +++ b/src/tools/clippy/tests/ui/useless_conversion_try.rs @@ -29,10 +29,10 @@ fn main() { let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); let _: String = format!("Hello {}", "world").try_into().unwrap(); - let _: String = "".to_owned().try_into().unwrap(); + let _: String = String::new().try_into().unwrap(); let _: String = match String::from("_").try_into() { Ok(a) => a, - Err(_) => "".into(), + Err(_) => String::new(), }; // FIXME this is a false negative #[allow(clippy::cmp_owned)] diff --git a/src/tools/clippy/tests/ui/useless_conversion_try.stderr b/src/tools/clippy/tests/ui/useless_conversion_try.stderr index b691c13f7db..12e74d61471 100644 --- a/src/tools/clippy/tests/ui/useless_conversion_try.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion_try.stderr @@ -62,7 +62,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:32:21 | -LL | let _: String = "".to_owned().try_into().unwrap(); +LL | let _: String = String::new().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider removing `.try_into()` diff --git a/src/tools/clippy/tests/ui/vec_resize_to_zero.rs b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs index 7ed27439ec6..a8307e741cf 100644 --- a/src/tools/clippy/tests/ui/vec_resize_to_zero.rs +++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.rs @@ -1,15 +1,19 @@ #![warn(clippy::vec_resize_to_zero)] fn main() { + let mut v = vec![1, 2, 3, 4, 5]; + // applicable here - vec![1, 2, 3, 4, 5].resize(0, 5); + v.resize(0, 5); // not applicable - vec![1, 2, 3, 4, 5].resize(2, 5); + v.resize(2, 5); + + let mut v = vec!["foo", "bar", "baz"]; // applicable here, but only implemented for integer literals for now - vec!["foo", "bar", "baz"].resize(0, "bar"); + v.resize(0, "bar"); // not applicable - vec!["foo", "bar", "baz"].resize(2, "bar") + v.resize(2, "bar") } diff --git a/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr b/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr index feb846298c6..7428cf62d6c 100644 --- a/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr +++ b/src/tools/clippy/tests/ui/vec_resize_to_zero.stderr @@ -1,10 +1,10 @@ error: emptying a vector with `resize` - --> $DIR/vec_resize_to_zero.rs:5:5 + --> $DIR/vec_resize_to_zero.rs:7:5 | -LL | vec![1, 2, 3, 4, 5].resize(0, 5); - | ^^^^^^^^^^^^^^^^^^^^------------ - | | - | help: ...or you can empty the vector with: `clear()` +LL | v.resize(0, 5); + | ^^------------ + | | + | help: ...or you can empty the vector with: `clear()` | = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings` = help: the arguments may be inverted... diff --git a/src/tools/clippy/tests/ui/verbose_file_reads.rs b/src/tools/clippy/tests/ui/verbose_file_reads.rs index e0065e05ade..df267e9872a 100644 --- a/src/tools/clippy/tests/ui/verbose_file_reads.rs +++ b/src/tools/clippy/tests/ui/verbose_file_reads.rs @@ -18,7 +18,7 @@ fn main() -> std::io::Result<()> { s.read_to_end(); s.read_to_string(); // Should catch this - let mut f = File::open(&path)?; + let mut f = File::open(path)?; let mut buffer = Vec::new(); f.read_to_end(&mut buffer)?; // ...and this diff --git a/src/tools/clippy/tests/workspace.rs b/src/tools/clippy/tests/workspace.rs index e13efb3e016..95325e06037 100644 --- a/src/tools/clippy/tests/workspace.rs +++ b/src/tools/clippy/tests/workspace.rs @@ -20,8 +20,8 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { .current_dir(&cwd) .env("CARGO_TARGET_DIR", &target_dir) .arg("clean") - .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) + .args(["-p", "subcrate"]) + .args(["-p", "path_dep"]) .output() .unwrap(); @@ -32,11 +32,11 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { .env("CARGO_INCREMENTAL", "0") .env("CARGO_TARGET_DIR", &target_dir) .arg("clippy") - .args(&["-p", "subcrate"]) + .args(["-p", "subcrate"]) .arg("--no-deps") .arg("--") .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) + .args(["--cfg", r#"feature="primary_package_test""#]) .output() .unwrap(); println!("status: {}", output.status); @@ -52,10 +52,10 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { .env("CARGO_INCREMENTAL", "0") .env("CARGO_TARGET_DIR", &target_dir) .arg("clippy") - .args(&["-p", "subcrate"]) + .args(["-p", "subcrate"]) .arg("--") .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) + .args(["--cfg", r#"feature="primary_package_test""#]) .output() .unwrap(); println!("status: {}", output.status); @@ -79,7 +79,7 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { .env("CARGO_INCREMENTAL", "0") .env("CARGO_TARGET_DIR", &target_dir) .arg("clippy") - .args(&["-p", "subcrate"]) + .args(["-p", "subcrate"]) .arg("--") .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir .output() diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs index 52eb7ce17d6..e4e43e97dde 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs @@ -37,23 +37,6 @@ pub struct SourceFile { type Level = super::proc_macro::Level; type LineColumn = super::proc_macro::LineColumn; -/// A structure representing a diagnostic message and associated children -/// messages. -#[derive(Clone, Debug)] -pub struct Diagnostic { - level: Level, - message: String, - spans: Vec<Span>, - children: Vec<Diagnostic>, -} - -impl Diagnostic { - /// Creates a new diagnostic with the given `level` and `message`. - pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic { - Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } - } -} - pub struct FreeFunctions; #[derive(Default)] @@ -65,8 +48,6 @@ impl server::Types for RustAnalyzer { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type SourceFile = SourceFile; - type MultiSpan = Vec<Span>; - type Diagnostic = Diagnostic; type Span = Span; type Symbol = Symbol; } @@ -90,6 +71,10 @@ impl server::FreeFunctions for RustAnalyzer { span: tt::TokenId::unspecified(), }) } + + fn emit_diagnostic(&mut self, _: bridge::Diagnostic<Self::Span>) { + // FIXME handle diagnostic + } } impl server::TokenStream for RustAnalyzer { @@ -282,30 +267,6 @@ impl server::SourceFile for RustAnalyzer { } } -impl server::Diagnostic for RustAnalyzer { - fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { - let mut diag = Diagnostic::new(level, msg); - diag.spans = spans; - diag - } - - fn sub( - &mut self, - _diag: &mut Self::Diagnostic, - _level: Level, - _msg: &str, - _spans: Self::MultiSpan, - ) { - // FIXME handle diagnostic - // - } - - fn emit(&mut self, _diag: Self::Diagnostic) { - // FIXME handle diagnostic - // diag.emit() - } -} - impl server::Span for RustAnalyzer { fn debug(&mut self, span: Self::Span) -> String { format!("{:?}", span.0) @@ -372,18 +333,6 @@ impl server::Span for RustAnalyzer { } } -impl server::MultiSpan for RustAnalyzer { - fn new(&mut self) -> Self::MultiSpan { - // FIXME handle span - vec![] - } - - fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) { - //TODP - other.push(span) - } -} - impl server::Symbol for RustAnalyzer { fn normalize_and_validate_ident(&mut self, string: &str) -> Result<Self::Symbol, ()> { // FIXME: nfc-normalize and validate idents diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 2fe17e979c8..05e1eb2bfb0 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -223,6 +223,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[ "time", "tinystr", "tinyvec", + "thin-vec", "tracing", "tracing-attributes", "tracing-core", |
