diff --git a/Cargo.lock b/Cargo.lock index d5357ac4758fc..76722d4e65837 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,12 +351,6 @@ version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - [[package]] name = "camino" version = "1.1.10" @@ -569,22 +563,16 @@ dependencies = [ "color-print", "declare_clippy_lint", "filetime", - "futures", - "if_chain", "itertools", - "parking_lot", "pulldown-cmark", - "quote", "regex", "rustc_tools_util 0.4.2", "serde", "serde_json", - "syn 2.0.104", "tempfile", - "termize 0.1.1", - "tokio", + "termize", "toml 0.7.8", - "ui_test", + "ui_test 0.30.2", "walkdir", ] @@ -1322,95 +1310,6 @@ dependencies = [ "new_debug_unreachable", ] -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "generate-copyright" version = "0.1.0" @@ -1848,12 +1747,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "ignore" version = "0.4.23" @@ -1947,17 +1840,6 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "io-uring" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipc-channel" version = "0.19.0" @@ -2409,7 +2291,7 @@ dependencies = [ "smallvec", "tempfile", "tikv-jemalloc-sys", - "ui_test", + "ui_test 0.29.2", ] [[package]] @@ -2868,12 +2750,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkg-config" version = "0.3.32" @@ -3584,6 +3460,7 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", + "rustc_mir_transform", "rustc_query_system", "rustc_serialize", "rustc_session", @@ -3773,7 +3650,7 @@ dependencies = [ "serde", "serde_json", "termcolor", - "termize 0.2.0", + "termize", "tracing", "windows 0.61.3", ] @@ -4567,7 +4444,7 @@ dependencies = [ "rustc_serialize", "rustc_span", "rustc_target", - "termize 0.2.0", + "termize", "tracing", "windows 0.61.3", ] @@ -5052,12 +4929,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" -[[package]] -name = "slab" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" - [[package]] name = "smallvec" version = "1.15.1" @@ -5084,6 +4955,17 @@ dependencies = [ "color-eyre", ] +[[package]] +name = "spanned" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4b0c055fde758f086eb4a6e73410247df8a3837fd606d2caeeaf72aa566d" +dependencies = [ + "anyhow", + "bstr", + "color-eyre", +] + [[package]] name = "spdx-expression" version = "0.5.2" @@ -5306,16 +5188,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "termize" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "termize" version = "0.2.0" @@ -5481,21 +5353,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "1.46.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" -dependencies = [ - "backtrace", - "bytes", - "io-uring", - "libc", - "mio", - "pin-project-lite", - "slab", -] - [[package]] name = "toml" version = "0.5.11" @@ -5727,7 +5584,33 @@ dependencies = [ "rustfix", "serde", "serde_json", - "spanned", + "spanned 0.3.0", +] + +[[package]] +name = "ui_test" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b56a6897cc4bb6f8daf1939b0b39cd9645856997f46f4d0b3e3cb7122dfe9251" +dependencies = [ + "annotate-snippets 0.11.5", + "anyhow", + "bstr", + "cargo-platform", + "cargo_metadata 0.18.1", + "color-eyre", + "colored", + "comma", + "crossbeam-channel", + "indicatif", + "levenshtein", + "prettydiff", + "regex", + "rustc_version", + "rustfix", + "serde", + "serde_json", + "spanned 0.4.1", ] [[package]] diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 323a8fab6d592..069bff67b97ac 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -1,24 +1,7 @@ //! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`. use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::Ident; -use rustc_span::def_id::DefId; - -use crate::MetaItem; pub mod allocator; pub mod autodiff_attrs; pub mod typetree; - -#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] -pub struct StrippedCfgItem { - pub parent_module: ModId, - pub ident: Ident, - pub cfg: MetaItem, -} - -impl StrippedCfgItem { - pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { - StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg } - } -} diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 348fe2ee40aa2..1c96a375035aa 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -42,7 +42,7 @@ use std::sync::Arc; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; -use rustc_attr_parsing::{AttributeParser, OmitDoc}; +use rustc_attr_parsing::{AttributeParser, Late, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -192,7 +192,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // interact with `gen`/`async gen` blocks allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), - attribute_parser: AttributeParser::new(tcx.sess, tcx.features(), registered_tools), + attribute_parser: AttributeParser::new( + tcx.sess, + tcx.features(), + registered_tools, + Late, + ), delayed_lints: Vec::new(), } } diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9b14ed88978b1..995ac514d8d6c 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -3,7 +3,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::{self as ast, AttrStyle}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::hygiene::Transparency; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use thin_vec::ThinVec; use crate::{DefaultBodyStability, PartialConstStability, PrintAttribute, RustcVersion, Stability}; @@ -69,6 +69,7 @@ pub enum ReprAttr { ReprAlign(Align), } pub use ReprAttr::*; +use rustc_span::def_id::DefId; pub enum TransparencyError { UnknownTransparency(Symbol, Span), @@ -140,6 +141,30 @@ pub enum UsedBy { Linker, } +#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] +pub struct StrippedCfgItem { + pub parent_module: ModId, + pub ident: Ident, + pub cfg: (CfgEntry, Span), +} + +impl StrippedCfgItem { + pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { + StrippedCfgItem { parent_module: f(self.parent_module), ident: self.ident, cfg: self.cfg } + } +} + +#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum CfgEntry { + All(ThinVec, Span), + Any(ThinVec, Span), + Not(Box, Span), + Bool(bool, Span), + NameValue { name: Symbol, name_span: Span, value: Option<(Symbol, Span)>, span: Span }, + Version(Option, Span), +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index a8d9229cbc35b..ce110980efaed 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -1,247 +1,282 @@ -use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; -use rustc_ast_pretty::pprust; -use rustc_attr_data_structures::RustcVersion; -use rustc_feature::{Features, GatedCfg, find_gated_cfg}; +use rustc_ast::{LitKind, NodeId}; +use rustc_attr_data_structures::{CfgEntry, RustcVersion}; +use rustc_feature::{AttributeTemplate, Features, template}; use rustc_session::Session; use rustc_session::config::ExpectedValues; +use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; -use rustc_session::lint::{BuiltinLintDiag, Lint}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; -use crate::session_diagnostics::{self, UnsupportedLiteralReason}; -use crate::{fluent_generated, parse_version}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; +use crate::{ + CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg, +}; -/// Emitter of a builtin lint from `cfg_matches`. -/// -/// Used to support emitting a lint (currently on check-cfg), either: -/// - as an early buffered lint (in `rustc`) -/// - or has a "normal" lint from HIR (in `rustdoc`) -pub trait CfgMatchesLintEmitter { - fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag); -} +pub const CFG_TEMPLATE: AttributeTemplate = template!(List: "predicate"); -impl CfgMatchesLintEmitter for NodeId { - fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) { - sess.psess.buffer_lint(lint, sp, *self, diag); - } -} - -#[derive(Clone, Debug)] -pub struct Condition { - pub name: Symbol, - pub name_span: Span, - pub value: Option, - pub value_span: Option, - pub span: Span, +pub fn parse_cfg_attr<'c, S: Stage>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, +) -> Option { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span); + return None; + }; + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + parse_cfg_entry(cx, single) } -/// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches( - cfg: &MetaItemInner, - sess: &Session, - lint_emitter: impl CfgMatchesLintEmitter, - features: Option<&Features>, -) -> bool { - eval_condition(cfg, sess, features, &mut |cfg| { - try_gate_cfg(cfg.name, cfg.span, sess, features); - match sess.psess.check_config.expecteds.get(&cfg.name) { - Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - lint_emitter.emit_span_lint( - sess, - UNEXPECTED_CFGS, - cfg.span, - BuiltinLintDiag::UnexpectedCfgValue( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); +fn parse_cfg_entry( + cx: &mut AcceptContext<'_, '_, S>, + item: &MetaItemOrLitParser<'_>, +) -> Option { + Some(match item { + MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() { + ArgParser::List(list) => match meta.path().word_sym() { + Some(sym::not) => { + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span) + } + Some(sym::any) => CfgEntry::Any( + list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(), + list.span, + ), + Some(sym::all) => CfgEntry::All( + list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(), + list.span, + ), + Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?, + Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?, + _ => { + cx.emit_err(session_diagnostics::InvalidPredicate { + span: meta.span(), + predicate: meta.path().to_string(), + }); + return None; + } + }, + a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { + let Some(name) = meta.path().word_sym() else { + cx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: meta.path().span(), + }); + return None; + }; + parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? } - None if sess.psess.check_config.exhaustive_names => { - lint_emitter.emit_span_lint( - sess, - UNEXPECTED_CFGS, - cfg.span, - BuiltinLintDiag::UnexpectedCfgName( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); + }, + MetaItemOrLitParser::Lit(lit) => match lit.kind { + LitKind::Bool(b) => CfgEntry::Bool(b, lit.span), + _ => { + cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span }); + return None; } - _ => { /* not unexpected */ } - } - sess.psess.config.contains(&(cfg.name, cfg.value)) + }, + MetaItemOrLitParser::Err(_, _) => return None, }) } -fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { - let gate = find_gated_cfg(|sym| sym == name); - if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(gated_cfg, span, sess, feats); - } +fn parse_cfg_entry_version( + cx: &mut AcceptContext<'_, '_, S>, + list: &MetaItemListParser<'_>, + meta_span: Span, +) -> Option { + try_gate_cfg(sym::version, meta_span, cx.sess(), Some(cx.features())); + let Some(version) = list.single() else { + cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span }); + return None; + }; + let Some(version_lit) = version.lit() else { + cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version.span() }); + return None; + }; + let Some(version_str) = version_lit.value_str() else { + cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version_lit.span }); + return None; + }; + + let min_version = parse_version(version_str).or_else(|| { + cx.sess() + .dcx() + .emit_warn(session_diagnostics::UnknownVersionLiteral { span: version_lit.span }); + None + }); + + Some(CfgEntry::Version(min_version, list.span)) } -#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { - let (cfg, feature, has_feature) = gated_cfg; - if !has_feature(features) && !cfg_span.allows_unstable(*feature) { - let explain = format!("`cfg({cfg})` is experimental and subject to change"); - feature_err(sess, *feature, cfg_span, explain).emit(); +fn parse_cfg_entry_target( + cx: &mut AcceptContext<'_, '_, S>, + list: &MetaItemListParser<'_>, + meta_span: Span, +) -> Option { + if !cx.features().cfg_target_compact() { + feature_err( + cx.sess(), + sym::cfg_target_compact, + meta_span, + fluent_generated::attr_parsing_unstable_cfg_target_compact, + ) + .emit(); } -} -/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to -/// evaluate individual items. -pub fn eval_condition( - cfg: &MetaItemInner, - sess: &Session, - features: Option<&Features>, - eval: &mut impl FnMut(Condition) -> bool, -) -> bool { - let dcx = sess.dcx(); - - let cfg = match cfg { - MetaItemInner::MetaItem(meta_item) => meta_item, - MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { - return *b; - } - _ => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: cfg.span(), - reason: UnsupportedLiteralReason::CfgBoolean, - is_bytestr: false, - start_point_span: sess.source_map().start_point(cfg.span()), + let mut result = ThinVec::new(); + for sub_item in list.mixed() { + // First, validate that this is a NameValue item + let Some(sub_item) = sub_item.meta_item() else { + cx.expected_name_value(sub_item.span(), None); + continue; + }; + let Some(nv) = sub_item.args().name_value() else { + cx.expected_name_value(sub_item.span(), None); + continue; + }; + + // Then, parse it as a name-value item + let Some(name) = sub_item.path().word_sym() else { + cx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: sub_item.path().span(), }); - return false; + return None; + }; + let name = Symbol::intern(&format!("target_{name}")); + if let Some(cfg) = + parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx) + { + result.push(cfg); } - }; + } + Some(CfgEntry::All(result, list.span)) +} - match &cfg.kind { - MetaItemKind::List(mis) if cfg.has_name(sym::version) => { - try_gate_cfg(sym::version, cfg.span, sess, features); - let (min_version, span) = match &mis[..] { - [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { - (sym, span) - } - [ - MetaItemInner::Lit(MetaItemLit { span, .. }) - | MetaItemInner::MetaItem(MetaItem { span, .. }), - ] => { - dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); - return false; - } - [..] => { - dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { - span: cfg.span, - }); - return false; - } - }; - let Some(min_version) = parse_version(*min_version) else { - dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); - return false; +fn parse_name_value( + name: Symbol, + name_span: Span, + value: Option<&NameValueParser>, + span: Span, + cx: &mut AcceptContext<'_, '_, S>, +) -> Option { + try_gate_cfg(name, span, cx.sess(), cx.features_option()); + + let value = match value { + None => None, + Some(value) => { + let Some(value_str) = value.value_as_str() else { + cx.expected_string_literal(value.value_span, Some(value.value_as_lit())); + return None; }; + Some((value_str, value.value_span)) + } + }; - // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details - if sess.psess.assume_incomplete_release { - RustcVersion::current_overridable() > min_version + Some(CfgEntry::NameValue { name, name_span, value, span }) +} + +pub fn eval_config_entry( + sess: &Session, + cfg_entry: &CfgEntry, + id: NodeId, + features: Option<&Features>, +) -> EvalConfigResult { + match cfg_entry { + CfgEntry::All(subs, ..) => { + for sub in subs { + let res = eval_config_entry(sess, sub, id, features); + if !res.as_bool() { + return res; + } + } + EvalConfigResult::True + } + CfgEntry::Any(subs, span) => { + if subs.iter().any(|sub| eval_config_entry(sess, sub, id, features).as_bool()) { + EvalConfigResult::True } else { - RustcVersion::current_overridable() >= min_version + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } } - MetaItemKind::List(mis) => { - for mi in mis.iter() { - if mi.meta_item_or_bool().is_none() { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: mi.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(mi.span()), - }); - return false; - } + CfgEntry::Not(sub, span) => { + if eval_config_entry(sess, sub, id, features).as_bool() { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } + } else { + EvalConfigResult::True } - - // The unwraps below may look dangerous, but we've already asserted - // that they won't fail with the loop above. - match cfg.name() { - Some(sym::any) => mis - .iter() - // We don't use any() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), - Some(sym::all) => mis - .iter() - // We don't use all() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), - Some(sym::not) => { - let [mi] = mis.as_slice() else { - dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); - return false; - }; - - !eval_condition(mi, sess, features, eval) - } - Some(sym::target) => { - if let Some(features) = features - && !features.cfg_target_compact() - { - feature_err( - sess, - sym::cfg_target_compact, - cfg.span, - fluent_generated::attr_parsing_unstable_cfg_target_compact, - ) - .emit(); - } - - mis.iter().fold(true, |res, mi| { - let Some(mut mi) = mi.meta_item().cloned() else { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { - span: mi.span(), - }); - return false; - }; - - if let [seg, ..] = &mut mi.path.segments[..] { - seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); - } - - res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval) - }) + } + CfgEntry::Bool(b, span) => { + if *b { + EvalConfigResult::True + } else { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } + } + } + CfgEntry::NameValue { name, name_span, value, span } => { + match sess.psess.check_config.expecteds.get(name) { + Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), + ); } - _ => { - dcx.emit_err(session_diagnostics::InvalidPredicate { - span: cfg.span, - predicate: pprust::path_to_string(&cfg.path), - }); - false + None if sess.psess.check_config.exhaustive_names => { + id.emit_span_lint( + sess, + UNEXPECTED_CFGS, + *span, + BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), + ); } + _ => { /* not unexpected */ } + } + + if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) { + EvalConfigResult::True + } else { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } } - MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); - true - } - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::CfgString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - true + CfgEntry::Version(min_version, version_span) => { + let Some(min_version) = min_version else { + return EvalConfigResult::False { + reason: cfg_entry.clone(), + reason_span: *version_span, + }; + }; + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + let min_version_ok = if sess.psess.assume_incomplete_release { + RustcVersion::current_overridable() > *min_version + } else { + RustcVersion::current_overridable() >= *min_version + }; + if min_version_ok { + EvalConfigResult::True + } else { + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *version_span } + } } - MetaItemKind::Word | MetaItemKind::NameValue(..) => { - let ident = cfg.ident().expect("multi-segment cfg predicate"); - eval(Condition { - name: ident.name, - name_span: ident.span, - value: cfg.value_str(), - value_span: cfg.name_value_literal_span(), - span: cfg.span, - }) + } +} + +pub enum EvalConfigResult { + True, + False { reason: CfgEntry, reason_span: Span }, +} + +impl EvalConfigResult { + pub fn as_bool(&self) -> bool { + match self { + EvalConfigResult::True => true, + EvalConfigResult::False { .. } => false, } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs new file mode 100644 index 0000000000000..c5025a8b6eac8 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs @@ -0,0 +1,247 @@ +use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; +use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::RustcVersion; +use rustc_feature::{Features, GatedCfg, find_gated_cfg}; +use rustc_session::Session; +use rustc_session::config::ExpectedValues; +use rustc_session::lint::builtin::UNEXPECTED_CFGS; +use rustc_session::lint::{BuiltinLintDiag, Lint}; +use rustc_session::parse::feature_err; +use rustc_span::{Span, Symbol, sym}; + +use crate::session_diagnostics::{self, UnsupportedLiteralReason}; +use crate::{fluent_generated, parse_version}; + +/// Emitter of a builtin lint from `cfg_matches`. +/// +/// Used to support emitting a lint (currently on check-cfg), either: +/// - as an early buffered lint (in `rustc`) +/// - or has a "normal" lint from HIR (in `rustdoc`) +pub trait CfgMatchesLintEmitter { + fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag); +} + +impl CfgMatchesLintEmitter for NodeId { + fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag) { + sess.psess.buffer_lint(lint, sp, *self, diag); + } +} + +#[derive(Clone, Debug)] +pub struct Condition { + pub name: Symbol, + pub name_span: Span, + pub value: Option, + pub value_span: Option, + pub span: Span, +} + +/// Tests if a cfg-pattern matches the cfg set +pub fn cfg_matches( + cfg: &MetaItemInner, + sess: &Session, + lint_emitter: impl CfgMatchesLintEmitter, + features: Option<&Features>, +) -> bool { + eval_condition(cfg, sess, features, &mut |cfg| { + try_gate_cfg(cfg.name, cfg.span, sess, features); + match sess.psess.check_config.expecteds.get(&cfg.name) { + Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { + lint_emitter.emit_span_lint( + sess, + UNEXPECTED_CFGS, + cfg.span, + BuiltinLintDiag::UnexpectedCfgValue( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + None if sess.psess.check_config.exhaustive_names => { + lint_emitter.emit_span_lint( + sess, + UNEXPECTED_CFGS, + cfg.span, + BuiltinLintDiag::UnexpectedCfgName( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + _ => { /* not unexpected */ } + } + sess.psess.config.contains(&(cfg.name, cfg.value)) + }) +} + +pub fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { + let gate = find_gated_cfg(|sym| sym == name); + if let (Some(feats), Some(gated_cfg)) = (features, gate) { + gate_cfg(gated_cfg, span, sess, feats); + } +} + +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable +fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { + let (cfg, feature, has_feature) = gated_cfg; + if !has_feature(features) && !cfg_span.allows_unstable(*feature) { + let explain = format!("`cfg({cfg})` is experimental and subject to change"); + feature_err(sess, *feature, cfg_span, explain).emit(); + } +} + +/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to +/// evaluate individual items. +pub fn eval_condition( + cfg: &MetaItemInner, + sess: &Session, + features: Option<&Features>, + eval: &mut impl FnMut(Condition) -> bool, +) -> bool { + let dcx = sess.dcx(); + + let cfg = match cfg { + MetaItemInner::MetaItem(meta_item) => meta_item, + MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + return *b; + } + _ => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: cfg.span(), + reason: UnsupportedLiteralReason::CfgBoolean, + is_bytestr: false, + start_point_span: sess.source_map().start_point(cfg.span()), + }); + return false; + } + }; + + match &cfg.kind { + MetaItemKind::List(mis) if cfg.has_name(sym::version) => { + try_gate_cfg(sym::version, cfg.span, sess, features); + let (min_version, span) = match &mis[..] { + [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { + (sym, span) + } + [ + MetaItemInner::Lit(MetaItemLit { span, .. }) + | MetaItemInner::MetaItem(MetaItem { span, .. }), + ] => { + dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); + return false; + } + [..] => { + dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { + span: cfg.span, + }); + return false; + } + }; + let Some(min_version) = parse_version(*min_version) else { + dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); + return false; + }; + + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + if sess.psess.assume_incomplete_release { + RustcVersion::current_overridable() > min_version + } else { + RustcVersion::current_overridable() >= min_version + } + } + MetaItemKind::List(mis) => { + for mi in mis.iter() { + if mi.meta_item_or_bool().is_none() { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: mi.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(mi.span()), + }); + return false; + } + } + + // The unwraps below may look dangerous, but we've already asserted + // that they won't fail with the loop above. + match cfg.name() { + Some(sym::any) => mis + .iter() + // We don't use any() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), + Some(sym::all) => mis + .iter() + // We don't use all() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), + Some(sym::not) => { + let [mi] = mis.as_slice() else { + dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); + return false; + }; + + !eval_condition(mi, sess, features, eval) + } + Some(sym::target) => { + if let Some(features) = features + && !features.cfg_target_compact() + { + feature_err( + sess, + sym::cfg_target_compact, + cfg.span, + fluent_generated::attr_parsing_unstable_cfg_target_compact, + ) + .emit(); + } + + mis.iter().fold(true, |res, mi| { + let Some(mut mi) = mi.meta_item().cloned() else { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: mi.span(), + }); + return false; + }; + + if let [seg, ..] = &mut mi.path.segments[..] { + seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); + } + + res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval) + }) + } + _ => { + dcx.emit_err(session_diagnostics::InvalidPredicate { + span: cfg.span, + predicate: pprust::path_to_string(&cfg.path), + }); + false + } + } + } + MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); + true + } + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::CfgString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); + true + } + MetaItemKind::Word | MetaItemKind::NameValue(..) => { + let ident = cfg.ident().expect("multi-segment cfg predicate"); + eval(Condition { + name: ident.name, + name_span: ident.span, + value: cfg.value_str(), + value_span: cfg.name_value_literal_span(), + span: cfg.span, + }) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 68c716d1a99d1..200f138196029 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -27,6 +27,7 @@ use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; pub(crate) mod cfg; +pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod deprecation; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6c70f2ee4ee0f..1f6675b4c5a8a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -1,6 +1,5 @@ use std::cell::RefCell; use std::collections::BTreeMap; -use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; @@ -202,7 +201,11 @@ pub trait Stage: Sized + 'static + Sealed { fn parsers() -> &'static group_type!(Self); - fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed; + fn emit_err<'sess>( + &self, + sess: &'sess Session, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed; } // allow because it's a sealed trait @@ -214,8 +217,16 @@ impl Stage for Early { fn parsers() -> &'static group_type!(Self) { &early::ATTRIBUTE_PARSERS } - fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { - sess.dcx().create_err(diag).delay_as_bug() + fn emit_err<'sess>( + &self, + sess: &'sess Session, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed { + if self.emit_errors { + sess.dcx().emit_err(diag) + } else { + sess.dcx().create_err(diag).delay_as_bug() + } } } @@ -228,20 +239,29 @@ impl Stage for Late { fn parsers() -> &'static group_type!(Self) { &late::ATTRIBUTE_PARSERS } - fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { + fn emit_err<'sess>( + &self, + tcx: &'sess Session, + diag: impl for<'x> Diagnostic<'x>, + ) -> ErrorGuaranteed { tcx.dcx().emit_err(diag) } } /// used when parsing attributes for miscellaneous things *before* ast lowering -pub struct Early; +pub struct Early { + /// Whether to emit errors or delay them as a bug + /// For most attributes, the attribute will be parsed again in the `Late` stage and in this case the errors should be delayed + /// But for some, such as `cfg`, the attribute will be removed before the `Late` stage so errors must be emitted + pub emit_errors: bool, +} /// used when parsing attributes during ast lowering pub struct Late; /// Context given to every attribute parser when accepting /// /// Gives [`AttributeParser`]s enough information to create errors, for example. -pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { +pub struct AcceptContext<'f, 'sess, S: Stage> { pub(crate) shared: SharedContext<'f, 'sess, S>, /// The span of the attribute currently being parsed pub(crate) attr_span: Span, @@ -257,7 +277,7 @@ pub(crate) struct AcceptContext<'f, 'sess, S: Stage> { impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed { - S::emit_err(&self.sess, diag) + self.stage.emit_err(&self.sess, diag) } /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing @@ -472,7 +492,7 @@ impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> { /// /// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create /// errors, for example. -pub(crate) struct SharedContext<'p, 'sess, S: Stage> { +pub struct SharedContext<'p, 'sess, S: Stage> { /// The parse context, gives access to the session and the /// diagnostics context. pub(crate) cx: &'p mut AttributeParser<'sess, S>, @@ -540,7 +560,7 @@ pub struct AttributeParser<'sess, S: Stage = Late> { pub(crate) tools: Vec, features: Option<&'sess Features>, sess: &'sess Session, - stage: PhantomData, + stage: S, /// *Only* parse attributes with this symbol. /// @@ -569,13 +589,14 @@ impl<'sess> AttributeParser<'sess, Early> { sym: Symbol, target_span: Span, target_node_id: NodeId, + features: Option<&'sess Features>, ) -> Option { let mut p = Self { - features: None, + features, tools: Vec::new(), parse_only: Some(sym), sess, - stage: PhantomData, + stage: Early { emit_errors: false }, }; let mut parsed = p.parse_attribute_list( attrs, @@ -591,11 +612,55 @@ impl<'sess> AttributeParser<'sess, Early> { parsed.pop() } + + pub fn parse_single( + sess: &'sess Session, + attr: &ast::Attribute, + target_span: Span, + target_node_id: NodeId, + features: Option<&'sess Features>, + emit_errors: bool, + parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T, + template: &AttributeTemplate, + ) -> T { + let mut parser = Self { + features, + tools: Vec::new(), + parse_only: None, + sess, + stage: Early { emit_errors }, + }; + let ast::AttrKind::Normal(normal_attr) = &attr.kind else { + panic!("parse_single called on a doc attr") + }; + let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx()); + let path = meta_parser.path(); + let args = meta_parser.args(); + let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext { + shared: SharedContext { + cx: &mut parser, + target_span, + target_id: target_node_id, + emit_lint: &mut |_lint| { + panic!("can't emit lints here for now (nothing uses this atm)"); + }, + }, + attr_span: attr.span, + template, + attr_path: path.get_attribute_path(), + }; + parse_fn(&mut cx, args) + } } impl<'sess, S: Stage> AttributeParser<'sess, S> { - pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec) -> Self { - Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData } + pub fn new( + sess: &'sess Session, + features: &'sess Features, + tools: Vec, + stage: S, + ) -> Self { + Self { features: Some(features), tools, parse_only: None, sess, stage } } pub(crate) fn sess(&self) -> &'sess Session { @@ -606,6 +671,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { self.features.expect("features not available at this point in the compiler") } + pub(crate) fn features_option(&self) -> Option<&'sess Features> { + self.features + } + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { self.sess().dcx() } diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 47eeb63bad3db..2102a26108bc4 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -90,7 +90,8 @@ mod lints; pub mod parser; mod session_diagnostics; -pub use attributes::cfg::*; +pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr}; +pub use attributes::cfg_old::*; pub use attributes::util::{ find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version, }; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 8a240639d75a8..97bf3d1c54955 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -593,7 +593,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.code(E0565); } AttributeParseErrorReason::ExpectedNameValue(None) => { - // The suggestion we add below this match already contains enough information + // If the span is the entire attribute, the suggestion we add below this match already contains enough information + if self.span != self.attr_span { + diag.span_label( + self.span, + format!("expected this to be of the form `... = \"...\"`"), + ); + } } AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { diag.span_label( diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 8c3093acea450..c55a9e73e3822 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -484,7 +484,7 @@ impl<'a> TraitDef<'a> { match item { Annotatable::Item(item) => { let is_packed = matches!( - AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id), + AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id, None), Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index cfae1b3ec98e5..1b7cfc2dd38b1 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -30,6 +30,7 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } +rustc_mir_transform = { path = "../rustc_mir_transform" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 6d6465dd798b5..6de272b81a1bc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -278,10 +278,14 @@ impl CleanupKind { /// MSVC requires unwinding code to be split to a tree of *funclets*, where each funclet can only /// branch to itself or to its parent. Luckily, the code we generates matches this pattern. /// Recover that structure in an analyze pass. -pub(crate) fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec { +pub(crate) fn cleanup_kinds( + mir: &mir::Body<'_>, + nop_landing_pads: &DenseBitSet, +) -> IndexVec { fn discover_masters<'tcx>( result: &mut IndexSlice, mir: &mir::Body<'tcx>, + nop_landing_pads: &DenseBitSet, ) { for (bb, data) in mir.basic_blocks.iter_enumerated() { match data.terminator().kind { @@ -300,7 +304,9 @@ pub(crate) fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec { - if let mir::UnwindAction::Cleanup(unwind) = unwind { + if let mir::UnwindAction::Cleanup(unwind) = unwind + && !nop_landing_pads.contains(unwind) + { debug!( "cleanup_kinds: {:?}/{:?} registering {:?} as funclet", bb, data, unwind @@ -381,7 +387,7 @@ pub(crate) fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec TerminatorCodegenHelper<'tcx> { } let unwind_block = match unwind { - mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)), + mir::UnwindAction::Cleanup(cleanup) => { + if !fx.nop_landing_pads.contains(cleanup) { + Some(self.llbb_with_cleanup(fx, cleanup)) + } else { + None + } + } mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate(reason) => { @@ -286,7 +292,13 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mergeable_succ: bool, ) -> MergingSucc { let unwind_target = match unwind { - mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)), + mir::UnwindAction::Cleanup(cleanup) => { + if !fx.nop_landing_pads.contains(cleanup) { + Some(self.llbb_with_cleanup(fx, cleanup)) + } else { + None + } + } mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)), mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index fa69820d5d2ed..393fd1e093ad5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -97,6 +97,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// A cold block is a block that is unlikely to be executed at runtime. cold_blocks: IndexVec, + nop_landing_pads: DenseBitSet, + /// The location where each MIR arg/var/tmp/ret is stored. This is /// usually an `PlaceRef` representing an alloca, but not always: /// sometimes we can skip the alloca and just store the value @@ -181,8 +183,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let mut mir = tcx.instance_mir(instance.def); - let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); - debug!("fn_abi: {:?}", fn_abi); + let nop_landing_pads = rustc_mir_transform::remove_noop_landing_pads::find_noop_landing_pads( + mir, + Some(rustc_mir_transform::remove_noop_landing_pads::ExtraInfo { + tcx, + instance, + typing_env: cx.typing_env(), + }), + ); if tcx.features().ergonomic_clones() { let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions( @@ -193,19 +201,23 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( mir = tcx.arena.alloc(optimize_use_clone::(cx, monomorphized_mir)); } + let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); + debug!("fn_abi: {:?}", fn_abi); + let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir); let start_llbb = Bx::append_block(cx, llfn, "start"); let mut start_bx = Bx::build(cx, start_llbb); - if mir.basic_blocks.iter().any(|bb| { - bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate(_))) + if mir::traversal::mono_reachable(&mir, tcx, instance).any(|(bb, block)| { + (block.is_cleanup && !nop_landing_pads.contains(bb)) + || matches!(block.terminator().unwind(), Some(mir::UnwindAction::Terminate(_))) }) { start_bx.set_personality_fn(cx.eh_personality()); } - let cleanup_kinds = - base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(&mir)); + let cleanup_kinds = base::wants_new_eh_instructions(tcx.sess) + .then(|| analyze::cleanup_kinds(&mir, &nop_landing_pads)); let cached_llbbs: IndexVec> = mir.basic_blocks @@ -233,6 +245,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( debug_context, per_local_var_debug_info: None, caller_location: None, + nop_landing_pads, }; // It may seem like we should iterate over `required_consts` to ensure they all successfully @@ -244,7 +257,36 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.compute_per_local_var_debug_info(&mut start_bx).unzip(); fx.per_local_var_debug_info = per_local_var_debug_info; - let traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance); + let mut traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance); + + // Filter out blocks that won't be codegen'd because of nop_landing_pads optimization. + // FIXME: We might want to integrate the nop_landing_pads analysis into mono reachability. + { + let mut reachable = DenseBitSet::new_empty(mir.basic_blocks.len()); + let mut to_visit = vec![mir::START_BLOCK]; + while let Some(next) = to_visit.pop() { + if !reachable.insert(next) { + continue; + } + + let block = &mir.basic_blocks[next]; + if let Some(mir::UnwindAction::Cleanup(target)) = block.terminator().unwind() + && fx.nop_landing_pads.contains(*target) + { + // This edge will not be followed when we actually codegen, so skip generating it here. + // + // It's guaranteed that the cleanup block (`target`) occurs only in + // UnwindAction::Cleanup(...) -- i.e., we can't incorrectly filter too much here -- + // because cleanup transitions must happen via UnwindAction::Cleanup. + to_visit.extend(block.terminator().successors().filter(|s| s != target)); + } else { + to_visit.extend(block.terminator().successors()); + } + } + + traversal_order.retain(|bb| reachable.contains(*bb)); + } + let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order); // Allocate variable and temp allocas diff --git a/compiler/rustc_error_codes/src/error_codes/E0536.md b/compiler/rustc_error_codes/src/error_codes/E0536.md index c081a3d9cfa87..f00d177394489 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0536.md +++ b/compiler/rustc_error_codes/src/error_codes/E0536.md @@ -3,22 +3,20 @@ The `not` cfg-predicate was malformed. Erroneous code example: ```compile_fail,E0536 -#[cfg(not())] // error: expected 1 cfg-pattern -pub fn something() {} - -pub fn main() {} +pub fn main() { + if cfg!(not()) { } +} ``` The `not` predicate expects one cfg-pattern. Example: ``` -#[cfg(not(target_os = "linux"))] // ok! -pub fn something() {} - -pub fn main() {} +pub fn main() { + if cfg!(not(target_os = "linux")) { } // ok! +} ``` -For more information about the `cfg` attribute, read the section on +For more information about the `cfg` macro, read the section on [Conditional Compilation][conditional-compilation] in the Reference. [conditional-compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 757a7e1c8e77f..ce3006c2604ac 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -11,7 +11,7 @@ use rustc_ast::token::MetaVarKind; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; -use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr}; +use rustc_attr_data_structures::{AttributeKind, CfgEntry, Deprecation, Stability, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; @@ -1128,7 +1128,13 @@ pub trait ResolverExpand { /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); - fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem); + fn append_stripped_cfg_item( + &mut self, + parent_node: NodeId, + ident: Ident, + cfg: CfgEntry, + cfg_span: Span, + ); /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 170ac39d1ec34..6922ddfd6bddc 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -11,6 +11,9 @@ use rustc_ast::{ NodeId, NormalAttr, }; use rustc_attr_parsing as attr; +use rustc_attr_parsing::{ + AttributeParser, CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr, +}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, @@ -18,6 +21,7 @@ use rustc_feature::{ }; use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; +use rustc_parse::validate_attr::deny_builtin_meta_unsafety; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -161,7 +165,10 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec attrs .iter() .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)) - .take_while(|attr| !is_cfg(attr) || strip_unconfigured.cfg_true(attr).0) + .take_while(|attr| { + !is_cfg(attr) + || strip_unconfigured.cfg_true(attr, strip_unconfigured.lint_node_id).as_bool() + }) .collect() } @@ -394,26 +401,42 @@ impl<'a> StripUnconfigured<'a> { /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0) + attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr, self.lint_node_id).as_bool()) } - pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option) { - let meta_item = match validate_attr::parse_meta(&self.sess.psess, attr) { - Ok(meta_item) => meta_item, + pub(crate) fn cfg_true(&self, attr: &Attribute, node: NodeId) -> EvalConfigResult { + // We need to run this to do basic validation of the attribute, such as that lits are valid, etc + // FIXME(jdonszelmann) this should not be necessary in the future + match validate_attr::parse_meta(&self.sess.psess, attr) { + Ok(_) => {} Err(err) => { err.emit(); - return (true, None); + return EvalConfigResult::True; } - }; + } - validate_attr::deny_builtin_meta_unsafety(&self.sess.psess, &meta_item); + // Unsafety check needs to be done explicitly here because this attribute will be removed before the normal check + deny_builtin_meta_unsafety( + self.sess.dcx(), + attr.get_normal_item().unsafety, + &rustc_ast::Path::from_ident(attr.ident().unwrap()), + ); + + let Some(cfg) = AttributeParser::parse_single( + self.sess, + attr, + attr.span, + node, + self.features, + true, + parse_cfg_attr, + &CFG_TEMPLATE, + ) else { + // Cfg attribute was not parsable, give up + return EvalConfigResult::True; + }; - ( - parse_cfg(&meta_item, self.sess).is_none_or(|meta_item| { - attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features) - }), - Some(meta_item), - ) + eval_config_entry(self.sess, &cfg, self.lint_node_id, self.features) } /// If attributes are not allowed on expressions, emit an error for `attr` @@ -465,6 +488,7 @@ impl<'a> StripUnconfigured<'a> { } } +/// FIXME: Still used by Rustdoc, should be removed after pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItemInner> { let span = meta_item.span; match meta_item.meta_item_list() { diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 2de09aa1a2802..f99060e9a2165 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -13,6 +13,7 @@ use rustc_ast::{ MetaItemKind, ModKind, NodeId, PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::EvalConfigResult; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::PResult; use rustc_feature::Features; @@ -2166,19 +2167,19 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn expand_cfg_true( &mut self, - node: &mut impl HasAttrs, + node: &mut (impl HasAttrs + HasNodeId), attr: ast::Attribute, pos: usize, - ) -> (bool, Option) { - let (res, meta_item) = self.cfg().cfg_true(&attr); - if res { + ) -> EvalConfigResult { + let res = self.cfg().cfg_true(&attr, node.node_id()); + if res.as_bool() { // A trace attribute left in AST in place of the original `cfg` attribute. // It can later be used by lints or other diagnostics. let trace_attr = attr_into_trace(attr, sym::cfg_trace); node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); } - (res, meta_item) + res } fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) { @@ -2199,20 +2200,21 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { return match self.take_first_attr(&mut node) { Some((attr, pos, derives)) => match attr.name() { Some(sym::cfg) => { - let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos); - if res { - continue; - } - - if let Some(meta_item) = meta_item { - for ident in node.declared_idents() { - self.cx.resolver.append_stripped_cfg_item( - self.cx.current_expansion.lint_node_id, - ident, - meta_item.clone(), - ) + let res = self.expand_cfg_true(&mut node, attr, pos); + match res { + EvalConfigResult::True => continue, + EvalConfigResult::False { reason, reason_span } => { + for ident in node.declared_idents() { + self.cx.resolver.append_stripped_cfg_item( + self.cx.current_expansion.lint_node_id, + ident, + reason.clone(), + reason_span, + ) + } } } + Default::default() } Some(sym::cfg_attr) => { @@ -2291,7 +2293,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { Some((attr, pos, derives)) => match attr.name() { Some(sym::cfg) => { let span = attr.span; - if self.expand_cfg_true(node, attr, pos).0 { + if self.expand_cfg_true(node, attr, pos).as_bool() { continue; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index cb106962be18e..364ad38556be0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -49,7 +49,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } = self.lower_poly_trait_ref( &trait_bound.trait_ref, trait_bound.span, - hir::BoundConstness::Never, + trait_bound.modifiers.constness, hir::BoundPolarity::Positive, dummy_self, &mut user_written_bounds, diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index bc9badbb232e6..db89396d1dc23 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -167,7 +167,7 @@ impl NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { let has_repr_c = matches!( - AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id), + AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id, None), Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC) ); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 8347626080e6c..915c973168802 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -7,7 +7,7 @@ use def_path_hash_map::DefPathHashMapRef; use encoder::EncodeContext; pub use encoder::{EncodedMetadata, encode_metadata, rendered_const}; use rustc_abi::{FieldIdx, ReprOptions, VariantIdx}; -use rustc_ast::expand::StrippedCfgItem; +use rustc_attr_data_structures::StrippedCfgItem; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_hir::PreciseCapturingArgKind; diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index f140ab4755bd2..43a7af9ce3809 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -110,7 +110,7 @@ macro_rules! arena_types { [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, - [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, + [] stripped_cfg_items: rustc_attr_data_structures::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, [decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 842d118449a55..935cc8895740b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -69,8 +69,8 @@ use std::sync::Arc; use rustc_abi::Align; use rustc_arena::TypedArena; -use rustc_ast::expand::StrippedCfgItem; use rustc_ast::expand::allocator::AllocatorKind; +use rustc_attr_data_structures::StrippedCfgItem; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a352ab3fd08a1..6e8f1e8fdd5bf 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -25,10 +25,9 @@ pub use generic_args::{GenericArgKind, TermKind, *}; pub use generics::*; pub use intrinsic::IntrinsicDef; use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx}; -use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; -use rustc_attr_data_structures::{AttributeKind, find_attr}; +use rustc_attr_data_structures::{AttributeKind, StrippedCfgItem, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 3d601fc05338c..dbacbe21edb77 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -87,7 +87,7 @@ trivially_parameterized_over_tcx! { ty::IntrinsicDef, rustc_ast::Attribute, rustc_ast::DelimArgs, - rustc_ast::expand::StrippedCfgItem, + rustc_attr_data_structures::StrippedCfgItem, rustc_attr_data_structures::ConstStability, rustc_attr_data_structures::DefaultBodyStability, rustc_attr_data_structures::Deprecation, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 08f25276cecc1..5088c0cf66b7d 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -159,7 +159,7 @@ declare_passes! { mod prettify : ReorderBasicBlocks, ReorderLocals; mod promote_consts : PromoteTemps; mod ref_prop : ReferencePropagation; - mod remove_noop_landing_pads : RemoveNoopLandingPads; + pub mod remove_noop_landing_pads : RemoveNoopLandingPads; mod remove_place_mention : RemovePlaceMention; mod remove_storage_markers : RemoveStorageMarkers; mod remove_uninit_drops : RemoveUninitDrops; diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index d1c2d6b508f2f..6f61215cee2d6 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -79,6 +79,7 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation { #[instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!(def_id = ?body.source.def_id()); + move_to_copy_pointers(tcx, body); while propagate_ssa(tcx, body) {} } @@ -87,11 +88,43 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation { } } +/// The SSA analysis done by [`SsaLocals`] treats [`Operand::Move`] as a read, even though in +/// general [`Operand::Move`] represents pass-by-pointer where the callee can overwrite the +/// pointee (Miri always considers the place deinitialized). CopyProp has a similar trick to +/// turn [`Operand::Move`] into [`Operand::Copy`] when required for an optimization, but in this +/// pass we just turn all moves of pointers into copies because pointers should be by-value anyway. +fn move_to_copy_pointers<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut visitor = MoveToCopyVisitor { tcx, local_decls: &body.local_decls }; + for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() { + visitor.visit_basic_block_data(bb, data); + } + + struct MoveToCopyVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + local_decls: &'a IndexVec>, + } + + impl<'a, 'tcx> MutVisitor<'tcx> for MoveToCopyVisitor<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) { + if let Operand::Move(place) = *operand { + if place.ty(self.local_decls, self.tcx).ty.is_any_ptr() { + *operand = Operand::Copy(place); + } + } + self.super_operand(operand, loc); + } + } +} + fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let typing_env = body.typing_env(tcx); let ssa = SsaLocals::new(tcx, body, typing_env); - let mut replacer = compute_replacement(tcx, body, &ssa); + let mut replacer = compute_replacement(tcx, body, ssa); debug!(?replacer.targets); debug!(?replacer.allowed_replacements); debug!(?replacer.storage_to_remove); @@ -119,7 +152,7 @@ enum Value<'tcx> { fn compute_replacement<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - ssa: &SsaLocals, + ssa: SsaLocals, ) -> Replacer<'tcx> { let always_live_locals = always_storage_live_locals(body); @@ -138,7 +171,7 @@ fn compute_replacement<'tcx>( // reborrowed references. let mut storage_to_remove = DenseBitSet::new_empty(body.local_decls.len()); - let fully_replaceable_locals = fully_replaceable_locals(ssa); + let fully_replaceable_locals = fully_replaceable_locals(&ssa); // Returns true iff we can use `place` as a pointee. // diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 797056ad52d4a..1bea1269bf660 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,6 +1,7 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty; +use rustc_middle::ty::{Instance, TyCtxt}; use rustc_target::spec::PanicStrategy; use tracing::debug; @@ -41,14 +42,13 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { let mut jumps_folded = 0; let mut landing_pads_removed = 0; - let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); + let nop_landing_pads = find_noop_landing_pads(body, None); // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. - let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); - for bb in postorder { + for (bb, block) in body.basic_blocks_mut().iter_enumerated_mut() { debug!(" processing {:?}", bb); - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { + if let Some(unwind) = block.terminator_mut().unwind_mut() { if let UnwindAction::Cleanup(unwind_bb) = *unwind { if nop_landing_pads.contains(unwind_bb) { debug!(" removing noop landing pad"); @@ -58,19 +58,13 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { } } - body[bb].terminator_mut().successors_mut(|target| { + block.terminator_mut().successors_mut(|target| { if *target != resume_block && nop_landing_pads.contains(*target) { debug!(" folding noop jump to {:?} to resume block", target); *target = resume_block; jumps_folded += 1; } }); - - let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads); - if is_nop_landing_pad { - nop_landing_pads.insert(bb); - } - debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); } debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); @@ -81,65 +75,112 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { } } -impl RemoveNoopLandingPads { - fn is_nop_landing_pad( - &self, - bb: BasicBlock, - body: &Body<'_>, - nop_landing_pads: &DenseBitSet, - ) -> bool { - for stmt in &body[bb].statements { - match &stmt.kind { - StatementKind::FakeRead(..) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::PlaceMention(..) - | StatementKind::AscribeUserType(..) - | StatementKind::Coverage(..) - | StatementKind::ConstEvalCounter - | StatementKind::BackwardIncompatibleDropHint { .. } - | StatementKind::Nop => { - // These are all noops in a landing pad - } +/// This provides extra information that allows further analysis. +/// +/// Used by rustc_codegen_ssa. +pub struct ExtraInfo<'tcx> { + pub tcx: TyCtxt<'tcx>, + pub instance: Instance<'tcx>, + pub typing_env: ty::TypingEnv<'tcx>, +} - StatementKind::Assign(box (place, Rvalue::Use(_) | Rvalue::Discriminant(_))) => { - if place.as_local().is_some() { - // Writing to a local (e.g., a drop flag) does not - // turn a landing pad to a non-nop - } else { - return false; - } - } +pub fn find_noop_landing_pads<'tcx>( + body: &Body<'tcx>, + extra: Option>, +) -> DenseBitSet { + let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); + + // This is a post-order traversal, so that if A post-dominates B + // then A will be visited before B. + let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); + for bb in postorder { + let is_nop_landing_pad = is_nop_landing_pad(bb, body, &nop_landing_pads, extra.as_ref()); + if is_nop_landing_pad { + nop_landing_pads.insert(bb); + } + debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad); + } + + nop_landing_pads +} - StatementKind::Assign { .. } - | StatementKind::SetDiscriminant { .. } - | StatementKind::Deinit(..) - | StatementKind::Intrinsic(..) - | StatementKind::Retag { .. } => { +fn is_nop_landing_pad<'tcx>( + bb: BasicBlock, + body: &Body<'tcx>, + nop_landing_pads: &DenseBitSet, + extra: Option<&ExtraInfo<'tcx>>, +) -> bool { + for stmt in &body[bb].statements { + match &stmt.kind { + StatementKind::FakeRead(..) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::PlaceMention(..) + | StatementKind::AscribeUserType(..) + | StatementKind::Coverage(..) + | StatementKind::ConstEvalCounter + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::Nop => { + // These are all noops in a landing pad + } + + StatementKind::Assign(box (place, Rvalue::Use(_) | Rvalue::Discriminant(_))) => { + if place.as_local().is_some() { + // Writing to a local (e.g., a drop flag) does not + // turn a landing pad to a non-nop + } else { return false; } } + + StatementKind::Assign { .. } + | StatementKind::SetDiscriminant { .. } + | StatementKind::Deinit(..) + | StatementKind::Intrinsic(..) + | StatementKind::Retag { .. } => { + return false; + } + } + } + + let terminator = body[bb].terminator(); + match terminator.kind { + TerminatorKind::Goto { .. } + | TerminatorKind::UnwindResume + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => { + terminator.successors().all(|succ| nop_landing_pads.contains(succ)) } - let terminator = body[bb].terminator(); - match terminator.kind { - TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - terminator.successors().all(|succ| nop_landing_pads.contains(succ)) + TerminatorKind::Drop { place, .. } => { + if let Some(extra) = extra { + let ty = place.ty(body, extra.tcx).ty; + debug!("monomorphize: instance={:?}", extra.instance); + let ty = extra.instance.instantiate_mir_and_normalize_erasing_regions( + extra.tcx, + extra.typing_env, + ty::EarlyBinder::bind(ty), + ); + let drop_fn = Instance::resolve_drop_in_place(extra.tcx, ty); + if let ty::InstanceKind::DropGlue(_, None) = drop_fn.def { + // no need to drop anything, if all of our successors are also no-op then we + // can be skipped. + return terminator.successors().all(|succ| nop_landing_pads.contains(succ)); + } } - TerminatorKind::CoroutineDrop - | TerminatorKind::Yield { .. } - | TerminatorKind::Return - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Unreachable - | TerminatorKind::Call { .. } - | TerminatorKind::TailCall { .. } - | TerminatorKind::Assert { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::InlineAsm { .. } => false, + + false } + + TerminatorKind::CoroutineDrop + | TerminatorKind::Yield { .. } + | TerminatorKind::Return + | TerminatorKind::UnwindTerminate(_) + | TerminatorKind::Call { .. } + | TerminatorKind::TailCall { .. } + | TerminatorKind::Unreachable + | TerminatorKind::Assert { .. } + | TerminatorKind::InlineAsm { .. } => false, } } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 5c748e956a060..e000d61083ddb 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -4,9 +4,9 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId, - Safety, + Path, Safety, }; -use rustc_errors::{Applicability, FatalError, PResult}; +use rustc_errors::{Applicability, DiagCtxtHandle, FatalError, PResult}; use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_session::errors::report_lit_error; use rustc_session::lint::BuiltinLintDiag; @@ -247,14 +247,12 @@ pub fn check_attribute_safety( // Called by `check_builtin_meta_item` and code that manually denies // `unsafe(...)` in `cfg` -pub fn deny_builtin_meta_unsafety(psess: &ParseSess, meta: &MetaItem) { +pub fn deny_builtin_meta_unsafety(diag: DiagCtxtHandle<'_>, unsafety: Safety, name: &Path) { // This only supports denying unsafety right now - making builtin attributes // support unsafety will requite us to thread the actual `Attribute` through // for the nice diagnostics. - if let Safety::Unsafe(unsafe_span) = meta.unsafety { - psess - .dcx() - .emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: meta.path.clone() }); + if let Safety::Unsafe(unsafe_span) = unsafety { + diag.emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: name.clone() }); } } @@ -326,7 +324,7 @@ pub fn check_builtin_meta_item( } if deny_unsafety { - deny_builtin_meta_unsafety(psess, meta); + deny_builtin_meta_unsafety(psess.dcx(), meta.unsafety, &meta.path); } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index e2caf632dd2d5..781e2ce970403 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -132,6 +132,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { &self.resolver.tcx.sess, self.resolver.tcx.features(), Vec::new(), + Early { emit_errors: false }, ); let attrs = parser.parse_attribute_list( &i.attrs, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index b2f16fb1dfba0..93d848787ef4b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,11 +1,10 @@ -use rustc_ast::expand::StrippedCfgItem; use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{ - self as ast, CRATE_NODE_ID, Crate, ItemKind, MetaItemInner, MetaItemKind, ModKind, NodeId, Path, -}; +use rustc_ast::{self as ast, CRATE_NODE_ID, Crate, ItemKind, ModKind, NodeId, Path}; use rustc_ast_pretty::pprust; -use rustc_attr_data_structures::{self as attr, AttributeKind, Stability, find_attr}; +use rustc_attr_data_structures::{ + self as attr, AttributeKind, CfgEntry, Stability, StrippedCfgItem, find_attr, +}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; @@ -2860,17 +2859,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let note = errors::FoundItemConfigureOut { span: ident.span }; err.subdiagnostic(note); - if let MetaItemKind::List(nested) = &cfg.kind - && let MetaItemInner::MetaItem(meta_item) = &nested[0] - && let MetaItemKind::NameValue(feature_name) = &meta_item.kind - { - let note = errors::ItemWasBehindFeature { - feature: feature_name.symbol, - span: meta_item.span, - }; + if let CfgEntry::NameValue { value: Some((feature, _)), .. } = cfg.0 { + let note = errors::ItemWasBehindFeature { feature, span: cfg.1 }; err.subdiagnostic(note); } else { - let note = errors::ItemWasCfgOut { span: cfg.span }; + let note = errors::ItemWasCfgOut { span: cfg.1 }; err.subdiagnostic(note); } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index f8d1b6783e398..c2b3451d4a1d2 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -36,12 +36,12 @@ use late::{ }; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use rustc_arena::{DroplessArena, TypedArena}; -use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; use rustc_ast::{ self as ast, AngleBracketedArg, CRATE_NODE_ID, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, NodeId, Path, attr, }; +use rustc_attr_data_structures::StrippedCfgItem; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5864b035f73df..cbb24289f51ff 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -5,10 +5,9 @@ use std::cell::Cell; use std::mem; use std::sync::Arc; -use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, Crate, NodeId, attr}; use rustc_ast_pretty::pprust; -use rustc_attr_data_structures::StabilityLevel; +use rustc_attr_data_structures::{CfgEntry, StabilityLevel, StrippedCfgItem}; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, @@ -485,8 +484,18 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.proc_macros.push(self.local_def_id(id)) } - fn append_stripped_cfg_item(&mut self, parent_node: NodeId, ident: Ident, cfg: ast::MetaItem) { - self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, ident, cfg }); + fn append_stripped_cfg_item( + &mut self, + parent_node: NodeId, + ident: Ident, + cfg: CfgEntry, + cfg_span: Span, + ) { + self.stripped_cfg_items.push(StrippedCfgItem { + parent_module: parent_node, + ident, + cfg: (cfg, cfg_span), + }); } fn registered_tools(&self) -> &RegisteredTools { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 1a24254d57fbe..59718b0ec427a 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -12,11 +12,11 @@ use rustc_infer::traits::solve::Goal; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::Certainty; use rustc_middle::ty::{ - self, SizedTraitKind, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode, + self, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt as _, TypingMode, }; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; -use crate::traits::{EvaluateConstErr, ObligationCause, specialization_graph}; +use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph}; #[repr(transparent)] pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>); @@ -76,19 +76,11 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< if trait_pred.polarity() == ty::PredicatePolarity::Positive { match self.0.tcx.as_lang_item(trait_pred.def_id()) { - Some(LangItem::Sized) - if self - .resolve_vars_if_possible(trait_pred.self_ty().skip_binder()) - .has_trivial_sizedness(self.0.tcx, SizedTraitKind::Sized) => - { - return Some(Certainty::Yes); - } - Some(LangItem::MetaSized) - if self - .resolve_vars_if_possible(trait_pred.self_ty().skip_binder()) - .has_trivial_sizedness(self.0.tcx, SizedTraitKind::MetaSized) => - { - return Some(Certainty::Yes); + Some(LangItem::Sized) | Some(LangItem::MetaSized) => { + let predicate = self.resolve_vars_if_possible(goal.predicate); + if sizedness_fast_path(self.tcx, predicate, goal.param_env) { + return Some(Certainty::Yes); + } } Some(LangItem::Copy | LangItem::Clone) => { let self_ty = diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 64a51e0550ba1..00b8e93ad5661 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -335,7 +335,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let infcx = self.selcx.infcx; - if sizedness_fast_path(infcx.tcx, obligation.predicate) { + if sizedness_fast_path(infcx.tcx, obligation.predicate, obligation.param_env) { return ProcessResult::Changed(thin_vec![]); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 22eeb285b3766..f24214145bafc 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -15,7 +15,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>, ) -> Option { - if sizedness_fast_path(tcx, key.value.predicate) { + if sizedness_fast_path(tcx, key.value.predicate, key.param_env) { return Some(()); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2e65750db25de..e999f52e88edf 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -596,7 +596,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => self.check_recursion_limit(&obligation, &obligation)?, } - if sizedness_fast_path(self.tcx(), obligation.predicate) { + if sizedness_fast_path(self.tcx(), obligation.predicate, obligation.param_env) { return Ok(EvaluatedToOk); } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 141454bfe3754..b1f7b5951f084 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -364,7 +364,11 @@ impl<'tcx> TypeFolder> for PlaceholderReplacer<'_, 'tcx> { } } -pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>) -> bool { +pub fn sizedness_fast_path<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to // canonicalize and all that for such cases. @@ -382,6 +386,20 @@ pub fn sizedness_fast_path<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc debug!("fast path -- trivial sizedness"); return true; } + + if matches!(trait_pred.self_ty().kind(), ty::Param(_) | ty::Placeholder(_)) { + for clause in param_env.caller_bounds() { + if let ty::ClauseKind::Trait(clause_pred) = clause.kind().skip_binder() + && clause_pred.polarity == ty::PredicatePolarity::Positive + && clause_pred.self_ty() == trait_pred.self_ty() + && (clause_pred.def_id() == trait_pred.def_id() + || (sizedness == SizedTraitKind::MetaSized + && tcx.is_lang_item(clause_pred.def_id(), LangItem::Sized))) + { + return true; + } + } + } } false diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 7771db855d704..819b8e3231ce2 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -24,7 +24,7 @@ fn evaluate_obligation<'tcx>( debug!("evaluate_obligation: goal={:#?}", goal); let ParamEnvAnd { param_env, value: predicate } = goal; - if sizedness_fast_path(tcx, predicate) { + if sizedness_fast_path(tcx, predicate, param_env) { return Ok(EvaluationResult::EvaluatedToOk); } diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 737550eb73e99..9912fad1756ea 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -12,12 +12,6 @@ use crate::{self as ty, Interner}; // avoid heap allocations. type TypeWalkerStack = SmallVec<[::GenericArg; 8]>; -pub struct TypeWalker { - stack: TypeWalkerStack, - last_subtree: usize, - pub visited: SsoHashSet, -} - /// An iterator for walking the type tree. /// /// It's very easy to produce a deeply @@ -26,6 +20,12 @@ pub struct TypeWalker { /// in this situation walker only visits each type once. /// It maintains a set of visited types and /// skips any types that are already there. +pub struct TypeWalker { + stack: TypeWalkerStack, + last_subtree: usize, + pub visited: SsoHashSet, +} + impl TypeWalker { pub fn new(root: I::GenericArg) -> Self { Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 16356f749c92b..62203e132b70c 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -374,9 +374,10 @@ impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] { } #[stable(feature = "index_trait_on_arrays", since = "1.50.0")] -impl Index for [T; N] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const Index for [T; N] where - [T]: Index, + [T]: ~const Index, { type Output = <[T] as Index>::Output; @@ -387,9 +388,10 @@ where } #[stable(feature = "index_trait_on_arrays", since = "1.50.0")] -impl IndexMut for [T; N] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const IndexMut for [T; N] where - [T]: IndexMut, + [T]: ~const IndexMut, { #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 8092fa9eb2fc5..d8489e9a94913 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -55,6 +55,8 @@ #[doc(alias = "]")] #[doc(alias = "[")] #[doc(alias = "[]")] +#[const_trait] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] pub trait Index { /// The returned type after indexing. #[stable(feature = "rust1", since = "1.0.0")] @@ -165,7 +167,9 @@ see chapter in The Book : Index { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +#[const_trait] +pub trait IndexMut: ~const Index { /// Performs the mutable indexing (`container[index]`) operation. /// /// # Panics diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 27b0c6830db61..2ad520b7ead72 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1524,10 +1524,11 @@ impl *const [T] { /// } /// ``` #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "const_index", issue = "143775")] #[inline] - pub unsafe fn get_unchecked(self, index: I) -> *const I::Output + pub const unsafe fn get_unchecked(self, index: I) -> *const I::Output where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { // SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds. unsafe { index.get_unchecked(self) } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 73efdf0445412..579e2461103d8 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1881,10 +1881,11 @@ impl *mut [T] { /// } /// ``` #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "const_index", issue = "143775")] #[inline(always)] - pub unsafe fn get_unchecked_mut(self, index: I) -> *mut I::Output + pub const unsafe fn get_unchecked_mut(self, index: I) -> *mut I::Output where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { // SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds. unsafe { index.get_unchecked_mut(self) } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index c4ca29a367971..62da6567cca75 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1597,10 +1597,11 @@ impl NonNull<[T]> { /// } /// ``` #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "const_index", issue = "143775")] #[inline] - pub unsafe fn get_unchecked_mut(self, index: I) -> NonNull + pub const unsafe fn get_unchecked_mut(self, index: I) -> NonNull where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { // SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds. // As a consequence, the resulting pointer cannot be null. diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 2276112a27bb3..5cd7956291ca2 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -186,14 +186,17 @@ impl IntoBounds for Range { } #[unstable(feature = "new_range_api", issue = "125687")] -impl From> for legacy::Range { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const From> for legacy::Range { #[inline] fn from(value: Range) -> Self { Self { start: value.start, end: value.end } } } + #[unstable(feature = "new_range_api", issue = "125687")] -impl From> for Range { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const From> for Range { #[inline] fn from(value: legacy::Range) -> Self { Self { start: value.start, end: value.end } @@ -362,7 +365,8 @@ impl IntoBounds for RangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -impl From> for legacy::RangeInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const From> for legacy::RangeInclusive { #[inline] fn from(value: RangeInclusive) -> Self { Self::new(value.start, value.end) @@ -506,14 +510,16 @@ impl IntoBounds for RangeFrom { } #[unstable(feature = "new_range_api", issue = "125687")] -impl From> for legacy::RangeFrom { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const From> for legacy::RangeFrom { #[inline] fn from(value: RangeFrom) -> Self { Self { start: value.start } } } #[unstable(feature = "new_range_api", issue = "125687")] -impl From> for RangeFrom { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const From> for RangeFrom { #[inline] fn from(value: legacy::RangeFrom) -> Self { Self { start: value.start } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index f725c3fdd94cc..322b3580eded2 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -6,9 +6,10 @@ use crate::ub_checks::assert_unsafe_precondition; use crate::{ops, range}; #[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for [T] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const ops::Index for [T] where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { type Output = I::Output; @@ -19,9 +20,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::IndexMut for [T] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const ops::IndexMut for [T] where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { #[inline(always)] fn index_mut(&mut self, index: I) -> &mut I::Output { @@ -158,6 +160,8 @@ mod private_slice_index { message = "the type `{T}` cannot be indexed by `{Self}`", label = "slice indices are of type `usize` or ranges of `usize`" )] +#[const_trait] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] pub unsafe trait SliceIndex: private_slice_index::Sealed { /// The output type returned by methods. #[stable(feature = "slice_get_slice", since = "1.28.0")] @@ -208,7 +212,8 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { /// The methods `index` and `index_mut` panic if the index is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for usize { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for usize { type Output = T; #[inline] @@ -278,7 +283,8 @@ unsafe impl SliceIndex<[T]> for usize { /// Because `IndexRange` guarantees `start <= end`, fewer checks are needed here /// than there are for a general `Range` (which might be `100..3`). -unsafe impl SliceIndex<[T]> for ops::IndexRange { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::IndexRange { type Output = [T]; #[inline] @@ -354,7 +360,8 @@ unsafe impl SliceIndex<[T]> for ops::IndexRange { /// - the start of the range is greater than the end of the range or /// - the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::Range { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::Range { type Output = [T]; #[inline] @@ -453,7 +460,8 @@ unsafe impl SliceIndex<[T]> for ops::Range { } #[unstable(feature = "new_range_api", issue = "125687")] -unsafe impl SliceIndex<[T]> for range::Range { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for range::Range { type Output = [T]; #[inline] @@ -491,7 +499,8 @@ unsafe impl SliceIndex<[T]> for range::Range { /// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeTo { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::RangeTo { type Output = [T]; #[inline] @@ -529,7 +538,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeTo { /// The methods `index` and `index_mut` panic if the start of the range is out of bounds. #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeFrom { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::RangeFrom { type Output = [T]; #[inline] @@ -574,7 +584,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeFrom { } #[unstable(feature = "new_range_api", issue = "125687")] -unsafe impl SliceIndex<[T]> for range::RangeFrom { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for range::RangeFrom { type Output = [T]; #[inline] @@ -611,7 +622,8 @@ unsafe impl SliceIndex<[T]> for range::RangeFrom { } #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeFull { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::RangeFull { type Output = [T]; #[inline] @@ -650,7 +662,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeFull { /// - the start of the range is greater than the end of the range or /// - the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex<[T]> for ops::RangeInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::RangeInclusive { type Output = [T]; #[inline] @@ -693,7 +706,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -unsafe impl SliceIndex<[T]> for range::RangeInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for range::RangeInclusive { type Output = [T]; #[inline] @@ -731,7 +745,8 @@ unsafe impl SliceIndex<[T]> for range::RangeInclusive { /// The methods `index` and `index_mut` panic if the end of the range is out of bounds. #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for ops::RangeToInclusive { type Output = [T]; #[inline] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index abcba5a621e23..32419024db953 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -568,9 +568,10 @@ impl [T] { #[rustc_no_implicit_autorefs] #[inline] #[must_use] - pub fn get(&self, index: I) -> Option<&I::Output> + #[rustc_const_unstable(feature = "const_index", issue = "143775")] + pub const fn get(&self, index: I) -> Option<&I::Output> where - I: SliceIndex, + I: ~const SliceIndex, { index.get(self) } @@ -594,9 +595,10 @@ impl [T] { #[rustc_no_implicit_autorefs] #[inline] #[must_use] - pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + #[rustc_const_unstable(feature = "const_index", issue = "143775")] + pub const fn get_mut(&mut self, index: I) -> Option<&mut I::Output> where - I: SliceIndex, + I: ~const SliceIndex, { index.get_mut(self) } @@ -633,9 +635,10 @@ impl [T] { #[inline] #[must_use] #[track_caller] - pub unsafe fn get_unchecked(&self, index: I) -> &I::Output + #[rustc_const_unstable(feature = "const_index", issue = "143775")] + pub const unsafe fn get_unchecked(&self, index: I) -> &I::Output where - I: SliceIndex, + I: ~const SliceIndex, { // SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`; // the slice is dereferenceable because `self` is a safe reference. @@ -677,9 +680,10 @@ impl [T] { #[inline] #[must_use] #[track_caller] - pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output + #[rustc_const_unstable(feature = "const_index", issue = "143775")] + pub const unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output where - I: SliceIndex, + I: ~const SliceIndex, { // SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`; // the slice is dereferenceable because `self` is a safe reference. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index fba3436496e3d..bb0106b9fd940 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -589,8 +589,9 @@ impl str { /// assert!(v.get(..42).is_none()); /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_index", issue = "143775")] #[inline] - pub fn get>(&self, i: I) -> Option<&I::Output> { + pub const fn get>(&self, i: I) -> Option<&I::Output> { i.get(self) } @@ -621,8 +622,9 @@ impl str { /// assert_eq!("HEllo", v); /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_index", issue = "143775")] #[inline] - pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { + pub const fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { i.get_mut(self) } diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index b9559c8317133..42ffc591b5bbf 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -49,9 +49,10 @@ impl PartialOrd for str { } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for str +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const ops::Index for str where - I: SliceIndex, + I: ~const SliceIndex, { type Output = I::Output; @@ -62,9 +63,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::IndexMut for str +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +impl const ops::IndexMut for str where - I: SliceIndex, + I: ~const SliceIndex, { #[inline] fn index_mut(&mut self, index: I) -> &mut I::Output { @@ -92,7 +94,8 @@ const fn str_index_overflow_fail() -> ! { /// /// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::RangeFull { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for ops::RangeFull { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -156,7 +159,8 @@ unsafe impl SliceIndex for ops::RangeFull { /// // &s[3 .. 100]; /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::Range { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for ops::Range { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -260,7 +264,8 @@ unsafe impl SliceIndex for ops::Range { } #[unstable(feature = "new_range_api", issue = "125687")] -unsafe impl SliceIndex for range::Range { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for range::Range { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -431,7 +436,8 @@ unsafe impl SliceIndex for (ops::Bound, ops::Bound) { /// Panics if `end` does not point to the starting byte offset of a /// character (as defined by `is_char_boundary`), or if `end > len`. #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::RangeTo { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for ops::RangeTo { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -499,7 +505,8 @@ unsafe impl SliceIndex for ops::RangeTo { /// Panics if `begin` does not point to the starting byte offset of /// a character (as defined by `is_char_boundary`), or if `begin > len`. #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::RangeFrom { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for ops::RangeFrom { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -554,7 +561,8 @@ unsafe impl SliceIndex for ops::RangeFrom { } #[unstable(feature = "new_range_api", issue = "125687")] -unsafe impl SliceIndex for range::RangeFrom { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for range::RangeFrom { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -625,7 +633,8 @@ unsafe impl SliceIndex for range::RangeFrom { /// to the ending byte offset of a character (`end + 1` is either a starting /// byte offset or equal to `len`), if `begin > end`, or if `end >= len`. #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex for ops::RangeInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for ops::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -662,7 +671,8 @@ unsafe impl SliceIndex for ops::RangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -unsafe impl SliceIndex for range::RangeInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for range::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -713,7 +723,8 @@ unsafe impl SliceIndex for range::RangeInclusive { /// (`end + 1` is either a starting byte offset as defined by /// `is_char_boundary`, or equal to `len`), or if `end >= len`. #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex for ops::RangeToInclusive { +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex for ops::RangeToInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 62ece4b696199..86d23df523816 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -92,6 +92,9 @@ backtrace = [ 'object/rustc-dep-of-std', 'miniz_oxide/rustc-dep-of-std', ] +# Disable symbolization in backtraces. For use with -Zbuild-std. +# FIXME: Ideally this should be an additive backtrace-symbolization feature +backtrace-trace-only = [] panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index efa6a896dad8f..272d0fa4d1a19 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -68,61 +68,67 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: return false; } - let mut hit = false; - backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { - hit = true; - - // `__rust_end_short_backtrace` means we are done hiding symbols - // for now. Print until we see `__rust_begin_short_backtrace`. - if print_fmt == PrintFmt::Short { - if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { - if sym.contains("__rust_end_short_backtrace") { - print = true; - return; - } - if print && sym.contains("__rust_begin_short_backtrace") { - print = false; - return; - } - if !print { - omitted_count += 1; + if cfg!(feature = "backtrace-trace-only") { + const HEX_WIDTH: usize = 2 + 2 * size_of::(); + let frame_ip = frame.ip(); + res = writeln!(bt_fmt.formatter(), "{idx:4}: {frame_ip:HEX_WIDTH$?}"); + } else { + let mut hit = false; + backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { + hit = true; + + // `__rust_end_short_backtrace` means we are done hiding symbols + // for now. Print until we see `__rust_begin_short_backtrace`. + if print_fmt == PrintFmt::Short { + if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { + if sym.contains("__rust_end_short_backtrace") { + print = true; + return; + } + if print && sym.contains("__rust_begin_short_backtrace") { + print = false; + return; + } + if !print { + omitted_count += 1; + } } } - } - if print { - if omitted_count > 0 { - debug_assert!(print_fmt == PrintFmt::Short); - // only print the message between the middle of frames - if !first_omit { - let _ = writeln!( - bt_fmt.formatter(), - " [... omitted {} frame{} ...]", - omitted_count, - if omitted_count > 1 { "s" } else { "" } - ); + if print { + if omitted_count > 0 { + debug_assert!(print_fmt == PrintFmt::Short); + // only print the message between the middle of frames + if !first_omit { + let _ = writeln!( + bt_fmt.formatter(), + " [... omitted {} frame{} ...]", + omitted_count, + if omitted_count > 1 { "s" } else { "" } + ); + } + first_omit = false; + omitted_count = 0; } - first_omit = false; - omitted_count = 0; + res = bt_fmt.frame().symbol(frame, symbol); } - res = bt_fmt.frame().symbol(frame, symbol); + }); + #[cfg(target_os = "nto")] + if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { + if !hit && print { + use crate::backtrace_rs::SymbolName; + res = bt_fmt.frame().print_raw( + frame.ip(), + Some(SymbolName::new("__my_thread_exit".as_bytes())), + None, + None, + ); + } + return false; } - }); - #[cfg(target_os = "nto")] - if libc::__my_thread_exit as *mut libc::c_void == frame.ip() { if !hit && print { - use crate::backtrace_rs::SymbolName; - res = bt_fmt.frame().print_raw( - frame.ip(), - Some(SymbolName::new("__my_thread_exit".as_bytes())), - None, - None, - ); + res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } - return false; - } - if !hit && print { - res = bt_fmt.frame().print_raw(frame.ip(), None, None, None); } idx += 1; diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 290c2eeed44c6..032f5272a9cd4 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -20,6 +20,7 @@ test = { path = "../test", public = true } [features] default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] backtrace = ["std/backtrace"] +backtrace-trace-only = ["std/backtrace-trace-only"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 0039d44785c34..2324d6822c88e 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -697,7 +697,7 @@ impl Config { config.change_id = toml.change_id.inner; let Build { - mut description, + description, build, host, target, @@ -749,7 +749,7 @@ impl Config { compiletest_diff_tool, compiletest_use_stage0_libtest, tidy_extra_checks, - mut ccache, + ccache, exclude, } = toml.build.unwrap_or_default(); @@ -942,7 +942,7 @@ impl Config { config.rust_profile_use = flags_rust_profile_use; config.rust_profile_generate = flags_rust_profile_generate; - config.apply_rust_config(toml.rust, flags_warnings, &mut description); + config.apply_rust_config(toml.rust, flags_warnings); config.reproducible_artifacts = flags_reproducible_artifact; config.description = description; @@ -963,7 +963,7 @@ impl Config { config.channel = channel; } - config.apply_llvm_config(toml.llvm, &mut ccache); + config.apply_llvm_config(toml.llvm); config.apply_gcc_config(toml.gcc); diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs index 4774e202bd83f..1f0cecd145c77 100644 --- a/src/bootstrap/src/core/config/toml/llvm.rs +++ b/src/bootstrap/src/core/config/toml/llvm.rs @@ -17,8 +17,6 @@ define_config! { tests: Option = "tests", enzyme: Option = "enzyme", plugins: Option = "plugins", - // FIXME: Remove this field at Q2 2025, it has been replaced by build.ccache - ccache: Option = "ccache", static_libstdcpp: Option = "static-libstdcpp", libzstd: Option = "libzstd", ninja: Option = "ninja", @@ -97,7 +95,6 @@ pub fn check_incompatible_options_for_ci_llvm( assertions: _, tests: _, plugins, - ccache: _, static_libstdcpp: _, libzstd, ninja: _, @@ -149,11 +146,7 @@ pub fn check_incompatible_options_for_ci_llvm( } impl Config { - pub fn apply_llvm_config( - &mut self, - toml_llvm: Option, - ccache: &mut Option, - ) { + pub fn apply_llvm_config(&mut self, toml_llvm: Option) { let mut llvm_tests = None; let mut llvm_enzyme = None; let mut llvm_offload = None; @@ -168,7 +161,6 @@ impl Config { tests, enzyme, plugins, - ccache: llvm_ccache, static_libstdcpp, libzstd, ninja, @@ -191,13 +183,7 @@ impl Config { download_ci_llvm, build_config, } = llvm; - if llvm_ccache.is_some() { - eprintln!("Warning: llvm.ccache is deprecated. Use build.ccache instead."); - } - if ccache.is_none() { - *ccache = llvm_ccache; - } set(&mut self.ninja_in_file, ninja); llvm_tests = tests; llvm_enzyme = enzyme; diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 0fae235bb93fb..307aa52294baf 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -36,8 +36,6 @@ define_config! { incremental: Option = "incremental", default_linker: Option = "default-linker", channel: Option = "channel", - // FIXME: Remove this field at Q2 2025, it has been replaced by build.description - description: Option = "description", musl_root: Option = "musl-root", rpath: Option = "rpath", strip: Option = "strip", @@ -320,7 +318,6 @@ pub fn check_incompatible_options_for_ci_rustc( jemalloc, rpath, channel, - description, default_linker, std_features, @@ -388,7 +385,6 @@ pub fn check_incompatible_options_for_ci_rustc( err!(current_rust_config.std_features, std_features, "rust"); warn!(current_rust_config.channel, channel, "rust"); - warn!(current_rust_config.description, description, "rust"); Ok(()) } @@ -415,12 +411,7 @@ pub(crate) fn validate_codegen_backends(backends: Vec, section: &str) -> } impl Config { - pub fn apply_rust_config( - &mut self, - toml_rust: Option, - warnings: Warnings, - description: &mut Option, - ) { + pub fn apply_rust_config(&mut self, toml_rust: Option, warnings: Warnings) { let mut debug = None; let mut rustc_debug_assertions = None; let mut std_debug_assertions = None; @@ -459,7 +450,6 @@ impl Config { randomize_layout, default_linker, channel: _, // already handled above - description: rust_description, musl_root, rpath, verbose_tests, @@ -552,14 +542,6 @@ impl Config { set(&mut self.jemalloc, jemalloc); set(&mut self.test_compare_mode, test_compare_mode); set(&mut self.backtrace, backtrace); - if rust_description.is_some() { - eprintln!( - "Warning: rust.description is deprecated. Use build.description instead." - ); - } - if description.is_none() { - *description = rust_description; - } set(&mut self.rust_dist_src, dist_src); set(&mut self.verbose_tests, verbose_tests); // in the case "false" is set explicitly, do not overwrite the command line args diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index dcafeb80f90ca..2569f95e3efad 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -221,10 +221,15 @@ fn default_compiler( } t if t.contains("-wasi") => { - let root = build - .wasi_sdk_path - .as_ref() - .expect("WASI_SDK_PATH mut be configured for a -wasi target"); + let root = if let Some(path) = build.wasi_sdk_path.as_ref() { + path + } else { + if build.config.is_running_on_ci { + panic!("ERROR: WASI_SDK_PATH must be configured for a -wasi target on CI"); + } + println!("WARNING: WASI_SDK_PATH not set, using default cc/cxx compiler"); + return None; + }; let compiler = match compiler { Language::C => format!("{t}-clang"), Language::CPlusPlus => format!("{t}-clang++"), diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 1f6ed8129da8b..47ac841d595bf 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -476,4 +476,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Option `tool.TOOL_NAME.features` now works on any subcommand, not just `build`.", }, + ChangeInfo { + change_id: 143926, + severity: ChangeSeverity::Warning, + summary: "Removed `rust.description` and `llvm.ccache` as it was deprecated in #137723 and #136941 long time ago.", + }, ]; diff --git a/src/doc/book b/src/doc/book index ef1ce8f87a8b1..b2d1a0821e12a 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit ef1ce8f87a8b18feb1b6a9cf9a4939a79bde6795 +Subproject commit b2d1a0821e12a676b496d61891b8e3d374a8e832 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index 41f688a598a50..fe88fbb68391a 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit 41f688a598a5022b749e23d37f3c524f6a0b28e1 +Subproject commit fe88fbb68391a465680dd91109f0a151a1676f3e diff --git a/src/doc/nomicon b/src/doc/nomicon index 8b61acfaea822..3ff384320598b 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 8b61acfaea822e9ac926190bc8f15791c33336e8 +Subproject commit 3ff384320598bbe8d8cfe5cb8f18f78a3a3e6b15 diff --git a/src/doc/reference b/src/doc/reference index e9fc99f107840..1f45bd41fa6c1 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit e9fc99f107840813916f62e16b3f6d9556e1f2d8 +Subproject commit 1f45bd41fa6c17b7c048ed6bfe5f168c4311206a diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 288b4e4948add..e386be5f44af7 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 288b4e4948add43f387cad35adc7b1c54ca6fe12 +Subproject commit e386be5f44af711854207c11fdd61bb576270b04 diff --git a/src/tools/clippy/.github/workflows/feature_freeze.yml b/src/tools/clippy/.github/workflows/feature_freeze.yml index a5f8d4bc145cc..7ad58af77d4ad 100644 --- a/src/tools/clippy/.github/workflows/feature_freeze.yml +++ b/src/tools/clippy/.github/workflows/feature_freeze.yml @@ -1,7 +1,11 @@ name: Feature freeze check on: - pull_request: + pull_request_target: + types: + - opened + branches: + - master paths: - 'clippy_lints/src/declared_lints.rs' @@ -9,6 +13,12 @@ jobs: auto-comment: runs-on: ubuntu-latest + permissions: + pull-requests: write + + # Do not in any case add code that runs anything coming from the the content + # of the pull request, as malicious code would be able to access the private + # GitHub token. steps: - name: Check PR Changes id: pr-changes @@ -19,7 +29,7 @@ jobs: run: | # Use GitHub API to create a comment on the PR PR_NUMBER=${{ github.event.pull_request.number }} - COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to August 1st and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team" + COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team\n\n@rustbot note Feature-freeze\n@rustbot blocked\n@rustbot label +A-lint\n" GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}" diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 70c805903d36e..003d03957399e 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -128,21 +128,27 @@ jobs: - name: Download JSON uses: actions/download-artifact@v4 + - name: Store PR number + run: echo ${{ github.event.pull_request.number }} > pr.txt + - name: Diff results - # GH's summery has a maximum size of 1024k: - # https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary - # That's why we first log to file and then to the summary and logs + # GH's summery has a maximum size of 1MiB: + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#step-isolation-and-limits + # We upload the full diff as an artifact in case it's truncated run: | - ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate >> truncated_diff.md - head -c 1024000 truncated_diff.md >> $GITHUB_STEP_SUMMARY - cat truncated_diff.md - ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json >> full_diff.md + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate | head -c 1M > $GITHUB_STEP_SUMMARY + ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --write-summary summary.json > full_diff.md - name: Upload full diff uses: actions/upload-artifact@v4 with: - name: diff - if-no-files-found: ignore + name: full_diff + path: full_diff.md + + - name: Upload summary + uses: actions/upload-artifact@v4 + with: + name: summary path: | - full_diff.md - truncated_diff.md + summary.json + pr.txt diff --git a/src/tools/clippy/.github/workflows/lintcheck_summary.yml b/src/tools/clippy/.github/workflows/lintcheck_summary.yml new file mode 100644 index 0000000000000..52f52e155a07b --- /dev/null +++ b/src/tools/clippy/.github/workflows/lintcheck_summary.yml @@ -0,0 +1,106 @@ +name: Lintcheck summary + +# The workflow_run event runs in the context of the Clippy repo giving it write +# access, needed here to create a PR comment when the PR originates from a fork +# +# The summary artifact is a JSON file that we verify in this action to prevent +# the creation of arbitrary comments +# +# This action must not checkout/run code from the originating workflow_run +# or directly interpolate ${{}} untrusted fields into code +# +# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run +# https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections + +on: + workflow_run: + workflows: [Lintcheck] + types: [completed] + +# Restrict the default permission scope https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defining-access-for-the-github_token-scopes +permissions: + pull-requests: write + +jobs: + download: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: summary + path: untrusted + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: Format comment + uses: actions/github-script@v7 + with: + script: | + const fs = require("fs"); + const assert = require("assert/strict"); + + function validateName(s) { + assert.match(s, /^[a-z0-9_:]+$/); + return s; + } + + function validateInt(i) { + assert.ok(Number.isInteger(i)); + return i; + } + + function tryReadSummary() { + try { + return JSON.parse(fs.readFileSync("untrusted/summary.json")); + } catch { + return null; + } + } + + const prNumber = parseInt(fs.readFileSync("untrusted/pr.txt"), 10); + core.exportVariable("PR", prNumber.toString()); + + const untrustedSummary = tryReadSummary(); + if (!Array.isArray(untrustedSummary)) { + return; + } + + let summary = `Lintcheck changes for ${context.payload.workflow_run.head_sha} + + | Lint | Added | Removed | Changed | + | ---- | ----: | ------: | ------: | + `; + + for (const untrustedRow of untrustedSummary) { + const name = validateName(untrustedRow.name); + + const added = validateInt(untrustedRow.added); + const removed = validateInt(untrustedRow.removed); + const changed = validateInt(untrustedRow.changed); + + const id = name.replace("clippy::", "user-content-").replaceAll("_", "-"); + const url = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.payload.workflow_run.id}#${id}`; + + summary += `| [\`${name}\`](${url}) | ${added} | ${removed} | ${changed} |\n`; + } + + summary += "\nThis comment will be updated if you push new changes"; + + fs.writeFileSync("summary.md", summary); + + - name: Create/update comment + run: | + if [[ -f summary.md ]]; then + gh pr comment "$PR" --body-file summary.md --edit-last --create-if-none + else + # There were no changes detected by Lintcheck + # - If a comment exists from a previous run that did detect changes, edit it (--edit-last) + # - If no comment exists do not create one, the `gh` command exits with an error which + # `|| true` ignores + gh pr comment "$PR" --body "No changes for ${{ github.event.workflow_run.head_sha }}" --edit-last || true + fi + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 1278427b5a768..f25f9ab54dd29 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -28,13 +28,13 @@ declare_clippy_lint = { path = "declare_clippy_lint" } rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } clippy_lints_internal = { path = "clippy_lints_internal", optional = true } tempfile = { version = "3.20", optional = true } -termize = "0.1" +termize = "0.2" color-print = "0.3.4" anstream = "0.6.18" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.29.2" +ui_test = "0.30.2" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" @@ -45,14 +45,6 @@ itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } -# UI test dependencies -if_chain = "1.0" -quote = "1.0.25" -syn = { version = "2.0", features = ["full"] } -futures = "0.3" -parking_lot = "0.12" -tokio = { version = "1", features = ["io-util"] } - [build-dependencies] rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" } diff --git a/src/tools/clippy/book/src/development/infrastructure/backport.md b/src/tools/clippy/book/src/development/infrastructure/backport.md index 9526d8af1c9dc..47ea6a412c503 100644 --- a/src/tools/clippy/book/src/development/infrastructure/backport.md +++ b/src/tools/clippy/book/src/development/infrastructure/backport.md @@ -109,4 +109,4 @@ worth backporting this. When a PR is backported to Rust `beta`, label the PR with `beta-accepted`. This will then get picked up when [writing the changelog]. -[writing the changelog]: changelog_update.md#31-include-beta-accepted-prs +[writing the changelog]: changelog_update.md#4-include-beta-accepted-prs diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md index eede6b78d9216..c96ff228b01af 100644 --- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md +++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md @@ -38,7 +38,7 @@ Usually you want to write the changelog of the **upcoming stable release**. Make sure though, that `beta` was already branched in the Rust repository. To find the commit hash, issue the following command when in a `rust-lang/rust` -checkout: +checkout (most of the time on the `upstream/beta` branch): ``` git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" ``` @@ -48,16 +48,13 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into Once you've got the correct commit range, run ``` -util/fetch_prs_between.sh commit1 commit2 > changes.txt +util/fetch_prs_between.sh start_commit end_commit > changes.txt ``` -where `commit2` is the commit hash from the previous command and `commit1` -is the commit hash from the current CHANGELOG file. +where `end_commit` is the commit hash from the previous command and `start_commit` +is [the commit hash][beta_section] from the current CHANGELOG file. Open `changes.txt` file in your editor of choice. -When updating the changelog it's also a good idea to make sure that `commit1` is -already correct in the current changelog. - ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you @@ -70,17 +67,7 @@ With the PRs filtered, you can start to take each PR and move the `changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it somewhat coherent. -The order should roughly be: - -1. New lints -2. Moves or deprecations of lints -3. Changes that expand what code existing lints cover -4. False positive fixes -5. ICE fixes -6. Documentation improvements -7. Others - -As section headers, we use: +The sections order should roughly be: ``` ### New Lints @@ -97,10 +84,10 @@ As section headers, we use: ### Others ``` -Please also be sure to update the Beta/Unreleased sections at the top with the -relevant commit ranges. +Please also be sure to update [the `Unreleased/Beta/In Rust Nightly` section][beta_section] at the top with the +relevant commits ranges and to add the `Rust ` section with release date and PR ranges. -#### 3.1 Include `beta-accepted` PRs +### 4. Include `beta-accepted` PRs Look for the [`beta-accepted`] label and make sure to also include the PRs with that label in the changelog. If you can, remove the `beta-accepted` labels @@ -109,7 +96,7 @@ that label in the changelog. If you can, remove the `beta-accepted` labels > _Note:_ Some of those PRs might even get backported to the previous `beta`. > Those have to be included in the changelog of the _previous_ release. -### 4. Update `clippy::version` attributes +### 5. Update `clippy::version` attributes Next, make sure to check that the `#[clippy::version]` attributes for the added lints contain the correct version. @@ -129,3 +116,4 @@ written for. If not, update the version to the changelog version. [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ +[beta_section]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#unreleased--beta--in-rust-nightly diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index e9b7f42a18310..992ed2c6aaaaa 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -892,6 +892,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) +* [`zero_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr) ## `pass-by-value-size-limit` diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 841facdca06db..555f54bcfb8b8 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -794,6 +794,7 @@ define_Conf! { unnested_or_patterns, unused_trait_names, use_self, + zero_ptr, )] msrv: Msrv = Msrv::default(), /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index c1b6b37070696..bd9e57c9f6dac 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -223,7 +223,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> { if check { return Err(Error::CheckFailed); } - fs::write(path, new_text.as_bytes())?; + fs::write(path, new_text)?; } Ok(()) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 3361443196ab5..40aadf4589a77 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -2,7 +2,6 @@ rustc_private, exit_status_error, if_let_guard, - let_chains, os_str_slice, os_string_truncate, slice_split_once diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 3a2bad6187c4d..07df893ae3ca1 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -268,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { .tcx .hir_attrs(item.hir_id()) .iter() - .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr{ .. }))) + .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr { .. }))) { // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API. return; 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 index ad0a4f8cdf35a..e3b125a8d5b9d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs @@ -18,7 +18,8 @@ pub(super) fn check<'tcx>( cast_to: &'tcx Ty<'_>, msrv: Msrv, ) -> bool { - if matches!(cast_to.kind, TyKind::Ptr(_)) + if let TyKind::Ptr(target) = cast_to.kind + && !matches!(target.ty.kind, TyKind::TraitObject(..)) && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index a2ecb5fb44ae0..2eebe84923274 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; -use clippy_utils::{expr_or_init, sym}; +use clippy_utils::{expr_or_init, is_in_const_context, sym}; use rustc_abi::IntegerType; use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; @@ -168,7 +168,9 @@ pub(super) fn check( span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, msg, |diag| { diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ..."); - if !cast_from.is_floating_point() { + // TODO: Remove the condition for const contexts when `try_from` and other commonly used methods + // become const fn. + if !is_in_const_context(cx) && !cast_from.is_floating_point() { offer_suggestion(cx, expr, cast_expr, cast_to_span, diag); } }); diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index daae9a8bb0850..37accff5eaa81 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -878,7 +878,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); - zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); + zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv); if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index a34af6bc226c4..f4738e7b0d51d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::SpanRangeExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; @@ -7,10 +8,10 @@ use rustc_lint::LateContext; use super::ZERO_PTR; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>, msrv: Msrv) { if let TyKind::Ptr(ref mut_ty) = to.kind && is_integer_literal(from, 0) - && !is_in_const_context(cx) + && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::PTR_NULL)) && let Some(std_or_core) = std_or_core(cx) { let (msg, sugg_fn) = match mut_ty.mutbl { diff --git a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs index 2b659253763fd..6217fc4c8977c 100644 --- a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs +++ b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; +use clippy_utils::sugg::{self, Sugg}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt}; use rustc_session::declare_lint_pass; @@ -49,23 +50,18 @@ declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - // If this expression has an effective type of `&dyn Any` ... - { - let coerced_ty = cx.typeck_results().expr_ty_adjusted(e); - - let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else { - return; - }; - if !is_dyn_any(cx.tcx, coerced_ref_ty) { - return; - } + // If this expression was coerced to `&dyn Any` ... + if !cx.typeck_results().expr_adjustments(e).last().is_some_and(|adj| { + matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)) && is_ref_dyn_any(cx.tcx, adj.target) + }) { + return; } let expr_ty = cx.typeck_results().expr_ty(e); let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else { return; }; - // ... but only due to coercion ... + // ... but it's not actually `&dyn Any` ... if is_dyn_any(cx.tcx, expr_ref_ty) { return; } @@ -78,23 +74,37 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { } // ... that's probably not intended. - let (span, deref_count) = match e.kind { + let (target_expr, deref_count) = match e.kind { // If `e` was already an `&` expression, skip `*&` in the suggestion - ExprKind::AddrOf(_, _, referent) => (referent.span, depth), - _ => (e.span, depth + 1), + ExprKind::AddrOf(_, _, referent) => (referent, depth), + _ => (e, depth + 1), }; + let ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else { + return; + }; + let sugg = sugg::make_unop( + &format!("{}{}", mutability.ref_prefix_str(), str::repeat("*", deref_count)), + Sugg::hir(cx, target_expr, ".."), + ); span_lint_and_sugg( cx, COERCE_CONTAINER_TO_ANY, e.span, - format!("coercing `{expr_ty}` to `&dyn Any`"), + format!("coercing `{expr_ty}` to `{}dyn Any`", mutability.ref_prefix_str()), "consider dereferencing", - format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")), + sugg.to_string(), Applicability::MaybeIncorrect, ); } } +fn is_ref_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + let ty::Ref(_, ref_ty, _) = *ty.kind() else { + return false; + }; + is_dyn_any(tcx, ref_ty) +} + fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { let ty::Dynamic(traits, ..) = ty.kind() else { return false; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 5c64216dd92ca..d5d937d913385 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -14,18 +14,25 @@ use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does - /// Checks for methods with high cognitive complexity. + /// We used to think it measured how hard a method is to understand. /// /// ### Why is this bad? - /// Methods of high cognitive complexity tend to be hard to - /// both read and maintain. Also LLVM will tend to optimize small methods better. + /// Ideally, we would like to be able to measure how hard a function is + /// to understand given its context (what we call its Cognitive Complexity). + /// But that's not what this lint does. See "Known problems" /// /// ### Known problems - /// Sometimes it's hard to find a way to reduce the - /// complexity. + /// The true Cognitive Complexity of a method is not something we can + /// calculate using modern technology. This lint has been left in the + /// `nursery` so as to not mislead users into using this lint as a + /// measurement tool. /// - /// ### Example - /// You'll see it when you get the warning. + /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) + /// + /// ### Lints to consider instead of this + /// + /// * [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) + /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines) #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index d1a8590c59b4d..cf964d4b58044 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -89,6 +89,10 @@ impl EarlyLintPass for DisallowedScriptIdents { // Fast path for ascii-only idents. if !symbol_str.is_ascii() && let Some(script) = symbol_str.chars().find_map(|c| { + if c.is_ascii() { + return None; + } + c.script_extension() .iter() .find(|script| !self.whitelist.contains(script)) diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index 9ee32fced8c44..3033ac0d0b0b7 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty}; +use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -99,13 +99,16 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { let mut panic_span = None; let typeck = cx.tcx.typeck_body(body_id); for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + if is_inside_always_const_context(cx.tcx, expr.hir_id) { + return ControlFlow::::Continue(()); + } + if let Some(macro_call) = root_macro_call_first_node(cx, expr) && (is_panic(cx, macro_call.def_id) || matches!( cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro) )) - && !cx.tcx.hir_is_inside_const_context(expr.hir_id) && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id]) && panic_span.is_none() { diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 5ea55e102dfe2..2bf52216b832a 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1249,7 +1249,9 @@ fn looks_like_refdef(doc: &str, range: Range) -> Option> { b'[' => { start = Some(i + offset); }, - b']' if let Some(start) = start => { + b']' if let Some(start) = start + && doc.as_bytes().get(i + offset + 1) == Some(&b':') => + { return Some(start..i + offset + 1); }, _ => {}, diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 7dda3e0fdb91c..8ad092790718b 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_attr_data_structures::AttributeKind; -use rustc_attr_data_structures::find_attr; - declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs index cc8e4d7d9e281..487db69027aff 100644 --- a/src/tools/clippy/clippy_lints/src/exit.rs +++ b/src/tools/clippy/clippy_lints/src/exit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_entrypoint_fn; use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -7,7 +6,8 @@ use rustc_span::sym; declare_clippy_lint! { /// ### What it does - /// Detects calls to the `exit()` function which terminates the program. + /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()` + /// immediately terminate the program. /// /// ### Why restrict this? /// `exit()` immediately terminates the program with no information other than an exit code. @@ -15,11 +15,24 @@ declare_clippy_lint! { /// /// Codebases may use this lint to require that all exits are performed either by panicking /// (which produces a message, a code location, and optionally a backtrace) - /// or by returning from `main()` (which is a single place to look). + /// or by calling `exit()` from `main()` (which is a single place to look). /// - /// ### Example + /// ### Good example /// ```no_run - /// std::process::exit(0) + /// fn main() { + /// std::process::exit(0); + /// } + /// ``` + /// + /// ### Bad example + /// ```no_run + /// fn main() { + /// other_function(); + /// } + /// + /// fn other_function() { + /// std::process::exit(0); + /// } /// ``` /// /// Use instead: @@ -36,7 +49,7 @@ declare_clippy_lint! { #[clippy::version = "1.41.0"] pub EXIT, restriction, - "detects `std::process::exit` calls" + "detects `std::process::exit` calls outside of `main`" } declare_lint_pass!(Exit => [EXIT]); @@ -48,10 +61,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) && let parent = cx.tcx.hir_get_parent_item(e.hir_id) - && let OwnerNode::Item(Item{kind: ItemKind::Fn{ .. }, ..}) = cx.tcx.hir_owner_node(parent) - // If the next item up is a function we check if it is an entry point + && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent) + // If the next item up is a function we check if it isn't named "main" // and only then emit a linter warning - && !is_entrypoint_fn(cx, parent.to_def_id()) + + // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before, + // in compilation contexts like --all-targets (which include --tests), you get false positives + // because in a test context, main is not the entrypoint function + && ident.name != sym::main { span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } 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 b3c9e8607589c..d5abaa547e8ec 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -2,8 +2,8 @@ use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, - numeric_literal, peel_blocks, sugg, sym, + eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context, + is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym, }; use rustc_ast::ast; use rustc_errors::Applicability; @@ -455,7 +455,6 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&' None } -// TODO: Fix rust-lang/rust-clippy#4735 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Binary( Spanned { @@ -491,6 +490,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { return; }; + // Check if any variable in the expression has an ambiguous type (could be f32 or f64) + // see: https://github.com/rust-lang/rust-clippy/issues/14897 + if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _))) + && has_ambiguous_literal_in_expr(cx, recv) + { + return; + } + span_lint_and_sugg( cx, SUBOPTIMAL_FLOPS, diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs index 823cf0f43221c..e809987d75a00 100644 --- a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs @@ -1,11 +1,22 @@ use super::EMPTY_LOOP; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_in_panic_handler, is_no_std_crate}; +use clippy_utils::{is_in_panic_handler, is_no_std_crate, sym}; -use rustc_hir::{Block, Expr}; +use rustc_hir::{Block, Expr, ItemKind, Node}; use rustc_lint::LateContext; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { + let parent_hir_id = cx.tcx.parent_hir_id(expr.hir_id); + if let Node::Item(parent_node) = cx.tcx.hir_node(parent_hir_id) + && matches!(parent_node.kind, ItemKind::Fn { .. }) + && let attrs = cx.tcx.hir_attrs(parent_hir_id) + && attrs.iter().any(|attr| attr.has_name(sym::rustc_intrinsic)) + { + // Intrinsic functions are expanded into an empty loop when lowering the AST + // to simplify the job of later passes which might expect any function to have a body. + return; + } + if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 9ff82cdcb6648..1f9a943f13dcd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -245,17 +245,36 @@ fn replace_in_pattern( } match pat.kind { - PatKind::Binding(_ann, _id, binding_name, opt_subpt) => { - let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else { - break 'a; - }; - let sn_pfx = binding_mode.prefix_str(); - let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); - if let Some(subpt) = opt_subpt { - let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); - return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + PatKind::Binding(ann, _id, binding_name, opt_subpt) => { + match (ident_map.get(&binding_name.name), opt_subpt) { + (Some((pat_to_put, binding_mode)), opt_subpt) => { + let sn_pfx = binding_mode.prefix_str(); + let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app); + if let Some(subpt) = opt_subpt { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + return format!("{sn_pfx}{sn_ptp} @ {subpt}"); + } + return format!("{sn_pfx}{sn_ptp}"); + }, + (None, Some(subpt)) => { + let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false); + // scanning for a value that matches is not sensitive to order + #[expect(rustc::potential_query_instability)] + if ident_map.values().any(|(other_pat, _)| { + if let PatKind::Binding(_, _, other_name, _) = other_pat.kind { + other_name == binding_name + } else { + false + } + }) { + // this name is shadowed, and, therefore, not usable + return subpt; + } + let binding_pfx = ann.prefix_str(); + return format!("{binding_pfx}{binding_name} @ {subpt}"); + }, + (None, None) => break 'a, } - return format!("{sn_pfx}{sn_ptp}"); }, PatKind::Or(pats) => { let patterns = pats diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 51696b388800f..2d52a93f34e3e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -4,16 +4,15 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_indent; use itertools::Itertools; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; -use rustc_attr_data_structures::find_attr; -use rustc_attr_data_structures::AttributeKind; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index ede68f3094138..ae277da089fd2 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -332,9 +332,7 @@ impl<'a> NormalizedPat<'a> { // TODO: Handle negative integers. They're currently treated as a wild match. PatExprKind::Lit { lit, negated: false } => match lit.node { LitKind::Str(sym, _) => Self::LitStr(sym), - LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => { - Self::LitBytes(byte_sym) - } + LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => Self::LitBytes(byte_sym), LitKind::Byte(val) => Self::LitInt(val.into()), LitKind::Char(val) => Self::LitInt(val.into()), LitKind::Int(val, _) => Self::LitInt(val.get()), diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs index 4a61c223d2c1e..93325ca488e4e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,14 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, sym}; +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{Span, Symbol}; use super::MANUAL_IS_VARIANT_AND; @@ -62,54 +64,154 @@ pub(super) fn check( ); } -fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) { - if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo())) - && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3))) - { - span_lint_and_sugg( - cx, - MANUAL_IS_VARIANT_AND, - parent.span, - format!( - "called `.map() {}= {}()`", - if op == BinOpKind::Eq { '=' } else { '!' }, - if is_option { "Some" } else { "Ok" }, - ), - "use", - if is_option && op == BinOpKind::Ne { - format!("{before_map_snippet}is_none_or{after_map_snippet}",) - } else { +#[derive(Clone, Copy, PartialEq)] +enum Flavor { + Option, + Result, +} + +impl Flavor { + const fn symbol(self) -> Symbol { + match self { + Self::Option => sym::Option, + Self::Result => sym::Result, + } + } + + const fn positive(self) -> Symbol { + match self { + Self::Option => sym::Some, + Self::Result => sym::Ok, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Op { + Eq, + Ne, +} + +impl std::fmt::Display for Op { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Eq => write!(f, "=="), + Self::Ne => write!(f, "!="), + } + } +} + +impl TryFrom for Op { + type Error = (); + fn try_from(op: BinOpKind) -> Result { + match op { + BinOpKind::Eq => Ok(Self::Eq), + BinOpKind::Ne => Ok(Self::Ne), + _ => Err(()), + } + } +} + +/// Represents the argument of the `.map()` function, as a closure or as a path +/// in case η-reduction is used. +enum MapFunc<'hir> { + Closure(&'hir Closure<'hir>), + Path(&'hir Expr<'hir>), +} + +impl<'hir> TryFrom<&'hir Expr<'hir>> for MapFunc<'hir> { + type Error = (); + + fn try_from(expr: &'hir Expr<'hir>) -> Result { + match expr.kind { + ExprKind::Closure(closure) => Ok(Self::Closure(closure)), + ExprKind::Path(_) => Ok(Self::Path(expr)), + _ => Err(()), + } + } +} + +impl<'hir> MapFunc<'hir> { + /// Build a suggestion suitable for use in a `.map()`-like function. η-expansion will be applied + /// as needed. + fn sugg(self, cx: &LateContext<'hir>, invert: bool, app: &mut Applicability) -> String { + match self { + Self::Closure(closure) => { + let body = Sugg::hir_with_applicability(cx, cx.tcx.hir_body(closure.body).value, "..", app); format!( - "{}{before_map_snippet}{}{after_map_snippet}", - if op == BinOpKind::Eq { "" } else { "!" }, - if is_option { "is_some_and" } else { "is_ok_and" }, + "{} {}", + snippet_with_applicability(cx, closure.fn_decl_span, "|..|", app), + if invert { !body } else { body } ) }, - Applicability::MachineApplicable, - ); + Self::Path(expr) => { + let path = snippet_with_applicability(cx, expr.span, "_", app); + if invert { + format!("|x| !{path}(x)") + } else { + path.to_string() + } + }, + } } } +fn emit_lint<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + op: Op, + flavor: Flavor, + in_some_or_ok: bool, + map_func: MapFunc<'tcx>, + recv: &Expr<'_>, +) { + let mut app = Applicability::MachineApplicable; + let recv = snippet_with_applicability(cx, recv.span, "_", &mut app); + + let (invert_expr, method, invert_body) = match (flavor, op) { + (Flavor::Option, Op::Eq) => (false, "is_some_and", !in_some_or_ok), + (Flavor::Option, Op::Ne) => (false, "is_none_or", in_some_or_ok), + (Flavor::Result, Op::Eq) => (false, "is_ok_and", !in_some_or_ok), + (Flavor::Result, Op::Ne) => (true, "is_ok_and", !in_some_or_ok), + }; + span_lint_and_sugg( + cx, + MANUAL_IS_VARIANT_AND, + span, + format!("called `.map() {op} {pos}()`", pos = flavor.positive(),), + "use", + format!( + "{inversion}{recv}.{method}({body})", + inversion = if invert_expr { "!" } else { "" }, + body = map_func.sugg(cx, invert_body, &mut app), + ), + app, + ); +} + pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::Binary(op, left, right) = parent_expr.kind - && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) && op.span.eq_ctxt(expr.span) + && let Ok(op) = Op::try_from(op.node) { // Check `left` and `right` expression in any order, and for `Option` and `Result` for (expr1, expr2) in [(left, right), (right, left)] { - for item in [sym::Option, sym::Result] { - if let ExprKind::Call(call, ..) = expr1.kind + for flavor in [Flavor::Option, Flavor::Result] { + if let ExprKind::Call(call, [arg]) = expr1.kind + && let ExprKind::Lit(lit) = arg.kind + && let LitKind::Bool(bool_cst) = lit.node && let ExprKind::Path(QPath::Resolved(_, path)) = call.kind && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res && let ty = cx.typeck_results().expr_ty(expr1) && let ty::Adt(adt, args) = ty.kind() - && cx.tcx.is_diagnostic_item(item, adt.did()) + && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did()) && args.type_at(0).is_bool() - && let ExprKind::MethodCall(_, recv, _, span) = expr2.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item) + && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol()) + && let Ok(map_func) = MapFunc::try_from(map_expr) { - return emit_lint(cx, op.node, parent_expr, span, item == sym::Option); + return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv); } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 2139466ce7466..6ce7dd3d4d0a8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -136,7 +136,7 @@ pub(super) fn check<'tcx>( fun_span: Option, ) -> bool { // (path, fn_has_argument, methods, suffix) - const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [ + const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [ (sym::BTreeEntry, false, &[sym::or_insert], "with"), (sym::HashMapEntry, false, &[sym::or_insert], "with"), ( @@ -146,7 +146,9 @@ pub(super) fn check<'tcx>( "else", ), (sym::Option, false, &[sym::get_or_insert], "with"), + (sym::Option, true, &[sym::and], "then"), (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"), + (sym::Result, true, &[sym::and], "then"), ]; if KNOW_TYPES.iter().any(|k| k.2.contains(&name)) diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs index df8544f92203e..54f38a322b8d9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs @@ -1,5 +1,5 @@ use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{self as hir, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArg, Ty}; use rustc_span::sym; @@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::visitors::for_each_unconsumed_temporary; -use clippy_utils::{get_parent_expr, peel_blocks}; +use clippy_utils::{peel_blocks, potential_return_of_enclosing_body}; use super::RETURN_AND_THEN; @@ -21,7 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'_>, ) { - if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() { + if !potential_return_of_enclosing_body(cx, expr) { return; } @@ -55,15 +55,28 @@ pub(super) fn check<'tcx>( None => &body_snip, }; - // If suggestion is going to get inserted as part of a `return` expression, it must be blockified. - let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) { - let base_indent = indent_of(cx, parent_expr.span); + // If suggestion is going to get inserted as part of a `return` expression or as a match expression + // arm, it must be blockified. + let (parent_span_for_indent, opening_paren, closing_paren) = match cx.tcx.parent_hir_node(expr.hir_id) { + Node::Expr(parent_expr) if matches!(parent_expr.kind, hir::ExprKind::Break(..)) => { + (Some(parent_expr.span), "(", ")") + }, + Node::Expr(parent_expr) => (Some(parent_expr.span), "", ""), + Node::Arm(match_arm) => (Some(match_arm.span), "", ""), + _ => (None, "", ""), + }; + let sugg = if let Some(span) = parent_span_for_indent { + let base_indent = indent_of(cx, span); let inner_indent = base_indent.map(|i| i + 4); format!( "{}\n{}\n{}", - reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent), + reindent_multiline( + &format!("{opening_paren}{{\nlet {arg_snip} = {recv_snip}?;"), + true, + inner_indent + ), reindent_multiline(inner, false, inner_indent), - reindent_multiline("}", false, base_indent), + reindent_multiline(&format!("}}{closing_paren}"), false, base_indent), ) } else { format!( diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs index de729fb343a34..e378cbd6ae0ad 100644 --- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,6 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::Adjust; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -11,12 +12,12 @@ use super::SWAP_WITH_TEMPORARY; const MSG_TEMPORARY: &str = "this expression returns a temporary value"; const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value"; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'_>, args: &'tcx [Expr<'_>]) { if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind && let Some(func_def_id) = func_path.res.opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id) { - match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) { + match (ArgKind::new(cx, &args[0]), ArgKind::new(cx, &args[1])) { (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => { emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp); }, @@ -28,10 +29,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args } enum ArgKind<'tcx> { - // Mutable reference to a place, coming from a macro - RefMutToPlaceAsMacro(&'tcx Expr<'tcx>), - // Place behind a mutable reference - RefMutToPlace(&'tcx Expr<'tcx>), + // Mutable reference to a place, coming from a macro, and number of dereferences to use + RefMutToPlaceAsMacro(&'tcx Expr<'tcx>, usize), + // Place behind a mutable reference, and number of dereferences to use + RefMutToPlace(&'tcx Expr<'tcx>, usize), // Temporary value behind a mutable reference RefMutToTemp(&'tcx Expr<'tcx>), // Any other case @@ -39,13 +40,29 @@ enum ArgKind<'tcx> { } impl<'tcx> ArgKind<'tcx> { - fn new(arg: &'tcx Expr<'tcx>) -> Self { - if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind { - if target.is_syntactic_place_expr() { + /// Build a new `ArgKind` from `arg`. There must be no false positive when returning a + /// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted. + fn new(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Self { + if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind + && let adjustments = cx.typeck_results().expr_adjustments(arg) + && adjustments + .first() + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + && adjustments + .last() + .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) + { + let extra_derefs = adjustments[1..adjustments.len() - 1] + .iter() + .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + // If a deref is used, `arg` might be a place expression. For example, a mutex guard + // would dereference into the mutex content which is probably not temporary. + if target.is_syntactic_place_expr() || extra_derefs > 0 { if arg.span.from_expansion() { - ArgKind::RefMutToPlaceAsMacro(arg) + ArgKind::RefMutToPlaceAsMacro(arg, extra_derefs) } else { - ArgKind::RefMutToPlace(target) + ArgKind::RefMutToPlace(target, extra_derefs) } } else { ArgKind::RefMutToTemp(target) @@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>, let mut applicability = Applicability::MachineApplicable; let ctxt = expr.span.ctxt(); let assign_target = match target { - ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => { - Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref() - }, - ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + ArgKind::Expr(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref(), + ArgKind::RefMutToPlaceAsMacro(arg, derefs) => (0..*derefs).fold( + Sugg::hir_with_context(cx, arg, ctxt, "_", &mut applicability).deref(), + |sugg, _| sugg.deref(), + ), + ArgKind::RefMutToPlace(target, derefs) => (0..*derefs).fold( + Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability), + |sugg, _| sugg.deref(), + ), ArgKind::RefMutToTemp(_) => unreachable!(), }; let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index b90748dd1585f..4a9007c607c88 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -62,7 +62,8 @@ pub(super) fn check<'a>( let ext_def_span = def.span.until(map.span); - let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind + let (sugg, method, applicability) = if cx.typeck_results().expr_adjustments(recv).is_empty() + && let ExprKind::Closure(map_closure) = map.kind && let closure_body = cx.tcx.hir_body(map_closure.body) && let closure_body_value = closure_body.value.peel_blocks() && let ExprKind::Binary(op, l, r) = closure_body_value.kind diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 3ed4b1c2ea947..b3aa1a7286a9c 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -199,11 +199,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { let mut applicability = Applicability::MachineApplicable; let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability); - let sugg = if a == b { + let mut sugg = if a == b { format!("{cond}; {lhs} = {a:?};") } else { format!("{lhs} = {};", if a { cond } else { !cond }) }; + + if is_else_clause(cx.tcx, e) { + sugg = format!("{{ {sugg} }}"); + } + span_lint_and_sugg( cx, NEEDLESS_BOOL_ASSIGN, diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index 442280f999821..946114e104195 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -1,13 +1,13 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::get_parent_expr; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -33,6 +33,19 @@ declare_clippy_lint! { declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); +fn is_in_parens_with_postfix(cx: &LateContext<'_>, mul_expr: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, mul_expr) { + let mult_snippet = snippet(cx, mul_expr.span, ""); + if has_enclosing_paren(&mult_snippet) + && let ExprKind::MethodCall(_, _, _, _) = parent.kind + { + return true; + } + } + + false +} + impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind @@ -40,15 +53,15 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply { { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e, lit, left), _ => {}, } } } } -fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { +fn check_mul(cx: &LateContext<'_>, mul_expr: &Expr<'_>, lit: &Expr<'_>, exp: &Expr<'_>) { const F16_ONE: u16 = 1.0_f16.to_bits(); const F128_ONE: u128 = 1.0_f128.to_bits(); if let ExprKind::Lit(l) = lit.kind @@ -63,8 +76,19 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { && cx.typeck_results().expr_ty(exp).is_numeric() { let mut applicability = Applicability::MachineApplicable; - let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { + let (snip, from_macro) = snippet_with_context(cx, exp.span, mul_expr.span.ctxt(), "..", &mut applicability); + + let needs_parens_for_postfix = is_in_parens_with_postfix(cx, mul_expr); + + let suggestion = if needs_parens_for_postfix { + // Special case: when the multiplication is in parentheses followed by a method call + // we need to preserve the grouping but negate the inner expression. + // Consider this expression: `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)` + // We need to end up with: `(-(a.delta - 0.5).abs()).total_cmp(&1.0)` + // Otherwise, without the parentheses we would try to negate an Ordering: + // `-(a.delta - 0.5).abs().total_cmp(&1.0)` + format!("(-{snip})") + } else if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") @@ -72,7 +96,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { span_lint_and_sugg( cx, NEG_MULTIPLY, - span, + mul_expr.span, "this multiplication by -1 can be written more succinctly", "consider using", suggestion, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 02c48166131ef..72e6503e7e495 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::has_drop; +use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{ in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks, }; @@ -340,11 +340,13 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(v.iter().collect()), ExprKind::Repeat(inner, _) - | ExprKind::Cast(inner, _) | ExprKind::Type(inner, _) | ExprKind::Unary(_, inner) | ExprKind::Field(inner, _) | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => { + reduce_expression(cx, inner).or_else(|| Some(vec![inner])) + }, ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index 0faa7b9e64665..21e1ab0f4f27d 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::get_enclosing_block; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -61,12 +61,13 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of both operands", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.multipart_suggestion( "use the values directly", - vec![(left.span, lsnip), (right.span, rsnip)], - Applicability::MachineApplicable, + vec![(left.span, lsnip.to_string()), (right.span, rsnip.to_string())], + applicability, ); }, ); @@ -80,13 +81,9 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of left operand", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); - diag.span_suggestion( - left.span, - "use the left value directly", - lsnip, - Applicability::MachineApplicable, - ); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); + diag.span_suggestion(left.span, "use the left value directly", lsnip, applicability); }, ); } else if !lcpy @@ -99,7 +96,8 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of right operand", |diag| { - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( right.span, "use the right value directly", @@ -131,7 +129,8 @@ pub(crate) fn check<'tcx>( e.span, "needlessly taken reference of left operand", |diag| { - let lsnip = snippet(cx, l.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( left.span, "use the left value directly", @@ -158,7 +157,8 @@ pub(crate) fn check<'tcx>( && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()]) { span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| { - let rsnip = snippet(cx, r.span, "...").to_string(); + let mut applicability = Applicability::MachineApplicable; + let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability); diag.span_suggestion( right.span, "use the right value directly", diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 84597269a58fa..1c23fe998ad8b 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -5,9 +5,7 @@ use hir::Param; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; -use rustc_hir::{ - ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit, -}; +use rustc_hir::{ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, intravisit as hir_visit}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; @@ -198,15 +196,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { hint = hint.asyncify(); } - let is_in_fn_call_arg = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) { - matches!(expr.kind, ExprKind::Call(_, _)) - } else { - false - }; - - // avoid clippy::double_parens - if !is_in_fn_call_arg { - hint = hint.maybe_paren(); + // If the closure body is a block with a single expression, suggest just the inner expression, + // not the block. Example: `(|| { Some(true) })()` should suggest + // `Some(true)` + if let ExprKind::Block(block, _) = body.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + { + hint = Sugg::hir_with_context(cx, expr, full_expr.span.ctxt(), "..", &mut applicability) + .maybe_paren(); } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 442b3250d865f..cf70e883bd098 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; use rustc_attr_data_structures::{StabilityLevel, StableSince}; use rustc_errors::Applicability; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Block, Body, HirId, Path, PathSegment}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; @@ -88,49 +88,52 @@ declare_clippy_lint! { } pub struct StdReexports { - lint_point: (Span, Option), + lint_points: Option<(Span, Vec)>, msrv: Msrv, } impl StdReexports { pub fn new(conf: &'static Conf) -> Self { Self { - lint_point: Default::default(), + lint_points: Option::default(), msrv: conf.msrv, } } - fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option)) { - if span.source_equal(self.lint_point.0) { - return; + fn lint_if_finish(&mut self, cx: &LateContext<'_>, krate: Span, lint_point: LintPoint) { + match &mut self.lint_points { + Some((prev_krate, prev_lints)) if prev_krate.overlaps(krate) => { + prev_lints.push(lint_point); + }, + _ => emit_lints(cx, self.lint_points.replace((krate, vec![lint_point]))), } - - if !self.lint_point.0.is_dummy() { - emit_lints(cx, &self.lint_point); - } - - self.lint_point = (span, item); } } impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); -type LintPoint = (&'static Lint, &'static str, &'static str); +#[derive(Debug)] +enum LintPoint { + Available(Span, &'static Lint, &'static str, &'static str), + Conflict, +} impl<'tcx> LateLintPass<'tcx> for StdReexports { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { - if let Res::Def(_, def_id) = path.res + if let Res::Def(def_kind, def_id) = path.res && let Some(first_segment) = get_first_segment(path) && is_stable(cx, def_id, self.msrv) && !path.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, &first_segment.ident) + && !matches!(def_kind, DefKind::Macro(_)) + && let Some(last_segment) = path.segments.last() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; }, }, @@ -138,44 +141,84 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { - self.lint_if_finish(cx, (first_segment.ident.span, None)); + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; } }, - _ => return, + _ => { + self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); + return; + }, }; - self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with)))); + self.lint_if_finish( + cx, + first_segment.ident.span, + LintPoint::Available(last_segment.ident.span, lint, used_mod, replace_with), + ); } } fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - self.lint_if_finish(cx, Default::default()); + emit_lints(cx, self.lint_points.take()); } } -fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option)) { - let Some((lint, used_mod, replace_with)) = item else { +fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec)>) { + let Some((krate_span, lint_points)) = lint_points else { return; }; - span_lint_and_sugg( - cx, - lint, - *span, - format!("used import from `{used_mod}` instead of `{replace_with}`"), - format!("consider importing the item from `{replace_with}`"), - (*replace_with).to_string(), - Applicability::MachineApplicable, - ); + let mut lint: Option<(&'static Lint, &'static str, &'static str)> = None; + let mut has_conflict = false; + for lint_point in &lint_points { + match lint_point { + LintPoint::Available(_, l, used_mod, replace_with) + if lint.is_none_or(|(prev_l, ..)| l.name == prev_l.name) => + { + lint = Some((l, used_mod, replace_with)); + }, + _ => { + has_conflict = true; + break; + }, + } + } + + if !has_conflict && let Some((lint, used_mod, replace_with)) = lint { + span_lint_and_sugg( + cx, + lint, + krate_span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + format!("consider importing the item from `{replace_with}`"), + (*replace_with).to_string(), + Applicability::MachineApplicable, + ); + return; + } + + for lint_point in lint_points { + let LintPoint::Available(span, lint, used_mod, replace_with) = lint_point else { + continue; + }; + span_lint_and_help( + cx, + lint, + span, + format!("used import from `{used_mod}` instead of `{replace_with}`"), + None, + format!("consider importing the item from `{replace_with}`"), + ); + } } /// Returns the first named segment of a [`Path`]. diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 8906e654c972a..cf603c6190ba1 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -143,7 +143,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { if let Some(tail) = block.expr && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) && !tail.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) { span_lint_and_then( @@ -167,7 +168,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { }; if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let HasSafetyComment::Yes(pos) = + stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes) && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { span_lint_and_then( @@ -540,7 +542,12 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { +fn stmt_has_safety_comment( + cx: &LateContext<'_>, + span: Span, + hir_id: HirId, + accept_comment_above_attributes: bool, +) -> HasSafetyComment { match span_from_macro_expansion_has_safety_comment(cx, span) { HasSafetyComment::Maybe => (), has_safety_comment => return has_safety_comment, @@ -555,6 +562,13 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H _ => return HasSafetyComment::Maybe, }; + // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib + // } + let mut span = span; + if accept_comment_above_attributes { + span = include_attrs_in_span(cx, hir_id, span); + } + let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs index 7b5d58824c38f..74712097e7166 100644 --- a/src/tools/clippy/clippy_lints_internal/src/symbols.rs +++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs @@ -65,7 +65,7 @@ pub struct Symbols { impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]); impl Symbols { - fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> { + fn lit_suggestion(&self, lit: Lit) -> Option<(Span, String)> { if let LitKind::Str(name, _) = lit.node { let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) { format!("{prefix}::{name}") diff --git a/src/tools/clippy/clippy_test_deps/Cargo.lock b/src/tools/clippy/clippy_test_deps/Cargo.lock new file mode 100644 index 0000000000000..a591dae3a1a62 --- /dev/null +++ b/src/tools/clippy/clippy_test_deps/Cargo.lock @@ -0,0 +1,505 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clippy_test_deps" +version = "0.1.0" +dependencies = [ + "futures", + "if_chain", + "itertools", + "parking_lot", + "quote", + "regex", + "serde", + "syn", + "tokio", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "pin-project-lite", + "slab", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/src/tools/clippy/clippy_test_deps/Cargo.toml b/src/tools/clippy/clippy_test_deps/Cargo.toml new file mode 100644 index 0000000000000..a23ffcaf2f978 --- /dev/null +++ b/src/tools/clippy/clippy_test_deps/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "clippy_test_deps" +version = "0.1.0" +edition = "2021" + +# Add dependencies here to make them available in ui tests. + +[dependencies] +regex = "1.5.5" +serde = { version = "1.0.145", features = ["derive"] } +if_chain = "1.0" +quote = "1.0.25" +syn = { version = "2.0", features = ["full"] } +futures = "0.3" +parking_lot = "0.12" +tokio = { version = "1", features = ["io-util"] } +itertools = "0.12" + +# Make sure we are not part of the rustc workspace. +[workspace] diff --git a/src/tools/clippy/clippy_test_deps/src/main.rs b/src/tools/clippy/clippy_test_deps/src/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/clippy/clippy_test_deps/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 649748d1534bb..645b644d9f4e8 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-06-26 +nightly-2025-07-10 ``` diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 4c7a589e185b9..34472eaab93cc 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,5 +1,8 @@ +use crate::source::SpanRangeExt; +use crate::{sym, tokenize_with_text}; use rustc_ast::attr; use rustc_ast::attr::AttributeExt; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::Applicability; use rustc_lexer::TokenKind; use rustc_lint::LateContext; @@ -7,10 +10,6 @@ use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::{Span, Symbol}; use std::str::FromStr; -use rustc_attr_data_structures::find_attr; -use crate::source::SpanRangeExt; -use crate::{sym, tokenize_with_text}; -use rustc_attr_data_structures::AttributeKind; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -168,7 +167,8 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { adt.is_variant_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..)) || adt.variants().iter().any(|variant_def| { - variant_def.is_field_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..)) + variant_def.is_field_list_non_exhaustive() + || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..)) }) || adt .all_fields() diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 6e8dccbccd515..f0d7fb89c4463 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -2,6 +2,7 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::tokenize_with_text; +use rustc_ast::ast; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; use rustc_hir::MatchSource::TryDesugar; @@ -9,8 +10,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, - Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, - TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, + Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::LateContext; @@ -1004,8 +1005,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); } }, - ExprKind::Match(e, arms, s) => { - self.hash_expr(e); + ExprKind::Match(scrutinee, arms, _) => { + self.hash_expr(scrutinee); for arm in *arms { self.hash_pat(arm.pat); @@ -1014,8 +1015,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } self.hash_expr(arm.body); } - - s.hash(&mut self.s); }, ExprKind::MethodCall(path, receiver, args, _fn_span) => { self.hash_name(path.ident.name); @@ -1058,8 +1057,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { ExprKind::Use(expr, _) => { self.hash_expr(expr); }, - ExprKind::Unary(lop, le) => { - std::mem::discriminant(lop).hash(&mut self.s); + ExprKind::Unary(l_op, le) => { + std::mem::discriminant(l_op).hash(&mut self.s); self.hash_expr(le); }, ExprKind::UnsafeBinderCast(kind, expr, ty) => { @@ -1394,3 +1393,70 @@ fn eq_span_tokens( } f(cx, left.into_range(), right.into_range(), pred) } + +/// Returns true if the expression contains ambiguous literals (unsuffixed float or int literals) +/// that could be interpreted as either f32/f64 or i32/i64 depending on context. +pub fn has_ambiguous_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(ref qpath) => { + if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id) + && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) + && local.ty.is_none() + && let Some(init) = local.init + { + return has_ambiguous_literal_in_expr(cx, init); + } + false + }, + ExprKind::Lit(lit) => matches!( + lit.node, + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) | ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) + ), + + ExprKind::Array(exprs) | ExprKind::Tup(exprs) => exprs.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Binary(_, lhs, rhs) => { + has_ambiguous_literal_in_expr(cx, lhs) || has_ambiguous_literal_in_expr(cx, rhs) + }, + + ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::DropTemps(e) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Field(e, _) + | ExprKind::Index(e, _, _) + | ExprKind::Yield(e, _) => has_ambiguous_literal_in_expr(cx, e), + + ExprKind::MethodCall(_, receiver, args, _) | ExprKind::Call(receiver, args) => { + has_ambiguous_literal_in_expr(cx, receiver) || args.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Closure(Closure { body, .. }) => { + let body = cx.tcx.hir_body(*body); + let closure_expr = crate::peel_blocks(body.value); + has_ambiguous_literal_in_expr(cx, closure_expr) + }, + + ExprKind::Block(blk, _) => blk.expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::If(cond, then_expr, else_expr) => { + has_ambiguous_literal_in_expr(cx, cond) + || has_ambiguous_literal_in_expr(cx, then_expr) + || else_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + ExprKind::Match(scrutinee, arms, _) => { + has_ambiguous_literal_in_expr(cx, scrutinee) + || arms.iter().any(|arm| has_ambiguous_literal_in_expr(cx, arm.body)) + }, + + ExprKind::Loop(body, ..) => body.expr.is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)), + + ExprKind::Ret(opt_expr) | ExprKind::Break(_, opt_expr) => { + opt_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)) + }, + + _ => false, + } +} diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 36b91b9c43ce1..ad8c816e204f8 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -77,7 +77,8 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, + HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr, + hash_stmt, is_bool, over, }; use core::mem; @@ -3497,3 +3498,64 @@ pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> false } } + +/// Checks if `expr` may be directly used as the return value of its enclosing body. +/// The following cases are covered: +/// - `expr` as the last expression of the body, or of a block that can be used as the return value +/// - `return expr` +/// - then or else part of a `if` in return position +/// - arm body of a `match` in a return position +/// - `break expr` or `break 'label expr` if the loop or block being exited is used as a return +/// value +/// +/// Contrary to [`TyCtxt::hir_get_fn_id_for_return_block()`], if `expr` is part of a +/// larger expression, for example a field expression of a `struct`, it will not be +/// considered as matching the condition and will return `false`. +/// +/// Also, even if `expr` is assigned to a variable which is later returned, this function +/// will still return `false` because `expr` is not used *directly* as the return value +/// as it goes through the intermediate variable. +pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let enclosing_body_owner = cx + .tcx + .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id)); + let mut prev_id = expr.hir_id; + let mut skip_until_id = None; + for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) { + if hir_id == enclosing_body_owner { + return true; + } + if let Some(id) = skip_until_id { + prev_id = hir_id; + if id == hir_id { + skip_until_id = None; + } + continue; + } + match node { + Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {}, + Node::Arm(arm) if arm.body.hir_id == prev_id => {}, + Node::Expr(expr) => match expr.kind { + ExprKind::Ret(_) => return true, + ExprKind::If(_, then, opt_else) + if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {}, + ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {}, + ExprKind::Block(block, _) if block.hir_id == prev_id => {}, + ExprKind::Break( + Destination { + target_id: Ok(target_id), + .. + }, + _, + ) => skip_until_id = Some(target_id), + _ => break, + }, + _ => break, + } + prev_id = hir_id; + } + + // `expr` is used as part of "something" and is not returned directly from its + // enclosing body. + false +} diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 7a0bef1a9bbbf..24ed4c3a8beca 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -74,7 +74,7 @@ msrv_aliases! { 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } - 1,24,0 { IS_ASCII_DIGIT } + 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 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 } diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 615a0910dfde3..ba5cbc738361f 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::msrvs::Msrv; +use crate::qualify_min_const_fn::is_stable_const_fn; use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; -use crate::qualify_min_const_fn::is_stable_const_fn; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -355,7 +355,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> ExprKind::Binary(_, lhs, rhs) if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (), + ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), ExprKind::Index(base, _, _) if matches!( @@ -390,7 +390,8 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> | ExprKind::Repeat(..) | ExprKind::Struct(..) | ExprKind::Tup(_) - | ExprKind::Type(..) => (), + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => (), _ => { return ControlFlow::Break(()); @@ -678,10 +679,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( helper(typeck, true, else_expr, f)?; } }, - ExprKind::Type(e, _) => { - helper(typeck, consume, e, f)?; - }, - ExprKind::UnsafeBinderCast(_, e, _) => { + ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => { helper(typeck, consume, e, f)?; }, diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index 83c3d7aba021a..3b2ebf0c28ac2 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -68,6 +68,9 @@ pub(crate) enum Commands { /// This will limit the number of warnings that will be printed for each lint #[clap(long)] truncate: bool, + /// Write the diff summary to a JSON file if there are any changes + #[clap(long, value_name = "PATH")] + write_summary: Option, }, /// Create a lintcheck crates TOML file containing the top N popular crates Popular { diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs index 8ea0a41ed368a..808997ff02204 100644 --- a/src/tools/clippy/lintcheck/src/json.rs +++ b/src/tools/clippy/lintcheck/src/json.rs @@ -4,8 +4,8 @@ //! loading warnings from JSON files, and generating human-readable diffs //! between different linting runs. -use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::{fmt, fs}; use itertools::{EitherOrBoth, Itertools}; use serde::{Deserialize, Serialize}; @@ -17,7 +17,6 @@ const DEFAULT_LIMIT_PER_LINT: usize = 300; /// Target for total warnings to display across all lints when truncating output. const TRUNCATION_TOTAL_TARGET: usize = 1000; -/// Representation of a single Clippy warning for JSON serialization. #[derive(Debug, Deserialize, Serialize)] struct LintJson { /// The lint name e.g. `clippy::bytes_nth` @@ -29,7 +28,6 @@ struct LintJson { } impl LintJson { - /// Returns a tuple of name and `file_line` for sorting and comparison. fn key(&self) -> impl Ord + '_ { (self.name.as_str(), self.file_line.as_str()) } @@ -40,6 +38,57 @@ impl LintJson { } } +#[derive(Debug, Serialize)] +struct SummaryRow { + name: String, + added: usize, + removed: usize, + changed: usize, +} + +#[derive(Debug, Serialize)] +struct Summary(Vec); + +impl fmt::Display for Summary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + "\ +| Lint | Added | Removed | Changed | +| ---- | ----: | ------: | ------: | +", + )?; + + for SummaryRow { + name, + added, + changed, + removed, + } in &self.0 + { + let html_id = to_html_id(name); + writeln!(f, "| [`{name}`](#{html_id}) | {added} | {changed} | {removed} |")?; + } + + Ok(()) + } +} + +impl Summary { + fn new(lints: &[LintWarnings]) -> Self { + Summary( + lints + .iter() + .map(|lint| SummaryRow { + name: lint.name.clone(), + added: lint.added.len(), + removed: lint.removed.len(), + changed: lint.changed.len(), + }) + .collect(), + ) + } +} + /// Creates the log file output for [`crate::config::OutputFormat::Json`] pub(crate) fn output(clippy_warnings: Vec) -> String { let mut lints: Vec = clippy_warnings @@ -74,7 +123,7 @@ fn load_warnings(path: &Path) -> Vec { /// /// Compares warnings from `old_path` and `new_path`, then displays a summary table /// and detailed information about added, removed, and changed warnings. -pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { +pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool, write_summary: Option) { let old_warnings = load_warnings(old_path); let new_warnings = load_warnings(new_path); @@ -108,13 +157,16 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { } } - print_summary_table(&lint_warnings); - println!(); - if lint_warnings.is_empty() { return; } + let summary = Summary::new(&lint_warnings); + if let Some(path) = write_summary { + let json = serde_json::to_string(&summary).unwrap(); + fs::write(path, json).unwrap(); + } + let truncate_after = if truncate { // Max 15 ensures that we at least have five messages per lint DEFAULT_LIMIT_PER_LINT @@ -126,6 +178,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) { usize::MAX }; + println!("{summary}"); for lint in lint_warnings { print_lint_warnings(&lint, truncate_after); } @@ -140,13 +193,11 @@ struct LintWarnings { changed: Vec<(LintJson, LintJson)>, } -/// Prints a formatted report for a single lint type with its warnings. fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { let name = &lint.name; let html_id = to_html_id(name); - // The additional anchor is added for non GH viewers that don't prefix ID's - println!(r#"## `{name}` "#); + println!(r#"

{name}

"#); println!(); print!( @@ -162,22 +213,6 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) { print_changed_diff(&lint.changed, truncate_after / 3); } -/// Prints a summary table of all lints with counts of added, removed, and changed warnings. -fn print_summary_table(lints: &[LintWarnings]) { - println!("| Lint | Added | Removed | Changed |"); - println!("| ------------------------------------------ | ------: | ------: | ------: |"); - - for lint in lints { - println!( - "| {:<62} | {:>7} | {:>7} | {:>7} |", - format!("[`{}`](#user-content-{})", lint.name, to_html_id(&lint.name)), - lint.added.len(), - lint.removed.len(), - lint.changed.len() - ); - } -} - /// Prints a section of warnings with a header and formatted code blocks. fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) { if warnings.is_empty() { @@ -248,17 +283,16 @@ fn truncate(list: &[T], truncate_after: usize) -> &[T] { } } -/// Prints a level 3 heading with an appropriate HTML ID for linking. fn print_h3(lint: &str, title: &str) { let html_id = to_html_id(lint); - // We have to use HTML here to be able to manually add an id. - println!(r#"### {title} "#); + // We have to use HTML here to be able to manually add an id, GitHub doesn't add them automatically + println!(r#"

{title}

"#); } -/// GitHub's markdown parsers doesn't like IDs with `::` and `_`. This simplifies -/// the lint name for the HTML ID. +/// Creates a custom ID allowed by GitHub, they must start with `user-content-` and cannot contain +/// `::`/`_` fn to_html_id(lint_name: &str) -> String { - lint_name.replace("clippy::", "").replace('_', "-") + lint_name.replace("clippy::", "user-content-").replace('_', "-") } /// This generates the `x added` string for the start of the job summery. @@ -270,9 +304,6 @@ fn count_string(lint: &str, label: &str, count: usize) -> String { format!("0 {label}") } else { let html_id = to_html_id(lint); - // GitHub's job summaries don't add HTML ids to headings. That's why we - // manually have to add them. GitHub prefixes these manual ids with - // `user-content-` and that's how we end up with these awesome links :D - format!("[{count} {label}](#user-content-{html_id}-{label})") + format!("[{count} {label}](#{html_id}-{label})") } } diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index eb390eecbcca8..3a60cfa79f410 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -303,7 +303,12 @@ fn main() { let config = LintcheckConfig::new(); match config.subcommand { - Some(Commands::Diff { old, new, truncate }) => json::diff(&old, &new, truncate), + Some(Commands::Diff { + old, + new, + truncate, + write_summary, + }) => json::diff(&old, &new, truncate, write_summary), Some(Commands::Popular { output, number }) => popular_crates::fetch(output, number).unwrap(), None => lintcheck(config), } diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 124756a360095..f46e079db3f10 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-06-26" +channel = "nightly-2025-07-10" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index a7f38275a6022..57d623b2cfc8b 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -16,8 +16,10 @@ use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::custom_flags::Flag; use ui_test::custom_flags::edition::Edition; use ui_test::custom_flags::rustfix::RustfixMode; +use ui_test::dependencies::DependencyBuilder; use ui_test::spanned::Spanned; -use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter}; +use ui_test::status_emitter::StatusEmitter; +use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; @@ -27,46 +29,26 @@ use std::path::{Path, PathBuf}; use std::sync::mpsc::{Sender, channel}; use std::{fs, iter, thread}; -// Test dependencies may need an `extern crate` here to ensure that they show up -// in the depinfo file (otherwise cargo thinks they are unused) -extern crate futures; -extern crate if_chain; -extern crate itertools; -extern crate parking_lot; -extern crate quote; -extern crate syn; -extern crate tokio; - mod test_utils; -/// All crates used in UI tests are listed here -static TEST_DEPENDENCIES: &[&str] = &[ - "clippy_config", - "clippy_lints", - "clippy_utils", - "futures", - "if_chain", - "itertools", - "parking_lot", - "quote", - "regex", - "serde_derive", - "serde", - "syn", - "tokio", -]; - -/// Produces a string with an `--extern` flag for all UI test crate -/// dependencies. +/// All crates used in internal UI tests are listed here. +/// We directly re-use these crates from their normal clippy builds, so we don't have them +/// in `clippy_test_devs`. That saves a lot of time but also means they don't work in a stage 1 +/// test in rustc bootstrap. +static INTERNAL_TEST_DEPENDENCIES: &[&str] = &["clippy_config", "clippy_lints", "clippy_utils"]; + +/// Produces a string with an `--extern` flag for all `INTERNAL_TEST_DEPENDENCIES`. /// /// The dependency files are located by parsing the depinfo file for this test /// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test /// dependencies must be added to Cargo.toml at the project root. Test /// dependencies that are not *directly* used by this test module require an /// `extern crate` declaration. -fn extern_flags() -> Vec { +fn internal_extern_flags() -> Vec { + let current_exe_path = env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); let current_exe_depinfo = { - let mut path = env::current_exe().unwrap(); + let mut path = current_exe_path.clone(); path.set_extension("d"); fs::read_to_string(path).unwrap() }; @@ -88,7 +70,7 @@ fn extern_flags() -> Vec { Some((name, path_str)) }; if let Some((name, path)) = parse_name_path() - && TEST_DEPENDENCIES.contains(&name) + && INTERNAL_TEST_DEPENDENCIES.contains(&name) { // A dependency may be listed twice if it is available in sysroot, // and the sysroot dependencies are listed first. As of the writing, @@ -96,7 +78,7 @@ fn extern_flags() -> Vec { crates.insert(name, path); } } - let not_found: Vec<&str> = TEST_DEPENDENCIES + let not_found: Vec<&str> = INTERNAL_TEST_DEPENDENCIES .iter() .copied() .filter(|n| !crates.contains_key(n)) @@ -111,6 +93,7 @@ fn extern_flags() -> Vec { crates .into_iter() .map(|(name, path)| format!("--extern={name}={path}")) + .chain([format!("-Ldependency={}", deps_path.display())]) .collect() } @@ -119,7 +102,6 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); struct TestContext { args: Args, - extern_flags: Vec, diagnostic_collector: Option, collector_thread: Option>, } @@ -134,7 +116,6 @@ impl TestContext { .unzip(); Self { args, - extern_flags: extern_flags(), diagnostic_collector, collector_thread, } @@ -167,6 +148,15 @@ impl TestContext { }; let defaults = config.comment_defaults.base(); defaults.set_custom("edition", Edition("2024".into())); + defaults.set_custom( + "dependencies", + DependencyBuilder { + program: CommandBuilder::cargo(), + crate_manifest_path: Path::new("clippy_test_deps").join("Cargo.toml"), + build_std: None, + bless_lockfile: self.args.bless, + }, + ); defaults.exit_status = None.into(); if mandatory_annotations { defaults.require_annotations = Some(Spanned::dummy(true)).into(); @@ -191,12 +181,10 @@ impl TestContext { "-Zui-testing", "-Zdeduplicate-diagnostics=no", "-Dwarnings", - &format!("-Ldependency={}", deps_path.display()), ] .map(OsString::from), ); - config.program.args.extend(self.extern_flags.iter().map(OsString::from)); // Prevent rustc from creating `rustc-ice-*` files the console output is enough. config.program.envs.push(("RUSTC_ICE".into(), Some("0".into()))); @@ -226,7 +214,7 @@ fn run_ui(cx: &TestContext) { vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -236,13 +224,17 @@ fn run_internal_tests(cx: &TestContext) { return; } let mut config = cx.base_config("ui-internal", true); + config + .program + .args + .extend(internal_extern_flags().iter().map(OsString::from)); config.bless_command = Some("cargo uitest --features internal -- -- --bless".into()); ui_test::run_tests_generic( vec![config], ui_test::default_file_filter, ui_test::default_per_file_config, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -266,7 +258,7 @@ fn run_ui_toml(cx: &TestContext) { .envs .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into()))); }, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } @@ -313,7 +305,7 @@ fn run_ui_cargo(cx: &TestContext) { .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path)) }, |_config, _file_contents| {}, - status_emitter::Text::from(cx.args.format), + Box::::from(cx.args.format), ) .unwrap(); } diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr index 20cdff5fcd12b..cebfc48a884f6 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr @@ -450,5 +450,13 @@ help: consider removing the safety comment LL | // SAFETY: unnecessary_safety_comment triggers here | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 52 previous errors +error: unsafe block missing a safety comment + --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12 + | +LL | return unsafe { h() }; + | ^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 53 previous errors diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs index 91a02bc3d7c65..a2d7c1b6c7967 100644 --- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs @@ -723,4 +723,15 @@ fn issue_13039() { _ = unsafe { foo() } } +fn rfl_issue15034() -> i32 { + unsafe fn h() -> i32 { + 1i32 + } + // This shouldn't lint with accept-comment-above-attributes! Thus fixing a false positive! + // SAFETY: My safety comment! + #[allow(clippy::unnecessary_cast)] + return unsafe { h() }; + //~[disabled]^ ERROR: unsafe block missing a safety comment +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed index 3ba2eea59f0b0..3f6e5245b8788 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed @@ -47,3 +47,9 @@ fn implicit_cast() { // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +} diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs index 8cdd0512da5f7..20f4f40e00197 100644 --- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs +++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs @@ -47,3 +47,9 @@ fn implicit_cast() { // Do not lint references to temporaries core::ptr::eq(&0i32, &1i32); } + +fn issue_15141() { + let a = String::new(); + // Don't lint cast to dyn trait pointers + let b = &a as *const dyn std::any::Any; +} diff --git a/src/tools/clippy/tests/ui/cast_size.32bit.stderr b/src/tools/clippy/tests/ui/cast_size.32bit.stderr index cb1620e36a267..5811cb3607ba0 100644 --- a/src/tools/clippy/tests/ui/cast_size.32bit.stderr +++ b/src/tools/clippy/tests/ui/cast_size.32bit.stderr @@ -177,6 +177,14 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: casting `usize` to `u16` may truncate the value + --> tests/ui/cast_size.rs:71:20 + | +LL | const N: u16 = M as u16; + | ^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... + error: literal out of range for `usize` --> tests/ui/cast_size.rs:63:5 | @@ -186,5 +194,5 @@ LL | 9_999_999_999_999_999usize as f64; = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295` = note: `#[deny(overflowing_literals)]` on by default -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/cast_size.64bit.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr index b6000a52abb34..ba1419583aeb5 100644 --- a/src/tools/clippy/tests/ui/cast_size.64bit.stderr +++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr @@ -177,5 +177,13 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi LL | 9_999_999_999_999_999usize as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: casting `usize` to `u16` may truncate the value + --> tests/ui/cast_size.rs:71:20 + | +LL | const N: u16 = M as u16; + | ^^^^^^^^ + | + = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ... + +error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index e5bef2a99d59e..ecc5866941918 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -65,3 +65,9 @@ fn main() { //~[32bit]^^ ERROR: literal out of range for `usize` // 999_999_999_999_999_999_999_999_999_999u128 as f128; } + +fn issue15163() { + const M: usize = 100; + const N: u16 = M as u16; + //~^ cast_possible_truncation +} diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed index ae9d3ef9656f1..b5b3f15b4de4b 100644 --- a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed +++ b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&*x); @@ -15,12 +15,23 @@ fn main() { let _: &dyn Any = &*x; //~^ coerce_container_to_any + let _: &dyn Any = &*x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut *x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.rs b/src/tools/clippy/tests/ui/coerce_container_to_any.rs index 9948bd48e0d8d..4d6527bb5525d 100644 --- a/src/tools/clippy/tests/ui/coerce_container_to_any.rs +++ b/src/tools/clippy/tests/ui/coerce_container_to_any.rs @@ -3,7 +3,7 @@ use std::any::Any; fn main() { - let x: Box = Box::new(()); + let mut x: Box = Box::new(()); let ref_x = &x; f(&x); @@ -15,12 +15,23 @@ fn main() { let _: &dyn Any = &x; //~^ coerce_container_to_any + let _: &dyn Any = &mut x; + //~^ coerce_container_to_any + + let _: &mut dyn Any = &mut x; + //~^ coerce_container_to_any + f(&42); f(&Box::new(())); f(&Box::new(Box::new(()))); + let ref_x = &x; f(&**ref_x); f(&*x); let _: &dyn Any = &*x; + + // https://github.com/rust-lang/rust-clippy/issues/15045 + #[allow(clippy::needless_borrow)] + (&x).downcast_ref::<()>().unwrap(); } fn f(_: &dyn Any) {} diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr index 00ab77e0ce0fc..26389c9186ebc 100644 --- a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr +++ b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr @@ -19,5 +19,17 @@ error: coercing `&std::boxed::Box` to `&dyn Any` LL | let _: &dyn Any = &x; | ^^ help: consider dereferencing: `&*x` -error: aborting due to 3 previous errors +error: coercing `&mut std::boxed::Box` to `&dyn Any` + --> tests/ui/coerce_container_to_any.rs:18:23 + | +LL | let _: &dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&*x` + +error: coercing `&mut std::boxed::Box` to `&mut dyn Any` + --> tests/ui/coerce_container_to_any.rs:21:27 + | +LL | let _: &mut dyn Any = &mut x; + | ^^^^^^ help: consider dereferencing: `&mut *x` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/disallowed_script_idents.rs b/src/tools/clippy/tests/ui/disallowed_script_idents.rs index 08fd1d9669ee9..dae380045ae42 100644 --- a/src/tools/clippy/tests/ui/disallowed_script_idents.rs +++ b/src/tools/clippy/tests/ui/disallowed_script_idents.rs @@ -15,3 +15,17 @@ fn main() { let カウンタ = 10; //~^ disallowed_script_idents } + +fn issue15116() { + const ÄÖÜ: u8 = 0; + const _ÄÖÜ: u8 = 0; + const Ä_ÖÜ: u8 = 0; + const ÄÖ_Ü: u8 = 0; + const ÄÖÜ_: u8 = 0; + let äöüß = 1; + let _äöüß = 1; + let ä_öüß = 1; + let äö_üß = 1; + let äöü_ß = 1; + let äöüß_ = 1; +} diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed index 065f4486e390a..5c57c58fbc0b2 100644 --- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed @@ -72,8 +72,6 @@ pub struct NotEmptyTight; /// ## Heading /// -/// - [x][] - Done -//~^ ERROR: link reference defined in list item -/// - [ ][] - Not Done -//~^ ERROR: link reference defined in list item +/// - [x] - Done +/// - [ ] - Not Done pub struct GithubCheckboxes; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs index c7eab50c8b386..06b6ba49e192e 100644 --- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs @@ -73,7 +73,5 @@ pub struct NotEmptyTight; /// ## Heading /// /// - [x] - Done -//~^ ERROR: link reference defined in list item /// - [ ] - Not Done -//~^ ERROR: link reference defined in list item pub struct GithubCheckboxes; diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr index 5a815dabf4d8f..27314c7e968dd 100644 --- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr @@ -144,29 +144,5 @@ help: for an intra-doc link, add `[]` between the label and the colon LL | /// - [link][]: def "title" | ++ -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:75:7 - | -LL | /// - [x] - Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [x][] - Done - | ++ - -error: link reference defined in list item - --> tests/ui/doc/doc_nested_refdef_list_item.rs:77:7 - | -LL | /// - [ ] - Not Done - | ^^^ - | - = help: link definitions are not shown in rendered documentation -help: for an intra-doc link, add `[]` between the label and the colon - | -LL | /// - [ ][] - Not Done - | ++ - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs b/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs new file mode 100644 index 0000000000000..a550e5609658d --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs @@ -0,0 +1,13 @@ +//@check-pass + +#![warn(clippy::empty_loop)] +#![feature(intrinsics)] +#![feature(rustc_attrs)] + +// From issue #15200 +#[rustc_intrinsic] +#[rustc_nounwind] +/// # Safety +pub const unsafe fn simd_insert(x: T, idx: u32, val: U) -> T; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs new file mode 100644 index 0000000000000..9f83ed3253369 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs @@ -0,0 +1,17 @@ +//@compile-flags: --test +#![warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + //~^ exit + } +} + +fn main() { + if true { + std::process::exit(2); + }; + not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr b/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr new file mode 100644 index 0000000000000..6e33c39f0d7b9 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr @@ -0,0 +1,11 @@ +error: usage of `process::exit` + --> tests/ui/exit1_compile_flag_test.rs:6:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::exit)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs new file mode 100644 index 0000000000000..0b994ebc56c3e --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs @@ -0,0 +1,15 @@ +//@compile-flags: --test +#![warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); + //~^ exit +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr b/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr new file mode 100644 index 0000000000000..51eb26e9c2a46 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr @@ -0,0 +1,11 @@ +error: usage of `process::exit` + --> tests/ui/exit2_compile_flag_test.rs:5:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::exit)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs new file mode 100644 index 0000000000000..f8131ead2da30 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@compile-flags: --test + +#![warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +} diff --git a/src/tools/clippy/tests/ui/exit4.rs b/src/tools/clippy/tests/ui/exit4.rs new file mode 100644 index 0000000000000..821a26fd78b05 --- /dev/null +++ b/src/tools/clippy/tests/ui/exit4.rs @@ -0,0 +1,8 @@ +//@ check-pass +//@compile-flags: --test + +#![warn(clippy::exit)] + +fn main() { + std::process::exit(0) +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed index 83aeddb2a1f4a..884bae0043204 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed @@ -69,3 +69,47 @@ fn _issue11831() { let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + let _ = 2.0f64.mul_add(x, 0.5); + //~^ suboptimal_flops + + let _ = 2.0f64.mul_add(4.0, x); + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y.mul_add(2.0, 0.5); + //~^ suboptimal_flops + let _ = 1.0f64.mul_add(2.0, 0.5); + //~^ suboptimal_flops +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs index 039ee8d053fc9..9ceb2ec96062f 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs @@ -69,3 +69,47 @@ fn _issue11831() { let _ = a + b * c; } + +fn _issue14897() { + let x = 1.0; + let _ = x * 2.0 + 0.5; // should not suggest mul_add + let _ = 0.5 + x * 2.0; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + let _ = 1.2 + x * 1.2; // should not suggest mul_add + + let x = -1.0; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = { 4.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = if 1 > 2 { 1.0 } else { 2.0 }; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 2.4 + 1.2; + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let f = || 4.0; + let x = f(); + let _ = 0.5 + f() * 1.2; // should not suggest mul_add + let _ = 0.5 + x * 1.2; // should not suggest mul_add + + let x = 0.1; + let y = x; + let z = y; + let _ = 0.5 + z * 1.2; // should not suggest mul_add + + let _ = 0.5 + 2.0 * x; + //~^ suboptimal_flops + let _ = 2.0 * x + 0.5; + //~^ suboptimal_flops + + let _ = x + 2.0 * 4.0; + //~^ suboptimal_flops + + let y: f64 = 1.0; + let _ = y * 2.0 + 0.5; + //~^ suboptimal_flops + let _ = 1.0 * 2.0 + 0.5; + //~^ suboptimal_flops +} diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr index 6482127bcc00b..dad65ddf2ec39 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -79,5 +79,35 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: aborting due to 13 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:102:13 + | +LL | let _ = 0.5 + 2.0 * x; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:104:13 + | +LL | let _ = 2.0 * x + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:107:13 + | +LL | let _ = x + 2.0 * 4.0; + | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:111:13 + | +LL | let _ = y * 2.0 + 0.5; + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> tests/ui/floating_point_mul_add.rs:113:13 + | +LL | let _ = 1.0 * 2.0 + 0.5; + | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed index 6425f32c09c42..65a9cfa6e64c2 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed @@ -61,7 +61,7 @@ fn option_methods() { let _ = Some(2).is_some_and(|x| x % 2 == 0); //~^ manual_is_variant_and - let _ = Some(2).is_none_or(|x| x % 2 == 0); + let _ = Some(2).is_none_or(|x| x % 2 != 0); //~^ manual_is_variant_and let _ = Some(2).is_some_and(|x| x % 2 == 0); //~^ manual_is_variant_and @@ -116,3 +116,113 @@ fn main() { option_methods(); result_methods(); } + +fn issue15202() { + let xs = [None, Some(b'_'), Some(b'1')]; + for x in xs { + let a1 = x.is_none_or(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_none_or(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_some_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_some_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + let xs = [Err("foo"), Ok(b'_'), Ok(b'1')]; + for x in xs { + let a1 = !x.is_ok_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = !x.is_ok_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_ok_and(|b| b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.is_ok_and(|b| !b.is_ascii_digit()); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } +} + +mod with_func { + fn iad(b: u8) -> bool { + b.is_ascii_digit() + } + + fn check_option(b: Option) { + let a1 = b.is_some_and(iad); + //~^ manual_is_variant_and + let a2 = b.is_some_and(iad); + assert_eq!(a1, a2); + + let a1 = b.is_some_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_some_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.is_none_or(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_none_or(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.is_none_or(iad); + //~^ manual_is_variant_and + let a2 = b.is_none_or(iad); + assert_eq!(a1, a2); + } + + fn check_result(b: Result) { + let a1 = b.is_ok_and(iad); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.is_ok_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = !b.is_ok_and(iad); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = !b.is_ok_and(|x| !iad(x)); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + } +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.rs b/src/tools/clippy/tests/ui/manual_is_variant_and.rs index e069e97a04ddc..85b45d654a7d0 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.rs +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.rs @@ -125,3 +125,113 @@ fn main() { option_methods(); result_methods(); } + +fn issue15202() { + let xs = [None, Some(b'_'), Some(b'1')]; + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); + //~^ manual_is_variant_and + let a2 = x.is_none_or(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); + //~^ manual_is_variant_and + let a2 = x.is_some_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + let xs = [Err("foo"), Ok(b'_'), Ok(b'1')]; + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); + //~^ manual_is_variant_and + let a2 = !x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| b.is_ascii_digit()); + assert_eq!(a1, a2); + } + + for x in xs { + let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); + //~^ manual_is_variant_and + let a2 = x.is_ok_and(|b| !b.is_ascii_digit()); + assert_eq!(a1, a2); + } +} + +mod with_func { + fn iad(b: u8) -> bool { + b.is_ascii_digit() + } + + fn check_option(b: Option) { + let a1 = b.map(iad) == Some(true); + //~^ manual_is_variant_and + let a2 = b.is_some_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) == Some(false); + //~^ manual_is_variant_and + let a2 = b.is_some_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Some(true); + //~^ manual_is_variant_and + let a2 = b.is_none_or(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Some(false); + //~^ manual_is_variant_and + let a2 = b.is_none_or(iad); + assert_eq!(a1, a2); + } + + fn check_result(b: Result) { + let a1 = b.map(iad) == Ok(true); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) == Ok(false); + //~^ manual_is_variant_and + let a2 = b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Ok(true); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(iad); + assert_eq!(a1, a2); + + let a1 = b.map(iad) != Ok(false); + //~^ manual_is_variant_and + let a2 = !b.is_ok_and(|x| !iad(x)); + assert_eq!(a1, a2); + } +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr index f770319a2681e..da36b5a07d210 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr @@ -54,7 +54,7 @@ error: called `.map() != Some()` --> tests/ui/manual_is_variant_and.rs:70:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 != 0)` error: called `.map() == Some()` --> tests/ui/manual_is_variant_and.rs:72:13 @@ -126,5 +126,101 @@ error: called `map().unwrap_or_default()` on a `Result` value LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` -error: aborting due to 15 previous errors +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:132:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| !b.is_ascii_digit())` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:139:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:146:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:153:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| !b.is_ascii_digit())` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:161:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| b.is_ascii_digit())` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:168:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| !b.is_ascii_digit())` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:175:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| b.is_ascii_digit())` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:182:18 + | +LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| !b.is_ascii_digit())` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:195:18 + | +LL | let a1 = b.map(iad) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(iad)` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:200:18 + | +LL | let a1 = b.map(iad) == Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(|x| !iad(x))` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:205:18 + | +LL | let a1 = b.map(iad) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(|x| !iad(x))` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:210:18 + | +LL | let a1 = b.map(iad) != Some(false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(iad)` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:217:18 + | +LL | let a1 = b.map(iad) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(iad)` + +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:222:18 + | +LL | let a1 = b.map(iad) == Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(|x| !iad(x))` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:227:18 + | +LL | let a1 = b.map(iad) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(iad)` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:232:18 + | +LL | let a1 = b.map(iad) != Ok(false); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.fixed b/src/tools/clippy/tests/ui/manual_let_else_match.fixed index 588ba5edd8f15..15f604aec2928 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.fixed +++ b/src/tools/clippy/tests/ui/manual_let_else_match.fixed @@ -137,3 +137,48 @@ fn not_fire() { fn issue11579() { let Some(msg) = Some("hi") else { unreachable!("can't happen") }; } + +#[derive(Clone, Copy)] +struct Issue9939 { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") }; + let issue = Some(Issue9939 { avalanche: true }); + let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") }; + + // with shadowing + let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") }; +} + +#[derive(Clone, Copy)] +struct Issue9939b { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") }; + assert!(erosion); + + // with shadowing + let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; + assert!(erosion); +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs index 6416753bac107..44a044b142bd8 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.rs +++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs @@ -177,3 +177,76 @@ fn issue11579() { _ => unreachable!("can't happen"), }; } + +#[derive(Clone, Copy)] +struct Issue9939 { + avalanche: T, +} + +fn issue9939() { + let issue = Some(Issue9939 { avalanche: 1 }); + let tornado = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche }) => avalanche, + _ => unreachable!("can't happen"), + }; + let issue = Some(Issue9939 { avalanche: true }); + let acid_rain = match issue { + //~^ manual_let_else + Some(Issue9939 { avalanche: tornado }) => tornado, + _ => unreachable!("can't happen"), + }; + assert_eq!(tornado, 1); + assert!(acid_rain); + + // without shadowing + let _y = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; + + // with shadowing + let _x = match issue { + //~^ manual_let_else + _x @ Some(Issue9939 { avalanche }) => avalanche, + None => unreachable!("can't happen"), + }; +} + +#[derive(Clone, Copy)] +struct Issue9939b { + earthquake: T, + hurricane: U, +} + +fn issue9939b() { + let issue = Some(Issue9939b { + earthquake: true, + hurricane: 1, + }); + let (issue, drought, flood) = match issue { + //~^ manual_let_else + flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert_eq!(drought, 1); + assert!(flood); + assert!(issue.is_some()); + + // without shadowing + let (_y, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); + + // with shadowing + let (_x, erosion) = match issue { + //~^ manual_let_else + _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), + None => unreachable!("can't happen"), + }; + assert!(erosion); +} diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index 393562c629bac..ed6117ebffb7d 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -101,5 +101,75 @@ LL | | _ => unreachable!("can't happen"), LL | | }; | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };` -error: aborting due to 10 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:188:5 + | +LL | / let tornado = match issue { +LL | | +LL | | Some(Issue9939 { avalanche }) => avalanche, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:194:5 + | +LL | / let acid_rain = match issue { +LL | | +LL | | Some(Issue9939 { avalanche: tornado }) => tornado, +LL | | _ => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:203:5 + | +LL | / let _y = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:210:5 + | +LL | / let _x = match issue { +LL | | +LL | | _x @ Some(Issue9939 { avalanche }) => avalanche, +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:228:5 + | +LL | / let (issue, drought, flood) = match issue { +LL | | +LL | | flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:238:5 + | +LL | / let (_y, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") };` + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:246:5 + | +LL | / let (_x, erosion) = match issue { +LL | | +LL | | _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake), +LL | | None => unreachable!("can't happen"), +LL | | }; + | |______^ help: consider writing: `let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index ffdae8504f72e..d016e099e303b 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -250,3 +250,31 @@ pub fn issue_12760() { } } } + +/// This needs documenting +pub fn unwrap_expect_etc_in_const() { + let a = const { std::num::NonZeroUsize::new(1).unwrap() }; + // This should still pass the lint even if it is guaranteed to panic at compile-time + let b = const { std::num::NonZeroUsize::new(0).unwrap() }; +} + +/// This needs documenting +pub const fn unwrap_expect_etc_in_const_fn_fails() { + //~^ missing_panics_doc + let a = std::num::NonZeroUsize::new(1).unwrap(); +} + +/// This needs documenting +pub const fn assert_in_const_fn_fails() { + //~^ missing_panics_doc + let x = 0; + if x == 0 { + panic!(); + } +} + +/// This needs documenting +pub const fn in_const_fn(n: usize) { + //~^ missing_panics_doc + assert!(N > n); +} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr index 7f0acf8de9b77..85a0091442734 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr @@ -180,5 +180,41 @@ note: first possible panic found here LL | *v.last().expect("passed an empty thing") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:262:1 + | +LL | pub const fn unwrap_expect_etc_in_const_fn_fails() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:264:13 + | +LL | let a = std::num::NonZeroUsize::new(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:268:1 + | +LL | pub const fn assert_in_const_fn_fails() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:272:9 + | +LL | panic!(); + | ^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> tests/ui/missing_panics_doc.rs:277:1 + | +LL | pub const fn in_const_fn(n: usize) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first possible panic found here + --> tests/ui/missing_panics_doc.rs:279:5 + | +LL | assert!(N > n); + | ^^^^^^^^^^^^^^ + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed index e0c717ecda21b..d6fab4c51b538 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed +++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed @@ -33,3 +33,12 @@ fn main() { b = true; } } + +fn issue15063(x: bool, y: bool) { + let mut z = false; + + if x && y { + todo!() + } else { z = x || y; } + //~^^^^^ needless_bool_assign +} diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs index 3e4fecefa7852..c504f61f4dd1d 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.rs +++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs @@ -45,3 +45,16 @@ fn main() { b = true; } } + +fn issue15063(x: bool, y: bool) { + let mut z = false; + + if x && y { + todo!() + } else if x || y { + z = true; + } else { + z = false; + } + //~^^^^^ needless_bool_assign +} diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index f33a4bc0c592c..1d09b8b25a091 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -51,5 +51,16 @@ LL | | } = note: `-D clippy::if-same-then-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` -error: aborting due to 4 previous errors +error: this if-then-else expression assigns a bool literal + --> tests/ui/needless_bool_assign.rs:54:12 + | +LL | } else if x || y { + | ____________^ +LL | | z = true; +LL | | } else { +LL | | z = false; +LL | | } + | |_____^ help: you can reduce it to: `{ z = x || y; }` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed index ff6e08300e298..32d466e88fc8b 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.fixed +++ b/src/tools/clippy/tests/ui/neg_multiply.fixed @@ -82,3 +82,15 @@ fn float() { -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64, +} + +fn nested() { + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs index b0f4e85c78e5d..241a72c6d990d 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.rs +++ b/src/tools/clippy/tests/ui/neg_multiply.rs @@ -82,3 +82,15 @@ fn float() { -1.0 * -1.0; // should be ok } + +struct Y { + delta: f64, +} + +fn nested() { + let a = Y { delta: 1.0 }; + let b = Y { delta: 1.0 }; + let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + //~^ neg_multiply + let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0); +} diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr index 2ef7e32ce05e1..f4fb6d3ce5414 100644 --- a/src/tools/clippy/tests/ui/neg_multiply.stderr +++ b/src/tools/clippy/tests/ui/neg_multiply.stderr @@ -97,5 +97,11 @@ error: this multiplication by -1 can be written more succinctly LL | (3.0_f32 as f64) * -1.0; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)` -error: aborting due to 16 previous errors +error: this multiplication by -1 can be written more succinctly + --> tests/ui/neg_multiply.rs:93:13 + | +LL | let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-(a.delta - 0.5).abs())` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/op_ref.fixed b/src/tools/clippy/tests/ui/op_ref.fixed index f412190b9fd9e..4bf4b91888c8c 100644 --- a/src/tools/clippy/tests/ui/op_ref.fixed +++ b/src/tools/clippy/tests/ui/op_ref.fixed @@ -110,3 +110,37 @@ mod issue_2597 { &array[idx] < val } } + +#[allow(clippy::needless_if)] +fn issue15063() { + use std::ops::BitAnd; + + macro_rules! mac { + ($e:expr) => { + $e.clone() + }; + } + + let x = 1; + if x == mac!(1) {} + //~^ op_ref + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & mac!(y); + //~^ op_ref +} diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs index a4bbd86c7e95b..9a192661aafcd 100644 --- a/src/tools/clippy/tests/ui/op_ref.rs +++ b/src/tools/clippy/tests/ui/op_ref.rs @@ -110,3 +110,37 @@ mod issue_2597 { &array[idx] < val } } + +#[allow(clippy::needless_if)] +fn issue15063() { + use std::ops::BitAnd; + + macro_rules! mac { + ($e:expr) => { + $e.clone() + }; + } + + let x = 1; + if &x == &mac!(1) {} + //~^ op_ref + + #[derive(Copy, Clone)] + struct Y(i32); + impl BitAnd for Y { + type Output = Y; + fn bitand(self, rhs: Y) -> Y { + Y(self.0 & rhs.0) + } + } + impl<'a> BitAnd<&'a Y> for Y { + type Output = Y; + fn bitand(self, rhs: &'a Y) -> Y { + Y(self.0 & rhs.0) + } + } + let x = Y(1); + let y = Y(2); + let z = x & &mac!(y); + //~^ op_ref +} diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr index 51c2963a9eed2..8a58b154c8109 100644 --- a/src/tools/clippy/tests/ui/op_ref.stderr +++ b/src/tools/clippy/tests/ui/op_ref.stderr @@ -36,5 +36,25 @@ LL | let _ = two + &three; | | | help: use the right value directly: `three` -error: aborting due to 4 previous errors +error: needlessly taken reference of both operands + --> tests/ui/op_ref.rs:125:8 + | +LL | if &x == &mac!(1) {} + | ^^^^^^^^^^^^^^ + | +help: use the values directly + | +LL - if &x == &mac!(1) {} +LL + if x == mac!(1) {} + | + +error: taken reference of right operand + --> tests/ui/op_ref.rs:144:13 + | +LL | let z = x & &mac!(y); + | ^^^^-------- + | | + | help: use the right value directly: `mac!(y)` + +error: aborting due to 6 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 34f3e04684193..bcd2602edb6ad 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -439,4 +439,24 @@ fn test_option_get_or_insert() { //~^ or_fun_call } +fn test_option_and() { + // assume that this is slow call + fn g() -> Option { + Some(99) + } + let mut x = Some(42_u8); + let _ = x.and_then(|_| g()); + //~^ or_fun_call +} + +fn test_result_and() { + // assume that this is slow call + fn g() -> Result { + Ok(99) + } + let mut x: Result = Ok(42); + let _ = x.and_then(|_| g()); + //~^ or_fun_call +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs index dc57bd6060ac0..8d1202ebf914a 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.rs +++ b/src/tools/clippy/tests/ui/or_fun_call.rs @@ -439,4 +439,24 @@ fn test_option_get_or_insert() { //~^ or_fun_call } +fn test_option_and() { + // assume that this is slow call + fn g() -> Option { + Some(99) + } + let mut x = Some(42_u8); + let _ = x.and(g()); + //~^ or_fun_call +} + +fn test_result_and() { + // assume that this is slow call + fn g() -> Result { + Ok(99) + } + let mut x: Result = Ok(42); + let _ = x.and(g()); + //~^ or_fun_call +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index 0f159fe8bff4b..585ee2d0e19d1 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -264,5 +264,17 @@ error: function call inside of `get_or_insert` LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` -error: aborting due to 41 previous errors +error: function call inside of `and` + --> tests/ui/or_fun_call.rs:448:15 + | +LL | let _ = x.and(g()); + | ^^^^^^^^ help: try: `and_then(|_| g())` + +error: function call inside of `and` + --> tests/ui/or_fun_call.rs:458:15 + | +LL | let _ = x.and(g()); + | ^^^^^^^^ help: try: `and_then(|_| g())` + +error: aborting due to 43 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed index 099c118e64e35..9f6643e8d52e8 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed @@ -60,7 +60,7 @@ fn issue9956() { //~^ redundant_closure_call // immediately calling only one closure, so we can't remove the other ones - let a = (|| || 123); + let a = || || 123; //~^ redundant_closure_call dbg!(a()()); @@ -144,3 +144,15 @@ fn issue_12358() { // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + Some(true) == Some(true); + //~^ redundant_closure_call + Some(true) == Some(true); + //~^ redundant_closure_call + Some(if 1 > 2 {1} else {2}) == Some(2); + //~^ redundant_closure_call + Some( 1 > 2 ) == Some(true); + //~^ redundant_closure_call +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs index da5dd7ef263b6..34f228786786b 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs @@ -144,3 +144,15 @@ fn issue_12358() { // different. make_closure!(x)(); } + +#[rustfmt::skip] +fn issue_9583() { + (|| { Some(true) })() == Some(true); + //~^ redundant_closure_call + (|| Some(true))() == Some(true); + //~^ redundant_closure_call + (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + //~^ redundant_closure_call + (|| { Some( 1 > 2 ) })() == Some(true); + //~^ redundant_closure_call +} diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr index 2c35aafbe3109..a5591cf7813b7 100644 --- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr +++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr @@ -95,7 +95,7 @@ error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:63:13 | LL | let a = (|| || || 123)(); - | ^^^^^^^^^^^^^^^^ help: try doing something like: `(|| || 123)` + | ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123` error: try not to call a closure in the expression where it is declared --> tests/ui/redundant_closure_call_fixable.rs:68:13 @@ -145,5 +145,29 @@ error: try not to call a closure in the expression where it is declared LL | std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32` -error: aborting due to 17 previous errors +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:150:5 + | +LL | (|| { Some(true) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:152:5 + | +LL | (|| Some(true))() == Some(true); + | ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:154:5 + | +LL | (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})` + +error: try not to call a closure in the expression where it is declared + --> tests/ui/redundant_closure_call_fixable.rs:156:5 + | +LL | (|| { Some( 1 > 2 ) })() == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )` + +error: aborting due to 21 previous errors diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed index 8d9481d159512..8ee259b97f3d4 100644 --- a/src/tools/clippy/tests/ui/return_and_then.fixed +++ b/src/tools/clippy/tests/ui/return_and_then.fixed @@ -99,6 +99,92 @@ fn main() { }; None } + + #[expect(clippy::diverging_sub_expression)] + fn with_return_in_expression() -> Option { + _ = ( + return { + let x = Some("")?; + if x.len() > 2 { Some(3) } else { None } + }, + //~^ return_and_then + 10, + ); + } + + fn inside_if(a: bool, i: Option) -> Option { + if a { + let i = i?; + if i > 3 { Some(i) } else { None } + //~^ return_and_then + } else { + Some(42) + } + } + + fn inside_match(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let i = i?; + if i > 3 { Some(i) } else { None } + }, + //~^ return_and_then + 3 | 4 => Some(42), + _ => None, + } + } + + fn inside_match_and_block_and_if(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let a = a * 3; + if a.is_multiple_of(2) { + let i = i?; + if i > 3 { Some(i) } else { None } + //~^ return_and_then + } else { + Some(10) + } + }, + 3 | 4 => Some(42), + _ => None, + } + } + + #[expect(clippy::never_loop)] + fn with_break(i: Option) -> Option { + match i { + Some(1) => loop { + break ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + }, + Some(2) => 'foo: loop { + loop { + break 'foo ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + } + }, + Some(3) => 'bar: { + break 'bar ({ + let i = i?; + if i > 3 { Some(i) } else { None } + }); + //~^ return_and_then + }, + Some(4) => 'baz: loop { + _ = loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + }; + }, + _ => None, + } + } } fn gen_option(n: i32) -> Option { @@ -124,3 +210,48 @@ mod issue14781 { Ok(()) } } + +mod issue15111 { + #[derive(Debug)] + struct EvenOdd { + even: Option, + odd: Option, + } + + impl EvenOdd { + fn new(i: Option) -> Self { + Self { + even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }), + odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }), + } + } + } + + fn with_if_let(i: Option) -> u32 { + if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) { + x + } else { + std::hint::black_box(0) + } + } + + fn main() { + let _ = EvenOdd::new(Some(2)); + } +} + +mod issue14927 { + use std::path::Path; + struct A { + pub func: fn(check: bool, a: &Path, b: Option<&Path>), + } + const MY_A: A = A { + func: |check, a, b| { + if check { + let _ = (); + } else if let Some(parent) = b.and_then(|p| p.parent()) { + let _ = (); + } + }, + }; +} diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs index beada921a9187..dcb344f142bb6 100644 --- a/src/tools/clippy/tests/ui/return_and_then.rs +++ b/src/tools/clippy/tests/ui/return_and_then.rs @@ -90,6 +90,75 @@ fn main() { }; None } + + #[expect(clippy::diverging_sub_expression)] + fn with_return_in_expression() -> Option { + _ = ( + return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }), + //~^ return_and_then + 10, + ); + } + + fn inside_if(a: bool, i: Option) -> Option { + if a { + i.and_then(|i| if i > 3 { Some(i) } else { None }) + //~^ return_and_then + } else { + Some(42) + } + } + + fn inside_match(a: u32, i: Option) -> Option { + match a { + 1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }), + //~^ return_and_then + 3 | 4 => Some(42), + _ => None, + } + } + + fn inside_match_and_block_and_if(a: u32, i: Option) -> Option { + match a { + 1 | 2 => { + let a = a * 3; + if a.is_multiple_of(2) { + i.and_then(|i| if i > 3 { Some(i) } else { None }) + //~^ return_and_then + } else { + Some(10) + } + }, + 3 | 4 => Some(42), + _ => None, + } + } + + #[expect(clippy::never_loop)] + fn with_break(i: Option) -> Option { + match i { + Some(1) => loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + }, + Some(2) => 'foo: loop { + loop { + break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + } + }, + Some(3) => 'bar: { + break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None }); + //~^ return_and_then + }, + Some(4) => 'baz: loop { + _ = loop { + break i.and_then(|i| if i > 3 { Some(i) } else { None }); + }; + }, + _ => None, + } + } } fn gen_option(n: i32) -> Option { @@ -115,3 +184,48 @@ mod issue14781 { Ok(()) } } + +mod issue15111 { + #[derive(Debug)] + struct EvenOdd { + even: Option, + odd: Option, + } + + impl EvenOdd { + fn new(i: Option) -> Self { + Self { + even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }), + odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }), + } + } + } + + fn with_if_let(i: Option) -> u32 { + if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) { + x + } else { + std::hint::black_box(0) + } + } + + fn main() { + let _ = EvenOdd::new(Some(2)); + } +} + +mod issue14927 { + use std::path::Path; + struct A { + pub func: fn(check: bool, a: &Path, b: Option<&Path>), + } + const MY_A: A = A { + func: |check, a, b| { + if check { + let _ = (); + } else if let Some(parent) = b.and_then(|p| p.parent()) { + let _ = (); + } + }, + }; +} diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr index 5feca88286057..33867ea818aa7 100644 --- a/src/tools/clippy/tests/ui/return_and_then.stderr +++ b/src/tools/clippy/tests/ui/return_and_then.stderr @@ -146,5 +146,99 @@ LL + if x.len() > 2 { Some(3) } else { None } LL ~ }; | -error: aborting due to 10 previous errors +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:97:20 + | +LL | return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ return { +LL + let x = Some("")?; +LL + if x.len() > 2 { Some(3) } else { None } +LL ~ }, + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:105:13 + | +LL | i.and_then(|i| if i > 3 { Some(i) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let i = i?; +LL + if i > 3 { Some(i) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:114:22 + | +LL | 1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ 1 | 2 => { +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }, + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:126:21 + | +LL | i.and_then(|i| if i > 3 { Some(i) } else { None }) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ let i = i?; +LL + if i > 3 { Some(i) } else { None } + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:141:23 + | +LL | break i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:146:32 + | +LL | break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break 'foo ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: use the `?` operator instead of an `and_then` call + --> tests/ui/return_and_then.rs:151:28 + | +LL | break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ break 'bar ({ +LL + let i = i?; +LL + if i > 3 { Some(i) } else { None } +LL ~ }); + | + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 1820ade422ff5..603ab0accb0b6 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -8,7 +8,6 @@ extern crate alloc; #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use core::hash::Hasher; @@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 32c4933098161..b6d4abad9f8fe 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -8,7 +8,6 @@ extern crate alloc; #[macro_use] extern crate proc_macro_derive; -#[warn(clippy::std_instead_of_core)] fn std_instead_of_core() { // Regular import use std::hash::Hasher; @@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core - -#[warn(clippy::std_instead_of_core)] -#[rustfmt::skip] -fn issue14982() { - use std::{collections::HashMap, hash::Hash}; -} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index 45d60d235ceb3..a5f8fbbe37cb8 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:14:9 + --> tests/ui/std_instead_of_core.rs:13:9 | LL | use std::hash::Hasher; | ^^^ help: consider importing the item from `core`: `core` @@ -8,61 +8,61 @@ LL | use std::hash::Hasher; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:17:11 + --> tests/ui/std_instead_of_core.rs:16:11 | LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:23:9 + --> tests/ui/std_instead_of_core.rs:22:9 | LL | use std::fmt::{Debug, Result}; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:28:9 + --> tests/ui/std_instead_of_core.rs:27:9 | LL | use std::{ | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:35:15 + --> tests/ui/std_instead_of_core.rs:34:15 | LL | let ptr = std::ptr::null::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:37:21 + --> tests/ui/std_instead_of_core.rs:36:21 | LL | let ptr_mut = ::std::ptr::null_mut::(); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:41:16 + --> tests/ui/std_instead_of_core.rs:40:16 | LL | let cell = std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:43:27 + --> tests/ui/std_instead_of_core.rs:42:27 | LL | let cell_absolute = ::std::cell::Cell::new(8u32); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:48:9 + --> tests/ui/std_instead_of_core.rs:47:9 | LL | use std::error::Error; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:52:9 + --> tests/ui/std_instead_of_core.rs:51:9 | LL | use std::iter::Iterator; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:59:9 + --> tests/ui/std_instead_of_core.rs:58:9 | LL | use std::vec; | ^^^ help: consider importing the item from `alloc`: `alloc` @@ -71,13 +71,13 @@ LL | use std::vec; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core.rs:61:9 + --> tests/ui/std_instead_of_core.rs:60:9 | LL | use std::vec::Vec; | ^^^ help: consider importing the item from `alloc`: `alloc` error: used import from `alloc` instead of `core` - --> tests/ui/std_instead_of_core.rs:67:9 + --> tests/ui/std_instead_of_core.rs:66:9 | LL | use alloc::slice::from_ref; | ^^^^^ help: consider importing the item from `core`: `core` @@ -86,13 +86,13 @@ LL | use alloc::slice::from_ref; = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:82:9 + --> tests/ui/std_instead_of_core.rs:81:9 | LL | std::intrinsics::copy(a, b, 1); | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:91:17 + --> tests/ui/std_instead_of_core.rs:90:17 | LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs new file mode 100644 index 0000000000000..957f472a45448 --- /dev/null +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs @@ -0,0 +1,18 @@ +//@no-rustfix + +#![warn(clippy::std_instead_of_core)] +#![warn(clippy::std_instead_of_alloc)] +#![allow(unused_imports)] + +#[rustfmt::skip] +fn issue14982() { + use std::{collections::HashMap, hash::Hash}; + //~^ std_instead_of_core +} + +#[rustfmt::skip] +fn issue15143() { + use std::{error::Error, vec::Vec, fs::File}; + //~^ std_instead_of_core + //~| std_instead_of_alloc +} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr new file mode 100644 index 0000000000000..0cdec56c99274 --- /dev/null +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr @@ -0,0 +1,30 @@ +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:9:43 + | +LL | use std::{collections::HashMap, hash::Hash}; + | ^^^^ + | + = help: consider importing the item from `core` + = note: `-D clippy::std-instead-of-core` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core_unfixable.rs:15:22 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^^^ + | + = help: consider importing the item from `core` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:15:34 + | +LL | use std::{error::Error, vec::Vec, fs::File}; + | ^^^ + | + = help: consider importing the item from `alloc` + = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.fixed b/src/tools/clippy/tests/ui/swap_with_temporary.fixed index 4007d998ba068..4b4b0d4aebd2b 100644 --- a/src/tools/clippy/tests/ui/swap_with_temporary.fixed +++ b/src/tools/clippy/tests/ui/swap_with_temporary.fixed @@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { swap(&mut s.t, v.get_mut(0).unwrap()); swap(w.unwrap(), &mut s.t); } + +fn issue15166() { + use std::sync::Mutex; + + struct A { + thing: Mutex>, + } + + impl A { + fn a(&self) { + let mut new_vec = vec![42]; + // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries + swap(&mut new_vec, &mut self.thing.lock().unwrap()); + for v in new_vec { + // Do something with v + } + // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix + *self.thing.lock().unwrap() = vec![42]; + //~^ ERROR: swapping with a temporary value is inefficient + } + } +} + +fn multiple_deref() { + let mut v1 = &mut &mut &mut vec![42]; + ***v1 = vec![]; + //~^ ERROR: swapping with a temporary value is inefficient + + struct Wrapper(T); + impl std::ops::Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + use std::sync::Mutex; + let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42]))); + ***v1.lock().unwrap() = vec![]; + //~^ ERROR: swapping with a temporary value is inefficient +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.rs b/src/tools/clippy/tests/ui/swap_with_temporary.rs index d403c086c0f4f..8e35e6144d99a 100644 --- a/src/tools/clippy/tests/ui/swap_with_temporary.rs +++ b/src/tools/clippy/tests/ui/swap_with_temporary.rs @@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) { swap(&mut s.t, v.get_mut(0).unwrap()); swap(w.unwrap(), &mut s.t); } + +fn issue15166() { + use std::sync::Mutex; + + struct A { + thing: Mutex>, + } + + impl A { + fn a(&self) { + let mut new_vec = vec![42]; + // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries + swap(&mut new_vec, &mut self.thing.lock().unwrap()); + for v in new_vec { + // Do something with v + } + // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix + swap(&mut vec![42], &mut self.thing.lock().unwrap()); + //~^ ERROR: swapping with a temporary value is inefficient + } + } +} + +fn multiple_deref() { + let mut v1 = &mut &mut &mut vec![42]; + swap(&mut ***v1, &mut vec![]); + //~^ ERROR: swapping with a temporary value is inefficient + + struct Wrapper(T); + impl std::ops::Deref for Wrapper { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + impl std::ops::DerefMut for Wrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + use std::sync::Mutex; + let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42]))); + swap(&mut vec![], &mut v1.lock().unwrap()); + //~^ ERROR: swapping with a temporary value is inefficient +} diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.stderr b/src/tools/clippy/tests/ui/swap_with_temporary.stderr index 59355771a9648..5ca4fccd37a2e 100644 --- a/src/tools/clippy/tests/ui/swap_with_temporary.stderr +++ b/src/tools/clippy/tests/ui/swap_with_temporary.stderr @@ -96,5 +96,41 @@ note: this expression returns a temporary value LL | swap(mac!(refmut y), &mut func()); | ^^^^^^ -error: aborting due to 8 previous errors +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:92:13 + | +LL | swap(&mut vec![42], &mut self.thing.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*self.thing.lock().unwrap() = vec![42]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:92:23 + | +LL | swap(&mut vec![42], &mut self.thing.lock().unwrap()); + | ^^^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:100:5 + | +LL | swap(&mut ***v1, &mut vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1 = vec![]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:100:27 + | +LL | swap(&mut ***v1, &mut vec![]); + | ^^^^^^ + +error: swapping with a temporary value is inefficient + --> tests/ui/swap_with_temporary.rs:118:5 + | +LL | swap(&mut vec![], &mut v1.lock().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1.lock().unwrap() = vec![]` + | +note: this expression returns a temporary value + --> tests/ui/swap_with_temporary.rs:118:15 + | +LL | swap(&mut vec![], &mut v1.lock().unwrap()); + | ^^^^^^ + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs index 2e67fb65efcd9..3bae23f198495 100644 --- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs +++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs @@ -4,6 +4,7 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@normalize-stderr-test: "src/tools/clippy/" -> "" #![warn(clippy::let_and_return, clippy::unnecessary_cast)] @@ -12,7 +13,7 @@ fn main() { let a = 3u32; let b = a as u32; //~^ unnecessary_cast - + // Check the provenance of a lint sent through `TyCtxt::node_span_lint()` let c = { let d = 42; diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr index 9d6538112bf05..d5533877b4515 100644 --- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr +++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr @@ -4,7 +4,7 @@ error: casting to the same type is unnecessary (`u32` -> `u32`) LL | let b = a as u32; | ^^^^^^^^ help: try: `a` | - = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/casts/unnecessary_cast.rs:LL:CC = note: `-D clippy::unnecessary-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` @@ -16,7 +16,7 @@ LL | let d = 42; LL | d | ^ | - = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index 3c7243972845f..3109c4af8e287 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -130,3 +130,13 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { //~^ unnecessary_map_or x && y } + +fn issue15180() { + let s = std::sync::Mutex::new(Some("foo")); + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); + //~^ unnecessary_map_or + + let s = &&&&Some("foo"); + _ = s.is_some_and(|s| s == "foo"); + //~^ unnecessary_map_or +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index e734a27badabd..52a55f9fc9e49 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -134,3 +134,13 @@ fn issue14201(a: Option, b: Option, s: &String) -> bool { //~^ unnecessary_map_or x && y } + +fn issue15180() { + let s = std::sync::Mutex::new(Some("foo")); + _ = s.lock().unwrap().map_or(false, |s| s == "foo"); + //~^ unnecessary_map_or + + let s = &&&&Some("foo"); + _ = s.map_or(false, |s| s == "foo"); + //~^ unnecessary_map_or +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 0f9466a6a6b30..99e17e8b34ba2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -326,5 +326,29 @@ LL - let y = b.map_or(true, |b| b == *s); LL + let y = b.is_none_or(|b| b == *s); | -error: aborting due to 26 previous errors +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:140:9 + | +LL | _ = s.lock().unwrap().map_or(false, |s| s == "foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - _ = s.lock().unwrap().map_or(false, |s| s == "foo"); +LL + _ = s.lock().unwrap().is_some_and(|s| s == "foo"); + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:144:9 + | +LL | _ = s.map_or(false, |s| s == "foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - _ = s.map_or(false, |s| s == "foo"); +LL + _ = s.is_some_and(|s| s == "foo"); + | + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed index 645b56fe95e74..ac9fa4de20a60 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed @@ -144,3 +144,16 @@ const fn foo() { assert!([42, 55].len() > get_usize()); //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box Option>; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs index 97e90269c5c0c..a3e6c6288ada5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_operation.rs +++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs @@ -150,3 +150,16 @@ const fn foo() { [42, 55][get_usize()]; //~^ unnecessary_operation } + +fn issue15173() { + // No lint as `Box::new(None)` alone would be ambiguous + Box::new(None) as Box>; +} + +#[expect(clippy::redundant_closure_call)] +fn issue15173_original(handler: impl FnOnce() -> MsU + Clone + 'static) { + Box::new(move |value| { + (|_| handler.clone()())(value); + None + }) as Box Option>; +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.fixed b/src/tools/clippy/tests/ui/zero_ptr.fixed index f2375d57f3a28..f9d9d2db17697 100644 --- a/src/tools/clippy/tests/ui/zero_ptr.fixed +++ b/src/tools/clippy/tests/ui/zero_ptr.fixed @@ -16,3 +16,11 @@ fn main() { let z = 0; let _ = z as *const usize; // this is currently not caught } + +const fn in_const_context() { + #[clippy::msrv = "1.23"] + let _: *const usize = 0 as *const _; + #[clippy::msrv = "1.24"] + let _: *const usize = std::ptr::null(); + //~^ zero_ptr +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.rs b/src/tools/clippy/tests/ui/zero_ptr.rs index ee01e426a43b4..41455fee5b5ff 100644 --- a/src/tools/clippy/tests/ui/zero_ptr.rs +++ b/src/tools/clippy/tests/ui/zero_ptr.rs @@ -16,3 +16,11 @@ fn main() { let z = 0; let _ = z as *const usize; // this is currently not caught } + +const fn in_const_context() { + #[clippy::msrv = "1.23"] + let _: *const usize = 0 as *const _; + #[clippy::msrv = "1.24"] + let _: *const usize = 0 as *const _; + //~^ zero_ptr +} diff --git a/src/tools/clippy/tests/ui/zero_ptr.stderr b/src/tools/clippy/tests/ui/zero_ptr.stderr index 8dc781f36258c..81269de6c607e 100644 --- a/src/tools/clippy/tests/ui/zero_ptr.stderr +++ b/src/tools/clippy/tests/ui/zero_ptr.stderr @@ -31,5 +31,11 @@ error: `0 as *mut _` detected LL | foo(0 as *const _, 0 as *mut _); | ^^^^^^^^^^^ help: try: `std::ptr::null_mut()` -error: aborting due to 5 previous errors +error: `0 as *const _` detected + --> tests/ui/zero_ptr.rs:24:27 + | +LL | let _: *const usize = 0 as *const _; + | ^^^^^^^^^^^^^ help: try: `std::ptr::null()` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 4f370758c0064..805baf2af6dd0 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -15,6 +15,8 @@ allow-unauthenticated = [ [close] +[transfer] + [issue-links] [mentions."clippy_lints/src/doc"] @@ -43,12 +45,15 @@ reviewed_label = "S-waiting-on-author" [autolabel."S-waiting-on-review"] new_pr = true +[concern] +# These labels are set when there are unresolved concerns, removed otherwise +labels = ["S-waiting-on-concerns"] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", "Manishearth", - "blyxyas", ] [assign.owners] diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index 865b9523c39e7..6f380ec8feef3 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -149,49 +149,45 @@

Clippy Lints

{# #}
{# #} {# #} {# #}
{# #}
{{Self::markdown(lint.docs)}}
{# #}
{# Applicability #} -
{# #} - Applicability: {# #} +
{# #} + Applicability: {#+ #} {{ lint.applicability_str() }} {# #} (?) {# #}
{# Clippy version #} -
{# #} - {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {# #} +
{# #} + {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #} {{lint.version}} {# #}
{# Open related issues #} -
{# #} +
{# #} Related Issues {# #}
{# Jump to source #} {% if let Some(id_location) = lint.id_location %} -
{# #} +
{# #} View Source {# #}
{% endif %} diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 285aa34e70186..ee13f1c0cd875 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -554,10 +554,10 @@ function addListeners() { return; } - if (event.target.classList.contains("lint-anchor")) { - lintAnchor(event); - } else if (event.target.classList.contains("copy-to-clipboard")) { + if (event.target.classList.contains("copy-to-clipboard")) { copyToClipboard(event); + } else if (event.target.classList.contains("anchor")) { + lintAnchor(event); } }); diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css index 3cc7a919c23ab..022ea87520004 100644 --- a/src/tools/clippy/util/gh-pages/style.css +++ b/src/tools/clippy/util/gh-pages/style.css @@ -50,11 +50,25 @@ div.panel div.panel-body button.open { .panel-heading { cursor: pointer; } -.panel-title { display: flex; flex-wrap: wrap;} -.panel-title .label { display: inline-block; } +.lint-title { + cursor: pointer; + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + display: flex; + flex-wrap: wrap; + background: var(--theme-hover); + color: var(--fg); + border: 1px solid var(--theme-popup-border); + padding: 10px 15px; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + gap: 4px; +} + +.lint-title .label { display: inline-block; } .panel-title-name { flex: 1; min-width: 400px;} -.panel-title-name span { vertical-align: bottom; } .panel .panel-title-name .anchor { display: none; } .panel:hover .panel-title-name .anchor { display: inline;} @@ -147,7 +161,7 @@ div.panel div.panel-body button.open { display: flex; flex-flow: column; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-top: 1px solid var(--theme-popup-border); } } @@ -156,12 +170,12 @@ div.panel div.panel-body button.open { display: flex; flex-flow: row; } - .lint-additional-info-item + .lint-additional-info-item { + .lint-additional-info-container > div + div { border-left: 1px solid var(--theme-popup-border); } } -.lint-additional-info-item { +.lint-additional-info-container > div { display: inline-flex; min-width: 200px; flex-grow: 1; diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 6bf968a3132ef..93133ea0bfd2e 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -971,6 +971,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-mips64", "only-msp430", "only-msvc", + "only-musl", "only-nightly", "only-nvptx64", "only-powerpc", diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index fa833b51fa31b..35555723f5d6d 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -34,4 +34,5 @@ windows-sys = { version = "0.59", features = [ "Wdk_Storage_FileSystem", ] } +# Make sure we are not part of the rustc workspace. [workspace] diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs index e4d244e14a4a1..0e11360bd5a02 100644 --- a/src/tools/run-make-support/src/symbols.rs +++ b/src/tools/run-make-support/src/symbols.rs @@ -1,6 +1,7 @@ +use std::collections::BTreeSet; use std::path::Path; -use object::{self, Object, ObjectSymbol, SymbolIterator}; +use object::{self, Object, ObjectSymbol}; /// Given an [`object::File`], find the exported dynamic symbol names via /// [`object::Object::exports`]. This does not distinguish between which section the symbols appear @@ -14,47 +15,180 @@ pub fn exported_dynamic_symbol_names<'file>(file: &'file object::File<'file>) -> .collect() } -/// Iterate through the symbols in an object file. See [`object::Object::symbols`]. +/// Check an object file's symbols for any matching **substrings**. That is, if an object file +/// contains a symbol named `hello_world`, it will be matched against a provided `substrings` of +/// `["hello", "bar"]`. +/// +/// Returns `true` if **any** of the symbols found in the object file at `path` contain a +/// **substring** listed in `substrings`. /// /// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be /// parsed as a recognized object file. +/// +/// # Platform-specific behavior +/// +/// On Windows MSVC, the binary (e.g. `main.exe`) does not contain the symbols, but in the separate +/// PDB file instead. Furthermore, you will need to use [`crate::llvm::llvm_pdbutil`] as `object` +/// crate does not handle PDB files. #[track_caller] -pub fn with_symbol_iter(path: P, func: F) -> R +pub fn object_contains_any_symbol_substring(path: P, substrings: &[S]) -> bool where P: AsRef, - F: FnOnce(&mut SymbolIterator<'_, '_>) -> R, + S: AsRef, { let path = path.as_ref(); let blob = crate::fs::read(path); - let f = object::File::parse(&*blob) + let obj = object::File::parse(&*blob) .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display())); - let mut iter = f.symbols(); - func(&mut iter) + let substrings = substrings.iter().map(|s| s.as_ref()).collect::>(); + for sym in obj.symbols() { + for substring in &substrings { + if sym.name_bytes().unwrap().windows(substring.len()).any(|x| x == substring.as_bytes()) + { + return true; + } + } + } + false } -/// Check an object file's symbols for substrings. +/// Check an object file's symbols for any exact matches against those provided in +/// `candidate_symbols`. /// -/// Returns `true` if any of the symbols found in the object file at `path` contain a substring -/// listed in `substrings`. +/// Returns `true` if **any** of the symbols found in the object file at `path` contain an **exact +/// match** against those listed in `candidate_symbols`. Take care to account for (1) platform +/// differences and (2) calling convention and symbol decorations differences. /// /// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be /// parsed as a recognized object file. +/// +/// # Platform-specific behavior +/// +/// See [`object_contains_any_symbol_substring`]. #[track_caller] -pub fn any_symbol_contains(path: impl AsRef, substrings: &[&str]) -> bool { - with_symbol_iter(path, |syms| { - for sym in syms { - for substring in substrings { - if sym - .name_bytes() - .unwrap() - .windows(substring.len()) - .any(|x| x == substring.as_bytes()) - { - eprintln!("{:?} contains {}", sym, substring); - return true; - } +pub fn object_contains_any_symbol(path: P, candidate_symbols: &[S]) -> bool +where + P: AsRef, + S: AsRef, +{ + let path = path.as_ref(); + let blob = crate::fs::read(path); + let obj = object::File::parse(&*blob) + .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display())); + let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref()).collect::>(); + for sym in obj.symbols() { + for candidate_symbol in &candidate_symbols { + if sym.name_bytes().unwrap() == candidate_symbol.as_bytes() { + return true; } } - false - }) + } + false +} + +#[derive(Debug, PartialEq)] +pub enum ContainsAllSymbolSubstringsOutcome<'a> { + Ok, + MissingSymbolSubstrings(BTreeSet<&'a str>), +} + +/// Check an object file's symbols for presence of all of provided **substrings**. That is, if an +/// object file contains symbols `["hello", "goodbye", "world"]`, it will be matched against a list +/// of `substrings` of `["he", "go"]`. In this case, `he` is a substring of `hello`, and `go` is a +/// substring of `goodbye`, so each of `substrings` was found. +/// +/// Returns `true` if **all** `substrings` were present in the names of symbols for the given object +/// file (as substrings of symbol names). +/// +/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be +/// parsed as a recognized object file. +/// +/// # Platform-specific behavior +/// +/// See [`object_contains_any_symbol_substring`]. +#[track_caller] +pub fn object_contains_all_symbol_substring<'s, P, S>( + path: P, + substrings: &'s [S], +) -> ContainsAllSymbolSubstringsOutcome<'s> +where + P: AsRef, + S: AsRef, +{ + let path = path.as_ref(); + let blob = crate::fs::read(path); + let obj = object::File::parse(&*blob) + .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display())); + let substrings = substrings.iter().map(|s| s.as_ref()); + let mut unmatched_symbol_substrings = BTreeSet::from_iter(substrings); + unmatched_symbol_substrings.retain(|unmatched_symbol_substring| { + for sym in obj.symbols() { + if sym + .name_bytes() + .unwrap() + .windows(unmatched_symbol_substring.len()) + .any(|x| x == unmatched_symbol_substring.as_bytes()) + { + return false; + } + } + + true + }); + + if unmatched_symbol_substrings.is_empty() { + ContainsAllSymbolSubstringsOutcome::Ok + } else { + ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(unmatched_symbol_substrings) + } +} + +#[derive(Debug, PartialEq)] +pub enum ContainsAllSymbolsOutcome<'a> { + Ok, + MissingSymbols(BTreeSet<&'a str>), +} + +/// Check an object file contains all symbols provided in `candidate_symbols`. +/// +/// Returns `true` if **all** of the symbols in `candidate_symbols` are found within the object file +/// at `path` by **exact match**. Take care to account for (1) platform differences and (2) calling +/// convention and symbol decorations differences. +/// +/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be +/// parsed as a recognized object file. +/// +/// # Platform-specific behavior +/// +/// See [`object_contains_any_symbol_substring`]. +#[track_caller] +pub fn object_contains_all_symbols( + path: P, + candidate_symbols: &[S], +) -> ContainsAllSymbolsOutcome<'_> +where + P: AsRef, + S: AsRef, +{ + let path = path.as_ref(); + let blob = crate::fs::read(path); + let obj = object::File::parse(&*blob) + .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display())); + let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref()); + let mut unmatched_symbols = BTreeSet::from_iter(candidate_symbols); + unmatched_symbols.retain(|unmatched_symbol| { + for sym in obj.symbols() { + if sym.name_bytes().unwrap() == unmatched_symbol.as_bytes() { + return false; + } + } + + true + }); + + if unmatched_symbols.is_empty() { + ContainsAllSymbolsOutcome::Ok + } else { + ContainsAllSymbolsOutcome::MissingSymbols(unmatched_symbols) + } } diff --git a/tests/codegen/mem-replace-big-type.rs b/tests/codegen/mem-replace-big-type.rs index 0b2229ba7d104..b28b505b4e5c9 100644 --- a/tests/codegen/mem-replace-big-type.rs +++ b/tests/codegen/mem-replace-big-type.rs @@ -25,7 +25,7 @@ pub fn replace_big(dst: &mut Big, src: Big) -> Big { // CHECK-NOT: call void @llvm.memcpy // For a large type, we expect exactly three `memcpy`s -// CHECK-LABEL: define internal void @{{.+}}mem{{.+}}replace{{.+}}(ptr +// CHECK-LABEL: define void @{{.+}}mem{{.+}}replace{{.+}}(ptr // CHECK-SAME: sret([56 x i8]){{.+}}[[RESULT:%.+]], ptr{{.+}}%dest, ptr{{.+}}%src) // CHECK-NOT: call void @llvm.memcpy // CHECK: call void @llvm.memcpy.{{.+}}(ptr align 8 [[RESULT]], ptr align 8 %dest, i{{.*}} 56, i1 false) diff --git a/tests/mir-opt/building/index_array_and_slice.rs b/tests/mir-opt/building/index_array_and_slice.rs index f91b37567f79b..42ede66d92ba0 100644 --- a/tests/mir-opt/building/index_array_and_slice.rs +++ b/tests/mir-opt/building/index_array_and_slice.rs @@ -55,7 +55,7 @@ struct WithSliceTail(f64, [i32]); fn index_custom(custom: &WithSliceTail, index: usize) -> &i32 { // CHECK: bb0: // CHECK: [[PTR:_.+]] = &raw const (fake) ((*_1).1: [i32]); - // CHECK: [[LEN:_.+]] = PtrMetadata(move [[PTR]]); + // CHECK: [[LEN:_.+]] = PtrMetadata(copy [[PTR]]); // CHECK: [[LT:_.+]] = Lt(copy _2, copy [[LEN]]); // CHECK: assert(move [[LT]], "index out of bounds{{.+}}", move [[LEN]], copy _2) -> [success: bb1, diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff index 0c52f1e058367..df008ececae30 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff @@ -46,7 +46,7 @@ StorageDead(_6); _4 = copy _5 as *mut [u8] (Transmute); StorageDead(_5); - _3 = move _4 as *mut u8 (PtrToPtr); + _3 = copy _4 as *mut u8 (PtrToPtr); StorageDead(_4); StorageDead(_3); - StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir index d1b1e3d7dd73d..ebe8b23354be7 100644 --- a/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_mut_usize.PreCodegen.after.panic-unwind.mir @@ -30,7 +30,7 @@ fn slice_get_mut_usize(_1: &mut [u32], _2: usize) -> Option<&mut u32> { StorageDead(_3); StorageLive(_5); _5 = &mut (*_1)[_2]; - _0 = Option::<&mut u32>::Some(move _5); + _0 = Option::<&mut u32>::Some(copy _5); StorageDead(_5); goto -> bb3; } diff --git a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff index 05ad9dbf3cccf..3da795b61f948 100644 --- a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff @@ -99,7 +99,8 @@ _13 = &(*_26); StorageLive(_15); _15 = RangeFull; - _12 = <[i32; 10] as Index>::index(move _13, move _15) -> [return: bb5, unwind continue]; +- _12 = <[i32; 10] as Index>::index(move _13, move _15) -> [return: bb5, unwind continue]; ++ _12 = <[i32; 10] as Index>::index(copy _13, move _15) -> [return: bb5, unwind continue]; } bb5: { diff --git a/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff index 3c6a9a9614c32..0f90cc40a3d8b 100644 --- a/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation.ReferencePropagation.diff @@ -218,8 +218,9 @@ - StorageLive(_14); - _14 = &_11; - _13 = &(*_14); +- _12 = move _13; + _13 = &_11; - _12 = move _13; ++ _12 = copy _13; StorageDead(_13); - StorageDead(_14); StorageLive(_15); @@ -252,7 +253,8 @@ StorageLive(_23); StorageLive(_24); _24 = copy _21; - _23 = opaque::<&&usize>(move _24) -> [return: bb3, unwind continue]; +- _23 = opaque::<&&usize>(move _24) -> [return: bb3, unwind continue]; ++ _23 = opaque::<&&usize>(copy _24) -> [return: bb3, unwind continue]; } bb3: { @@ -276,7 +278,8 @@ StorageLive(_30); StorageLive(_31); _31 = copy _28; - _30 = opaque::<*mut &usize>(move _31) -> [return: bb4, unwind continue]; +- _30 = opaque::<*mut &usize>(move _31) -> [return: bb4, unwind continue]; ++ _30 = opaque::<*mut &usize>(copy _31) -> [return: bb4, unwind continue]; } bb4: { @@ -299,7 +302,8 @@ StorageLive(_36); StorageLive(_37); _37 = copy _34; - _36 = opaque::<&usize>(move _37) -> [return: bb5, unwind continue]; +- _36 = opaque::<&usize>(move _37) -> [return: bb5, unwind continue]; ++ _36 = opaque::<&usize>(copy _37) -> [return: bb5, unwind continue]; } bb5: { @@ -328,7 +332,8 @@ StorageLive(_45); StorageLive(_46); _46 = copy _44; - _45 = opaque::<&usize>(move _46) -> [return: bb6, unwind continue]; +- _45 = opaque::<&usize>(move _46) -> [return: bb6, unwind continue]; ++ _45 = opaque::<&usize>(copy _46) -> [return: bb6, unwind continue]; } bb6: { @@ -368,8 +373,9 @@ - StorageLive(_55); - _55 = &(*_1); - _54 = &(*_55); +- _2 = move _54; + _54 = &(*_1); - _2 = move _54; ++ _2 = copy _54; StorageDead(_54); - StorageDead(_55); StorageLive(_56); diff --git a/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff index 75fe99de93810..99ef07a212cf0 100644 --- a/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation_const_ptr.ReferencePropagation.diff @@ -233,7 +233,8 @@ _12 = &raw const _10; StorageLive(_13); _13 = &raw const _11; - _12 = move _13; +- _12 = move _13; ++ _12 = copy _13; StorageDead(_13); StorageLive(_14); _14 = copy (*_12); @@ -265,7 +266,8 @@ StorageLive(_22); StorageLive(_23); _23 = copy _20; - _22 = opaque::<&*const usize>(move _23) -> [return: bb3, unwind continue]; +- _22 = opaque::<&*const usize>(move _23) -> [return: bb3, unwind continue]; ++ _22 = opaque::<&*const usize>(copy _23) -> [return: bb3, unwind continue]; } bb3: { @@ -289,7 +291,8 @@ StorageLive(_29); StorageLive(_30); _30 = copy _27; - _29 = opaque::<*mut *const usize>(move _30) -> [return: bb4, unwind continue]; +- _29 = opaque::<*mut *const usize>(move _30) -> [return: bb4, unwind continue]; ++ _29 = opaque::<*mut *const usize>(copy _30) -> [return: bb4, unwind continue]; } bb4: { @@ -312,7 +315,8 @@ StorageLive(_35); StorageLive(_36); _36 = copy _33; - _35 = opaque::<*const usize>(move _36) -> [return: bb5, unwind continue]; +- _35 = opaque::<*const usize>(move _36) -> [return: bb5, unwind continue]; ++ _35 = opaque::<*const usize>(copy _36) -> [return: bb5, unwind continue]; } bb5: { @@ -341,7 +345,8 @@ StorageLive(_44); StorageLive(_45); _45 = copy _43; - _44 = opaque::<*const usize>(move _45) -> [return: bb6, unwind continue]; +- _44 = opaque::<*const usize>(move _45) -> [return: bb6, unwind continue]; ++ _44 = opaque::<*const usize>(copy _45) -> [return: bb6, unwind continue]; } bb6: { @@ -379,7 +384,8 @@ _52 = &raw const (*_2); StorageLive(_53); _53 = &raw const (*_1); - _2 = move _53; +- _2 = move _53; ++ _2 = copy _53; StorageDead(_53); StorageLive(_54); _54 = copy (*_52); diff --git a/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff index f35b4974d6eba..e2fab8a5f2e4d 100644 --- a/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation_mut.ReferencePropagation.diff @@ -218,8 +218,9 @@ - StorageLive(_14); - _14 = &mut _11; - _13 = &mut (*_14); +- _12 = move _13; + _13 = &mut _11; - _12 = move _13; ++ _12 = copy _13; StorageDead(_13); - StorageDead(_14); StorageLive(_15); @@ -251,7 +252,8 @@ StorageLive(_23); StorageLive(_24); _24 = copy _21; - _23 = opaque::<&&mut usize>(move _24) -> [return: bb3, unwind continue]; +- _23 = opaque::<&&mut usize>(move _24) -> [return: bb3, unwind continue]; ++ _23 = opaque::<&&mut usize>(copy _24) -> [return: bb3, unwind continue]; } bb3: { @@ -275,7 +277,8 @@ StorageLive(_30); StorageLive(_31); _31 = copy _28; - _30 = opaque::<*mut &mut usize>(move _31) -> [return: bb4, unwind continue]; +- _30 = opaque::<*mut &mut usize>(move _31) -> [return: bb4, unwind continue]; ++ _30 = opaque::<*mut &mut usize>(copy _31) -> [return: bb4, unwind continue]; } bb4: { @@ -296,8 +299,10 @@ _35 = copy (*_34); StorageLive(_36); StorageLive(_37); - _37 = move _34; - _36 = opaque::<&mut usize>(move _37) -> [return: bb5, unwind continue]; +- _37 = move _34; +- _36 = opaque::<&mut usize>(move _37) -> [return: bb5, unwind continue]; ++ _37 = copy _34; ++ _36 = opaque::<&mut usize>(copy _37) -> [return: bb5, unwind continue]; } bb5: { @@ -316,15 +321,19 @@ StorageLive(_41); _41 = copy (*_40); StorageLive(_42); - _42 = move _40; +- _42 = move _40; ++ _42 = copy _40; StorageLive(_43); _43 = copy (*_42); StorageLive(_44); - _44 = move _42; +- _44 = move _42; ++ _44 = copy _42; StorageLive(_45); StorageLive(_46); - _46 = move _44; - _45 = opaque::<&mut usize>(move _46) -> [return: bb6, unwind continue]; +- _46 = move _44; +- _45 = opaque::<&mut usize>(move _46) -> [return: bb6, unwind continue]; ++ _46 = copy _44; ++ _45 = opaque::<&mut usize>(copy _46) -> [return: bb6, unwind continue]; } bb6: { @@ -364,8 +373,9 @@ - StorageLive(_55); - _55 = &mut (*_1); - _54 = &mut (*_55); +- _2 = move _54; + _54 = &mut (*_1); - _2 = move _54; ++ _2 = copy _54; StorageDead(_54); - StorageDead(_55); StorageLive(_56); diff --git a/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff b/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff index 21b322b721876..c49254ee6c652 100644 --- a/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.reference_propagation_mut_ptr.ReferencePropagation.diff @@ -214,7 +214,8 @@ _12 = &raw mut _10; StorageLive(_13); _13 = &raw mut _11; - _12 = move _13; +- _12 = move _13; ++ _12 = copy _13; StorageDead(_13); StorageLive(_14); _14 = copy (*_12); @@ -245,7 +246,8 @@ StorageLive(_22); StorageLive(_23); _23 = copy _20; - _22 = opaque::<&*mut usize>(move _23) -> [return: bb3, unwind continue]; +- _22 = opaque::<&*mut usize>(move _23) -> [return: bb3, unwind continue]; ++ _22 = opaque::<&*mut usize>(copy _23) -> [return: bb3, unwind continue]; } bb3: { @@ -269,7 +271,8 @@ StorageLive(_29); StorageLive(_30); _30 = copy _27; - _29 = opaque::<*mut *mut usize>(move _30) -> [return: bb4, unwind continue]; +- _29 = opaque::<*mut *mut usize>(move _30) -> [return: bb4, unwind continue]; ++ _29 = opaque::<*mut *mut usize>(copy _30) -> [return: bb4, unwind continue]; } bb4: { @@ -291,7 +294,8 @@ StorageLive(_35); StorageLive(_36); _36 = copy _33; - _35 = opaque::<*mut usize>(move _36) -> [return: bb5, unwind continue]; +- _35 = opaque::<*mut usize>(move _36) -> [return: bb5, unwind continue]; ++ _35 = opaque::<*mut usize>(copy _36) -> [return: bb5, unwind continue]; } bb5: { @@ -318,7 +322,8 @@ StorageLive(_44); StorageLive(_45); _45 = copy _43; - _44 = opaque::<*mut usize>(move _45) -> [return: bb6, unwind continue]; +- _44 = opaque::<*mut usize>(move _45) -> [return: bb6, unwind continue]; ++ _44 = opaque::<*mut usize>(copy _45) -> [return: bb6, unwind continue]; } bb6: { @@ -356,7 +361,8 @@ _52 = &raw mut (*_2); StorageLive(_53); _53 = &raw mut (*_1); - _2 = move _53; +- _2 = move _53; ++ _2 = copy _53; StorageDead(_53); StorageLive(_54); _54 = copy (*_52); diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index 00d4893807163..c4b63b6313c23 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -30,7 +30,7 @@ fn reference_propagation<'a, T: Copy>(single: &'a T, mut multiple: &'a T) { // CHECK: [[a2:_.*]] = const 7_usize; // CHECK: [[b:_.*]] = &[[a]]; // CHECK: [[btmp:_.*]] = &[[a2]]; - // CHECK: [[b]] = move [[btmp]]; + // CHECK: [[b]] = copy [[btmp]]; // CHECK: [[c:_.*]] = copy (*[[b]]); let a = 5_usize; @@ -122,7 +122,7 @@ fn reference_propagation<'a, T: Copy>(single: &'a T, mut multiple: &'a T) { // CHECK: bb7: { // CHECK: [[a:_.*]] = &(*_2); // CHECK: [[tmp:_.*]] = &(*_1); - // CHECK: _2 = move [[tmp]]; + // CHECK: _2 = copy [[tmp]]; // CHECK: [[b:_.*]] = copy (*[[a]]); let a = &*multiple; @@ -186,7 +186,7 @@ fn reference_propagation_mut<'a, T: Copy>(single: &'a mut T, mut multiple: &'a m // CHECK: [[a2:_.*]] = const 7_usize; // CHECK: [[b:_.*]] = &mut [[a]]; // CHECK: [[btmp:_.*]] = &mut [[a2]]; - // CHECK: [[b]] = move [[btmp]]; + // CHECK: [[b]] = copy [[btmp]]; // CHECK: [[c:_.*]] = copy (*[[b]]); let mut a = 5_usize; @@ -247,9 +247,9 @@ fn reference_propagation_mut<'a, T: Copy>(single: &'a mut T, mut multiple: &'a m // CHECK: [[a:_.*]] = const 7_usize; // CHECK: [[b1:_.*]] = &mut [[a]]; // CHECK: [[c:_.*]] = copy (*[[b1]]); - // CHECK: [[b2:_.*]] = move [[b1]]; + // CHECK: [[b2:_.*]] = copy [[b1]]; // CHECK: [[c2:_.*]] = copy (*[[b2]]); - // CHECK: [[b3:_.*]] = move [[b2]]; + // CHECK: [[b3:_.*]] = copy [[b2]]; let mut a = 7_usize; let b1 = &mut a; @@ -278,7 +278,7 @@ fn reference_propagation_mut<'a, T: Copy>(single: &'a mut T, mut multiple: &'a m // CHECK: bb7: { // CHECK: [[a:_.*]] = &mut (*_2); // CHECK: [[tmp:_.*]] = &mut (*_1); - // CHECK: _2 = move [[tmp]]; + // CHECK: _2 = copy [[tmp]]; // CHECK: [[b:_.*]] = copy (*[[a]]); let a = &mut *multiple; @@ -343,7 +343,7 @@ fn reference_propagation_const_ptr(single: *const T, mut multiple: *con // CHECK: [[a2:_.*]] = const 7_usize; // CHECK: [[b:_.*]] = &raw const [[a]]; // CHECK: [[btmp:_.*]] = &raw const [[a2]]; - // CHECK: [[b]] = move [[btmp]]; + // CHECK: [[b]] = copy [[btmp]]; // CHECK: [[c:_.*]] = copy (*[[b]]); let a = 5_usize; @@ -435,7 +435,7 @@ fn reference_propagation_const_ptr(single: *const T, mut multiple: *con // CHECK: bb7: { // CHECK: [[a:_.*]] = &raw const (*_2); // CHECK: [[tmp:_.*]] = &raw const (*_1); - // CHECK: _2 = move [[tmp]]; + // CHECK: _2 = copy [[tmp]]; // CHECK: [[b:_.*]] = copy (*[[a]]); let a = &raw const *multiple; @@ -514,7 +514,7 @@ fn reference_propagation_mut_ptr(single: *mut T, mut multiple: *mut T) // CHECK: [[a2:_.*]] = const 7_usize; // CHECK: [[b:_.*]] = &raw mut [[a]]; // CHECK: [[btmp:_.*]] = &raw mut [[a2]]; - // CHECK: [[b]] = move [[btmp]]; + // CHECK: [[b]] = copy [[btmp]]; // CHECK: [[c:_.*]] = copy (*[[b]]); let mut a = 5_usize; @@ -606,7 +606,7 @@ fn reference_propagation_mut_ptr(single: *mut T, mut multiple: *mut T) // CHECK: bb7: { // CHECK: [[a:_.*]] = &raw mut (*_2); // CHECK: [[tmp:_.*]] = &raw mut (*_1); - // CHECK: _2 = move [[tmp]]; + // CHECK: _2 = copy [[tmp]]; // CHECK: [[b:_.*]] = copy (*[[a]]); let a = &raw mut *multiple; diff --git a/tests/mir-opt/reference_prop_do_not_reuse_move.rs b/tests/mir-opt/reference_prop_do_not_reuse_move.rs new file mode 100644 index 0000000000000..8859d30aed194 --- /dev/null +++ b/tests/mir-opt/reference_prop_do_not_reuse_move.rs @@ -0,0 +1,44 @@ +//@ test-mir-pass: ReferencePropagation + +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] +#![crate_type = "lib"] + +use std::intrinsics::mir::*; + +#[inline(never)] +fn opaque(_: impl Sized, _: impl Sized) {} + +#[custom_mir(dialect = "runtime")] +pub fn fn0() { + // CHECK-LABEL: fn0 + // CHECK: _9 = opaque::<&u8, &u64>(copy (_2.1: &u8), copy _6) -> [return: bb1, unwind unreachable]; + mir! { + let _1: (u8, u8); + let _2: (u64, &u8); + let _3: (u8, &&u64); + let _4: u64; + let _5: &u64; + let _6: &u64; + let _7: &u64; + let _8: u64; + let n: (); + { + _3.0 = 0; + _1 = (0, _3.0); + _4 = 0; + _2.1 = &_1.0; + _8 = 0; + _5 = &_8; + _5 = &_4; + _6 = _5; + _7 = _6; + _3.1 = &_6; + Call(n = opaque(_2.1, Move(_6)), ReturnTo(bb1), UnwindUnreachable()) + } + bb1 = { + _2.0 = *_7; + Return() + } + } +} diff --git a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir index 6bf4be652bef3..89f4cbc2edea5 100644 --- a/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir +++ b/tests/mir-opt/uninhabited_not_read.main.SimplifyLocals-final.after.mir @@ -31,7 +31,7 @@ fn main() -> () { StorageLive(_2); StorageLive(_3); _3 = &raw const _1; - _2 = move _3 as *const ! (PtrToPtr); + _2 = copy _3 as *const ! (PtrToPtr); StorageDead(_3); StorageDead(_2); StorageDead(_1); @@ -40,7 +40,7 @@ fn main() -> () { StorageLive(_5); StorageLive(_6); _6 = &raw const _4; - _5 = move _6 as *const ! (PtrToPtr); + _5 = copy _6 as *const ! (PtrToPtr); StorageDead(_6); StorageDead(_5); StorageDead(_4); diff --git a/tests/run-make/compiletest-self-test/symbols-helpers/rmake.rs b/tests/run-make/compiletest-self-test/symbols-helpers/rmake.rs new file mode 100644 index 0000000000000..73826214aac60 --- /dev/null +++ b/tests/run-make/compiletest-self-test/symbols-helpers/rmake.rs @@ -0,0 +1,92 @@ +//! `run_make_support::symbols` helpers self test. + +// Only intended as a basic smoke test, does not try to account for platform or calling convention +// specific symbol decorations. +//@ only-x86_64-unknown-linux-gnu +//@ ignore-cross-compile + +use std::collections::BTreeSet; + +use object::{Object, ObjectSymbol}; +use run_make_support::symbols::{ + ContainsAllSymbolSubstringsOutcome, ContainsAllSymbolsOutcome, + object_contains_all_symbol_substring, object_contains_all_symbols, object_contains_any_symbol, + object_contains_any_symbol_substring, +}; +use run_make_support::{object, rfs, rust_lib_name, rustc}; + +fn main() { + rustc().input("sample.rs").emit("obj").edition("2024").run(); + + // `sample.rs` has two `no_mangle` functions, `eszett` and `beta`, in addition to `main`. + // + // These two symbol names and the test substrings used below are carefully picked to make sure + // they do not overlap with `sample` and contain non-hex characters, to avoid accidentally + // matching against CGU names like `sample.dad0f15d00c84e70-cgu.0`. + + let obj_filename = "sample.o"; + let blob = rfs::read(obj_filename); + let obj = object::File::parse(&*blob).unwrap(); + eprintln!("found symbols:"); + for sym in obj.symbols() { + eprintln!("symbol = {}", sym.name().unwrap()); + } + + // `hello` contains `hel` + assert!(object_contains_any_symbol_substring(obj_filename, &["zett"])); + assert!(object_contains_any_symbol_substring(obj_filename, &["zett", "does_not_exist"])); + assert!(!object_contains_any_symbol_substring(obj_filename, &["does_not_exist"])); + + assert!(object_contains_any_symbol(obj_filename, &["eszett"])); + assert!(object_contains_any_symbol(obj_filename, &["eszett", "beta"])); + assert!(!object_contains_any_symbol(obj_filename, &["zett"])); + assert!(!object_contains_any_symbol(obj_filename, &["does_not_exist"])); + + assert_eq!( + object_contains_all_symbol_substring(obj_filename, &["zett"]), + ContainsAllSymbolSubstringsOutcome::Ok + ); + assert_eq!( + object_contains_all_symbol_substring(obj_filename, &["zett", "bet"]), + ContainsAllSymbolSubstringsOutcome::Ok + ); + assert_eq!( + object_contains_all_symbol_substring(obj_filename, &["does_not_exist"]), + ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(BTreeSet::from([ + "does_not_exist" + ])) + ); + assert_eq!( + object_contains_all_symbol_substring(obj_filename, &["zett", "does_not_exist"]), + ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(BTreeSet::from([ + "does_not_exist" + ])) + ); + assert_eq!( + object_contains_all_symbol_substring(obj_filename, &["zett", "bet", "does_not_exist"]), + ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(BTreeSet::from([ + "does_not_exist" + ])) + ); + + assert_eq!( + object_contains_all_symbols(obj_filename, &["eszett"]), + ContainsAllSymbolsOutcome::Ok + ); + assert_eq!( + object_contains_all_symbols(obj_filename, &["eszett", "beta"]), + ContainsAllSymbolsOutcome::Ok + ); + assert_eq!( + object_contains_all_symbols(obj_filename, &["zett"]), + ContainsAllSymbolsOutcome::MissingSymbols(BTreeSet::from(["zett"])) + ); + assert_eq!( + object_contains_all_symbols(obj_filename, &["zett", "beta"]), + ContainsAllSymbolsOutcome::MissingSymbols(BTreeSet::from(["zett"])) + ); + assert_eq!( + object_contains_all_symbols(obj_filename, &["does_not_exist"]), + ContainsAllSymbolsOutcome::MissingSymbols(BTreeSet::from(["does_not_exist"])) + ); +} diff --git a/tests/run-make/compiletest-self-test/symbols-helpers/sample.rs b/tests/run-make/compiletest-self-test/symbols-helpers/sample.rs new file mode 100644 index 0000000000000..3566d29976685 --- /dev/null +++ b/tests/run-make/compiletest-self-test/symbols-helpers/sample.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] + +#[unsafe(no_mangle)] +pub extern "C" fn eszett() -> i8 { + 42 +} + +#[unsafe(no_mangle)] +pub extern "C" fn beta() -> u32 { + 1 +} + +fn main() {} diff --git a/tests/run-make/fmt-write-bloat/rmake.rs b/tests/run-make/fmt-write-bloat/rmake.rs index af6ff6c298331..b7f18b384cb00 100644 --- a/tests/run-make/fmt-write-bloat/rmake.rs +++ b/tests/run-make/fmt-write-bloat/rmake.rs @@ -20,7 +20,7 @@ use run_make_support::artifact_names::bin_name; use run_make_support::env::std_debug_assertions_enabled; use run_make_support::rustc; -use run_make_support::symbols::any_symbol_contains; +use run_make_support::symbols::object_contains_any_symbol_substring; fn main() { rustc().input("main.rs").opt().run(); @@ -31,5 +31,5 @@ fn main() { // otherwise, add them to the list of symbols to deny. panic_syms.extend_from_slice(&["panicking", "panic_fmt", "pad_integral", "Display"]); } - assert!(!any_symbol_contains(bin_name("main"), &panic_syms)); + assert!(!object_contains_any_symbol_substring(bin_name("main"), &panic_syms)); } diff --git a/tests/run-make/used/rmake.rs b/tests/run-make/used/rmake.rs index bcdb84132d3f5..456321e2f56c3 100644 --- a/tests/run-make/used/rmake.rs +++ b/tests/run-make/used/rmake.rs @@ -8,9 +8,9 @@ // https://rust-lang.github.io/rfcs/2386-used.html use run_make_support::rustc; -use run_make_support::symbols::any_symbol_contains; +use run_make_support::symbols::object_contains_any_symbol_substring; fn main() { rustc().opt_level("3").emit("obj").input("used.rs").run(); - assert!(any_symbol_contains("used.o", &["FOO"])); + assert!(object_contains_any_symbol_substring("used.o", &["FOO"])); } diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index aa52de63a6094..5026687b97b05 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -100,7 +100,7 @@ //~^ ERROR malformed //~| ERROR the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type #[cfg] -//~^ ERROR is not followed by parentheses +//~^ ERROR malformed #[cfg_attr] //~^ ERROR malformed #[instruction_set] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 3dc3c32c936b2..56f2be353e768 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -1,8 +1,11 @@ -error: `cfg` is not followed by parentheses +error[E0539]: malformed `cfg` attribute input --> $DIR/malformed-attrs.rs:102:1 | LL | #[cfg] - | ^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` error: malformed `cfg_attr` attribute input --> $DIR/malformed-attrs.rs:104:1 diff --git a/tests/ui/cfg/cfg-target-compact-errors.rs b/tests/ui/cfg/cfg-target-compact-errors.rs index e00d42fe4d966..cfb19c58a197d 100644 --- a/tests/ui/cfg/cfg-target-compact-errors.rs +++ b/tests/ui/cfg/cfg-target-compact-errors.rs @@ -3,19 +3,23 @@ #![feature(cfg_target_compact)] #[cfg(target(o::o))] -//~^ ERROR `cfg` predicate key must be an identifier +//~^ ERROR malformed `cfg` attribute input fn one() {} #[cfg(target(os = 8))] -//~^ ERROR literal in `cfg` predicate value must be a string +//~^ ERROR malformed `cfg` attribute input fn two() {} #[cfg(target(os = "linux", pointer(width = "64")))] -//~^ ERROR invalid predicate `target_pointer` +//~^ ERROR malformed `cfg` attribute input fn three() {} #[cfg(target(true))] -//~^ ERROR `cfg` predicate key must be an identifier +//~^ ERROR malformed `cfg` attribute input fn four() {} +#[cfg(target(clippy::os = "linux"))] +//~^ ERROR `cfg` predicate key must be an identifier +fn five() {} + fn main() {} diff --git a/tests/ui/cfg/cfg-target-compact-errors.stderr b/tests/ui/cfg/cfg-target-compact-errors.stderr index 219d9732c32b6..7df6729e88127 100644 --- a/tests/ui/cfg/cfg-target-compact-errors.stderr +++ b/tests/ui/cfg/cfg-target-compact-errors.stderr @@ -1,28 +1,45 @@ -error: `cfg` predicate key must be an identifier - --> $DIR/cfg-target-compact-errors.rs:5:14 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-target-compact-errors.rs:5:1 | LL | #[cfg(target(o::o))] - | ^^^^ + | ^^^^^^^^^^^^^----^^^ + | | | + | | expected this to be of the form `... = "..."` + | help: must be of the form: `#[cfg(predicate)]` -error[E0565]: literal in `cfg` predicate value must be a string - --> $DIR/cfg-target-compact-errors.rs:9:19 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-target-compact-errors.rs:9:1 | LL | #[cfg(target(os = 8))] - | ^ + | ^^^^^^^^^^^^^^^^^^-^^^ + | | | + | | expected a string literal here + | help: must be of the form: `#[cfg(predicate)]` -error[E0537]: invalid predicate `target_pointer` - --> $DIR/cfg-target-compact-errors.rs:13:28 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-target-compact-errors.rs:13:1 | LL | #[cfg(target(os = "linux", pointer(width = "64")))] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------^^^ + | | | + | | expected this to be of the form `... = "..."` + | help: must be of the form: `#[cfg(predicate)]` -error: `cfg` predicate key must be an identifier - --> $DIR/cfg-target-compact-errors.rs:17:14 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-target-compact-errors.rs:17:1 | LL | #[cfg(target(true))] - | ^^^^ + | ^^^^^^^^^^^^^----^^^ + | | | + | | expected this to be of the form `... = "..."` + | help: must be of the form: `#[cfg(predicate)]` + +error: `cfg` predicate key must be an identifier + --> $DIR/cfg-target-compact-errors.rs:21:14 + | +LL | #[cfg(target(clippy::os = "linux"))] + | ^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0537, E0565. -For more information about an error, try `rustc --explain E0537`. +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/cfg/cfg-version/syntax.stderr b/tests/ui/cfg/cfg-version/syntax.stderr index 2facd96076317..188f6d7aa5dc1 100644 --- a/tests/ui/cfg/cfg-version/syntax.stderr +++ b/tests/ui/cfg/cfg-version/syntax.stderr @@ -17,10 +17,10 @@ LL | #[cfg(version(false))] | ^^^^^ error: expected single version literal - --> $DIR/syntax.rs:23:7 + --> $DIR/syntax.rs:23:14 | LL | #[cfg(version("1.43", "1.44", "1.45"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: unknown version literal format, assuming it refers to a future version --> $DIR/syntax.rs:51:15 diff --git a/tests/ui/cfg/diagnostics-cross-crate.stderr b/tests/ui/cfg/diagnostics-cross-crate.stderr index 3e32a856e9540..155b3db2f4298 100644 --- a/tests/ui/cfg/diagnostics-cross-crate.stderr +++ b/tests/ui/cfg/diagnostics-cross-crate.stderr @@ -10,10 +10,10 @@ note: found an item that was configured out LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ note: the item is gated here - --> $DIR/auxiliary/cfged_out.rs:5:5 + --> $DIR/auxiliary/cfged_out.rs:5:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0425]: cannot find function `uwu` in crate `cfged_out` --> $DIR/diagnostics-cross-crate.rs:7:16 @@ -33,10 +33,10 @@ note: found an item that was configured out LL | pub fn uwu() {} | ^^^ note: the item is gated here - --> $DIR/auxiliary/cfged_out.rs:2:5 + --> $DIR/auxiliary/cfged_out.rs:2:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0425]: cannot find function `meow` in module `cfged_out::inner::right` --> $DIR/diagnostics-cross-crate.rs:24:30 @@ -67,10 +67,10 @@ note: found an item that was configured out LL | pub fn vanished() {} | ^^^^^^^^ note: the item is gated here - --> $DIR/auxiliary/cfged_out.rs:21:1 + --> $DIR/auxiliary/cfged_out.rs:21:7 | LL | #[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 5 previous errors diff --git a/tests/ui/cfg/diagnostics-reexport-2.stderr b/tests/ui/cfg/diagnostics-reexport-2.stderr index 95ac5a19b0b99..e1f91fcc5d10c 100644 --- a/tests/ui/cfg/diagnostics-reexport-2.stderr +++ b/tests/ui/cfg/diagnostics-reexport-2.stderr @@ -10,10 +10,10 @@ note: found an item that was configured out LL | pub mod gated { | ^^^^^ note: the item is gated here - --> $DIR/diagnostics-reexport-2.rs:4:5 + --> $DIR/diagnostics-reexport-2.rs:4:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0433]: failed to resolve: could not find `gated` in `reexport2` --> $DIR/diagnostics-reexport-2.rs:46:16 @@ -27,10 +27,10 @@ note: found an item that was configured out LL | pub mod gated { | ^^^^^ note: the item is gated here - --> $DIR/diagnostics-reexport-2.rs:4:5 + --> $DIR/diagnostics-reexport-2.rs:4:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0433]: failed to resolve: could not find `gated` in `reexport30` --> $DIR/diagnostics-reexport-2.rs:50:17 @@ -44,10 +44,10 @@ note: found an item that was configured out LL | pub mod gated { | ^^^^^ note: the item is gated here - --> $DIR/diagnostics-reexport-2.rs:4:5 + --> $DIR/diagnostics-reexport-2.rs:4:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0433]: failed to resolve: could not find `gated` in `reexport31` --> $DIR/diagnostics-reexport-2.rs:54:17 @@ -61,10 +61,10 @@ note: found an item that was configured out LL | pub mod gated { | ^^^^^ note: the item is gated here - --> $DIR/diagnostics-reexport-2.rs:4:5 + --> $DIR/diagnostics-reexport-2.rs:4:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0433]: failed to resolve: could not find `gated` in `reexport32` --> $DIR/diagnostics-reexport-2.rs:58:17 @@ -78,10 +78,10 @@ note: found an item that was configured out LL | pub mod gated { | ^^^^^ note: the item is gated here - --> $DIR/diagnostics-reexport-2.rs:4:5 + --> $DIR/diagnostics-reexport-2.rs:4:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 5 previous errors diff --git a/tests/ui/cfg/diagnostics-reexport.stderr b/tests/ui/cfg/diagnostics-reexport.stderr index 95dc4fac945e2..bf1bbcba7e4ce 100644 --- a/tests/ui/cfg/diagnostics-reexport.stderr +++ b/tests/ui/cfg/diagnostics-reexport.stderr @@ -10,10 +10,10 @@ note: found an item that was configured out LL | pub fn x() {} | ^ note: the item is gated here - --> $DIR/diagnostics-reexport.rs:17:5 + --> $DIR/diagnostics-reexport.rs:17:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0432]: unresolved imports `b::x`, `b::y` --> $DIR/diagnostics-reexport.rs:22:13 @@ -29,20 +29,20 @@ note: found an item that was configured out LL | pub fn x() {} | ^ note: the item is gated here - --> $DIR/diagnostics-reexport.rs:28:5 + --> $DIR/diagnostics-reexport.rs:28:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ note: found an item that was configured out --> $DIR/diagnostics-reexport.rs:32:12 | LL | pub fn y() {} | ^ note: the item is gated here - --> $DIR/diagnostics-reexport.rs:31:5 + --> $DIR/diagnostics-reexport.rs:31:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0425]: cannot find function `uwu` in module `inner` --> $DIR/diagnostics-reexport.rs:38:12 @@ -56,10 +56,10 @@ note: found an item that was configured out LL | pub use super::uwu; | ^^^ note: the item is gated here - --> $DIR/diagnostics-reexport.rs:7:5 + --> $DIR/diagnostics-reexport.rs:7:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/cfg/diagnostics-same-crate.stderr b/tests/ui/cfg/diagnostics-same-crate.stderr index 75a1bc39a013c..121f25ca0906e 100644 --- a/tests/ui/cfg/diagnostics-same-crate.stderr +++ b/tests/ui/cfg/diagnostics-same-crate.stderr @@ -10,10 +10,10 @@ note: found an item that was configured out LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ note: the item is gated here - --> $DIR/diagnostics-same-crate.rs:8:5 + --> $DIR/diagnostics-same-crate.rs:8:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0432]: unresolved import `super::inner::doesnt_exist` --> $DIR/diagnostics-same-crate.rs:35:23 @@ -27,10 +27,10 @@ note: found an item that was configured out LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ note: the item is gated here - --> $DIR/diagnostics-same-crate.rs:8:5 + --> $DIR/diagnostics-same-crate.rs:8:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` --> $DIR/diagnostics-same-crate.rs:54:12 @@ -44,10 +44,10 @@ note: found an item that was configured out LL | pub mod doesnt_exist { | ^^^^^^^^^^^^ note: the item is gated here - --> $DIR/diagnostics-same-crate.rs:8:5 + --> $DIR/diagnostics-same-crate.rs:8:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0425]: cannot find function `uwu` in module `inner` --> $DIR/diagnostics-same-crate.rs:49:12 @@ -61,10 +61,10 @@ note: found an item that was configured out LL | pub fn uwu() {} | ^^^ note: the item is gated here - --> $DIR/diagnostics-same-crate.rs:4:5 + --> $DIR/diagnostics-same-crate.rs:4:11 | LL | #[cfg(false)] - | ^^^^^^^^^^^^^ + | ^^^^^ error[E0425]: cannot find function `meow` in module `inner::right` --> $DIR/diagnostics-same-crate.rs:58:19 diff --git a/tests/ui/codegen/duplicated-path-in-error.stderr b/tests/ui/codegen/duplicated-path-in-error.gnu.stderr similarity index 100% rename from tests/ui/codegen/duplicated-path-in-error.stderr rename to tests/ui/codegen/duplicated-path-in-error.gnu.stderr diff --git a/tests/ui/codegen/duplicated-path-in-error.musl.stderr b/tests/ui/codegen/duplicated-path-in-error.musl.stderr new file mode 100644 index 0000000000000..2892ebffdde6a --- /dev/null +++ b/tests/ui/codegen/duplicated-path-in-error.musl.stderr @@ -0,0 +1,2 @@ +error: couldn't load codegen backend /non-existing-one.so: Error loading shared library /non-existing-one.so: No such file or directory + diff --git a/tests/ui/codegen/duplicated-path-in-error.rs b/tests/ui/codegen/duplicated-path-in-error.rs index a446395de208f..fed93828ee2a6 100644 --- a/tests/ui/codegen/duplicated-path-in-error.rs +++ b/tests/ui/codegen/duplicated-path-in-error.rs @@ -1,8 +1,15 @@ +//@ revisions: musl gnu //@ only-linux +//@ ignore-cross-compile because this relies on host libc behaviour //@ compile-flags: -Zcodegen-backend=/non-existing-one.so +//@[gnu] only-gnu +//@[musl] only-musl // This test ensures that the error of the "not found dylib" doesn't duplicate // the path of the dylib. +// +// glibc and musl have different dlopen error messages, so the expected error +// message differs between the two. fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs index 47418b4e091b2..df87a3d846efc 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs @@ -1,16 +1,24 @@ -#[cfg] //~ ERROR `cfg` is not followed by parentheses +#[cfg] +//~^ ERROR malformed `cfg` attribute +//~| NOTE expected this to be a list struct S1; -#[cfg = 10] //~ ERROR `cfg` is not followed by parentheses +#[cfg = 10] +//~^ ERROR malformed `cfg` attribute +//~| NOTE expected this to be a list struct S2; -#[cfg()] //~ ERROR `cfg` predicate is not specified +#[cfg()] +//~^ ERROR malformed `cfg` attribute +//~| NOTE expected a single argument here struct S3; -#[cfg(a, b)] //~ ERROR multiple `cfg` predicates are specified +#[cfg(a, b)] +//~^ ERROR malformed `cfg` attribute +//~| NOTE expected a single argument here struct S4; -#[cfg("str")] //~ ERROR `cfg` predicate key cannot be a literal +#[cfg("str")] //~ ERROR `cfg` predicate key must be an identifier struct S5; #[cfg(a::b)] //~ ERROR `cfg` predicate key must be an identifier @@ -19,10 +27,12 @@ struct S6; #[cfg(a())] //~ ERROR invalid predicate `a` struct S7; -#[cfg(a = 10)] //~ ERROR literal in `cfg` predicate value must be a string +#[cfg(a = 10)] //~ ERROR malformed `cfg` attribute input +//~^ NOTE expected a string literal here struct S8; -#[cfg(a = b"hi")] //~ ERROR literal in `cfg` predicate value must be a string +#[cfg(a = b"hi")] //~ ERROR malformed `cfg` attribute input +//~^ NOTE expected a normal string literal, not a byte string literal struct S9; macro_rules! generate_s10 { @@ -34,5 +44,6 @@ macro_rules! generate_s10 { } generate_s10!(concat!("nonexistent")); +//~^ NOTE in this expansion of generate_s10! fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index 66ce2ee98589d..75e9b9209c08f 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -1,61 +1,78 @@ -error: `cfg` is not followed by parentheses +error[E0539]: malformed `cfg` attribute input --> $DIR/cfg-attr-syntax-validation.rs:1:1 | LL | #[cfg] - | ^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` -error: `cfg` is not followed by parentheses - --> $DIR/cfg-attr-syntax-validation.rs:4:1 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-attr-syntax-validation.rs:6:1 | LL | #[cfg = 10] - | ^^^^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + | ^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` -error: `cfg` predicate is not specified - --> $DIR/cfg-attr-syntax-validation.rs:7:1 +error[E0805]: malformed `cfg` attribute input + --> $DIR/cfg-attr-syntax-validation.rs:11:1 | LL | #[cfg()] - | ^^^^^^^^ help: expected syntax is: `cfg(/* predicate */)` + | ^^^^^--^ + | | | + | | expected a single argument here + | help: must be of the form: `#[cfg(predicate)]` -error: multiple `cfg` predicates are specified - --> $DIR/cfg-attr-syntax-validation.rs:10:10 +error[E0805]: malformed `cfg` attribute input + --> $DIR/cfg-attr-syntax-validation.rs:16:1 | LL | #[cfg(a, b)] - | ^ + | ^^^^^------^ + | | | + | | expected a single argument here + | help: must be of the form: `#[cfg(predicate)]` -error: `cfg` predicate key cannot be a literal - --> $DIR/cfg-attr-syntax-validation.rs:13:7 +error: `cfg` predicate key must be an identifier + --> $DIR/cfg-attr-syntax-validation.rs:21:7 | LL | #[cfg("str")] | ^^^^^ error: `cfg` predicate key must be an identifier - --> $DIR/cfg-attr-syntax-validation.rs:16:7 + --> $DIR/cfg-attr-syntax-validation.rs:24:7 | LL | #[cfg(a::b)] | ^^^^ error[E0537]: invalid predicate `a` - --> $DIR/cfg-attr-syntax-validation.rs:19:7 + --> $DIR/cfg-attr-syntax-validation.rs:27:7 | LL | #[cfg(a())] | ^^^ -error[E0565]: literal in `cfg` predicate value must be a string - --> $DIR/cfg-attr-syntax-validation.rs:22:11 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-attr-syntax-validation.rs:30:1 | LL | #[cfg(a = 10)] - | ^^ + | ^^^^^^^^^^--^^ + | | | + | | expected a string literal here + | help: must be of the form: `#[cfg(predicate)]` -error[E0565]: literal in `cfg` predicate value must be a string - --> $DIR/cfg-attr-syntax-validation.rs:25:11 +error[E0539]: malformed `cfg` attribute input + --> $DIR/cfg-attr-syntax-validation.rs:34:1 | LL | #[cfg(a = b"hi")] - | -^^^^ + | ^^^^^^^^^^-^^^^^^ | | | help: consider removing the prefix + | + = note: expected a normal string literal, not a byte string literal error: expected unsuffixed literal, found `expr` metavariable - --> $DIR/cfg-attr-syntax-validation.rs:30:25 + --> $DIR/cfg-attr-syntax-validation.rs:40:25 | LL | #[cfg(feature = $expr)] | ^^^^^ @@ -67,5 +84,5 @@ LL | generate_s10!(concat!("nonexistent")); error: aborting due to 10 previous errors -Some errors have detailed explanations: E0537, E0565. +Some errors have detailed explanations: E0537, E0539, E0805. For more information about an error, try `rustc --explain E0537`. diff --git a/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr b/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr index 6e428079afe8e..1db9101dba2ff 100644 --- a/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr +++ b/tests/ui/consts/const-eval/ub-slice-get-unchecked.stderr @@ -1,11 +1,25 @@ -error[E0015]: cannot call non-const method `core::slice::::get_unchecked::>` in constants +error[E0658]: cannot call conditionally-const method `core::slice::::get_unchecked::>` in constants --> $DIR/ub-slice-get-unchecked.rs:7:29 | LL | const B: &[()] = unsafe { A.get_unchecked(3..1) }; | ^^^^^^^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: see issue #67792 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: `core::slice::::get_unchecked` is not yet stable as a const fn + --> $DIR/ub-slice-get-unchecked.rs:7:27 + | +LL | const B: &[()] = unsafe { A.get_unchecked(3..1) }; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: add `#![feature(const_index)]` to the crate attributes to enable + | +LL + #![feature(const_index)] + | + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/consts/issue-94675.rs b/tests/ui/consts/issue-94675.rs index 87c8b04452bac..22791e7d15e5d 100644 --- a/tests/ui/consts/issue-94675.rs +++ b/tests/ui/consts/issue-94675.rs @@ -7,7 +7,9 @@ struct Foo<'a> { impl<'a> Foo<'a> { const fn spam(&mut self, baz: &mut Vec) { self.bar[0] = baz.len(); - //~^ ERROR: cannot call + //~^ ERROR: `Vec: [const] Index<_>` is not satisfied + //~| ERROR: `Vec: [const] Index` is not satisfied + //~| ERROR: `Vec: [const] IndexMut` is not satisfied } } diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index 63a86b456332f..608ce0cfef012 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -1,13 +1,24 @@ -error[E0015]: cannot call non-const operator in constant functions - --> $DIR/issue-94675.rs:9:17 +error[E0277]: the trait bound `Vec: [const] Index<_>` is not satisfied + --> $DIR/issue-94675.rs:9:9 | LL | self.bar[0] = baz.len(); - | ^^^ + | ^^^^^^^^^^^ + +error[E0277]: the trait bound `Vec: [const] IndexMut` is not satisfied + --> $DIR/issue-94675.rs:9:9 + | +LL | self.bar[0] = baz.len(); + | ^^^^^^^^^^^ + +error[E0277]: the trait bound `Vec: [const] Index` is not satisfied + --> $DIR/issue-94675.rs:9:9 + | +LL | self.bar[0] = baz.len(); + | ^^^^^^^^^^^ | -note: impl defined here, but it is not `const` - --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants +note: required by a bound in `std::ops::IndexMut::index_mut` + --> $SRC_DIR/core/src/ops/index.rs:LL:COL -error: aborting due to 1 previous error +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/macros/macro-outer-attributes.stderr b/tests/ui/macros/macro-outer-attributes.stderr index a894c90f4c32a..9215754d4bb14 100644 --- a/tests/ui/macros/macro-outer-attributes.stderr +++ b/tests/ui/macros/macro-outer-attributes.stderr @@ -10,16 +10,10 @@ note: found an item that was configured out LL | pub fn bar() { }); | ^^^ note: the item is gated here - --> $DIR/macro-outer-attributes.rs:5:45 - | -LL | $i:item) => (mod $nm { #[$a] $i }); } - | ^^^^^ -LL | -LL | / test!(a, -LL | | #[cfg(false)], -LL | | pub fn bar() { }); - | |_______________________- in this macro invocation - = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + --> $DIR/macro-outer-attributes.rs:8:13 + | +LL | #[cfg(false)], + | ^^^^^ help: consider importing this function | LL + use b::bar; diff --git a/tests/ui/parser/issues/issue-35813-postfix-after-cast.rs b/tests/ui/parser/issues/issue-35813-postfix-after-cast.rs index 316c612940c9f..439793ea8f72e 100644 --- a/tests/ui/parser/issues/issue-35813-postfix-after-cast.rs +++ b/tests/ui/parser/issues/issue-35813-postfix-after-cast.rs @@ -1,6 +1,6 @@ //@ edition:2018 #![crate_type = "lib"] -#![feature(type_ascription)] +#![feature(type_ascription, const_index, const_trait_impl)] use std::future::Future; use std::pin::Pin; @@ -129,7 +129,6 @@ pub fn inside_block() { static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]); //~^ ERROR: cast cannot be followed by indexing -//~| ERROR: cannot call non-const operator in statics static bar2: &[i32] = &(&[1i32,2,3]: &[i32; 3][0..1]); //~^ ERROR: expected one of diff --git a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr index 64cf8baf9a5d4..ce7129da9371f 100644 --- a/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr +++ b/tests/ui/parser/issues/issue-35813-postfix-after-cast.stderr @@ -219,13 +219,13 @@ LL | static bar: &[i32] = &((&[1,2,3] as &[i32])[0..1]); | + + error: expected one of `)`, `,`, `.`, `?`, or an operator, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:134:36 + --> $DIR/issue-35813-postfix-after-cast.rs:133:36 | LL | static bar2: &[i32] = &(&[1i32,2,3]: &[i32; 3][0..1]); | ^ expected one of `)`, `,`, `.`, `?`, or an operator error: cast cannot be followed by `?` - --> $DIR/issue-35813-postfix-after-cast.rs:139:5 + --> $DIR/issue-35813-postfix-after-cast.rs:138:5 | LL | Err(0u64) as Result?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -236,25 +236,25 @@ LL | (Err(0u64) as Result)?; | + + error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:141:14 + --> $DIR/issue-35813-postfix-after-cast.rs:140:14 | LL | Err(0u64): Result?; | ^ expected one of `.`, `;`, `?`, `}`, or an operator error: expected identifier, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:153:13 + --> $DIR/issue-35813-postfix-after-cast.rs:152:13 | LL | drop_ptr: F(); | ^ expected identifier error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:160:13 + --> $DIR/issue-35813-postfix-after-cast.rs:159:13 | LL | drop_ptr: fn(u8); | ^ expected one of 8 possible tokens error: cast cannot be followed by a function call - --> $DIR/issue-35813-postfix-after-cast.rs:166:5 + --> $DIR/issue-35813-postfix-after-cast.rs:165:5 | LL | drop as fn(u8)(0); | ^^^^^^^^^^^^^^ @@ -265,13 +265,13 @@ LL | (drop as fn(u8))(0); | + + error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:168:13 + --> $DIR/issue-35813-postfix-after-cast.rs:167:13 | LL | drop_ptr: fn(u8)(0); | ^ expected one of 8 possible tokens error: cast cannot be followed by `.await` - --> $DIR/issue-35813-postfix-after-cast.rs:173:5 + --> $DIR/issue-35813-postfix-after-cast.rs:172:5 | LL | Box::pin(noop()) as Pin>>.await; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -282,13 +282,13 @@ LL | (Box::pin(noop()) as Pin>>).await; | + + error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:176:21 + --> $DIR/issue-35813-postfix-after-cast.rs:175:21 | LL | Box::pin(noop()): Pin>.await; | ^ expected one of `.`, `;`, `?`, `}`, or an operator error: cast cannot be followed by a field access - --> $DIR/issue-35813-postfix-after-cast.rs:188:5 + --> $DIR/issue-35813-postfix-after-cast.rs:187:5 | LL | Foo::default() as Foo.bar; | ^^^^^^^^^^^^^^^^^^^^^ @@ -299,7 +299,7 @@ LL | (Foo::default() as Foo).bar; | + + error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:` - --> $DIR/issue-35813-postfix-after-cast.rs:190:19 + --> $DIR/issue-35813-postfix-after-cast.rs:189:19 | LL | Foo::default(): Foo.bar; | ^ expected one of `.`, `;`, `?`, `}`, or an operator @@ -322,21 +322,11 @@ LL | if true { 33 } else { 44 }: i32.max(0) | ^ expected one of `,`, `.`, `?`, or an operator error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/issue-35813-postfix-after-cast.rs:151:13 + --> $DIR/issue-35813-postfix-after-cast.rs:150:13 | LL | drop as F(); | ^^^ only `Fn` traits may use parentheses -error[E0015]: cannot call non-const operator in statics - --> $DIR/issue-35813-postfix-after-cast.rs:130:42 - | -LL | static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]); - | ^^^^^^ - | - = note: calls in statics are limited to constant functions, tuple structs and tuple variants - = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` - -error: aborting due to 40 previous errors +error: aborting due to 39 previous errors -Some errors have detailed explanations: E0015, E0214. -For more information about an error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0214`. diff --git a/tests/ui/span/E0536.rs b/tests/ui/span/E0536.rs index 72de7b6f4530f..ae07336335419 100644 --- a/tests/ui/span/E0536.rs +++ b/tests/ui/span/E0536.rs @@ -1,4 +1,3 @@ -#[cfg(not())] //~ ERROR E0536 -pub fn something() {} - -pub fn main() {} +pub fn main() { + if cfg!(not()) { } //~ ERROR E0536 +} diff --git a/tests/ui/span/E0536.stderr b/tests/ui/span/E0536.stderr index b0f652208c4a9..6c25f9140a1ab 100644 --- a/tests/ui/span/E0536.stderr +++ b/tests/ui/span/E0536.stderr @@ -1,8 +1,8 @@ error[E0536]: expected 1 cfg-pattern - --> $DIR/E0536.rs:1:7 + --> $DIR/E0536.rs:2:13 | -LL | #[cfg(not())] - | ^^^^^ +LL | if cfg!(not()) { } + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.rs b/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.rs index ece87529c3e3b..1d1da9b0e3dfc 100644 --- a/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.rs +++ b/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.rs @@ -14,5 +14,7 @@ fn main() { trait NonConst {} const fn handle(_: &dyn const NonConst) {} //~^ ERROR const trait bounds are not allowed in trait object types +//~| ERROR `const` can only be applied to `#[const_trait]` traits const fn take(_: &dyn [const] NonConst) {} //~^ ERROR `[const]` is not allowed here +//~| ERROR `[const]` can only be applied to `#[const_trait]` traits diff --git a/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.stderr b/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.stderr index 090555c637791..06e0493024c1b 100644 --- a/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.stderr +++ b/tests/ui/traits/const-traits/const-trait-bounds-trait-objects.stderr @@ -19,12 +19,34 @@ LL | const fn handle(_: &dyn const NonConst) {} | ^^^^^^^^^^^^^^ error: `[const]` is not allowed here - --> $DIR/const-trait-bounds-trait-objects.rs:17:23 + --> $DIR/const-trait-bounds-trait-objects.rs:18:23 | LL | const fn take(_: &dyn [const] NonConst) {} | ^^^^^^^ | = note: trait objects cannot have `[const]` trait bounds -error: aborting due to 4 previous errors +error: `const` can only be applied to `#[const_trait]` traits + --> $DIR/const-trait-bounds-trait-objects.rs:15:25 + | +LL | const fn handle(_: &dyn const NonConst) {} + | ^^^^^ can't be applied to `NonConst` + | +help: mark `NonConst` as `#[const_trait]` to allow it to have `const` implementations + | +LL | #[const_trait] trait NonConst {} + | ++++++++++++++ + +error: `[const]` can only be applied to `#[const_trait]` traits + --> $DIR/const-trait-bounds-trait-objects.rs:18:23 + | +LL | const fn take(_: &dyn [const] NonConst) {} + | ^^^^^^^ can't be applied to `NonConst` + | +help: mark `NonConst` as `#[const_trait]` to allow it to have `const` implementations + | +LL | #[const_trait] trait NonConst {} + | ++++++++++++++ + +error: aborting due to 6 previous errors diff --git a/tests/ui/where-clauses/ignore-err-clauses.rs b/tests/ui/where-clauses/ignore-err-clauses.rs index 428ebf4b40836..6f21e5ccbaade 100644 --- a/tests/ui/where-clauses/ignore-err-clauses.rs +++ b/tests/ui/where-clauses/ignore-err-clauses.rs @@ -1,13 +1,13 @@ use std::ops::Add; fn dbl(x: T) -> ::Output -//~^ ERROR type annotations needed where T: Copy + Add, UUU: Copy, //~^ ERROR cannot find type `UUU` in this scope { x + x + //~^ ERROR use of moved value: `x` } fn main() { diff --git a/tests/ui/where-clauses/ignore-err-clauses.stderr b/tests/ui/where-clauses/ignore-err-clauses.stderr index fbf1b99334f42..9c76c1c6a0486 100644 --- a/tests/ui/where-clauses/ignore-err-clauses.stderr +++ b/tests/ui/where-clauses/ignore-err-clauses.stderr @@ -1,16 +1,33 @@ error[E0412]: cannot find type `UUU` in this scope - --> $DIR/ignore-err-clauses.rs:7:5 + --> $DIR/ignore-err-clauses.rs:6:5 | LL | UUU: Copy, | ^^^ not found in this scope -error[E0282]: type annotations needed - --> $DIR/ignore-err-clauses.rs:3:14 +error[E0382]: use of moved value: `x` + --> $DIR/ignore-err-clauses.rs:9:9 | LL | fn dbl(x: T) -> ::Output - | ^ cannot infer type for type parameter `T` + | - move occurs because `x` has type `T`, which does not implement the `Copy` trait +... +LL | x + x + | ----^ + | | | + | | value used here after move + | `x` moved due to usage in operator + | +help: if `T` implemented `Clone`, you could clone the value + --> $DIR/ignore-err-clauses.rs:3:8 + | +LL | fn dbl(x: T) -> ::Output + | ^ consider constraining this type parameter with `Clone` +... +LL | x + x + | - you could clone this value +note: calling this operator moves the left-hand side + --> $SRC_DIR/core/src/ops/arith.rs:LL:COL error: aborting due to 2 previous errors -Some errors have detailed explanations: E0282, E0412. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0382, E0412. +For more information about an error, try `rustc --explain E0382`.