diff --git a/Cargo.lock b/Cargo.lock index c50e836574641..b8dfdd7cae290 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,7 +687,7 @@ dependencies = [ "color-print", "declare_clippy_lint", "filetime", - "itertools", + "itertools 0.12.1", "pulldown-cmark", "regex", "rustc_tools_util 0.4.2", @@ -705,7 +705,7 @@ name = "clippy_config" version = "0.1.98" dependencies = [ "clippy_utils", - "itertools", + "itertools 0.12.1", "serde", "toml 0.7.8", "walkdir", @@ -718,7 +718,7 @@ dependencies = [ "chrono", "clap", "indoc", - "itertools", + "itertools 0.12.1", "opener", "rustc-literal-escaper", "walkdir", @@ -733,7 +733,7 @@ dependencies = [ "clippy_config", "clippy_utils", "declare_clippy_lint", - "itertools", + "itertools 0.12.1", "quine-mc_cluskey", "regex-syntax", "semver", @@ -751,7 +751,7 @@ version = "0.0.1" dependencies = [ "clippy_config", "clippy_utils", - "itertools", + "itertools 0.12.1", "regex", "rustc-semver", ] @@ -761,7 +761,7 @@ name = "clippy_utils" version = "0.1.98" dependencies = [ "arrayvec", - "itertools", + "itertools 0.12.1", "rustc_apfloat", "serde", ] @@ -933,7 +933,7 @@ name = "coverage-dump" version = "0.1.0" dependencies = [ "anyhow", - "itertools", + "itertools 0.15.0", "leb128", "md-5", "miniz_oxide", @@ -2129,6 +2129,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -3686,7 +3695,7 @@ dependencies = [ name = "rustc_ast_passes" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_ast_pretty", @@ -3706,7 +3715,7 @@ dependencies = [ name = "rustc_ast_pretty" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_ast", "rustc_lexer", "rustc_span", @@ -3750,7 +3759,7 @@ name = "rustc_borrowck" version = "0.0.0" dependencies = [ "either", - "itertools", + "itertools 0.15.0", "polonius-engine", "rustc_abi", "rustc_data_structures", @@ -3804,7 +3813,7 @@ version = "0.0.0" dependencies = [ "bitflags", "gimli 0.31.1", - "itertools", + "itertools 0.15.0", "libc", "libloading 0.9.0", "measureme", @@ -3840,7 +3849,7 @@ dependencies = [ "bitflags", "bstr", "find-msvc-tools", - "itertools", + "itertools 0.15.0", "libc", "object 0.37.3", "pathdiff", @@ -4110,7 +4119,7 @@ dependencies = [ name = "rustc_hir_analysis" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_arena", "rustc_ast", @@ -4157,7 +4166,7 @@ dependencies = [ name = "rustc_hir_typeck" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4312,6 +4321,7 @@ dependencies = [ "rustc_parse_format", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "rustc_trait_selection", "smallvec", @@ -4435,7 +4445,7 @@ dependencies = [ name = "rustc_mir_build" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_apfloat", "rustc_arena", @@ -4479,7 +4489,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_arena", "rustc_ast", @@ -4684,7 +4694,7 @@ name = "rustc_resolve" version = "0.0.0" dependencies = [ "indexmap", - "itertools", + "itertools 0.15.0", "pulldown-cmark", "rustc_arena", "rustc_ast", @@ -4846,7 +4856,7 @@ checksum = "a3b75158011a63889ba12084cf1224baad7bcad50f6ee7c842f772b74aa148ed" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4880,7 +4890,7 @@ dependencies = [ name = "rustc_transmute" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_data_structures", "rustc_hir", @@ -4894,7 +4904,7 @@ dependencies = [ name = "rustc_ty_utils" version = "0.0.0" dependencies = [ - "itertools", + "itertools 0.15.0", "rustc_abi", "rustc_data_structures", "rustc_errors", @@ -4971,7 +4981,7 @@ dependencies = [ "base64", "expect-test", "indexmap", - "itertools", + "itertools 0.15.0", "minifier", "proc-macro2", "pulldown-cmark-escape", @@ -5059,7 +5069,7 @@ dependencies = [ "dirs", "getopts", "ignore", - "itertools", + "itertools 0.12.1", "regex", "rustfmt-config_proc_macro", "semver", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 11a531353eba0..bd5ce5d18b839 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3059,13 +3059,15 @@ impl FnDecl { } /// The marker index for "no splatted arguments". + /// Higher values are also not supported, for performance reasons. + /// /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. - pub const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Returns a splatted argument index, if any are present. - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { - if index == Self::NO_SPLATTED_ARG_INDEX as usize { + if index >= usize::from(Self::NO_SPLATTED_ARG_INDEX) { // AST validation has already checked the splatted argument index is valid, so just // ignore invalid indexes here. None @@ -3073,7 +3075,7 @@ impl FnDecl { arg.attrs .iter() .any(|attr| attr.has_name(sym::splat)) - .then_some(u16::try_from(index).unwrap()) + .then_some(u8::try_from(index).unwrap()) } }) } diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index fc466246bf137..7021ac8cfedaf 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -55,7 +55,9 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::kw; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; -use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; +use crate::delegation::generics::{ + GenericsGenerationResult, GenericsGenerationResults, GenericsPosition, +}; use crate::diagnostics::{ CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion, DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee, @@ -93,7 +95,7 @@ struct ParamInfo { pub c_variadic: bool, /// The index of the splatted parameter, if any. - pub splatted: Option, + pub splatted: Option, } const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; @@ -364,11 +366,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn param_info(&self, def_id: DefId) -> ParamInfo { let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - // FIXME(splat): use `sig.splatted()` once FnSig has it ParamInfo { param_count: sig.inputs().len() + usize::from(sig.c_variadic()), c_variadic: sig.c_variadic(), - splatted: None, + splatted: sig.splatted(), } } @@ -506,6 +507,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res: Res::Local(param_id), args: None, infer_args: false, + delegation_child_segment: false, })); let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments }); @@ -715,6 +717,8 @@ impl<'hir> LoweringContext<'_, 'hir> { result.args_segment_id = segment.hir_id; result.use_for_sig_inheritance = !result.generics.is_trait_impl(); + segment.delegation_child_segment = result.generics.pos() == GenericsPosition::Child; + segment } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 79a3961c22a53..2a751ffae3eb7 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -12,7 +12,7 @@ use rustc_span::{Ident, Span, sym}; use crate::LoweringContext; use crate::diagnostics::DelegationInfersMismatch; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub(super) enum GenericsPosition { Parent, Child, @@ -155,7 +155,7 @@ impl<'hir> DelegationGenericArgsIterator<'hir> { impl<'hir> HirOrTyGenerics<'hir> { pub(super) fn into_hir_generics(&mut self, ctx: &mut LoweringContext<'_, 'hir>, span: Span) { if let HirOrTyGenerics::Ty(ty) = self { - let rename_self = matches!(ty.pos, GenericsPosition::Child); + let rename_self = ty.pos == GenericsPosition::Child; let params = ctx.uplift_delegation_generic_params(span, &ty.data, rename_self); *self = HirOrTyGenerics::Hir(DelegationGenerics { @@ -218,6 +218,13 @@ impl<'hir> HirOrTyGenerics<'hir> { .expect("`Self` generic param is not found while expected"), } } + + pub(crate) fn pos(&self) -> GenericsPosition { + match self { + HirOrTyGenerics::Ty(ty) => ty.pos, + HirOrTyGenerics::Hir(hir) => hir.pos, + } + } } impl<'hir> GenericsGenerationResult<'hir> { @@ -590,6 +597,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ident: p.name.ident(), infer_args: false, res, + delegation_child_segment: false, }]), res, span: p.span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 06e83a7486100..35e16ce78a8a8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1014,6 +1014,7 @@ impl<'hir> LoweringContext<'_, 'hir> { res, args, infer_args: args.is_none(), + delegation_child_segment: false, }]), }) } @@ -2791,7 +2792,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::ConstBlock(anon_const) => { let def_id = self.local_def_id(anon_const.id); - assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id)); + assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); self.lower_anon_const_to_const_arg(anon_const, span) } _ => overly_complex_const(self), diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 42c4af5add12d..18e037527836a 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -412,6 +412,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { Some(generic_args.into_generic_args(self)) }, + delegation_child_segment: false, } } diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index da9b4633fdae9..4fa0e2b1fc7fa 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 186a43ca1693b..06925994b052c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -412,11 +412,11 @@ impl<'a> AstValidator<'a> { }) .unzip(); - // A splatted argument at the "no splatted" marker index is not supported (this is an - // unlikely edge case). + // A splatted argument greater than or equal to the "no splatted" marker index is not + // supported. if let (Some(&splatted_arg_index), Some(&splatted_span)) = (splatted_arg_indexes.last(), splatted_spans.last()) - && splatted_arg_index == FnDecl::NO_SPLATTED_ARG_INDEX + && splatted_arg_index >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) { self.dcx().emit_err(diagnostics::InvalidSplattedArg { splatted_arg_index, diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml index 70da6ba0591ce..b33f41eb302c0 100644 --- a/compiler/rustc_ast_pretty/Cargo.toml +++ b/compiler/rustc_ast_pretty/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_ast = { path = "../rustc_ast" } rustc_lexer = { path = "../rustc_lexer" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 78aedbd4f7f4b..176e91e544ec5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use ast::{ForLoopKind, MatchKind}; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; @@ -170,8 +170,8 @@ impl<'a> State<'a> { } let cb = self.cbox(0); for (pos, field) in fields.iter().with_position() { - let is_first = matches!(pos, Position::First | Position::Only); - let is_last = matches!(pos, Position::Last | Position::Only); + let is_first = pos.is_first(); + let is_last = pos.is_last(); self.maybe_print_comment(field.span.hi()); self.print_outer_attributes(&field.attrs); if is_first { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 86fb0e62bdaa4..1dbd5719ae066 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -1,5 +1,5 @@ use ast::StaticItem; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_ast::{self as ast, EiiImpl, ModKind, Safety, TraitAlias}; use rustc_span::Ident; @@ -923,7 +923,7 @@ impl<'a> State<'a> { self.zerobreak(); let ib = self.ibox(0); for (pos, use_tree) in items.iter().with_position() { - let is_last = matches!(pos, Position::Last | Position::Only); + let is_last = pos.is_last(); self.print_use_tree(&use_tree.0); if !is_last { self.word(","); diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 7beee7e341b90..e3865345cae84 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -10,7 +10,7 @@ use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, + SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -524,15 +524,6 @@ impl CombineAttributeParser for TargetFeatureParser { was_forced: false, }; const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]); - const STABILITY: AttributeStability = AttributeStability::Stable; - - fn extend( - cx: &mut AcceptContext<'_, '_>, - args: &ArgParser, - ) -> impl IntoIterator { - parse_tf_attribute(cx, args) - } - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), @@ -544,6 +535,29 @@ impl CombineAttributeParser for TargetFeatureParser { Warn(Target::MacroDef), Warn(Target::MacroCall), ]); + const STABILITY: AttributeStability = AttributeStability::Stable; + + fn extend( + cx: &mut AcceptContext<'_, '_>, + args: &ArgParser, + ) -> impl IntoIterator { + parse_tf_attribute(cx, args) + } + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + // `#[target_feature]` is incompatible with lang item functions, + // except on WASM where calling target-feature functions is safe (see #84988). + if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc { + // `#[panic_handler]` is checked first so it takes priority in the diagnostic. + let lang_kind = cx + .all_attrs + .iter() + .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s))); + if let Some(kind) = lang_kind { + cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span }); + } + } + } } pub(crate) struct ForceTargetFeatureParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 988df2b200f86..23eef6334ccee 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -1,25 +1,29 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; use rustc_errors::{Applicability, msg}; use rustc_feature::AttributeStability; use rustc_hir::Target; use rustc_hir::attrs::{ - AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow, + AttributeKind, CfgEntry, CfgHideShow, DocAttribute, DocCfgHideShow, DocCfgHideShowValue, + DocInline, HideOrShow, }; use rustc_session::errors::feature_err; use rustc_span::{Span, Symbol, edition, sym}; -use thin_vec::ThinVec; use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser, template}; use crate::context::{AcceptContext, FinalizeContext}; use crate::diagnostics::{ AttrCrateLevelOnly, DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgWrongLiteral, - DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude, - DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, - IllFormedAttributeInput, MalformedDoc, + DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowNoIdentBeforeValues, + DocAutoCfgHideShowUnexpectedItem, DocAutoCfgHideShowUnexpectedItemAfterValues, + DocAutoCfgHideShowValuesMix, DocAutoCfgWrongLiteral, DocTestLiteral, DocTestTakesList, + DocTestUnknown, DocUnknownAny, DocUnknownInclude, DocUnknownPasses, DocUnknownPlugins, + DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs, IllFormedAttributeInput, MalformedDoc, +}; +use crate::parser::{ + ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser, }; -use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel, DocAttributeNotAttribute, DocKeywordNotKeyword, UnusedDuplicate, @@ -304,6 +308,81 @@ impl DocParser { } } + // Parses the `doc(auto_cfg(hide/show(..., values())))` attribute. + fn parse_auto_cfg_values( + &self, + cx: &mut AcceptContext<'_, '_>, + list: &MetaItemListParser, + values: &mut Option, + ) { + let mut cfg_values = DocCfgHideShow::new(); + + let mut values_set = FxHashSet::default(); + for item in list.mixed() { + match item { + // If it's a string literal, all good. + MetaItemOrLitParser::Lit(MetaItemLit { + kind: LitKind::Str(symbol, _), + span, + .. + }) => match &mut cfg_values { + DocCfgHideShow::Any(any_span) => { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowValuesMix { value_span: *span }, + *any_span, + ); + } + DocCfgHideShow::List(symbols) => { + if values_set.insert(symbol) { + symbols.push(DocCfgHideShowValue::new(*symbol, *span)); + } + } + }, + // If it's any other kind of literal, then it's wrong and we emit a lint. + MetaItemOrLitParser::Lit(lit) => cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItem { attr_name: lit.symbol }, + lit.span, + ), + // If it's a list, then only `any()` and `none()` are allowed and they must not + // contain any item. + MetaItemOrLitParser::MetaItemParser(sub_item) => { + if let Some(ident) = sub_item.ident() + && [sym::any, sym::none].contains(&ident.name) + && let ArgParser::List(list) = sub_item.args() + && list.mixed().count() == 0 + { + if ident.name == sym::any { + if let DocCfgHideShow::List(values) = &cfg_values + && let Some(value) = values.first() + { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowValuesMix { value_span: value.span }, + sub_item.span(), + ); + } else { + cfg_values.merge_with(&DocCfgHideShow::Any(sub_item.span())); + } + } else { + cfg_values.push_none(sub_item.span()); + } + } else { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItem { + attr_name: sub_item.ident().unwrap().name, + }, + sub_item.span(), + ); + } + } + } + } + *values = Some(cfg_values); + } + fn parse_auto_cfg( &mut self, cx: &mut AcceptContext<'_, '_>, @@ -315,7 +394,7 @@ impl DocParser { self.attribute.auto_cfg_change.push((true, path.span())); } ArgParser::List(list) => { - for meta in list.mixed() { + 'main: for meta in list.mixed() { let MetaItemOrLitParser::MetaItemParser(item) = meta else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, @@ -324,6 +403,8 @@ impl DocParser { ); continue; }; + // Only `hide` and `show` are allowed in `auto_cfg` if it's a list, and both + // must be a list. let (kind, attr_name) = match item.path().word_sym() { Some(sym::hide) => (HideOrShow::Hide, sym::hide), Some(sym::show) => (HideOrShow::Show, sym::show), @@ -345,8 +426,10 @@ impl DocParser { continue; }; - let mut cfg_hide_show = CfgHideShow { kind, values: ThinVec::new() }; + let mut cfg_hide_show = CfgHideShow { kind, values: FxIndexMap::default() }; + let mut cfg_names = FxHashSet::default(); + let mut values = None; for item in list.mixed() { let MetaItemOrLitParser::MetaItemParser(sub_item) = item else { cx.emit_lint( @@ -354,47 +437,60 @@ impl DocParser { DocAutoCfgHideShowUnexpectedItem { attr_name }, item.span(), ); - continue; + continue 'main; }; match sub_item.args() { - a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { + ArgParser::NoArgs if values.is_none() => { let Some(name) = sub_item.path().word_sym() else { - // FIXME: remove this method once merged and uncomment the line - // below instead. - // cx.expected_identifier(sub_item.path().span()); + cx.adcx().expected_identifier(sub_item.path().span()); + continue 'main; + }; + cfg_names.insert(name); + } + // The only accepted list is `values()`. + ArgParser::List(list) if values.is_none() => { + let Some(sym::values) = sub_item.path().word_sym() else { + cx.adcx().expected_identifier(sub_item.path().span()); + continue 'main; + }; + if cfg_names.is_empty() { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - MalformedDoc, - sub_item.path().span(), - ); - continue; - }; - if let Ok(CfgEntry::NameValue { name, value, .. }) = - super::cfg::parse_name_value( - name, - sub_item.path().span(), - a.as_name_value(), + DocAutoCfgHideShowNoIdentBeforeValues, sub_item.span(), - cx, - ) - { - cfg_hide_show.values.push(CfgInfo { - name, - name_span: sub_item.path().span(), - // If `value` is `Some`, `a.name_value()` will always return - // `Some` as well. - value: value - .map(|v| (v, a.as_name_value().unwrap().value_span)), - }) + ); + continue 'main; } + self.parse_auto_cfg_values(cx, list, &mut values); } - _ => { + // No `name = value` is allowed. + ArgParser::NameValue(_) => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, DocAutoCfgHideShowUnexpectedItem { attr_name }, sub_item.span(), ); - continue; + } + // If `values()` was already used, no item should come after it. + _ => { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + DocAutoCfgHideShowUnexpectedItemAfterValues, + sub_item.span(), + ); + } + } + } + + let values = values.unwrap_or(DocCfgHideShow::new_with_only_key(item.span())); + #[allow(rustc::potential_query_instability)] + for cfg_name in &cfg_names { + match cfg_hide_show.values.entry(*cfg_name) { + IndexEntry::Vacant(v) => { + v.insert(values.clone()); + } + IndexEntry::Occupied(mut o) => { + o.get_mut().merge_with(&values); } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 2357d6846935c..2c640ab5385b9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -14,11 +14,11 @@ use super::util::parse_single_integer; use crate::attributes::AttributeSafety; use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ - AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, - ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, - InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows, - WholeArchiveNeedsStatic, + AsNeededCompatibility, BothFfiConstAndPure, BundleNeedsStatic, EmptyLinkName, + ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, + InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, + LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, + RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -575,6 +575,13 @@ impl NoArgsAttributeParser for FfiPureParser { AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); const STABILITY: AttributeStability = unstable!(ffi_pure); const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + // `#[ffi_const]` functions cannot be `#[ffi_pure]`. + if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { + cx.emit_err(BothFfiConstAndPure { attr_span }); + } + } } pub(crate) struct RustcStdInternalSymbolParser; diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 17aaa6ddf9333..d3ddd79a97619 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -147,6 +147,14 @@ pub(crate) trait SingleAttributeParser: 'static { /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Unlike [`convert`](Self::convert), this + /// has access to the sibling attributes via [`FinalizeContext::all_attrs`], so it can + /// reject incompatible combinations. `attr_span` is the span of this attribute. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`SingleAttributeParser`]. @@ -177,8 +185,10 @@ impl AttributeParser for Single { const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { - Some(self.1?.0) + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { + let (kind, span) = self.1?; + T::finalize_check(cx, span); + Some(kind) } } @@ -258,6 +268,14 @@ pub(crate) trait NoArgsAttributeParser: 'static { /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Has access to the sibling attributes via + /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// `attr_span` is the span of this attribute. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } pub(crate) struct WithoutArgs(PhantomData); @@ -280,6 +298,10 @@ impl SingleAttributeParser for WithoutArgs { let _ = cx.expect_no_args(args); Some(T::CREATE(cx.attr_span)) } + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + T::finalize_check(cx, attr_span) + } } type ConvertFn = fn(ThinVec, Span) -> AttributeKind; @@ -313,6 +335,14 @@ pub(crate) trait CombineAttributeParser: 'static { cx: &mut AcceptContext<'_, '_>, args: &ArgParser, ) -> impl IntoIterator; + + /// Optional cross-attribute validation, run once during finalization after all + /// attributes on the item have been parsed. Has access to the sibling attributes via + /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// `attr_span` is the span of the first attribute that was encountered. + /// + /// Defaults to a no-op. + fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`CombineAttributeParser`]. @@ -345,8 +375,9 @@ impl AttributeParser for Combine { const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { if let Some(first_span) = self.first_span { + T::finalize_check(cx, first_span); Some(T::CONVERT(self.items, first_span)) } else { None diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index d4e15b8964ff7..24df9b100ce95 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -582,7 +582,7 @@ impl SingleAttributeParser for LangParser { // Only weak lang items may be applied to foreign items if [Target::ForeignFn, Target::ForeignStatic, Target::ForeignTy, Target::ForeignMod] .contains(&cx.target) - && !lang_item.is_weak() + && !(lang_item.is_weak() || lang_item.is_weak_only()) { cx.emit_err(UnknownExternLangItem { span: cx.attr_span, lang_item: lang_item.name() }); return None; @@ -591,6 +591,8 @@ impl SingleAttributeParser for LangParser { // Check the target let allowed_targets: &[_] = if lang_item == LangItem::PanicImpl { &[Allow(Target::Fn), Allow(Target::ForeignFn)] + } else if lang_item.is_weak_only() { + &[Allow(Target::ForeignFn)] } else { &[Allow(lang_item.target())] }; diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index d8dbe167ed175..0cfb68d523f9b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -40,6 +40,7 @@ const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Static), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), + Allow(Target::ForeignTy), Allow(Target::ExternCrate), ]); diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index d8b7144aa01ba..50667952b814d 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -169,11 +169,22 @@ pub(crate) struct DocAutoCfgExpectsHideOrShow; pub(crate) struct AmbiguousDeriveHelpers; #[derive(Diagnostic)] -#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items")] +#[diag("`#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or `values(...)`")] pub(crate) struct DocAutoCfgHideShowUnexpectedItem { pub attr_name: Symbol, } +#[derive(Diagnostic)] +#[diag("`any()` was used when other values were provided")] +pub(crate) struct DocAutoCfgHideShowValuesMix { + #[label("value declared here")] + pub value_span: Span, +} + +#[derive(Diagnostic)] +#[diag("unexpected item after `values()`")] +pub(crate) struct DocAutoCfgHideShowUnexpectedItemAfterValues; + #[derive(Diagnostic)] #[diag("`#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items")] pub(crate) struct DocAutoCfgHideShowExpectsList { @@ -239,6 +250,10 @@ pub(crate) struct DocUnknownAny { #[diag("expected boolean for `#[doc(auto_cfg = ...)]`")] pub(crate) struct DocAutoCfgWrongLiteral; +#[derive(Diagnostic)] +#[diag("there must be at least one identifier before `values(...)`")] +pub(crate) struct DocAutoCfgHideShowNoIdentBeforeValues; + #[derive(Diagnostic)] #[diag("`#[doc(test(...)]` takes a list of attributes")] pub(crate) struct DocTestTakesList; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index fef6f7d9e5503..63e33e0ae4882 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -13,6 +13,13 @@ use rustc_target::spec::TargetTuple; use crate::AttributeTemplate; use crate::context::Suggestion; +#[derive(Diagnostic)] +#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] +pub(crate) struct BothFfiConstAndPure { + #[primary_span] + pub attr_span: Span, +} + #[derive(Diagnostic)] #[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { @@ -73,6 +80,26 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } +#[derive(Diagnostic)] +#[diag( + "`#[target_feature]` cannot be applied to a {$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function" +)] +pub(crate) struct TargetFeatureOnLangItem { + #[primary_span] + pub attr_span: Span, + pub kind: Symbol, + #[label( + "{$kind -> + [panic_handler] `#[panic_handler]` + *[other] lang item + } function is not allowed to have `#[target_feature]`" + )] + pub item_span: Span, +} + #[derive(Diagnostic)] #[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml index 55a09dbf1d724..8c9c8349c3149 100644 --- a/compiler/rustc_borrowck/Cargo.toml +++ b/compiler/rustc_borrowck/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1.5.0" -itertools = "0.12" +itertools = "0.15" polonius-engine = "0.13.0" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index da472a08e7e40..86e5a6700b039 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1461,15 +1461,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let cause = ObligationCause::misc(expr.span, self.mir_def_id()); ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait); let errors = ocx.evaluate_obligations_error_on_ambiguity(); - if errors.iter().all(|error| { - match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { - Some(clause) => match clause.self_ty().skip_binder().kind() { - ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait, - _ => false, - }, - None => false, - } - }) { + if !errors.is_empty() + && errors.iter().all(|error| { + match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) { + Some(clause) => match clause.self_ty().skip_binder().kind() { + ty::Adt(def, _) => { + def.did().is_local() && clause.def_id() == clone_trait + } + _ => false, + }, + None => false, + } + }) + { let mut type_spans = vec![]; let mut types = FxIndexSet::default(); for clause in errors diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 5d154cc7b9050..f6abafb874165 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -706,7 +706,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { Some(Cause::LiveVar(..) | Cause::DropVar(..)) | None => { // Here, under NLL: no cause was found. Under polonius: no cause was found, or a // boring local was found, which we ignore like NLLs do to match its diagnostics. - if let Some(region) = self.to_error_region_vid(borrow_region_vid) { + if let Some(region) = self.regioncx.to_error_region_vid(borrow_region_vid) { let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); if let Some(region_name) = region_name { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 56e0c74c85e97..a5b7a26a10ab4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -116,11 +116,42 @@ impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> { pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { self.buffered_diags.push(BufferedDiag::NonError(diag)); } + pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { + self.buffered_diags.push(BufferedDiag::Error(diag)); + } + + pub(crate) fn emit_errors(&mut self) -> Option { + let mut res = None; + + // Buffer any move errors that we collected and de-duplicated. + for (_, (_, diag)) in std::mem::take(&mut self.buffered_move_errors) { + // We have already set tainted for this error, so just buffer it. + self.buffer_error(diag); + } + for (_, (mut diag, count)) in std::mem::take(&mut self.buffered_mut_errors) { + if count > 10 { + diag.note(format!("...and {} other attempted mutable borrows", count - 10)); + } + self.buffer_error(diag); + } + + if !self.buffered_diags.is_empty() { + self.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); + for buffered_diag in self.buffered_diags.drain(..) { + match buffered_diag { + BufferedDiag::Error(diag) => res = Some(diag.emit()), + BufferedDiag::NonError(diag) => diag.emit(), + } + } + } + + res + } } impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) { - self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag)); + self.diags_buffer.buffer_error(diag); } pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) { @@ -152,34 +183,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.diags_buffer.buffered_mut_errors.insert(span, (diag, count)); } - pub(crate) fn emit_errors(&mut self) -> Option { - let mut res = self.infcx.tainted_by_errors(); - - // Buffer any move errors that we collected and de-duplicated. - for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) { - // We have already set tainted for this error, so just buffer it. - self.buffer_error(diag); - } - for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) { - if count > 10 { - diag.note(format!("...and {} other attempted mutable borrows", count - 10)); - } - self.buffer_error(diag); - } - - if !self.diags_buffer.buffered_diags.is_empty() { - self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); - for buffered_diag in self.diags_buffer.buffered_diags.drain(..) { - match buffered_diag { - BufferedDiag::Error(diag) => res = Some(diag.emit()), - BufferedDiag::NonError(diag) => diag.emit(), - } - } - } - - res - } - pub(crate) fn has_buffered_diags(&self) -> bool { self.diags_buffer.buffered_diags.is_empty() } diff --git a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs index cddb27af5368f..da947376446cb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs +++ b/compiler/rustc_borrowck/src/diagnostics/opaque_types.rs @@ -28,11 +28,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let infcx = self.infcx; - let mut guar = None; let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = None; for error in errors { - guar = Some(match error { + match error { DeferredOpaqueTypeError::InvalidOpaqueTypeArgs(err) => err.report(infcx), DeferredOpaqueTypeError::LifetimeMismatchOpaqueParam(err) => { infcx.dcx().emit_err(err) @@ -82,11 +81,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) ), ), - }); + }; } - let guar = guar.unwrap(); - self.root_cx.set_tainted_by_errors(guar); - self.infcx.set_tainted_by_errors(guar); } /// Try to note when an opaque is involved in a borrowck error and that diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 96b1366dfb0ff..1166e1265dadb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -28,6 +28,7 @@ use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; use tracing::{debug, instrument, trace}; use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource}; +use crate::consumers::RegionInferenceContext; use crate::nll::ConstraintDescription; use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ @@ -134,7 +135,7 @@ pub(crate) struct ErrorConstraintInfo<'tcx> { pub(super) span: Span, } -impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { +impl<'tcx> RegionInferenceContext<'tcx> { /// Converts a region inference variable into a `ty::Region` that /// we can use for error reporting. If `r` is universally bound, /// then we use the name that we have on record for it. If `r` is @@ -142,20 +143,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// to find a good name from that. Returns `None` if we can't find /// one (e.g., this is just some random part of the CFG). pub(super) fn to_error_region(&self, r: RegionVid) -> Option> { - self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name) + self.to_error_region_vid(r).and_then(|r| self.region_definition(r).external_name) } /// Returns the `RegionVid` corresponding to the region returned by /// `to_error_region`. pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option { - if self.regioncx.universal_regions().is_universal_region(r) { + if self.universal_regions().is_universal_region(r) { Some(r) } else { // We just want something nameable, even if it's not // actually an upper bound. - let upper_bound = self.regioncx.approx_universal_upper_bound(r); + let upper_bound = self.approx_universal_upper_bound(r); - if self.regioncx.upper_bound_in_region_scc(r, upper_bound) { + if self.upper_bound_in_region_scc(r, upper_bound) { self.to_error_region_vid(upper_bound) } else { None @@ -179,14 +180,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(r) = self.to_error_region(fr) && let ty::ReLateParam(late_param) = r.kind() && let ty::LateParamRegionKind::ClosureEnv = late_param.kind - && let DefiningTy::Closure(_, args) = self.regioncx.universal_regions().defining_ty + && let DefiningTy::Closure(_, args) = self.universal_regions().defining_ty { return args.as_closure().kind() == ty::ClosureKind::FnMut; } false } +} +impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // For generic associated types (GATs) which implied 'static requirement // from higher-ranked trait bounds (HRTB). Try to locate span of the trait // and the span which bounded to the trait for adding 'static lifetime suggestion @@ -309,12 +312,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { RegionErrorKind::TypeTestError { type_test } => { // Try to convert the lower-bound region into something named we can print for // the user. - let lower_bound_region = self.to_error_region(type_test.lower_bound); + let lower_bound_region = self.regioncx.to_error_region(type_test.lower_bound); let type_test_span = type_test.span; if let Some(lower_bound_region) = lower_bound_region { - let generic_ty = self.name_regions( + let generic_ty = self.regioncx.name_regions( self.infcx.tcx, type_test.generic_kind.to_ty(self.infcx.tcx), ); @@ -324,7 +327,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.body.source.def_id().expect_local(), type_test_span, Some(origin), - self.name_regions(self.infcx.tcx, type_test.generic_kind), + self.regioncx.name_regions(self.infcx.tcx, type_test.generic_kind), lower_bound_region, )); } else { @@ -450,7 +453,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); // Check if we can use one of the "nice region errors". - if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) { + if let (Some(f), Some(o)) = + (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) + { let infer_err = self.infcx.err_ctxt(); let nice = NiceRegionError::new_from_span(&infer_err, self.mir_def_id(), cause.span, o, f); @@ -481,7 +486,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { d.note("meoow :c"); d } - (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => { + (ConstraintCategory::Return(kind), true, false) + if self.regioncx.is_closure_fn_mut(fr) => + { self.report_fnmut_error(&errci, kind) } (ConstraintCategory::Assignment, true, false) @@ -736,7 +743,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // Only show an extra note if we can find an 'error region' for both of the region // variables. This avoids showing a noisy note that just mentions 'synthetic' regions // that don't help the user understand the error. - match (self.to_error_region(errci.fr), self.to_error_region(errci.outlived_fr)) { + match ( + self.regioncx.to_error_region(errci.fr), + self.regioncx.to_error_region(errci.outlived_fr), + ) { (Some(f), Some(o)) => { self.maybe_suggest_constrain_dyn_trait_impl(&mut diag, f, o, category); @@ -842,7 +852,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { outlived_fr: RegionVid, ) { if let (Some(f), Some(outlived_f)) = - (self.to_error_region(fr), self.to_error_region(outlived_fr)) + (self.regioncx.to_error_region(fr), self.regioncx.to_error_region(outlived_fr)) { if outlived_f.kind() != ty::ReStatic { return; @@ -1013,7 +1023,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } fn suggest_adding_lifetime_params(&self, diag: &mut Diag<'_>, sub: RegionVid, sup: RegionVid) { - let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else { + let (Some(sub), Some(sup)) = + (self.regioncx.to_error_region(sub), self.regioncx.to_error_region(sup)) + else { return; }; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index a00d39eea12d3..b8037fc83aad0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -283,7 +283,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { /// named variants. #[instrument(level = "trace", skip(self))] fn give_name_from_error_region(&self, fr: RegionVid) -> Option { - let error_region = self.to_error_region(fr)?; + let error_region = self.regioncx.to_error_region(fr)?; let tcx = self.infcx.tcx; @@ -1015,7 +1015,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { + let ty::ReEarlyParam(region) = self.regioncx.to_error_region(fr)?.kind() else { return None; }; if region.is_named() { @@ -1050,7 +1050,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, fr: RegionVid, ) -> Option { - let ty::ReEarlyParam(region) = self.to_error_region(fr)?.kind() else { + let ty::ReEarlyParam(region) = self.regioncx.to_error_region(fr)?.kind() else { return None; }; if region.is_named() { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 88b893f1add98..22e7d83e5b509 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -48,6 +48,7 @@ use rustc_mir_dataflow::points::DenseLocationMap; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_trait_selection::traits::query::type_op::{QueryTypeOp, TypeOp, TypeOpOutput}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -323,7 +324,6 @@ fn borrowck_collect_region_constraints<'tcx>( let input_promoted: &IndexSlice<_, _> = &promoted.borrow(); if let Some(e) = input_body.tainted_by_errors { infcx.set_tainted_by_errors(e); - root_cx.set_tainted_by_errors(e); } // Replace all regions with fresh inference variables. This @@ -571,15 +571,16 @@ fn borrowck_check_region_constraints<'tcx>( debug!("mbcx.used_mut: {:?}", mbcx.used_mut); mbcx.lint_unused_mut(); - if let Some(guar) = mbcx.emit_errors() { - mbcx.root_cx.set_tainted_by_errors(guar); - } let result = PropagatedBorrowCheckResults { closure_requirements: opt_closure_req, used_mut_upvars: mbcx.used_mut_upvars, }; + if let Some(guar) = mbcx.diags_buffer.emit_errors().or(infcx.tainted_by_errors()) { + root_cx.set_tainted_by_errors(guar); + } + if let Some(consumer) = &mut root_cx.consumer { consumer.insert_body( def, @@ -707,6 +708,14 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { next_region } + + fn fully_perform + TypeVisitable>>( + &self, + q: Q, + span: Span, + ) -> Result>, ErrorGuaranteed> { + self.param_env.and(q).fully_perform(&self.infcx, self.root_def_id, span) + } } impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { @@ -718,7 +727,7 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> { } pub(crate) struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { - root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + root_cx: &'a BorrowCheckRootCtxt<'tcx>, infcx: &'infcx BorrowckInferCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index dd6eb17947577..b11f1868b64c9 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -111,7 +111,7 @@ pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>( /// /// This may result in errors being reported. pub(crate) fn compute_regions<'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, + root_cx: &BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, location_table: &PoloniusLocationTable, diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index a082aba35b8a7..e406f158db216 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -72,7 +72,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { } pub(super) fn used_mut_upvars( - &mut self, + &self, nested_body_def_id: LocalDefId, ) -> &SmallVec<[FieldIdx; 8]> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 4d93fa08fe0bd..82e623bb42535 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{ self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions, }; use rustc_span::Span; -use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::TypeOpOutput; use tracing::{debug, instrument}; use crate::constraints::OutlivesConstraint; @@ -287,11 +287,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { - match self.infcx.param_env.and(DeeplyNormalize { value: ty }).fully_perform( - self.infcx, - self.infcx.root_def_id, - self.span, - ) { + match self.infcx.fully_perform(DeeplyNormalize { value: ty }, self.span) { Ok(TypeOpOutput { output: ty, constraints, .. }) => { // FIXME(higher_ranked_auto): What should we do with the assumptions here? if let Some(QueryRegionConstraints { constraints, assumptions: _ }) = constraints { diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 6c7beb85f1e8c..62becc8e298f4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -10,7 +10,7 @@ use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; +use rustc_trait_selection::traits::query::type_op; use tracing::{debug, instrument}; use type_op::TypeOpOutput; @@ -242,15 +242,14 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { if let Some(c) = constraints_unnorm { constraints.push(c) } - let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = - param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) - .unwrap_or_else(|guar| TypeOpOutput { - output: Ty::new_error(self.infcx.tcx, guar), - constraints: None, - error_info: None, - }); + let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self + .infcx + .fully_perform(DeeplyNormalize { value: ty }, span) + .unwrap_or_else(|guar| TypeOpOutput { + output: Ty::new_error(self.infcx.tcx, guar), + constraints: None, + error_info: None, + }); if let Some(c) = constraints_normalize { constraints.push(c) } @@ -299,9 +298,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = param_env - .and(DeeplyNormalize { value: ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span); + let result: Result<_, ErrorGuaranteed> = + self.infcx.fully_perform(DeeplyNormalize { value: ty }, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; }; @@ -354,11 +352,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { output: normalized_outlives, constraints: constraints_normalize, error_info: _, - }) = self.infcx.param_env.and(DeeplyNormalize { value: outlives }).fully_perform( - self.infcx, - self.infcx.root_def_id, - span, - ) + }) = self.infcx.fully_perform(DeeplyNormalize { value: outlives }, span) else { self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); return; @@ -381,9 +375,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { ) -> Option<&'tcx QueryRegionConstraints<'tcx>> { let TypeOpOutput { output: bounds, constraints, .. } = self .infcx - .param_env - .and(type_op::ImpliedOutlivesBounds { ty }) - .fully_perform(self.infcx, self.infcx.root_def_id, span) + .fully_perform(type_op::ImpliedOutlivesBounds { ty }, span) .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty)) .ok()?; debug!(?bounds, ?constraints); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index 840210496eb44..5a06f54cd2e41 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::query::dropck_outlives; -use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput}; +use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOpOutput}; use tracing::debug; use crate::polonius; @@ -638,9 +638,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { ) -> DropData<'tcx> { debug!("compute_drop_data(dropped_ty={:?})", dropped_ty); - let op = typeck.infcx.param_env.and(DropckOutlives { dropped_ty }); + let goal = DropckOutlives { dropped_ty }; - match op.fully_perform(typeck.infcx, typeck.root_cx.root_def_id(), DUMMY_SP) { + match typeck.infcx.fully_perform(goal, DUMMY_SP) { Ok(TypeOpOutput { output, constraints, .. }) => { DropData { dropck_result: output, region_constraint_data: constraints } } @@ -655,7 +655,9 @@ impl<'tcx> LivenessContext<'_, '_, 'tcx> { typeck.infcx.probe(|_| { let ocx = ObligationCtxt::new_with_diagnostics(&typeck.infcx); let errors = match dropck_outlives::compute_dropck_outlives_with_errors( - &ocx, op, span, + &ocx, + typeck.infcx.param_env.and(goal), + span, ) { Ok(_) => ocx.evaluate_obligations_error_on_ambiguity(), Err(e) => e, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5281b85b1ed04..123bc1f568495 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -95,7 +95,7 @@ mod relate_tys; /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis /// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'tcx>( - root_cx: &mut BorrowCheckRootCtxt<'tcx>, + root_cx: &BorrowCheckRootCtxt<'tcx>, infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, @@ -198,7 +198,6 @@ pub(crate) fn type_check<'tcx>( debug!("encountered an error region; removing constraints!"); constraints.outlives_constraints = Default::default(); constraints.type_tests = Default::default(); - root_cx.set_tainted_by_errors(guar); infcx.set_tainted_by_errors(guar); } @@ -229,7 +228,7 @@ enum FieldAccessError { /// way, it accrues region constraints -- these can later be used by /// NLL region checking. struct TypeChecker<'a, 'tcx> { - root_cx: &'a mut BorrowCheckRootCtxt<'tcx>, + root_cx: &'a BorrowCheckRootCtxt<'tcx>, infcx: &'a BorrowckInferCtxt<'tcx>, last_span: Span, body: &'a Body<'tcx>, @@ -1760,13 +1759,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => match uv.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - Some(UnevaluatedConst { def: def_id, args: uv.args, promoted: None }) - } + ty::ConstKind::Alias(_, alias_const) => match alias_const.kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => Some(UnevaluatedConst { + def: def_id, + args: alias_const.args, + promoted: None, + }), }, _ => None, }, diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 804d2757cf998..b0adbcd6db4ef 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -615,7 +615,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { BodyOwnerKind::Const { .. } | BodyOwnerKind::Static(..) => { match tcx.def_kind(self.mir_def) { - DefKind::InlineConst => { + DefKind::InlineConst if !tcx.is_type_system_inline_const(self.mir_def) => { // This is required for `AscribeUserType` canonical query, which will call // `type_of(inline_const_def_id)`. That `type_of` would inject erased lifetimes // into borrowck, which is ICE #78174. diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 8f965a5ef7b12..012951098fa15 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -271,6 +271,7 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ .map(|local| { let arg_ty = fx.monomorphize(fx.mir.local_decls[local].ty); + // FIXME(splat): un-tuple splatted arguments in codegen, for performance // Adapted from https://github.com/rust-lang/rust/blob/145155dc96757002c7b2e9de8489416e2fdbbd57/src/librustc_codegen_llvm/mir/mod.rs#L442-L482 if Some(local) == fx.mir.spread_arg { // This argument (e.g. the last argument in the "rust-call" ABI) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs index 2dee9176936fd..9ec84d135dc8f 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -19,7 +19,7 @@ pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( } match intrinsic { - "llvm.prefetch" => { + "llvm.prefetch.p0" => { // Nothing to do. This is merely a perf hint. } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 8ae4dedff8f28..6cbc0054cc015 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -993,7 +993,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { loaded_value.to_rvalue() } - fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { + fn volatile_load(&mut self, ty: Type<'gcc>, ptr: RValue<'gcc>, _: Align) -> RValue<'gcc> { + // FIXME(antoyo): set alignment. let ptr = self.context.new_cast(self.location, ptr, ty.make_volatile().make_pointer()); // (FractalFir): We insert a local here, to ensure this volatile load can't move across // blocks. diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 64f3748e4952e..7450f11f3ce80 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -59,13 +59,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> // or is it using a more efficient representation? match bytes.len() % 8 { 0 => { + debug_assert_eq!( + bytes.len() % 8, + 0, + "bytes length is not a multiple of 8, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 8); let elements: Vec<_> = bytes - .chunks_exact(8) - .map(|arr| { - let arr: [u8; 8] = arr.try_into().unwrap(); + .as_chunks::<8>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_long( byte_type, // Since we are representing arbitrary byte runs as integers, we need to follow the target @@ -80,13 +86,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> context.new_array_constructor(None, typ, &elements) } 4 => { + debug_assert_eq!( + bytes.len() % 4, + 0, + "bytes length is not a multiple of 4, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 4); let elements: Vec<_> = bytes - .chunks_exact(4) - .map(|arr| { - let arr: [u8; 4] = arr.try_into().unwrap(); + .as_chunks::<4>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_int( byte_type, match cx.sess().target.options.endian { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index d58697f1bf270..41efe3e8209bf 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -1044,7 +1044,7 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function #[cfg(feature = "master")] pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> { let gcc_name = match name { - "llvm.prefetch" => { + "llvm.prefetch.p0" => { let gcc_name = "__builtin_prefetch"; let func = cx.context.get_builtin_function(gcc_name); cx.functions.borrow_mut().insert(gcc_name.to_string(), func); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a12116d5b9d39..78a4c7e88c895 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -5,7 +5,7 @@ mod simd; use std::iter; use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp}; -use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; +use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; @@ -368,8 +368,9 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::volatile_load | sym::unaligned_volatile_load => { let ptr = args[0].immediate(); - let load = self.volatile_load(result.layout.gcc_type(self), ptr); - // FIXME(antoyo): set alignment. + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + let load = self.volatile_load(result.layout.gcc_type(self), ptr, ptr_align); if let BackendRepr::Scalar(scalar) = result.layout.backend_repr { self.to_immediate_scalar(load, scalar) } else { diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 0ffff2d331b1d..ec850cf7ab3d2 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -12,7 +12,7 @@ bitflags = "2.4.1" # To avoid duplicate dependencies, this should match the version of gimli used # by `rustc_codegen_ssa` via its `thorin-dwp` dependency. gimli = "0.31" -itertools = "0.12" +itertools = "0.15" libc = "0.2" libloading = { version = "0.9.0" } measureme = "12.0.1" diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5717247351ed6..58a62145d576e 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -687,9 +687,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value) -> &'ll Value { + fn volatile_load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { - let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); + let load = self.load(ty, ptr, align); llvm::LLVMSetVolatile(load, llvm::TRUE); load } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 6f411636bc69b..96fc781b15f9d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1015,7 +1015,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { - matches!(name, sym::volatile_load | sym::unaligned_volatile_load | sym::black_box) + matches!(name, sym::black_box) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index f88f0c2fb0994..f5b843b4e3e74 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -1,5 +1,6 @@ // .debug_gdb_scripts binary section. +use rustc_abi::Align; use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; use rustc_hir::attrs::DebuggerVisualizerType; @@ -18,10 +19,7 @@ pub(crate) fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Buil let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx); // Load just the first byte as that's all that's necessary to force // LLVM to keep around the reference to the global. - let volatile_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section); - unsafe { - llvm::LLVMSetAlignment(volatile_load_instruction, 1); - } + bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section, Align::ONE); } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 196d2df9e28de..7bd604bdbbd76 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -355,25 +355,39 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::volatile_load | sym::unaligned_volatile_load => { - let result = PlaceRef { - val: result_place.unwrap(), - layout: result_layout, - }; - + // Note that we cannot just load the `llvm_type` because we should never load non-scalars. + // Trying to do so blows up horribly in some cases -- for example loading a + // `MaybeUninint<&dyn Trait>` would load as `{ [i64x2] }` which gives assertions later + // (if we're lucky) from things not being pointers that ought to be. let ptr = args[0].immediate(); - let load = self.volatile_load(result_layout.llvm_type(self), ptr); - let align = if name == sym::unaligned_volatile_load { - 1 + let abi_align = result_layout.align.abi; + let ptr_align = if name == sym::volatile_load { abi_align } else { Align::ONE }; + if result_layout.is_zst() { + return IntrinsicResult::Operand(OperandValue::ZeroSized); + } else if let BackendRepr::Scalar(scalar) = result_layout.backend_repr { + let load = self.volatile_load(self.type_from_scalar(scalar), ptr, ptr_align); + self.to_immediate_scalar(load, scalar) } else { - result_layout.align.bytes() as u32 - }; - unsafe { - llvm::LLVMSetAlignment(load, align); - } - if !result_layout.is_zst() { - self.store_to_place(load, result.val); + // One day Rust will probably want to define how we split up a volatile load + // of something that's *not* just an ordinary scalar, but for now we can just + // use an LLVM integer type of the correct width and let it split it however. + let llty = self.type_ix(result_layout.size.bits()); + let temp = if let Some(result_place) = result_place { + PlaceRef { + val: result_place, + layout: result_layout, + } + } else { + PlaceRef::alloca(self, result_layout) + }; + let llval = self.volatile_load(llty, ptr, ptr_align); + self.store(llval, temp.val.llval, abi_align); + return if result_place.is_none() { + IntrinsicResult::Operand(self.load_operand(temp).val) + } else { + IntrinsicResult::WroteIntoPlace + }; } - return IntrinsicResult::WroteIntoPlace; } sym::volatile_store => { let dst = args[0].deref(self.cx()); @@ -399,7 +413,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let ptr = args[0].immediate(); let locality = fn_args.const_at(1).to_leaf().to_i32(); self.call_intrinsic( - "llvm.prefetch", + "llvm.prefetch.p0", &[self.val_ty(ptr)], &[ ptr, diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index c7d7b7429f34a..7996e808b2779 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,7 +9,7 @@ ar_archive_writer = "0.5" bitflags = "2.4.1" bstr = "1.11.3" find-msvc-tools = "0.1.2" -itertools = "0.12" +itertools = "0.15" pathdiff = "0.2.0" regex = "1.4" rustc_abi = { path = "../rustc_abi" } diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index bdf346d8e69d2..4fc516b244857 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -22,7 +22,7 @@ use rustc_target::spec::Arch; use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; -use super::rmeta_link; +use super::rmeta_link::{self, RmetaLinkCache}; use super::symbol_edit::{apply_edits, collect_internal_names}; use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs @@ -311,7 +311,7 @@ fn find_binutils_dlltool(sess: &Session) -> OsString { } pub enum AddArchiveKind<'a> { - Rlib(/*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), + Rlib(&'a mut RmetaLinkCache, /*skip*/ &'a dyn Fn(&str, ArchiveEntryKind) -> bool), Other, } @@ -466,7 +466,11 @@ pub fn try_extract_macho_fat_archive( } impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { - fn add_archive(&mut self, archive_path: &Path, ar_kind: AddArchiveKind<'_>) -> io::Result<()> { + fn add_archive( + &mut self, + archive_path: &Path, + mut ar_kind: AddArchiveKind<'_>, + ) -> io::Result<()> { let mut archive_path = archive_path.to_path_buf(); if self.sess.target.llvm_target.contains("-apple-macosx") && let Some(new_archive_path) = try_extract_macho_fat_archive(self.sess, &archive_path)? @@ -481,8 +485,14 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? }; let archive = ArchiveFile::parse(&*archive_map) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - let metadata_link = match ar_kind { - AddArchiveKind::Rlib(..) => rmeta_link::read(&archive, &archive_map, &archive_path), + let skip = match &ar_kind { + AddArchiveKind::Rlib(_, skip) => Some(*skip), + AddArchiveKind::Other => None, + }; + let metadata_link = match &mut ar_kind { + AddArchiveKind::Rlib(cache, _) => cache.get_or_insert_with(&archive_path, || { + rmeta_link::read(&archive, &archive_map, &archive_path) + }), AddArchiveKind::Other => None, }; let archive_index = self.src_archives.len(); @@ -512,9 +522,9 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { } else { ArchiveEntryKind::Other }; - let drop = match ar_kind { - AddArchiveKind::Rlib(skip) => skip(&file_name, kind), - AddArchiveKind::Other => false, + let drop = match skip { + Some(skip) => skip(&file_name, kind), + None => false, }; if !drop { let source = if entry.is_thin() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b7e153f47b024..07c713a006994 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -59,6 +59,7 @@ use super::archive::{ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; +use super::rmeta_link::RmetaLinkCache; use super::rpath::{self, RPathConfig}; use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; @@ -86,6 +87,7 @@ pub fn link_binary( let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); + let mut rmeta_link_cache = RmetaLinkCache::default(); for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -139,6 +141,7 @@ pub fn link_binary( link_staticlib( sess, archive_builder_builder, + &mut rmeta_link_cache, &compiled_modules, &crate_info, &metadata, @@ -150,6 +153,7 @@ pub fn link_binary( link_natively( sess, archive_builder_builder, + &mut rmeta_link_cache, crate_type, &out_filename, &compiled_modules, @@ -502,6 +506,7 @@ fn link_rlib<'a>( fn link_staticlib( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, compiled_modules: &CompiledModules, crate_info: &CrateInfo, metadata: &EncodedMetadata, @@ -531,7 +536,7 @@ fn link_staticlib( let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); ab.add_archive( path, - AddArchiveKind::Rlib(&|fname: &str, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|fname: &str, entry_kind| { // Ignore metadata and rmeta-link files. if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME { return true; @@ -939,6 +944,7 @@ fn report_linker_output( fn link_natively( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, out_filename: &Path, compiled_modules: &CompiledModules, @@ -965,6 +971,7 @@ fn link_natively( flavor, sess, archive_builder_builder, + rmeta_link_cache, crate_type, tmpdir, temp_filename, @@ -2562,6 +2569,7 @@ fn linker_with_args( flavor: LinkerFlavor, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_type: CrateType, tmpdir: &Path, out_filename: &Path, @@ -2690,6 +2698,7 @@ fn linker_with_args( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, crate_type, tmpdir, @@ -3126,6 +3135,7 @@ fn add_upstream_rust_crates( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, crate_type: CrateType, tmpdir: &Path, @@ -3178,6 +3188,7 @@ fn add_upstream_rust_crates( cmd, sess, archive_builder_builder, + rmeta_link_cache, crate_info, tmpdir, cnum, @@ -3309,6 +3320,7 @@ fn add_static_crate( cmd: &mut dyn Linker, sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, + rmeta_link_cache: &mut RmetaLinkCache, crate_info: &CrateInfo, tmpdir: &Path, cnum: CrateNum, @@ -3339,7 +3351,7 @@ fn add_static_crate( let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( cratepath, - AddArchiveKind::Rlib(&|f, entry_kind| { + AddArchiveKind::Rlib(rmeta_link_cache, &|f, entry_kind| { if f == METADATA_FILENAME || f == rmeta_link::FILENAME { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs index 68b23ca9ac5cb..58da783277dfe 100644 --- a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs +++ b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs @@ -2,9 +2,10 @@ //! and potentially other data collected and used when building or linking a rlib. //! See . -use std::path::Path; +use std::path::{Path, PathBuf}; use object::read::archive::ArchiveFile; +use rustc_data_structures::fx::FxHashMap; use rustc_serialize::opaque::mem_encoder::MemEncoder; use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; use rustc_serialize::{Decodable, Encodable}; @@ -54,3 +55,18 @@ pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option>, +} + +impl RmetaLinkCache { + pub fn get_or_insert_with( + &mut self, + rlib_path: &Path, + load: impl FnOnce() -> Option, + ) -> Option<&RmetaLink> { + self.cache.entry(rlib_path.to_path_buf()).or_insert_with(load).as_ref() + } +} diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 9b8d05c9f72c0..5ece363fcd8d9 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -375,6 +375,7 @@ fn push_debuginfo_type_name<'tcx>( output.push_str("fn("); } + // FIXME(splat): should debuginfo be de-tupled in the callee (and caller)? if !sig.inputs().is_empty() { for ¶meter_type in sig.inputs() { push_debuginfo_type_name(tcx, parameter_type, true, output, visited); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 84d0e6a7bf146..0173b84a4d9a1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1202,6 +1202,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; // Split the rust-call tupled arguments off. + // FIXME(splat): un-tuple splatted arguments in codegen, for performance let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall && let Some((tup, args)) = args.split_last() { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 67c9a85ca408f..68fed6c867fa5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -433,6 +433,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg_decl = &mir.local_decls[local]; let arg_ty = fx.monomorphize(arg_decl.ty); + // FIXME(splat): re-tuple splatted arguments that were un-tupled in the ABI if Some(local) == mir.spread_arg { // This argument (e.g., the last argument in the "rust-call" ABI) // is a tuple that was spread at the ABI level and now we have diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index d68549c6871f4..39c2529152566 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -242,7 +242,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; - fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; + fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn atomic_load( &mut self, ty: Self::Type, diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 111ada0241116..d6578def58692 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -344,13 +344,13 @@ where let uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => None, - // Unevaluated consts in MIR bodies don't have associated MIR (e.g. `type const`). - ty::ConstKind::Unevaluated(_, _) => None, + // Alias consts in MIR bodies don't have associated MIR (e.g. `type const`). + ty::ConstKind::Alias(_, _) => None, // FIXME(mgca): Investigate whether using `None` for `ConstKind::Value` is overly // strict, and if instead we should be doing some kind of value-based analysis. ty::ConstKind::Value(_) => None, _ => bug!( - "expected ConstKind::Param, ConstKind::Value, ConstKind::Unevaluated, or ConstKind::Error here, found {:?}", + "expected ConstKind::Param, ConstKind::Value, ConstKind::Alias, or ConstKind::Error here, found {:?}", ct ), }, diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index 3d8d9592459c4..8f58e39d419ce 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -7,7 +7,7 @@ use rustc_ast::Mutability; use rustc_hir::LangItem; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{self, Const, FnHeader, FnSigTys, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, Const, FnHeader, FnSigKind, FnSigTys, ScalarInt, Ty, TyCtxt}; use rustc_span::{Symbol, sym}; use crate::const_eval::CompileTimeMachine; @@ -436,6 +436,22 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { sym::variadic => { self.write_scalar(Scalar::from_bool(fn_sig_kind.c_variadic()), &field_place)?; } + sym::is_splatted => { + self.write_scalar( + Scalar::from_bool(fn_sig_kind.splatted().is_some()), + &field_place, + )?; + } + sym::splatted_index => { + self.write_scalar( + Scalar::from_u8( + // Currently the same encoding as FnSigKind.splatted + // FIXME(splat): make these two fields into a single Option, or choose a stable encoding + fn_sig_kind.splatted().unwrap_or(FnSigKind::NO_SPLATTED_ARG_INDEX), + ), + &field_place, + )?; + } other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 44420b7148478..e2fe8c3a79690 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -681,6 +681,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { }; // Special handling for the closure ABI: untuple the last argument. + // FIXME(splat): un-tuple splatted arguments that were tupled in typecheck let args: Cow<'_, [FnArg<'tcx, M::Provenance>]> = if caller_abi == ExternAbi::RustCall && !args.is_empty() { // Untuple diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md index bd93626a8db4d..b194e11b7f85e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0755.md +++ b/compiler/rustc_error_codes/src/error_codes/E0755.md @@ -20,7 +20,7 @@ side effects or infinite loops: extern "C" { #[unsafe(ffi_pure)] // ok! - pub fn strlen(s: *const i8) -> isize; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0756.md b/compiler/rustc_error_codes/src/error_codes/E0756.md index daafc2a5ac092..74233a6ad7a7a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0756.md +++ b/compiler/rustc_error_codes/src/error_codes/E0756.md @@ -21,7 +21,7 @@ which have no side effects except for their return value: extern "C" { #[unsafe(ffi_const)] // ok! - pub fn strlen(s: *const i8) -> i32; + pub fn strlen(s: *const std::ffi::c_char) -> usize; } # fn main() {} ``` diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 7c125c3a8983e..acae158acb2b4 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -515,19 +515,66 @@ pub enum HideOrShow { Show, } -#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct CfgInfo { - pub name: Symbol, - pub name_span: Span, - pub value: Option<(Symbol, Span)>, +#[derive(Clone, Copy, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub struct DocCfgHideShowValue { + pub span: Span, + /// If `value` is `None`, then it's a `none()` value. + pub value: Option, +} + +impl DocCfgHideShowValue { + pub fn new(value: Symbol, span: Span) -> Self { + Self { span, value: Some(value) } + } + + pub fn new_none(span: Span) -> Self { + Self { span, value: None } + } +} + +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq)] +pub enum DocCfgHideShow { + Any(Span), + List(ThinVec), } -impl CfgInfo { - pub fn span_for_name_and_value(&self) -> Span { - if let Some((_, value_span)) = self.value { - self.name_span.with_hi(value_span.hi()) - } else { - self.name_span +impl DocCfgHideShow { + pub fn new() -> Self { + Self::List(ThinVec::new()) + } + + pub fn new_with_only_key(span: Span) -> Self { + let mut values = ThinVec::with_capacity(1); + values.push(DocCfgHideShowValue { span, value: None }); + Self::List(values) + } + + pub fn push_none(&mut self, span: Span) { + if let Self::List(values) = self + && !values.iter().any(|v| v.value.is_none()) + { + values.push(DocCfgHideShowValue { span, value: None }); + } + } + + pub fn merge_with(&mut self, other: &Self) { + match (self, other) { + (Self::Any(_), Self::Any(_) | Self::List(_)) => { + // Nothing to do. + } + (s, Self::Any(span)) => { + // We "upgrade" the list values to "all". + *s = Self::Any(*span); + } + (Self::List(values), Self::List(other_values)) => { + // Having duplicates is not an issue, we simply ignore them. Would be more + // convenient to have a `set` type though. T_T + for other in other_values { + if !values.iter().any(|value| value.value == other.value) { + values.push(*other); + } + } + } } } } @@ -535,7 +582,7 @@ impl CfgInfo { #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct CfgHideShow { pub kind: HideOrShow, - pub values: ThinVec, + pub values: FxIndexMap, } #[derive(Clone, Debug, Default, StableHash, Decodable, PrintAttribute)] diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 9d14f9de3078d..d826aa363f349 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -81,7 +81,7 @@ impl PrintAttribute for ThinVec { p.word("]"); } } -impl PrintAttribute for FxIndexMap { +impl PrintAttribute for FxIndexMap { fn should_render(&self) -> bool { self.is_empty() || self[0].should_render() } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index e473db48b04c2..d0300d63d1213 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -448,43 +448,6 @@ impl DefKind { | DefKind::ExternCrate => false, } } - - /// Returns `true` if `self` is a kind of definition that does not have its own - /// type-checking context, i.e. closure, coroutine or inline const. - #[inline] - pub fn is_typeck_child(self) -> bool { - match self { - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, - DefKind::Mod - | DefKind::Struct - | DefKind::Union - | DefKind::Enum - | DefKind::Variant - | DefKind::Trait - | DefKind::TyAlias - | DefKind::ForeignTy - | DefKind::TraitAlias - | DefKind::AssocTy - | DefKind::TyParam - | DefKind::Fn - | DefKind::Const { .. } - | DefKind::ConstParam - | DefKind::Static { .. } - | DefKind::Ctor(_, _) - | DefKind::AssocFn - | DefKind::AssocConst { .. } - | DefKind::Macro(_) - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::OpaqueTy - | DefKind::Field - | DefKind::LifetimeParam - | DefKind::GlobalAsm - | DefKind::Impl { .. } => false, - } - } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f429d14e3a51c..254d5fdd5f80c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -387,12 +387,24 @@ pub struct PathSegment<'hir> { /// out of those only the segments with no type parameters /// to begin with, e.g., `Vec::new` is `>::new::<..>`. pub infer_args: bool, + + /// Whether this segment is a delegation's child segment: + /// `reuse Trait::foo`, in this case `foo` is a delegation's child segment. + /// Used for faster check during generic args lowering. + pub delegation_child_segment: bool, } impl<'hir> PathSegment<'hir> { /// Converts an identifier to the corresponding segment. pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> { - PathSegment { ident, hir_id, res, infer_args: true, args: None } + PathSegment { + ident, + hir_id, + res, + infer_args: true, + args: None, + delegation_child_segment: false, + } } pub fn invalid() -> Self { @@ -4045,12 +4057,12 @@ pub struct Param<'hir> { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SplattedArgIndexError { /// The splatted argument index is invalid. - /// Currently the only unsupported index is `u16::MAX`, which is used to indicate that no argument - /// is splatted. - InvalidIndex { splatted_arg_index: u16 }, + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, /// The splatted argument index is outside the bounds of the function arguments. - OutOfBounds { splatted_arg_index: u16, args_len: u16 }, + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, } /// Contains the packed non-type fields of a function declaration. @@ -4061,9 +4073,9 @@ pub struct FnDeclFlags { flags: u8, /// Which function argument is splatted into multiple arguments in callers, if any? - /// Splatting functions with `u16::MAX` arguments is not supported, see `FnSigKind` for + /// Splatting functions with `>= u8::MAX` arguments is not supported, see `FnSigKind` for /// details. - splatted: u16, + splatted: u8, } impl fmt::Debug for FnDeclFlags { @@ -4101,13 +4113,13 @@ impl FnDeclFlags { /// Marker index for "no splatted argument". /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. - const NO_SPLATTED_ARG_INDEX: u16 = u16::MAX; + const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; /// Create a new FnDeclKind with no implicit self, no lifetime elision, no C-style variadic /// argument, and no splatting. /// To modify these flags, use the `set_*` methods, for readability. // FIXME: use Default instead when that trait is const stable. - pub const fn default() -> Self { + pub fn default() -> Self { Self { flags: 0, splatted: 0 } .set_implicit_self(ImplicitSelfKind::None) .set_lifetime_elision_allowed(false) @@ -4117,7 +4129,7 @@ impl FnDeclFlags { /// Set the implicit self kind. #[must_use = "this method does not modify the receiver"] - pub const fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { + pub fn set_implicit_self(mut self, implicit_self: ImplicitSelfKind) -> Self { self.flags &= !Self::IMPLICIT_SELF_MASK; match implicit_self { @@ -4133,7 +4145,7 @@ impl FnDeclFlags { /// Set the C-style variadic argument flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_c_variadic(mut self, c_variadic: bool) -> Self { + pub fn set_c_variadic(mut self, c_variadic: bool) -> Self { if c_variadic { self.flags |= Self::C_VARIADIC_FLAG; } else { @@ -4145,7 +4157,7 @@ impl FnDeclFlags { /// Set the lifetime elision allowed flag. #[must_use = "this method does not modify the receiver"] - pub const fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { + pub fn set_lifetime_elision_allowed(mut self, allowed: bool) -> Self { if allowed { self.flags |= Self::LIFETIME_ELISION_ALLOWED_FLAG; } else { @@ -4158,16 +4170,17 @@ impl FnDeclFlags { /// Set the splatted argument index. /// The number of function arguments is used for error checking. #[must_use = "this method does not modify the receiver"] - pub const fn set_splatted( + pub fn set_splatted( mut self, - splatted: Option, + splatted: Option, args_len: usize, ) -> Result { if let Some(splatted_arg_index) = splatted { if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); - } else if splatted_arg_index as usize >= args_len { + } else if usize::from(splatted_arg_index) >= args_len { return Err(SplattedArgIndexError::OutOfBounds { splatted_arg_index, args_len: args_len as u16, @@ -4184,14 +4197,14 @@ impl FnDeclFlags { /// Set "no splatted arguments" for the function declaration. #[must_use = "this method does not modify the receiver"] - pub const fn set_no_splatted_args(mut self) -> Self { + pub fn set_no_splatted_args(mut self) -> Self { self.splatted = Self::NO_SPLATTED_ARG_INDEX; self } /// Get the implicit self kind. - pub const fn implicit_self(self) -> ImplicitSelfKind { + pub fn implicit_self(self) -> ImplicitSelfKind { match self.flags & Self::IMPLICIT_SELF_MASK { 0 => ImplicitSelfKind::None, 1 => ImplicitSelfKind::Imm, @@ -4203,17 +4216,17 @@ impl FnDeclFlags { } /// Do the function arguments end with a C-style variadic argument? - pub const fn c_variadic(self) -> bool { + pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } /// Is lifetime elision allowed? - pub const fn lifetime_elision_allowed(self) -> bool { + pub fn lifetime_elision_allowed(self) -> bool { self.flags & Self::LIFETIME_ELISION_ALLOWED_FLAG != 0 } /// Get the splatted argument index, if any. - pub const fn splatted(self) -> Option { + pub fn splatted(self) -> Option { if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } } } @@ -4263,7 +4276,7 @@ impl<'hir> FnDecl<'hir> { self.fn_decl_kind.lifetime_elision_allowed() } - pub fn splatted(&self) -> Option { + pub fn splatted(&self) -> Option { self.fn_decl_kind.splatted() } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9e0eaef596420..f1f542e9ce1e9 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1468,7 +1468,8 @@ pub fn walk_path_segment<'v, V: Visitor<'v>>( visitor: &mut V, segment: &'v PathSegment<'v>, ) -> V::Result { - let PathSegment { ident, hir_id, res: _, args, infer_args: _ } = segment; + let PathSegment { ident, hir_id, res: _, args, infer_args: _, delegation_child_segment: _ } = + segment; try_visit!(visitor.visit_ident(*ident)); try_visit!(visitor.visit_id(*hir_id)); visit_opt!(visitor, visit_generic_args, *args); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 2069b746b5a7f..9c3f7789f9ed9 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -447,6 +447,14 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); + + // Runtime symbols + MemCpy, sym::memcpy_fn, memcpy_fn, Target::Fn, GenericRequirement::None; + MemMove, sym::memmove_fn, memmove_fn, Target::Fn, GenericRequirement::None; + MemSet, sym::memset_fn, memset_fn, Target::Fn, GenericRequirement::None; + MemCmp, sym::memcmp_fn, memcmp_fn, Target::Fn, GenericRequirement::None; + Bcmp, sym::bcmp_fn, bcmp_fn, Target::Fn, GenericRequirement::None; + StrLen, sym::strlen_fn, strlen_fn, Target::Fn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 85b0e3958c2d6..f9d94bd505724 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -23,7 +23,28 @@ macro_rules! weak_lang_items { } } +macro_rules! weak_only_lang_items { + ($($item:ident,)*) => { + pub static WEAK_ONLY_LANG_ITEMS: &[LangItem] = &[$(LangItem::$item,)*]; + + impl LangItem { + pub fn is_weak_only(self) -> bool { + matches!(self, $(LangItem::$item)|*) + } + } + } +} + weak_lang_items! { PanicImpl, rust_begin_unwind; EhPersonality, rust_eh_personality; } + +weak_only_lang_items! { + MemCpy, + MemMove, + MemSet, + MemCmp, + Bcmp, + StrLen, +} diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 0dbd4333ed3ca..955629d40cee2 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -9,7 +9,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e98307c7c7b18..0aa31c3a016b9 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -778,8 +778,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), if has_default { // need to store default and type of default let ct = tcx.const_param_default(param.def_id).skip_binder(); - if let ty::ConstKind::Unevaluated(_, uv) = ct.kind() - && let Some(def_id) = uv.kind.opt_def_id() + if let ty::ConstKind::Alias(_, alias_const) = ct.kind() + && let Some(def_id) = alias_const.kind.opt_def_id() { tcx.ensure_ok().type_of(def_id); } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e1bbc8f0641de..9d8e0a512ab66 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -444,11 +444,13 @@ fn fn_sig_suggestion<'tcx>( predicates: impl IntoIterator, Span)>, assoc: ty::AssocItem, ) -> String { + let splatted_arg_index = sig.splatted().map(usize::from); let args = sig .inputs() .iter() .enumerate() .map(|(i, ty)| { + let splat = if splatted_arg_index == Some(i) { "#[splat] " } else { "" }; let arg_ty = match ty.kind() { ty::Param(_) if assoc.is_method() && i == 0 => "self".to_string(), ty::Ref(reg, ref_ty, mutability) if i == 0 => { @@ -477,7 +479,7 @@ fn fn_sig_suggestion<'tcx>( } } }; - Some(format!("{arg_ty}")) + Some(format!("{splat}{arg_ty}")) }) .chain(std::iter::once(if sig.c_variadic() { Some("...".to_string()) } else { None })) .flatten() diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index bf8593eb61882..1d17621be217f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1488,7 +1488,7 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: // be sure if it will error or not as user might always specify the other. // FIXME(generic_const_exprs): This is incorrect when dealing with unused const params. // E.g: `struct Foo;`. Here, we should - // eagerly error but we don't as we have `ConstKind::Unevaluated(.., [N, M])`. + // eagerly error but we don't as we have `ConstKind::Alias(.., [N, M])`. if !default.has_param() { wfcx.register_wf_obligation( tcx.def_span(param.def_id), @@ -1509,7 +1509,9 @@ pub(super) fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, def_id: | ty::ConstKind::Bound(_, _) => unreachable!(), ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(wfcx.param_env) } @@ -1714,7 +1716,16 @@ fn check_fn_or_method<'tcx>( let span = tcx.def_span(def_id); let has_implicit_self = hir_decl.implicit_self().has_implicit_self(); let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 }); - // FIXME(splat): use `sig.splatted()` once FnSig has it + // FIXME(splat): support the rest of closure splatting, or replace this code with an error + if let Some(mut splatted_arg_index) = sig.splatted() { + let mut inputs_count = sig.inputs().len(); + if has_implicit_self { + splatted_arg_index = splatted_arg_index.strict_sub(1); + inputs_count = inputs_count.strict_sub(1); + } + debug!(?splatted_arg_index, ?inputs_count, ?has_implicit_self, ?sig); + sig = sig.set_splatted(Some(splatted_arg_index), inputs_count).unwrap(); + } // Check that the argument is a tuple and is sized if let Some(ty) = inputs.next() { wfcx.register_bound( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 275fed00a4f8a..05648bf7c2ece 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,7 +15,7 @@ //! crate as a kind of pass. This should eventually be factored away. use std::cell::Cell; -use std::{assert_matches, iter}; +use std::{assert_matches, debug_assert_matches, iter}; use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; @@ -1635,10 +1635,12 @@ fn const_param_default<'tcx>( } fn anon_const_kind<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> ty::AnonConstKind { + debug_assert_matches!(tcx.def_kind(def), DefKind::AnonConst | DefKind::InlineConst); let hir_id = tcx.local_def_id_to_hir_id(def); let const_arg_id = tcx.parent_hir_id(hir_id); match tcx.hir_node(const_arg_id) { - hir::Node::ConstArg(_) => { + hir::Node::ConstArg(const_arg) => { + debug_assert_matches!(const_arg.kind, hir::ConstArgKind::Anon(hir::AnonConst { def_id, .. }) if *def_id == def); let parent_hir_node = tcx.hir_node(tcx.parent_hir_id(const_arg_id)); if tcx.features().generic_const_exprs() { ty::AnonConstKind::GCE diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 64f12fd6644c5..811ed83e0bf48 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -124,11 +124,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // ^ parent_def_id // // then we only want to return generics for params to the left of `N`. If we don't do that we - // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, args: [N#0])`. + // end up with that const looking like: `ty::ConstKind::Alias(def_id, args: [N#0])`. // // This causes ICEs (#86580) when building the args for Foo in `fn foo() -> Foo { .. }` as // we instantiate the defaults with the partially built args when we build the args. Instantiating - // the `N#0` on the unevaluated const indexes into the empty args we're in the process of building. + // the `N#0` on the alias const indexes into the empty args we're in the process of building. // // We fix this by having this function return the parent's generics ourselves and truncating the // generics to only include non-forward declared params (with the exception of the `Self` ty) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index e1798c24838d1..0dc89381c5eb6 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -415,8 +415,8 @@ fn const_evaluatable_predicates_of<'tcx>( preds: FxIndexSet<(ty::Clause<'tcx>, Span)>, } - fn is_const_param_default(tcx: TyCtxt<'_>, kind: ty::UnevaluatedConstKind<'_>) -> bool { - let ty::UnevaluatedConstKind::Anon { def_id } = kind else { return false }; + fn is_const_param_default(tcx: TyCtxt<'_>, kind: ty::AliasConstKind<'_>) -> bool { + let ty::AliasConstKind::Anon { def_id } = kind else { return false }; let Some(local) = def_id.as_local() else { return false }; let hir_id = tcx.local_def_id_to_hir_id(local); @@ -433,8 +433,8 @@ fn const_evaluatable_predicates_of<'tcx>( impl<'tcx> TypeVisitor> for ConstCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { - if let ty::ConstKind::Unevaluated(_, uv) = c.kind() { - if is_const_param_default(self.tcx, uv.kind) { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { + if is_const_param_default(self.tcx, alias_const.kind) { // Do not look into const param defaults, // these get checked when they are actually instantiated. // @@ -446,11 +446,11 @@ fn const_evaluatable_predicates_of<'tcx>( } // Skip type consts as mGCA doesn't support evaluatable clauses. - if uv.kind.is_type_const(self.tcx) { + if alias_const.kind.is_type_const(self.tcx) { return; } - let span = uv.kind.def_span(self.tcx); + let span = alias_const.kind.def_span(self.tcx); self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span)); } } diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 46b2eb538f080..a392ff5d3a552 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -93,7 +93,7 @@ impl<'tcx> TypeVisitor> for ParameterCollector { fn visit_const(&mut self, c: ty::Const<'tcx>) { match c.kind() { - ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { + ty::ConstKind::Alias(..) if !self.include_nonconstraining => { // Constant expressions are not injective in general. return; } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 810e14a7d6d0b..d730683b132d4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -467,6 +467,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 39402cfc35dce..9d22d3060e1ff 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -409,9 +409,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn report_ambiguous_assoc_item( &self, - bound1: ty::PolyTraitRef<'tcx>, - bound2: ty::PolyTraitRef<'tcx>, - matching_candidates: impl Iterator>, + matching_candidates: &[ty::PolyTraitRef<'tcx>], qself: AssocItemQSelf, assoc_tag: ty::AssocTag, assoc_ident: Ident, @@ -443,7 +441,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // predicates!). // FIXME: Turn this into a structured, translatable & more actionable suggestion. let mut where_bounds = vec![]; - for bound in [bound1, bound2].into_iter().chain(matching_candidates) { + for &bound in matching_candidates { let bound_id = bound.def_id(); let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( tcx, @@ -473,6 +471,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { res: Res::Err, args: Some(constraint.gen_args), infer_args: false, + delegation_child_segment: false, }; let alias_args = self.lower_generic_args_of_assoc_item( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index a36bb7bce9bab..45c2ed205c74d 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -434,7 +434,7 @@ pub(crate) fn check_generic_arg_count( // Suppress this warning for delegations as it is compiler generated and lifetimes are // propagated while late-bound lifetimes may be present. - let explicit_late_bound = match tcx.hir_is_delegation_child_segment(seg) { + let explicit_late_bound = match seg.delegation_child_segment { true => ExplicitLateBound::No, false => prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos), }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index baa9fddc2a651..b29e469348f81 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -24,6 +24,7 @@ use std::{assert_matches, slice}; use rustc_abi::FIRST_VARIANT; use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::sso::SsoHashSet; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, @@ -482,7 +483,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let parent_def_id = self.item_def_id(); if let Res::Def(DefKind::ConstParam, _) = res - && tcx.def_kind(parent_def_id) == DefKind::AnonConst + && matches!(tcx.def_kind(parent_def_id), DefKind::AnonConst | DefKind::InlineConst) && let ty::AnonConstKind::MCG = tcx.anon_const_kind(parent_def_id) { let folder = ForbidParamUsesFolder { @@ -511,15 +512,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Inline consts and closures can be nested inside anon consts that forbid generic // params (e.g. an enum discriminant). Walk up the def parent chain to find the // nearest enclosing AnonConst and use that to determine the context. + let parent_def_id = tcx.typeck_root_def_id(parent_def_id.into()); + let anon_const_def_id = match tcx.def_kind(parent_def_id) { DefKind::AnonConst => parent_def_id, - DefKind::InlineConst | DefKind::Closure => { - let root = tcx.typeck_root_def_id(parent_def_id.into()); - match tcx.def_kind(root) { - DefKind::AnonConst => root.expect_local(), - _ => return None, - } - } + DefKind::InlineConst if tcx.is_type_system_inline_const(parent_def_id) => parent_def_id, _ => return None, }; @@ -869,7 +866,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span, generic_args: segment.args(), infer_args: segment.infer_args, - create_synth_args: tcx.hir_is_delegation_child_segment(segment), + create_synth_args: segment.delegation_child_segment, incorrect_args: &arg_count.correct, }; @@ -1262,6 +1259,74 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) } + /// When there are multiple traits which contain an identically named + /// associated item, this function eliminates any traits which are a + /// supertrait of another candidate trait. + /// + /// This is the type-level analogue of + /// `rustc_hir_typeck::method::probe::ProbeContext::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// + /// This implements RFC #3624. + fn collapse_candidates_to_subtrait_pick( + &self, + matching_candidates: &[ty::PolyTraitRef<'tcx>], + ) -> Option> { + if !self.tcx().features().supertrait_item_shadowing() { + return None; + } + + let mut child_trait = matching_candidates[0]; + let mut supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), child_trait.def_id()).collect(); + + let mut remaining_candidates: Vec<_> = matching_candidates[1..].iter().copied().collect(); + while !remaining_candidates.is_empty() { + let mut made_progress = false; + let mut next_round = vec![]; + + for remaining_trait in remaining_candidates { + if supertraits.contains(&remaining_trait.def_id()) { + made_progress = true; + continue; + } + + // This candidate is not a supertrait of the `child_trait`. + // Check if it's a subtrait of the `child_trait`, instead. + // If it is, then it must have been a subtrait of every + // other pick we've eliminated at this point. It will + // take over at this point. + let remaining_trait_supertraits: SsoHashSet<_> = + traits::supertrait_def_ids(self.tcx(), remaining_trait.def_id()).collect(); + if remaining_trait_supertraits.contains(&child_trait.def_id()) { + child_trait = remaining_trait; + supertraits = remaining_trait_supertraits; + made_progress = true; + continue; + } + + // Neither `child_trait` or the current candidate are + // supertraits of each other. + // Don't bail here, since we may be comparing two supertraits + // of a common subtrait. These two supertraits won't be related + // at all, but we will pick them up next round when we find their + // child as we continue iterating in this round. + next_round.push(remaining_trait); + } + + if made_progress { + // If we've made progress, iterate again. + remaining_candidates = next_round; + } else { + // Otherwise, we must have at least two candidates which + // are not related to each other at all. + return None; + } + } + + Some(child_trait) + } + /// Search for a single trait bound whose trait defines the associated item given by /// `assoc_ident`. /// @@ -1296,10 +1361,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; if let Some(bound2) = matching_candidates.next() { + let all_matching_candidates: Vec<_> = + [bound1, bound2].into_iter().chain(matching_candidates).collect(); + if let Some(bound) = self.collapse_candidates_to_subtrait_pick(&all_matching_candidates) + { + return Ok(bound); + } + return Err(self.report_ambiguous_assoc_item( - bound1, - bound2, - matching_candidates, + &all_matching_candidates, qself, assoc_tag, assoc_ident, @@ -1402,7 +1472,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(def_id) = alias_ct.kind.opt_def_id() { self.require_type_const_attribute(def_id, span)?; } - let ct = Const::new_unevaluated(tcx, ty::IsRigid::No, alias_ct); + let ct = Const::new_alias(tcx, ty::IsRigid::No, alias_ct); let ct = self.check_param_uses_if_mcg(ct, span, false); Ok(ct) } @@ -1865,12 +1935,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::AssocTag::Const, )?; self.require_type_const_attribute(item_def_id, span)?; - let uv = ty::UnevaluatedConst::new( + let alias_const = ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, item_def_id), + ty::AliasConstKind::new_from_def_id(tcx, item_def_id), item_args, ); - Ok(Const::new_unevaluated(tcx, ty::IsRigid::No, uv)) + Ok(Const::new_alias(tcx, ty::IsRigid::No, alias_const)) } /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path. @@ -2772,14 +2842,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let _ = self .prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None); let args = self.lower_generic_args_of_path_segment(span, did, segment); - ty::Const::new_unevaluated( + ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, did), - args, - ), + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, did), args), ) } Res::Def(kind @ DefKind::Ctor(ctor_of, CtorKind::Const), did) => { @@ -2866,7 +2932,27 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { did, path.segments.last().unwrap(), ); - ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + + if self.tcx().generics_of(did).own_synthetic_params_count() == 0 { + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } else { + let tcx = self.tcx(); + let generics = tcx.generics_of(did); + + // Use infer tys for synthetic params; otherwise the impl header's trait ref may + // contain callee-owned synthetic params and fail when instantiated with impl args. + // See issue #155834 + let args = args.iter().enumerate().map(|(index, arg)| { + let param = generics.param_at(index, tcx); + if param.kind.is_synthetic() { + self.ty_infer(Some(param), span).into() + } else { + arg + } + }); + + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } } // Exhaustive match to be clear about what exactly we're considering to be @@ -2915,7 +3001,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.check_param_uses_if_mcg(ct, span, false) } - /// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`. + /// Literals are eagerly converted to a constant, everything else becomes `ConstKind::Alias`. #[instrument(skip(self), level = "debug")] fn lower_const_arg_anon(&self, anon: &AnonConst) -> Const<'tcx> { let tcx = self.tcx(); @@ -2930,12 +3016,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match self.try_lower_anon_const_lit(ty, expr) { Some(v) => v, - None => ty::Const::new_unevaluated( + None => ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new( + ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::Anon { def_id: anon.def_id.to_def_id() }, + ty::AliasConstKind::Anon { def_id: anon.def_id.to_def_id() }, ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()), ), ), @@ -3582,11 +3668,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { debug!(?output_ty); debug!(?abi, ?safety, ?decl.fn_decl_kind, input_tys_len = ?input_tys.len()); - // FIXME(splat): use `set_splatted()` once FnSig has it let fn_sig_kind = FnSigKind::default() .set_abi(abi) .set_safety(safety) - .set_c_variadic(decl.fn_decl_kind.c_variadic()); + .set_c_variadic(decl.fn_decl_kind.c_variadic()) + .set_splatted(decl.splatted(), input_tys.len()) + .unwrap(); let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, fn_sig_kind); let fn_ptr_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c5a6ce75c0e41..ff00e18783706 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -186,9 +186,13 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } _ => (), } - // Skip `AnonConst`s because we feed their `type_of`. + // Skip `AnonConst`s and type system `InlineConst`s because we feed their `type_of` in + // `feed_anon_const_type`. // Also skip items for which typeck forwards to parent typeck. - if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { + if !(def_kind == DefKind::AnonConst + || def_kind == DefKind::InlineConst && tcx.is_type_system_inline_const(item_def_id) + || tcx.is_typeck_child(item_def_id.to_def_id())) + { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 095213322a9fd..a2e3883b0684e 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -408,8 +408,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance); match &c.kind() { - ty::ConstKind::Unevaluated(_, uv) => { - self.add_constraints_from_invariant_args(current, uv.args, variance); + ty::ConstKind::Alias(_, alias_const) => { + self.add_constraints_from_invariant_args(current, alias_const.args, variance); } _ => {} } diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 43db09b0eab8f..9d185a565fe00 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 57ab29ac752ad..bb822f8d0f68d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -9,11 +9,11 @@ use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; -use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; @@ -58,9 +58,13 @@ pub(crate) fn check_legal_trait_for_method_call( tcx.ensure_result().coherent_trait(trait_id) } +/// State machine for typechecking a call, based on the callee type. #[derive(Debug)] enum CallStep<'tcx> { + /// Typecheck a call to a function definition or pointer. + /// Includes functions with splatted arguments. Builtin(Ty<'tcx>), + /// Deferred closure Fn* trait typechecking, when the callee is a closure. DeferredClosure(LocalDefId, ty::FnSig<'tcx>), /// Call overloading when callee implements one of the Fn* traits. Overloaded(MethodCallee<'tcx>), @@ -544,7 +548,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (fn_sig, def_id) = match *callee_ty.kind() { + let (fn_sig, def_id, callee_generic_args) = match *callee_ty.kind() { ty::FnDef(def_id, args) => { self.enforce_context_effects(Some(call_expr.hir_id), call_expr.span, def_id, args); let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args).skip_norm_wip(); @@ -573,11 +577,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit(); } } - (fn_sig, Some(def_id)) + (fn_sig, Some(def_id), Some(args)) } // FIXME(const_trait_impl): these arms should error because we can't enforce them - ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None), + ty::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None, None), _ => unreachable!(), }; @@ -595,12 +599,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); self.check_argument_types_maybe_method_like( - &fn_sig, call_expr, arg_exprs, expected, def_id, + &fn_sig, + call_expr, + arg_exprs, + expected, + TupleArgumentsFlag::with_fn_sig_kind(fn_sig.fn_sig_kind, false), + def_id, + callee_generic_args, ); + // Splatting is currently incompatible with RustCall. if fn_sig.abi() == rustc_abi::ExternAbi::RustCall { let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span); - if let Some(ty) = fn_sig.inputs().last().copied() { + if let Some(ty) = fn_sig.inputs().last().copied() + && fn_sig.splatted().is_none() + { self.register_bound( ty, self.tcx.require_lang_item(hir::LangItem::Tuple, sp), @@ -627,7 +640,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, + tuple_arguments_flag: TupleArgumentsFlag, def_id: Option, + callee_generic_args: Option>, ) { let do_check = || { self.check_argument_types( @@ -638,8 +653,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected, arg_exprs, fn_sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + tuple_arguments_flag, def_id, + callee_generic_args, ); }; @@ -1009,9 +1025,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output(), expected, arg_exprs, - fn_sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + fn_sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(closure_def_id.to_def_id()), + None, ); fn_sig.output() @@ -1092,6 +1109,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, method: MethodCallee<'tcx>, ) -> Ty<'tcx> { + // FIXME(splat): if we ever support splatting here, decrement the splatted index, because + // the receiver argument is removed below. + assert_eq!( + method.sig.fn_sig_kind.splatted(), + None, + "splatting is not supported on RustCall tuples", + ); self.check_argument_types( call_expr.span, call_expr, @@ -1099,9 +1123,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output(), expected, arg_exprs, - method.sig.c_variadic(), - TupleArgumentsFlag::TupleArguments, + method.sig.fn_sig_kind.c_variadic(), + TupleArgumentsFlag::rust_fn_trait_call(), Some(method.def_id), + None, ); self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method); @@ -1161,11 +1186,16 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> { ); } None => { - span_bug!( - self.call_expr.span, - "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", - self.closure_ty - ) + let guar = fcx.tainted_by_errors().unwrap_or_else(|| { + fcx.dcx().span_delayed_bug( + self.call_expr.span, + format!( + "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`", + self.closure_ty + ), + ) + }); + fcx.write_resolution(self.call_expr.hir_id, Err(guar)); } } } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 41aebea1c7ebe..95e67b135e411 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -720,6 +720,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in this binder we are creating. assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); let bound_sig = expected_sig.sig.map_bound(|sig| { + // Ignore splatting, it is unsupported on closures. let fn_sig_kind = FnSigKind::default() .set_abi(ExternAbi::RustCall) .set_safety(hir::Safety::Safe) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 6e4d6aa80a63a..bd8295ba3a328 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,11 +3,11 @@ use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, find_attr}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_middle::bug; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, BottomUpFolder, Ty, TypeFoldable, TypeVisitableExt}; +use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; @@ -402,9 +402,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unify the method signature with our incompatible arg, to // do inference in the *opposite* direction and to find out // what our ideal rcvr ty would look like. + let Some(input_arg) = method.sig.inputs().get(idx + 1) else { + if method.sig.splatted().is_some() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + return None; + } else { + span_bug!( + self.tcx.def_span(method.def_id), + "arg index {} out of bounds for method with {} inputs", + idx + 1, + method.sig.inputs().len(), + ); + } + }; let _ = self .at(&ObligationCause::dummy(), self.param_env) - .eq(DefineOpaqueTypes::Yes, method.sig.inputs()[idx + 1], arg_ty) + .eq(DefineOpaqueTypes::Yes, *input_arg, arg_ty) .ok()?; self.select_obligations_where_possible(|errs| { // Yeet the errors, we're already reporting errors. diff --git a/compiler/rustc_hir_typeck/src/diagnostics.rs b/compiler/rustc_hir_typeck/src/diagnostics.rs index a9a819935287c..a5c39bd1584a1 100644 --- a/compiler/rustc_hir_typeck/src/diagnostics.rs +++ b/compiler/rustc_hir_typeck/src/diagnostics.rs @@ -100,6 +100,7 @@ impl IntoDiagArg for ReturnLikeStatementKind { } } +// FIXME(splat): add "non-splatted" to all 4 instances of this error message #[derive(Diagnostic)] #[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")] pub(crate) struct RustCallIncorrectArgs { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0e13f7a4bfe1c..22c211b199474 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1463,16 +1463,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(method) => { self.write_method_call_and_enforce_effects(expr.hir_id, expr.span, method); + // Handle splatted method arguments + // self is already handled as `rcvr`, so it's never splatted here + let method_inputs = &method.sig.inputs()[1..]; + let method_tuple_args_flag = + TupleArgumentsFlag::with_fn_sig_kind(method.sig.fn_sig_kind, true); + self.check_argument_types( segment.ident.span, expr, - &method.sig.inputs()[1..], + method_inputs, method.sig.output(), expected, args, - method.sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, + method.sig.fn_sig_kind.c_variadic(), + method_tuple_args_flag, Some(method.def_id), + Some(method.args), ); self.check_call_abi(method.sig.abi(), expr.span); @@ -1495,6 +1502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, TupleArgumentsFlag::DontTupleArguments, None, + Some(GenericArgsRef::default()), ); err_output diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index c8c6de4f99c03..9a471d3ea007b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, - SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Unnormalized, - UserArgs, UserSelfTy, + SizedTraitKind, SplattedDef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, + Unnormalized, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -143,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// also select obligations if it seems useful, in an effort /// to get more type information. // FIXME(-Znext-solver): A lot of the calls to this method should - // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead. + // probably be `resolve_vars_with_obligations` or `structurally_resolve_type` instead. #[instrument(skip(self), level = "debug", ret)] pub(crate) fn resolve_vars_with_obligations>>( &self, @@ -236,6 +236,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_resolution( + &self, + hir_id: HirId, + r: Result, + ) { + self.typeck_results.borrow_mut().splatted_defs_mut().insert(hir_id, r); + } + #[instrument(level = "debug", skip(self))] pub(crate) fn write_method_call_and_enforce_effects( &self, @@ -248,6 +257,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_args(hir_id, method.args); } + #[instrument(level = "debug", skip(self))] + pub(crate) fn write_splatted_call( + &self, + hir_id: HirId, + span: Span, + callee_def_id: Option, + callee_generic_args: Option>, + first_tupled_arg_index: u16, + tupled_args_count: u16, + ) { + // FIXME(const_trait_impl): enforce constness using enforce_context_effects() and add + // _and_enforce_effects to this method's name + + self.write_splatted_resolution( + hir_id, + Ok(SplattedDef { + def_id: callee_def_id, + arg_index: first_tupled_arg_index, + arg_count: tupled_args_count, + }), + ); + if let Some(callee_generic_args) = callee_generic_args { + self.write_args(hir_id, callee_generic_args); + } + } + fn write_args(&self, node_id: HirId, args: GenericArgsRef<'tcx>) { if !args.is_empty() { debug!("write_args({:?}, {:?}) in fcx {}", node_id, args, self.tag()); @@ -1453,7 +1488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ct = self.resolve_vars_with_obligations(ct); if self.next_trait_solver() - && let ty::ConstKind::Unevaluated(..) = ct.kind() + && let ty::ConstKind::Alias(..) = ct.kind() { // We need to use a separate variable here as otherwise the temporary for // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 890110e431fa2..7d85a5a4ded63 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -50,6 +50,19 @@ rustc_index::newtype_index! { pub(crate) struct GenericIdx {} } +/// Outcome of checking arguments that are tupled by "rust-call" or `#[splat]`. +#[derive(Debug, Clone, Eq, PartialEq)] +struct TupledArgCheckOutcome<'tcx> { + /// The error code to emit if the arguments are not compatible. + new_err_code: Option, + + /// The formal input types after checking. + untupled_formal_input_tys: Vec>, + + /// The expected input types after checking. + untupled_expected_input_tys: Option>>, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { let mut deferred_cast_checks = self.root_ctxt.deferred_cast_checks.borrow_mut(); @@ -161,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_, _) => enforce_copy_bound(element, element_ty), + | ty::ConstKind::Alias(_, _) => enforce_copy_bound(element, element_ty), ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => { unreachable!() @@ -185,13 +198,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expectation: Expectation<'tcx>, // The expressions for each provided argument provided_args: &'tcx [hir::Expr<'tcx>], - // Whether the function is variadic, for example when imported from C - // FIXME(splat): maybe change this to FnSigKind? + // Whether the function is variadic (e.g. from C) c_variadic: bool, - // Whether the arguments have been bundled in a tuple (ex: closures) + // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, // The DefId for the function being called, for better error messages fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, ) { let tcx = self.tcx; @@ -220,11 +234,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // First, let's unify the formal method signature with the expectation eagerly. - // We use this to guide coercion inference; it's output is "fudged" which means + // We use this to guide coercion inference; its output is "fudged" which means // any remaining type variables are assigned to new, unrelated variables. This // is because the inference guidance here is only speculative. + // FIXME(splat): do we need to splat arguments before this type inference? let formal_output = self.resolve_vars_with_obligations(formal_output); - let expected_input_tys: Option> = expectation + let mut expected_input_tys: Option> = expectation .only_has_type(self) .and_then(|expected_output| { // FIXME(#149379): This operation results in expected input @@ -272,45 +287,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err_code = E0061; - // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - ty::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - err_code = E0057; - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.get(0) { - Some(ty) => match ty.kind() { - ty::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages - let guar = struct_span_code_err!( - self.dcx(), - call_span, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - (self.err_args(provided_args.len(), guar), None) - } + let mut formal_input_tys = formal_input_tys.to_vec(); + + // If the arguments should be wrapped in a tuple (ex: closures, splats), unwrap them here + if tuple_arguments.is_tupled() { + // Caller arguments are tupled before typechecking, starting at the given index. + // Tupling makes the callee and caller argument counts match. + let outcome = self.check_tupled_arguments( + call_span, + call_expr, + formal_input_tys, + provided_args, + expected_input_tys, + c_variadic, + tuple_arguments, + fn_def_id, + callee_generic_args, + ); + let TupledArgCheckOutcome { + new_err_code, + untupled_formal_input_tys, + untupled_expected_input_tys, + } = outcome; + if let Some(new_err_code) = new_err_code { + err_code = new_err_code; } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + formal_input_tys = untupled_formal_input_tys; + expected_input_tys = untupled_expected_input_tys; + } // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { @@ -556,6 +560,257 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Check arguments that are tupled by "rust-call" or `#[splat]`. + fn check_tupled_arguments( + &self, + // Span enclosing the call site + call_span: Span, + // Expression of the call site + call_expr: &'tcx hir::Expr<'tcx>, + // Types (as defined in the *signature* of the target function) + mut formal_input_tys: Vec>, + // The expressions for each provided argument + provided_args: &'tcx [hir::Expr<'tcx>], + // The expected input types from the context of the call site + mut expected_input_tys: Option>>, + // Whether the function is variadic (e.g. from C) + c_variadic: bool, + // Whether all the arguments have been bundled in a tuple (ex: closures). + // Splatting is handled separately. + tuple_arguments: TupleArgumentsFlag, + // The DefId for the function being called, for better error messages + fn_def_id: Option, + // The generics of the function being called. Only used for splatting + callee_generic_args: Option>, + ) -> TupledArgCheckOutcome<'tcx> { + let (first_tupled_arg_index, is_self_splatted) = tuple_arguments.tupled_arg_index(); + let Some(first_tupled_arg_index) = first_tupled_arg_index else { + // If we're not tupling any of the current arguments, we're done. + return TupledArgCheckOutcome { + new_err_code: None, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + }; + }; + + // The argument difference can range from -1 to u16::MAX - 1, so we count the number + // of tupled arguments instead. + // (An empty argument list becomes a unit tuple in the callee.) + // 0: f() -> f(#[splat] _: ()) + // 1: f(a) -> f(#[splat] _: (A,)) + // 2: f(a, b) -> f(#[splat] _: (A, B)) + // The Fn* traits ensure this by construction, and `#[splat]` can only be applied to + // an actual argument. + let tupled_args_count = (1 + provided_args.len()).checked_sub(formal_input_tys.len()); + debug!( + ?first_tupled_arg_index, ?is_self_splatted, + ?tupled_args_count, ?tuple_arguments, ?c_variadic, + provided_args_len = ?provided_args.len(), formal_input_tys_len = ?formal_input_tys.len() + ); + + // If earlier code has modified the FnSig argument list without adjusting the splatted + // argument, indexing into the formal input types will panic. + if first_tupled_arg_index >= formal_input_tys.len() { + span_bug!( + call_span, + "splatted argument index is out of bounds: {first_tupled_arg_index:?} >= {}, \ + is_self_splatted = {is_self_splatted:?}, \ + tupled_args_count = {tupled_args_count:?}, {tuple_arguments:?}, \ + c_variadic = {c_variadic:?}, provided_args: {}", + formal_input_tys.len(), + provided_args.len(), + ); + } + + // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. + let tuple_type = if tuple_arguments.is_splatted() { + let callee_tuple_type = + self.resolve_vars_with_obligations(formal_input_tys[first_tupled_arg_index]); + if callee_tuple_type.is_ty_var() + && let Some(tupled_args_count) = tupled_args_count + { + // Make the original type variable resolve to a tuple containing new type variables + let ocx = ObligationCtxt::new(self); + let origin = self.misc(call_span); + + let new_tupled_type = Ty::new_tup_from_iter( + self.tcx, + iter::repeat_with(|| self.next_ty_var(call_span)).take(tupled_args_count), + ); + + // FIXME(splat): should this be a sub/super type relationship? + let ocx_error = ocx.eq(&origin, self.param_env, callee_tuple_type, new_tupled_type); + if let Err(ocx_error) = ocx_error { + // FIXME(splat): add a test for this error and the one below, if they are reachable + struct_span_code_err!( + self.dcx(), + call_span, + // FIXME(splat): add a new error code before stabilization (and below as well) + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + ocx_error, + ) + .emit(); + } + + let type_errors = ocx.try_evaluate_obligations(); + if type_errors.is_empty() { + new_tupled_type + } else { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0277, + "cannot resolve splatted arguments; splatted type parameters \ + must be a tuple or unit type: {:?}", + type_errors, + ) + .emit(); + Ty::new_error(self.tcx, guar) + } + } else { + // Otherwise, just let the argument type checker make a suggestion + callee_tuple_type + } + } else { + self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + }; + + // We expected a tuple and got a tuple (or made one ourselves). + // If it's not a tuple, we error out in the next block. + let mut err_code = None; + if let ty::Tuple(detup_formal_arg_tys) = tuple_type.kind() { + // Argument length differs + // FIXME(splat): update the error code E0057 docs when splat is stabilized + if Some(detup_formal_arg_tys.len()) != tupled_args_count { + err_code = Some(E0057); + } + if let Some(ref mut expected_input_tys) = expected_input_tys + && let Some(ty) = expected_input_tys.get(first_tupled_arg_index) + && let ty::Tuple(detup_expected_arg_tys) = ty.kind() + { + let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { + detup_expected_arg_tys.iter() + } else { + // Just fall back to the formal argument types + detup_formal_arg_tys.iter() + }; + + expected_input_tys + .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + } else { + expected_input_tys = None; + } + // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments + if tuple_arguments.is_splatted() { + // FIXME(const_trait_impl): does not enforce constness yet + self.write_splatted_call( + call_expr.hir_id, + call_span, + fn_def_id, + callee_generic_args, + first_tupled_arg_index.try_into().unwrap(), + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + + formal_input_tys.splice( + first_tupled_arg_index..=first_tupled_arg_index, + detup_formal_arg_tys.iter(), + ); + if let Some(ref expected_input_tys) = expected_input_tys { + assert_eq!( + formal_input_tys.len(), + expected_input_tys.len(), + "incorrectly constructed input type tuples, argument counts must match: \ + tuple_arguments: {tuple_arguments:?}", + ) + } + } + + // Otherwise, there's a mismatch during splatting or a rust-call. + // So clear out what we're expecting, and set our input types to err_args so we don't + // blow up the error messages. + let guar = + if tuple_arguments == TupleAllCallArgs && !matches!(tuple_type.kind(), ty::Tuple(_)) { + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + + Some(guar) + } else if tuple_arguments.is_splatted() { + // If we don't check argument counts here, and there's a subtle bug in the code above, + // later compilation stages can fail in unrelated places with confusing errors. + if !matches!(tuple_type.kind(), ty::Tuple(_)) { + let spans = if let Some(def_id) = fn_def_id + && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) + && let Some(fn_decl) = hir_node.fn_decl() + && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index) + { + let arg_def_span = arg_ty.span; + vec![call_span, arg_def_span] + } else { + vec![call_span] + }; + let guar = struct_span_code_err!( + self.dcx(), + spans, + // FIXME(splat): add a new error code before stabilization + E0277, + "cannot use splat attribute; the splatted argument type \ + must be a tuple or unit, not a {:?} ({:?})", + tuple_type.kind(), + self.structurally_resolve_type( + call_span, + formal_input_tys[first_tupled_arg_index] + ) + .kind(), + ) + .emit(); + + Some(guar) + } else if formal_input_tys.len() != provided_args.len() { + // FIXME(splat): suggest alternative argument counts, if there are any + let guar = struct_span_code_err!( + self.dcx(), + call_span, + E0057, + "this splatted function takes {} arguments, but {} {} provided", + formal_input_tys.len(), + provided_args.len(), + if provided_args.len() == 1 { "was" } else { "were" }, + ) + .emit(); + + Some(guar) + } else { + None + } + } else { + None + }; + + if let Some(guar) = guar { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: self.err_args(provided_args.len(), guar), + untupled_expected_input_tys: None, + } + } else { + TupledArgCheckOutcome { + new_err_code: err_code, + untupled_formal_input_tys: formal_input_tys, + untupled_expected_input_tys: expected_input_tys, + } + } + } + /// If `unsized_fn_params` is active, check that unsized values are place expressions. Since /// the removal of `unsized_locals` in we can't /// store them in MIR locals as temporaries. @@ -581,6 +836,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_def_id: Option, call_span: Span, call_expr: &'tcx hir::Expr<'tcx>, + // FIXME(splat): when the feature design is settled, improve the errors here tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error @@ -1354,8 +1610,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly // (eg invoking a closure) we want to point at the underlying callable, // not the method implicitly invoked (eg call_once). - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - if tuple_arguments == TupleArguments + // TupleAllCallArgs is set only when this is an implicit call `my_closure(...)` rather + // than explicit `my_closure.call(...)`. + if tuple_arguments == TupleAllCallArgs && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) // Since this is an associated item, it might point at either an impl or a trait item. // We want it to always point to the trait item. @@ -1442,25 +1699,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deps: SmallVec<[ExpectedIdx; 4]>, } - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !tuple_arguments.is_splatted() { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } // Gather all mismatched parameters with generics. let mut mismatched_params = Vec::>::new(); + let mut use_splat_fallback = false; if let Some(expected_idx) = expected_idx { let expected_idx = ExpectedIdx::from_usize(expected_idx); - let &(expected_generic, ref expected_param) = - ¶ms_with_generics[expected_idx]; - if let Some(expected_generic) = expected_generic { - mismatched_params.push(MismatchedParam { - idx: expected_idx, - generic: expected_generic, - param: expected_param, - deps: SmallVec::new(), - }); - } else { - // Still mark the mismatched parameter - spans.push_span_label(expected_param.span(), ""); - } - } else { + match params_with_generics.get(expected_idx) { + Some(&(Some(expected_generic), ref expected_param)) => mismatched_params + .push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }), + Some((None, expected_param)) => { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); + } + None => { + if tuple_arguments.is_splatted() { + // FIXME(splat): when the arg is splatted, adjust its index, to handle the type mismatch properly + use_splat_fallback = true; + } else { + span_bug!( + self.tcx.def_span(def_id), + "arg index {} out of bounds for method with {} inputs", + expected_idx.as_usize(), + params_with_generics.len(), + ); + } + } + }; + } + + if expected_idx.is_none() || use_splat_fallback { mismatched_params.extend( params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( |((idx, &(generic, ref param)), matched_idx)| { @@ -1671,15 +1947,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_arg_tys: &IndexVec, Span)>, formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, + is_splat: bool, ) { let Some(def_id) = callable_def_id else { return; }; if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // FIXME(splat): fix the generic mismatch earlier, so it doesn't reach here + if !is_splat { + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + } for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { - if matched_inputs[idx].is_none() { + if matched_inputs.get(idx).flatten_ref().is_none() { continue; } @@ -1701,7 +1981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[other_idx].is_some() { + if matched_inputs.get(other_idx).flatten_ref().is_some() { return false; } other_generic_param == generic_param @@ -2004,7 +2284,11 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, format!( "{call_name} takes {}{} but {} {} supplied", - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { + "at least " + } else { + "" + }, potentially_plural_count( self.formal_and_expected_inputs.len(), "argument" @@ -2147,14 +2431,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!("arguments to this {call_name} are incorrect"), ); - self.fn_ctxt.label_generic_mismatches( - &mut err, - self.fn_def_id, - &self.matched_inputs, - &self.provided_arg_tys, - &self.formal_and_expected_inputs, - self.call_metadata.is_method, - ); + self.label_generic_mismatches(&mut err); if let hir::ExprKind::MethodCall(_, rcvr, _, _) = self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind @@ -2237,7 +2514,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { format!( "this {} takes {}{} but {} {} supplied", self.call_metadata.call_name, - if self.c_variadic { "at least " } else { "" }, + if self.arg_matching_ctxt.args_ctxt.c_variadic { "at least " } else { "" }, potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), potentially_plural_count(self.provided_args.len(), "argument"), pluralize!("was", self.provided_args.len()) @@ -2614,6 +2891,7 @@ impl<'a, 'tcx> FnCallDiagCtxt<'a, 'tcx> { &self.provided_arg_tys, &self.formal_and_expected_inputs, self.call_metadata.is_method, + self.arg_matching_ctxt.tuple_arguments.is_splatted(), ); } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 3941f06c3e7d3..3b3f5caa85c09 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -3,6 +3,7 @@ #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(never_type)] +#![feature(option_reference_flattening)] #![feature(trim_prefix_suffix)] // tidy-alphabetical-end @@ -50,7 +51,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt, Unnormalized}; +use rustc_middle::ty::{self, FnSigKind, Ty, TyCtxt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -589,12 +590,10 @@ fn report_unexpected_variant_res( .emit() } -/// Controls whether the arguments are tupled. This is used for the call -/// operator. +/// Controls whether all arguments are tupled. This is used for the call operator only. /// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: +/// Tupling means that all call-side arguments are packed into a tuple and passed as a single +/// parameter. For example, if tupling is enabled, this function: /// ``` /// fn f(x: (isize, isize)) {} /// ``` @@ -608,10 +607,72 @@ fn report_unexpected_variant_res( /// # fn f(x: (isize, isize)) {} /// f((1, 2)); /// ``` -#[derive(Copy, Clone, Eq, PartialEq)] +/// +/// Note: splatted arguments are handled separately. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] enum TupleArgumentsFlag { + /// Arguments are typechecked unchanged. DontTupleArguments, - TupleArguments, + /// This is a call operator: all caller arguments are tupled before typechecking. + /// Set based on the "rust-call" ABI and Fn* traits. + TupleAllCallArgs, + /// The `self` method argument is splatted, so `Self` should be tupled before typechecking. + TupleSplattedSelfArg, + /// A non-self argument is splatted, so that argument should be tupled before typechecking. + TupleSplattedArg(u8), +} + +impl TupleArgumentsFlag { + /// Returns the TupleArgumentsFlag for a known RustCall function. + fn rust_fn_trait_call() -> Self { + Self::TupleAllCallArgs + } + + /// Returns the appropriate TupleArgumentsFlag for the given FnSigKind and method flag. + fn with_fn_sig_kind<'tcx>(fn_sig_kind: FnSigKind<'tcx>, is_method: bool) -> Self { + if let Some(splatted_arg_index) = fn_sig_kind.splatted() { + if is_method { + if let Some(splatted_arg_index) = splatted_arg_index.checked_sub(1) { + return Self::TupleSplattedArg(splatted_arg_index); + } else { + // In `check_argument_types`, this is effectively `TupleSplattedArg(-1)` + return Self::TupleSplattedSelfArg; + } + } + + return Self::TupleSplattedArg(splatted_arg_index); + } + + Self::DontTupleArguments + } + + /// Returns true if the arguments are tupled through "rust-call" or splatting. + fn is_tupled(self) -> bool { + match self { + Self::DontTupleArguments => false, + Self::TupleAllCallArgs | Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + } + } + + /// Returns true if the arguments are tupled through splatting. + /// (But false if they are "rust-call" or not tupled.) + fn is_splatted(self) -> bool { + match self { + Self::TupleSplattedSelfArg | Self::TupleSplattedArg(_) => true, + Self::DontTupleArguments | Self::TupleAllCallArgs => false, + } + } + + /// Returns the tupled argument index, and whether the `self` argument is splatted. + /// Returns `None` if the arguments are not tupled, or if the `self` argument is splatted. + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { + match self { + Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleAllCallArgs => (Some(0), false), + Self::TupleSplattedSelfArg => (None, true), + Self::DontTupleArguments => (None, false), + } + } } fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { diff --git a/compiler/rustc_hir_typeck/src/loops.rs b/compiler/rustc_hir_typeck/src/loops.rs index 21aad64f58d38..ebd8842bd753f 100644 --- a/compiler/rustc_hir_typeck/src/loops.rs +++ b/compiler/rustc_hir_typeck/src/loops.rs @@ -84,6 +84,11 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir CheckLoopVisitor { tcx, cx_stack: vec![Normal], block_breaks: Default::default() }; let cx = match tcx.def_kind(def_id) { DefKind::AnonConst => AnonConst, + DefKind::InlineConst => { + // only type system inline consts are typeck roots + debug_assert!(tcx.is_type_system_inline_const(def_id)); + ConstBlock + } _ => Fn, }; check.with_context(cx, |v| v.visit_body(body)); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e8921841c4405..ce187aa1aff14 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2359,6 +2359,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// multiple conflicting picks if there is one pick whose trait container is a subtrait /// of the trait containers of all of the other picks. /// + /// This is the method-probe analogue of + /// `rustc_hir_analysis::hir_ty_lowering::HirTyLowerer::collapse_candidates_to_subtrait_pick`; + /// keep both implementations in sync. + /// /// This implements RFC #3624. fn collapse_candidates_to_subtrait_pick( &self, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 298fee525fc55..3d50befe042f1 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -663,6 +663,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().insert(hir_id, def); } + // Export splatted function call resolutions. + if let Some(def) = self.fcx.typeck_results.borrow_mut().splatted_defs_mut().remove(hir_id) { + self.typeck_results.splatted_defs_mut().insert(hir_id, def); + } + // Resolve any borrowings for the node with id `node_id` self.visit_adjustments(span, hir_id); diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index c058bebddcf03..f0fd46e6cac74 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -158,7 +158,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Value(_) - | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Alias(..) | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 92be5dec28542..aa2fe2ee21dfa 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1266,7 +1266,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_, _) + | ty::ConstKind::Alias(_, _) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => ct, diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index f50fd4a32328e..7881b85997e8c 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -77,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> { /// would result in an infinite type as we continuously replace an inference variable /// in `ct` with `ct` itself. /// - /// This is especially important as unevaluated consts use their parents generics. + /// This is especially important as alias consts use their parents generics. /// They therefore often contain unused args, making these errors far more likely. /// /// A good example of this is the following: @@ -95,7 +95,7 @@ impl<'tcx> InferCtxt<'tcx> { /// } /// ``` /// - /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// Here `3 + 4` ends up as `ConstKind::Alias` which uses the generics /// of `fn bind` (meaning that its args contain `N`). /// /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. @@ -112,8 +112,8 @@ impl<'tcx> InferCtxt<'tcx> { target_vid: ty::ConstVid, source_ct: ty::Const<'tcx>, ) -> RelateResult<'tcx, ()> { - // FIXME(generic_const_exprs): Occurs check failures for unevaluated - // constants and generic expressions are not yet handled correctly. + // FIXME(generic_const_exprs): Occurs check failures for alias consts + // and generic expressions are not yet handled correctly. debug_assert!( self.inner.borrow_mut().const_unification_table().probe_value(target_vid).is_unknown() ); @@ -752,7 +752,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { } } } - // FIXME: Unevaluated constants are also not rigid, so the current + // FIXME: Alias consts are also not rigid, so the current // approach of always relating them structurally is incomplete. // // FIXME: replace the StructurallyRelateAliases::Yes branch with @@ -760,26 +760,26 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // // We only need to be careful with potentially normalizeable // aliases here. See `generalize_alias_term` for more information. - ty::ConstKind::Unevaluated(ty::IsRigid::No, uv) => { + ty::ConstKind::Alias(ty::IsRigid::No, alias_const) => { match self.structurally_relate_aliases { // Hack: Fall back to old behavior if GCE is enabled (it used to just be the Yes // path), as doing this new No path breaks some GCE things. I expect GCE to be // ripped out soon so this shouldn't matter soon. StructurallyRelateAliases::No if !tcx.features().generic_const_exprs() => { - self.generalize_alias_term(uv.into()).map(|v| v.expect_const()) + self.generalize_alias_term(alias_const.into()).map(|v| v.expect_const()) } _ => { - let ty::UnevaluatedConst { kind, args, .. } = uv; + let ty::AliasConst { kind, args, .. } = alias_const; let args = self.relate_with_variance( ty::Invariant, ty::VarianceDiagInfo::default(), args, args, )?; - Ok(ty::Const::new_unevaluated( + Ok(ty::Const::new_alias( tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new(tcx, kind, args), + ty::AliasConst::new(tcx, kind, args), )) } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index c70a2497967c5..ca6e48cb67fe5 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -803,8 +803,8 @@ fn resolver_for_lowering_raw<'tcx>( ); let krate = configure_and_expand(krate, &pre_configured_attrs, &mut resolver); - // Make sure we don't mutate the cstore from here on. - tcx.untracked().cstore.freeze(); + // Don't mutate the cstore or stable crate id map from here on. + tcx.untracked().freeze_cstore(); let ResolverOutputs { global_ctxt: untracked_resolutions, diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 758d2762a6af4..176890bab85f2 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" } rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 348f1f55d3215..e992f1ed385e7 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -46,21 +46,39 @@ type LateLintPassFactory = Box Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync>; /// Information about the registered lints. +// +// About the pass factories: these should only be called once, but since we +// want to avoid locks or interior mutability, we don't enforce this. Lints +// should, in theory, be compatible with being constructed more than once, +// though not necessarily in a sane manner. This is safe though. pub struct LintStore { /// Registered lints. lints: Vec<&'static Lint>, - /// Constructor functions for each variety of lint pass. + /// This lint pass kind is softly deprecated. It misses expanded code and has caused a few + /// errors in the past. Currently, it is only used in Clippy. New implementations + /// should avoid using this interface, as it might be removed in the future. + /// + /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) + /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) + pub(crate) pre_expansion_lint_passes: Vec, + + /// These lint passes run on AST nodes. + pub(crate) early_lint_passes: Vec, + + /// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit + /// from incremental compilation. `late_lint_mod_passes` should be used in preference where + /// possible; only use `late_lint_passes` for lints that implement `check_crate` and/or + /// `check_crate_post` and accumulate cross-module state. /// - /// These should only be called once, but since we want to avoid locks or - /// interior mutability, we don't enforce this (and lints should, in theory, - /// be compatible with being constructed more than once, though not - /// necessarily in a sane manner. This is safe though.) - pub pre_expansion_passes: Vec, - pub early_passes: Vec, - pub late_passes: Vec, - /// This is unique in that we construct them per-module, so not once. - pub late_module_passes: Vec, + /// The exception is Clippy, which uses `late_lint_passes` for all late lint passes. It needs + /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout + /// for consistency. This is ok because Clippy isn't wired for incremental compilation. + pub(crate) late_lint_passes: Vec, + + /// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times). + /// They benefit from incremental compilation. + pub(crate) late_lint_mod_passes: Vec, /// Lints indexed by name. by_name: UnordMap, @@ -136,10 +154,10 @@ impl LintStore { pub fn new() -> LintStore { LintStore { lints: vec![], - pre_expansion_passes: vec![], - early_passes: vec![], - late_passes: vec![], - late_module_passes: vec![], + pre_expansion_lint_passes: vec![], + early_lint_passes: vec![], + late_lint_passes: vec![], + late_lint_mod_passes: vec![], by_name: Default::default(), lint_groups: Default::default(), } @@ -166,26 +184,24 @@ impl LintStore { self.lint_groups.keys().copied() } - pub fn register_early_pass(&mut self, pass: EarlyLintPassFactory) { - self.early_passes.push(pass); + /// See the comment on `LintStore::pre_expansion_lint_passes`. + pub fn register_pre_expansion_lint_pass(&mut self, pass: EarlyLintPassFactory) { + self.pre_expansion_lint_passes.push(pass); } - /// This lint pass is softly deprecated. It misses expanded code and has caused a few - /// errors in the past. Currently, it is only used in Clippy. New implementations - /// should avoid using this interface, as it might be removed in the future. - /// - /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838) - /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518) - pub fn register_pre_expansion_pass(&mut self, pass: EarlyLintPassFactory) { - self.pre_expansion_passes.push(pass); + /// See the comment on `LintStore::early_lint_passes`. + pub fn register_early_lint_pass(&mut self, pass: EarlyLintPassFactory) { + self.early_lint_passes.push(pass); } - pub fn register_late_pass(&mut self, pass: LateLintPassFactory) { - self.late_passes.push(pass); + /// See the comment on `LintStore::late_lint_passes`. + pub fn register_late_lint_pass(&mut self, pass: LateLintPassFactory) { + self.late_lint_passes.push(pass); } - pub fn register_late_mod_pass(&mut self, pass: LateLintPassFactory) { - self.late_module_passes.push(pass); + /// See the comment on `LintStore::late_lint_mod_passes`. + pub fn register_late_lint_mod_pass(&mut self, pass: LateLintPassFactory) { + self.late_lint_mod_passes.push(pass); } /// Helper method for register_early/late_pass diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 93d150c79d0df..9c123f67abd5f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -329,11 +329,11 @@ pub fn check_ast_node<'a>( let context = if pre_expansion { let builtin_lints = crate::BuiltinCombinedPreExpansionLintPass::new(); - let passes = &lint_store.pre_expansion_passes; + let passes = &lint_store.pre_expansion_lint_passes; run_passes(check_node, context, builtin_lints, passes) } else { let builtin_lints = crate::BuiltinCombinedEarlyLintPass::new(); - let passes = &lint_store.early_passes; + let passes = &lint_store.early_lint_passes; run_passes(check_node, context, builtin_lints, passes) }; diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 27a21381ffce9..3010eadb61057 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -329,6 +329,14 @@ fn structurally_same_type_impl<'tcx>( let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig); let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig); + // FIXME(splat): Is splatting ever repr(C)? + // Can two splatted functions to have the same structure? + // Can a splatted and non-splatted function have the same structure? + // For now, we require splatting to match exactly. + if a_sig.splatted() != b_sig.splatted() { + return false; + } + (a_sig.abi(), a_sig.safety(), a_sig.c_variadic()) == (b_sig.abi(), b_sig.safety(), b_sig.c_variadic()) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { diff --git a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs deleted file mode 100644 index 699004a3b37e0..0000000000000 --- a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs +++ /dev/null @@ -1,79 +0,0 @@ -use rustc_hir as hir; -use rustc_session::{declare_lint, declare_lint_pass}; - -use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion}; -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer - /// and a pointer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(fuzzy_provenance_casts)] - /// - /// fn main() { - /// let _dangling = 16_usize as *const u8; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting an integer to a pointer is considered bad style, as a pointer - /// contains, besides the *address* also a *provenance*, indicating what - /// memory the pointer is allowed to read/write. Casting an integer, which - /// doesn't have provenance, to a pointer requires the compiler to assign - /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::with_exposed_provenance`] for more information about this - /// "exposing"). This penalizes the optimiser and is not well suited for - /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI - /// platforms). - /// - /// It is much better to use [`ptr::with_addr`] instead to specify the - /// provenance you want. If using this function is not possible because the - /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::with_exposed_provenance`]. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html - pub FUZZY_PROVENANCE_CASTS, - Allow, - "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint_pass!( - /// Lint for `as` casts between an integer and a pointer. - FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS] -); - -impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; - - let typeck_results = cx.typeck_results(); - // Only lint casts from integer to pointer - let cast_from_ty = typeck_results.expr_ty(cast_from_expr); - if !cast_from_ty.is_integral() { - return; - } - let cast_to_ty = typeck_results.expr_ty(expr); - if !cast_to_ty.is_raw_ptr() { - return; - } - - let sugg = - expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion { - lo: cast_from_expr.span.shrink_to_lo(), - hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), - }); - let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; - cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) - } -} diff --git a/compiler/rustc_lint/src/implicit_provenance_casts.rs b/compiler/rustc_lint/src/implicit_provenance_casts.rs new file mode 100644 index 0000000000000..c77ecbe273387 --- /dev/null +++ b/compiler/rustc_lint/src/implicit_provenance_casts.rs @@ -0,0 +1,122 @@ +use rustc_ast::util::parser::ExprPrecedence; +use rustc_hir as hir; +use rustc_middle::ty::Ty; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{ + ImplicitProvenanceCastsInt2Ptr, ImplicitProvenanceCastsPtr2Int, Int2PtrSuggestion, + Ptr2IntSuggestion, +}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `implicit_provenance_casts` lint detects integer-to-pointer and pointer-to-integer + /// casts. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(implicit_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let addr: usize = &x as *const u8 as usize; + /// let _ptr = addr as *const u8; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint exists to help migrate code to [*Strict Provenance* APIs][strict-provenance] where + /// possible, and make remaining uses of [*Exposed Provenance*][exposed-provenance] explicit. + /// For more information on pointer provenance, see the [`std::ptr` documentation][provenance]. + /// + /// Earlier versions of Rust did not have a clear answer how integer-to-pointer and + /// pointer-to-integer casts interact with provenance. Such casts are now defined to use the + /// exposed provenance model, but in many cases the code can be updated to strict provenance + /// APIs, which is preferable as it enables more precise reasoning about unsafe code, both by + /// humans and by tools like [Miri]. + /// + /// However, there are situations where exposed provenance is required or following the strict + /// provenance model requires major refactorings. In those cases, it's still useful to replace + /// the `as` casts with equivalent explicit use of exposed provenance APIs and a comment + /// explaining why they are needed. + /// + /// [provenance]: https://doc.rust-lang.org/core/ptr/index.html#provenance + /// [strict-provenance]: https://doc.rust-lang.org/core/ptr/index.html#strict-provenance + /// [exposed-provenance]: https://doc.rust-lang.org/core/ptr/index.html#exposed-provenance + /// [Miri]: https://github.com/rust-lang/miri + pub IMPLICIT_PROVENANCE_CASTS, + Allow, + "an `as` cast relying on exposed provenance is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for int2ptr and ptr2int `as` casts. + ImplicitProvenanceCasts => [IMPLICIT_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for ImplicitProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if cast_from_ty.is_raw_ptr() { + let cast_to_ty = typeck_results.expr_ty(expr); + if cast_to_ty.is_integral() { + lint_ptr2int(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty) + } + } else if cast_from_ty.is_integral() { + let cast_to_ty = typeck_results.expr_ty(expr); + if cast_to_ty.is_raw_ptr() { + lint_int2ptr(cx, expr, cast_from_expr, cast_from_ty, cast_to_hir, cast_to_ty) + } + } + } +} + +fn lint_ptr2int<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + cast_from_expr: &'tcx hir::Expr<'tcx>, + cast_from_ty: Ty<'tcx>, + cast_to_hir: &'tcx hir::Ty<'tcx>, + cast_to_ty: Ty<'tcx>, +) { + let sugg = expr.span.can_be_used_for_suggestions().then(|| { + let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; + let needs_cast = !cast_to_ty.is_usize(); + let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); + let expr_span = cast_from_expr.span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => Ptr2IntSuggestion::NeedsParensCast { expr_span, cast_span, cast_to_ty }, + (true, false) => Ptr2IntSuggestion::NeedsParens { expr_span, cast_span }, + (false, true) => Ptr2IntSuggestion::NeedsCast { cast_span, cast_to_ty }, + (false, false) => Ptr2IntSuggestion::Other { cast_span }, + } + }); + + let lint = ImplicitProvenanceCastsPtr2Int { cast_from_ty, cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); +} + +fn lint_int2ptr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + cast_from_expr: &'tcx hir::Expr<'tcx>, + cast_from_ty: Ty<'tcx>, + cast_to_hir: &'tcx hir::Ty<'tcx>, + cast_to_ty: Ty<'tcx>, +) { + let sugg = expr.span.can_be_used_for_suggestions().then(|| Int2PtrSuggestion { + lo: cast_from_expr.span.shrink_to_lo(), + hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), + }); + let lint = ImplicitProvenanceCastsInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(IMPLICIT_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) +} diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index fea6b2ab2f9bd..c17fe6f2e510f 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -355,7 +355,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. let mut passes: Vec<_> = unerased_lint_store(tcx.sess) - .late_module_passes + .late_lint_mod_passes .iter() .map(|mk_pass| mk_pass(tcx)) .filter(|pass| is_lint_pass_required(skippable_lints, &pass.get_lints())) @@ -403,7 +403,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { // Note: `passes` is often empty after filtering. let passes: Vec<_> = unerased_lint_store(tcx.sess) - .late_passes + .late_lint_passes .iter() .map(|mk_pass| mk_pass(tcx)) .filter(|pass| is_lint_pass_required(skippable_lints, &pass.get_lints())) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 14c70100c5f97..74bee7d705e3a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -45,10 +45,10 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; mod function_cast_as_integer; -mod fuzzy_provenance_casts; mod gpukernel_abi; mod if_let_rescope; mod impl_trait_overcaptures; +mod implicit_provenance_casts; mod interior_mutable_consts; mod internal; mod invalid_from_utf8; @@ -57,7 +57,6 @@ mod let_underscore; mod levels; pub mod lifetime_syntax; mod lints; -mod lossy_provenance_casts; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod multiple_supertrait_upcastable; @@ -72,6 +71,7 @@ mod precedence; mod ptr_nulls; mod redundant_semicolon; mod reference_casting; +mod runtime_symbols; mod shadowed_into_iter; mod static_mut_refs; mod traits; @@ -94,16 +94,15 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use function_cast_as_integer::*; -use fuzzy_provenance_casts::FuzzyProvenanceCasts; use gpukernel_abi::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; +use implicit_provenance_casts::ImplicitProvenanceCasts; use interior_mutable_consts::*; use internal::*; use invalid_from_utf8::*; use let_underscore::*; use lifetime_syntax::*; -use lossy_provenance_casts::LossyProvenanceCasts; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -117,6 +116,7 @@ use precedence::*; use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; +use runtime_symbols::*; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalModDefId; use rustc_middle::query::Providers; @@ -152,7 +152,7 @@ pub fn provide(providers: &mut Providers) { } fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { - late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); + late_lint_mod(tcx, module_def_id, BuiltinCombinedLateLintModPass::new()); } early_lint_methods!( @@ -205,7 +205,7 @@ early_lint_methods!( late_lint_methods!( declare_combined_late_lint_pass, [ - BuiltinCombinedModuleLateLintPass, + BuiltinCombinedLateLintModPass, [ ForLoopsOverFallibles: ForLoopsOverFallibles, DefaultCouldBeDerived: DefaultCouldBeDerived, @@ -259,6 +259,7 @@ late_lint_methods!( AsyncFnInTrait: AsyncFnInTrait, NonLocalDefinitions: NonLocalDefinitions::default(), InteriorMutableConsts: InteriorMutableConsts, + RuntimeSymbols: RuntimeSymbols, ImplTraitOvercaptures: ImplTraitOvercaptures, IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, @@ -267,8 +268,7 @@ late_lint_methods!( CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, - FuzzyProvenanceCasts: FuzzyProvenanceCasts, - LossyProvenanceCasts: LossyProvenanceCasts, + ImplicitProvenanceCasts: ImplicitProvenanceCasts, ] ] ); @@ -276,7 +276,7 @@ late_lint_methods!( late_lint_methods!( declare_combined_late_lint_pass, [ - InternalCombinedModuleLateLintPass, + InternalCombinedLateLintModPass, [ DefaultHashTypes: DefaultHashTypes, QueryStability: QueryStability, @@ -314,7 +314,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedPreExpansionLintPass::lint_vec()); store.register_lints(&BuiltinCombinedEarlyLintPass::lint_vec()); - store.register_lints(&BuiltinCombinedModuleLateLintPass::lint_vec()); + store.register_lints(&BuiltinCombinedLateLintModPass::lint_vec()); store.register_lints(&foreign_modules::lint_vec()); store.register_lints(&hardwired::lint_vec()); @@ -407,6 +407,8 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes"); + store.register_renamed("fuzzy_provenance_casts", "implicit_provenance_casts"); + store.register_renamed("lossy_provenance_casts", "implicit_provenance_casts"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use @@ -695,10 +697,12 @@ fn register_builtins(store: &mut LintStore) { fn register_internals(store: &mut LintStore) { store.register_lints(&InternalCombinedEarlyLintPass::lint_vec()); - store.register_early_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); + store.register_early_lint_pass(Box::new(|| Box::new(InternalCombinedEarlyLintPass::new()))); - store.register_lints(&InternalCombinedModuleLateLintPass::lint_vec()); - store.register_late_mod_pass(Box::new(|_| Box::new(InternalCombinedModuleLateLintPass::new()))); + store.register_lints(&InternalCombinedLateLintModPass::lint_vec()); + store.register_late_lint_mod_pass(Box::new(|_| { + Box::new(InternalCombinedLateLintModPass::new()) + })); store.register_group( false, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d3048d08ebf77..83827c57aef00 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -808,6 +808,45 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion { }, } +// runtime_symbols.rs +#[derive(Diagnostic)] +pub(crate) enum RedefiningRuntimeSymbolsDiag<'tcx> { + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + FnDefInvalid { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "suspicious definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `{$found_fn_sig}`" + )] + #[help( + "either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = \"{$symbol_name}\")]`, or `#[link_name = \"{$symbol_name}\"]`" + )] + #[help("allow this lint if the signature is compatible")] + FnDefSuspicious { symbol_name: String, expected_fn_sig: Ty<'tcx>, found_fn_sig: Ty<'tcx> }, + #[diag( + "invalid definition of the runtime `{$symbol_name}` symbol used by the standard library" + )] + #[note( + "expected `{$expected_fn_sig}` + found `static {$symbol_name}: {$static_ty}`" + )] + #[help( + "either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = \"{$symbol_name}\")]`" + )] + Static { symbol_name: String, static_ty: Ty<'tcx>, expected_fn_sig: Ty<'tcx> }, +} + // drop_forget_useless.rs #[derive(Diagnostic)] #[diag("calls to `std::mem::drop` with a reference instead of an owned value does nothing")] @@ -2895,23 +2934,24 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { pub(crate) struct EqInternalMethodImplemented; #[derive(Diagnostic)] -#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] +#[diag("cast from `{$expr_ty}` to `{$cast_ty}` implicitly relies on exposed provenance")] #[help( - "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" + "if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()`" )] -pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { +#[note("for more information, visit ")] +pub(crate) struct ImplicitProvenanceCastsInt2Ptr<'tcx> { pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: Option, + pub sugg: Option, } #[derive(Subdiagnostic)] #[multipart_suggestion( - "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", + "use `.with_addr()` to adjust the address of a valid pointer in the same allocation", applicability = "has-placeholders" )] -pub(crate) struct LossyProvenanceInt2PtrSuggestion { +pub(crate) struct Int2PtrSuggestion { #[suggestion_part(code = "(...).with_addr(")] pub lo: Span, #[suggestion_part(code = ")")] @@ -2919,21 +2959,18 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { } #[derive(Diagnostic)] -#[diag( - "under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`" -)] -#[help( - "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" -)] -pub(crate) struct LossyProvenancePtr2Int<'tcx> { +#[diag("cast from `{$cast_from_ty}` to `{$cast_to_ty}` implicitly exposes pointer provenance")] +#[help("if conforming to strict provenance is not possible, use `.expose_provenance()`")] +#[note("for more information, visit ")] +pub(crate) struct ImplicitProvenanceCastsPtr2Int<'tcx> { pub cast_from_ty: Ty<'tcx>, pub cast_to_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: Option>, + pub sugg: Option>, } #[derive(Subdiagnostic)] -pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { +pub(crate) enum Ptr2IntSuggestion<'tcx> { #[multipart_suggestion( "use `.addr()` to obtain the address of a pointer", applicability = "maybe-incorrect" diff --git a/compiler/rustc_lint/src/lossy_provenance_casts.rs b/compiler/rustc_lint/src/lossy_provenance_casts.rs deleted file mode 100644 index bdb5bdf6bb0e3..0000000000000 --- a/compiler/rustc_lint/src/lossy_provenance_casts.rs +++ /dev/null @@ -1,98 +0,0 @@ -use rustc_ast::util::parser::ExprPrecedence; -use rustc_hir as hir; -use rustc_session::{declare_lint, declare_lint_pass}; - -use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion}; -use crate::{LateContext, LateLintPass}; - -declare_lint! { - /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer - /// and an integer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(lossy_provenance_casts)] - /// - /// fn main() { - /// let x: u8 = 37; - /// let _addr: usize = &x as *const u8 as usize; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting a pointer to an integer is a lossy operation, because beyond - /// just an *address* a pointer may be associated with a particular - /// *provenance*. This information is used by the optimiser and for dynamic - /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). - /// - /// Since this cast is lossy, it is considered good style to use the - /// [`ptr::addr`] method instead, which has a similar effect, but doesn't - /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information - /// about exposing pointer provenance. - /// - /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, - /// which preserves the behaviour of `as usize` casts while being explicit - /// about the semantics. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance - pub LOSSY_PROVENANCE_CASTS, - Allow, - "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance_lints; -} - -declare_lint_pass!( - /// Lint for `as` casts between a pointer and an integer. - LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS] -); - -impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; - - let typeck_results = cx.typeck_results(); - // Only lint casts from pointer to integer - let cast_from_ty = typeck_results.expr_ty(cast_from_expr); - if !cast_from_ty.is_raw_ptr() { - return; - } - let cast_to_ty = typeck_results.expr_ty(expr); - if !cast_to_ty.is_integral() { - return; - } - - let sugg = expr.span.can_be_used_for_suggestions().then(|| { - let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; - let needs_cast = !cast_to_ty.is_usize(); - let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); - let expr_span = cast_from_expr.span.shrink_to_lo(); - match (needs_parens, needs_cast) { - (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_to_ty, - }, - (true, false) => { - LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty } - } - (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span }, - } - }); - - let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg }; - cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); - } -} diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 5c101048b8010..12cf58907566d 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -203,7 +203,7 @@ macro_rules! expand_combined_early_lint_pass_methods { /// Combines multiple lints passes into a single lint pass, at compile time, /// for maximum speed. Each `check_foo` method in `$methods` within this pass /// simply calls `check_foo` once per `$pass`. Compare with -/// `EarlyLintPassObjects`, which is similar, but combines lint passes at +/// `RuntimeCombinedEarlyLintPass`, which is similar, but combines lint passes at /// runtime. #[macro_export] macro_rules! declare_combined_early_lint_pass { diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs new file mode 100644 index 0000000000000..a583ee70074b6 --- /dev/null +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -0,0 +1,255 @@ +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{self as hir, FnSig, ForeignItemKind, LanguageItems}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::Span; +use rustc_trait_selection::infer::TyCtxtInferExt; + +use crate::lints::RedefiningRuntimeSymbolsDiag; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `invalid_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbol expected by `core` differs significantly from the + /// expected signature (like mismatch ABI, mismatch C variadics, mismatch argument count, + /// missing return type, ...). + /// + /// ### Example + /// + #[cfg_attr(bootstrap, doc = "```rust")] + #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + #[cfg_attr(not(bootstrap), doc = "#[unsafe(no_mangle)]")] + /// pub fn strlen() {} // invalid definition of the `strlen` function + #[doc = "```"] + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub INVALID_RUNTIME_SYMBOL_DEFINITIONS, + Deny, + "invalid definition of a symbol used by the standard library" +} + +declare_lint! { + /// The `suspicious_runtime_symbol_definitions` lint checks the signature of items whose + /// symbol name is a runtime symbol expected by `core`. + /// + /// ### Example + /// + /// ```rust,no_run,standalone_crate + #[cfg_attr(not(bootstrap), doc = "#[unsafe(no_mangle)]")] + /// pub extern "C" fn strlen(ptr: *mut f32) -> usize { 0 } + /// // suspicious definition of the `strlen` function + /// // `ptr` should be `*const std::ffi::c_char` + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Up-most care is required when defining runtime symbols assumed and + /// used by the standard library. They must follow the C specification, not use any + /// standard-library facility or undefined behavior may occur. + /// + /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, + /// `bcmp` and `strlen`. + /// + /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library + pub SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + Warn, + "suspicious definition of a symbol used by the standard library" +} + +declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS, SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]); + +static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ + ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, + ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn }, + ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn }, + ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn }, + ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn }, + ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn }, +]; + +#[derive(Copy, Clone, Debug)] +struct ExpectedSymbol { + symbol: &'static str, + lang: fn(&LanguageItems) -> Option, +} + +impl<'tcx> LateLintPass<'tcx> for RuntimeSymbols { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + // Bail-out if the item is not a function/method or static. + match item.kind { + hir::ItemKind::Fn { sig, ident: _, generics, body: _, has_body: _ } => { + // Generic functions cannot have the same runtime symbol as we do not allow + // any symbol attributes. + if !generics.params.is_empty() { + return; + } + + // Try to get the overridden symbol name of this function (our mangling + // cannot ever conflict with runtime symbols, so no need to check for those). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + check_fn(cx, &symbol_name, sig, item.owner_id.def_id); + } + hir::ItemKind::Static(..) => { + // Compute the symbol name of this static (without mangling, as our mangling + // cannot ever conflict with runtime symbols). + let Some(symbol_name) = rustc_symbol_mangling::symbol_name_from_attrs( + cx.tcx, + rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()), + ) else { + return; + }; + + let def_id = item.owner_id.def_id; + + check_static(cx, &symbol_name, def_id, item.span); + } + hir::ItemKind::ForeignMod { abi: _, items } => { + for item in items { + let item = cx.tcx.hir_foreign_item(*item); + + let did = item.owner_id.def_id; + let instance = Instance::new_raw( + did.to_def_id(), + ty::List::identity_for_item(cx.tcx, did), + ); + let symbol_name = cx.tcx.symbol_name(instance); + + match item.kind { + ForeignItemKind::Fn(fn_sig, _idents, _generics) => { + check_fn(cx, &symbol_name.name, fn_sig, did); + } + ForeignItemKind::Static(..) => { + check_static(cx, &symbol_name.name, did, item.span); + } + ForeignItemKind::Type => return, + } + } + } + _ => return, + } + } +} + +fn check_fn(cx: &LateContext<'_>, symbol_name: &str, sig: FnSig<'_>, did: LocalDefId) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the two function signatures + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + let user_sig = cx + .tcx + .normalize_erasing_regions(cx.typing_env(), cx.tcx.fn_sig(did).instantiate_identity()); + + // Compare the two signatures with an inference context + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let cause = rustc_middle::traits::ObligationCause::misc(sig.span, did); + let result = infcx.at(&cause, cx.param_env).eq(DefineOpaqueTypes::No, lang_sig, user_sig); + + // If they don't match, emit our own mismatch signatures + if let Err(_terr) = result { + // Create fn pointers for diagnostics purpose + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + let actual = Ty::new_fn_ptr(cx.tcx, user_sig); + + if lang_sig.abi() != user_sig.abi() + || lang_sig.c_variadic() != user_sig.c_variadic() + || lang_sig.inputs().skip_binder().len() != user_sig.inputs().skip_binder().len() + || (!lang_sig.output().skip_binder().is_unit() + && user_sig.output().skip_binder().is_unit()) + { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDefInvalid { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + } else { + cx.emit_span_lint( + SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, + sig.span, + RedefiningRuntimeSymbolsDiag::FnDefSuspicious { + symbol_name: symbol_name.to_string(), + found_fn_sig: actual, + expected_fn_sig: expected, + }, + ); + }; + } +} + +fn check_static<'tcx>(cx: &LateContext<'tcx>, symbol_name: &str, did: LocalDefId, sp: Span) { + let Some(expected_symbol) = EXPECTED_SYMBOLS.iter().find(|es| es.symbol == symbol_name) else { + // The symbol name does not correspond to a runtime symbols, bail out + return; + }; + + let Some(expected_def_id) = (expected_symbol.lang)(&cx.tcx.lang_items()) else { + // Can't find the corresponding language item, bail out + return; + }; + + // Get the static type + let static_ty = cx.tcx.type_of(did).instantiate_identity().skip_norm_wip(); + + // Peel Option<...> and get the inner type (see std weak! macro with #[linkage = "extern_weak"]) + let inner_static_ty: Ty<'_> = match static_ty.kind() { + ty::Adt(def, args) if Some(def.did()) == cx.tcx.lang_items().option_type() => { + args.type_at(0) + } + _ => static_ty, + }; + + // Get the expected symbol function signature + let lang_sig = cx.tcx.normalize_erasing_regions( + cx.typing_env(), + cx.tcx.fn_sig(expected_def_id).instantiate_identity(), + ); + + let expected = Ty::new_fn_ptr(cx.tcx, lang_sig); + + // Compare the expected function signature with the static type, report an error if they don't match + if expected != inner_static_ty { + cx.emit_span_lint( + INVALID_RUNTIME_SYMBOL_DEFINITIONS, + sp, + RedefiningRuntimeSymbolsDiag::Static { + static_ty, + symbol_name: symbol_name.to_string(), + expected_fn_sig: expected, + }, + ); + } +} diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index db59ddfb9efa6..0c34d9da66f1d 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -358,6 +358,8 @@ struct VisitorState { /// Flags describing both the immediate context in which the current Ty is, /// linked to how it relates to its parent Ty (or lack thereof). outer_ty_kind: OuterTyKind, + /// Type recursion depth, to prevent infinite recursion + depth: usize, } impl RootUseFlags { @@ -385,6 +387,7 @@ impl VisitorState { VisitorState { root_use_flags: self.root_use_flags, outer_ty_kind: OuterTyKind::from_ty(current_ty), + depth: self.depth + 1, } } @@ -399,6 +402,7 @@ impl VisitorState { FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR, }, outer_ty_kind: OuterTyKind::from_ty(current_ty), + depth: self.depth + 1, } } @@ -410,12 +414,16 @@ impl VisitorState { (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION, (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION, }; - VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None } + VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None, depth: 0 } } /// Get the proper visitor state for a static variable's type fn static_entry_point() -> Self { - VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None } + VisitorState { + root_use_flags: RootUseFlags::STATIC_TY, + outer_ty_kind: OuterTyKind::None, + depth: 0, + } } /// Whether the type is used in a function. @@ -737,9 +745,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Protect against infinite recursion, for example // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. - if !self.cache.insert(ty) { + if !(self.cache.insert(ty) && self.cx.tcx.recursion_limit().value_within_limit(state.depth)) + { return FfiSafe; } @@ -994,6 +1001,8 @@ impl<'tcx> ImproperCTypesLint { fn_mode: CItemKind, ) { struct FnPtrFinder<'tcx> { + current_depth: usize, + depths: Vec, spans: Vec, tys: Vec>, } @@ -1001,13 +1010,16 @@ impl<'tcx> ImproperCTypesLint { impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); + self.current_depth += 1; if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind && !abi.is_rustic_abi() { + self.depths.push(self.current_depth); self.spans.push(ty.span); } hir::intravisit::walk_ty(self, ty); + self.current_depth -= 1; } } @@ -1025,16 +1037,25 @@ impl<'tcx> ImproperCTypesLint { } } - let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; + let mut visitor = FnPtrFinder { + spans: Vec::new(), + tys: Vec::new(), + depths: Vec::new(), + current_depth: 0, + }; ty.visit_with(&mut visitor); visitor.visit_ty_unambig(hir_ty); - let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); - for (fn_ptr_ty, span) in all_types { + let all_types = iter::zip( + visitor.depths.drain(..), + iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)), + ); + for (depth, (fn_ptr_ty, span)) in all_types { let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty); let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode); + let bridge_state = VisitorState { depth, ..state }; // FIXME(ctypes): make a check_for_fnptr - let ffi_res = visitor.check_type(state, fn_ptr_ty); + let ffi_res = visitor.check_type(bridge_state, fn_ptr_ty); self.process_ffi_result(cx, span, ffi_res, fn_mode); } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 5dcaa5f6f84b8..45229e5399dd5 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -288,7 +288,11 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, // Just print a bare list of target CPU names, and let Rust-side code handle // the full formatting of `--print=target-cpus`. for (auto &CPU : CPUTable) { +#if LLVM_VERSION_GE(23, 0) + OS << CPU.key() << "\n"; +#else OS << CPU.Key << "\n"; +#endif } } @@ -315,9 +319,15 @@ extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, #endif const ArrayRef FeatTable = MCInfo.getAllProcessorFeatures(); +#if LLVM_VERSION_GE(23, 0) + const SubtargetFeatureKV &Feat = FeatTable[Index]; + *Feature = Feat.key(); + *Desc = Feat.desc(); +#else const SubtargetFeatureKV Feat = FeatTable[Index]; *Feature = Feat.Key; *Desc = Feat.Desc; +#endif } extern "C" const char *LLVMRustGetHostCPUName(size_t *OutLen) { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index bc1c1d8240559..dff8a4078da16 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -556,15 +556,15 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { ) }, crates: |tcx, ()| { - // The list of loaded crates is now frozen in query cache, - // so make sure cstore is not mutably accessed from here on. - tcx.untracked().cstore.freeze(); + // The loaded-crate list is now frozen in the query cache; stop + // mutating the cstore and stable crate id map from here on. + tcx.untracked().freeze_cstore(); tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum)) }, used_crates: |tcx, ()| { - // The list of loaded crates is now frozen in query cache, - // so make sure cstore is not mutably accessed from here on. - tcx.untracked().cstore.freeze(); + // The loaded-crate list is now frozen in the query cache; stop + // mutating the cstore and stable crate id map from here on. + tcx.untracked().freeze_cstore(); tcx.arena.alloc_from_iter( CStore::from_tcx(tcx) .iter_crate_data() diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c232f595d4229..0f63c8469953d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1624,7 +1624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } - if let DefKind::AnonConst = def_kind { + if let DefKind::AnonConst | DefKind::InlineConst = def_kind { record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); } if should_encode_const_of_item(self.tcx, def_id, def_kind) { diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 3cab936c45c1f..6a97327504721 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -22,7 +22,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; use crate::hir::{ModuleItems, ProjectedMaybeOwner, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::{IntoQueryKey, LocalCrate}; -use crate::ty::TyCtxt; +use crate::ty::{self, TyCtxt}; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -879,13 +879,6 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_opt_delegation_info(delegation_id).expect("processing delegation") } - pub fn hir_is_delegation_child_segment(self, segment: &PathSegment<'_>) -> bool { - let parent_def = self.hir_get_parent_item(segment.hir_id).def_id; - - self.hir_opt_delegation_info(parent_def) - .is_some_and(|info| info.child_seg_id == segment.hir_id) - } - #[inline] fn hir_opt_ident(self, id: HirId) -> Option { match self.hir_node(id) { @@ -1115,6 +1108,12 @@ impl<'tcx> TyCtxt<'tcx> { } } + pub fn is_type_system_inline_const(self, def_id: impl IntoQueryKey) -> bool { + let def_id = def_id.into_query_key(); + debug_assert_eq!(self.def_kind(def_id), DefKind::InlineConst); + self.anon_const_kind(def_id) != ty::AnonConstKind::NonTypeSystem + } + pub fn hir_maybe_get_struct_pattern_shorthand_field(self, expr: &Expr<'_>) -> Option { let local = match expr { Expr { diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 6effeacac5fcf..44c5e66279c14 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -221,7 +221,7 @@ pub enum Const<'tcx> { /// An unevaluated mir constant which is not part of the type system. /// - /// Note that `Ty(ty::ConstKind::Unevaluated)` and this variant are *not* identical! `Ty` will + /// Note that `Ty(ty::ConstKind::Alias)` and this variant are *not* identical! `Ty` will /// always flow through a valtree, so all data not captured in the valtree is lost. This variant /// directly uses the evaluated result of the given constant, including e.g. data stored in /// padding. @@ -472,13 +472,9 @@ pub struct UnevaluatedConst<'tcx> { impl<'tcx> UnevaluatedConst<'tcx> { #[inline] - pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::UnevaluatedConst<'tcx> { + pub fn shrink(self, tcx: TyCtxt<'tcx>) -> ty::AliasConst<'tcx> { assert_eq!(self.promoted, None); - ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, self.def), - self.args, - ) + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, self.def), self.args) } } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 51cf856a2d477..accd00a8ebb51 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -90,7 +90,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn const_eval_resolve_for_typeck( self, typing_env: ty::TypingEnv<'tcx>, - ct: ty::UnevaluatedConst<'tcx>, + ct: ty::AliasConst<'tcx>, span: Span, ) -> ConstToValTreeResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference @@ -104,10 +104,10 @@ impl<'tcx> TyCtxt<'tcx> { } let def_id = match ct.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => def_id, + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => def_id, }; let cid = match ty::Instance::try_resolve(self, typing_env, def_id, ct.args) { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index c1fd68b5702c8..8e83d44fbb78b 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1491,16 +1491,14 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { let val = match const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Param(p) => format!("ty::Param({p})"), - ty::ConstKind::Unevaluated(_, uv) => { - let kind = match uv.kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - self.tcx.def_path_str(def_id) - } + ty::ConstKind::Alias(_, alias_const) => { + let kind = match alias_const.kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), }; - format!("ty::Unevaluated({}, {:?})", kind, uv.args) + format!("ty::AliasConst({}, {:?})", kind, alias_const.args) } ty::ConstKind::Value(cv) => { format!("ty::Valtree({})", fmt_valtree(&cv)) diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 08a34fd397357..43ed22927da56 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -52,13 +52,13 @@ impl<'tcx> TyCtxt<'tcx> { } fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> { let ct = match c.kind() { - ty::ConstKind::Unevaluated(_, uv) - if let Some(def_id) = uv.kind.opt_def_id() => + ty::ConstKind::Alias(_, alias_const) + if let Some(def_id) = alias_const.kind.opt_def_id() => { match self.tcx.thir_abstract_const(def_id) { Err(e) => ty::Const::new_error(self.tcx, e), Ok(Some(bac)) => { - let args = self.tcx.erase_and_anonymize_regions(uv.args); + let args = self.tcx.erase_and_anonymize_regions(alias_const.args); let bac = bac.instantiate(self.tcx, args).skip_norm_wip(); return bac.fold_with(self); } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 3455e770b40d1..df25b08168609 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -21,8 +21,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed}; pub use valtree::*; pub type ConstKind<'tcx> = ir::ConstKind>; -pub type UnevaluatedConst<'tcx> = ir::UnevaluatedConst>; -pub type UnevaluatedConstKind<'tcx> = ir::UnevaluatedConstKind>; +pub type AliasConst<'tcx> = ir::AliasConst>; +pub type AliasConstKind<'tcx> = ir::AliasConstKind>; #[cfg(target_pointer_width = "64")] rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); @@ -107,12 +107,12 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn new_unevaluated( + pub fn new_alias( tcx: TyCtxt<'tcx>, is_rigid: ty::IsRigid, - uv: ty::UnevaluatedConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Unevaluated(is_rigid, uv)) + Const::new(tcx, ty::ConstKind::Alias(is_rigid, alias_const)) } #[inline] @@ -157,7 +157,7 @@ impl<'tcx> Const<'tcx> { true } ty::ConstKind::Infer(_) - | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Alias(..) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => false, @@ -194,12 +194,12 @@ impl<'tcx> rustc_type_ir::inherent::Const> for Const<'tcx> { Const::new_placeholder(tcx, placeholder) } - fn new_unevaluated( + fn new_alias( interner: TyCtxt<'tcx>, is_rigid: ty::IsRigid, - uv: ty::UnevaluatedConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Self { - Const::new_unevaluated(interner, is_rigid, uv) + Const::new_alias(interner, is_rigid, alias_const) } fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5e87dd7667c14..3d7aef0b273f1 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -601,7 +601,14 @@ impl<'tcx> TyCtxt<'tcx> { /// effect. However, we do not want this as a general capability, so this interface restricts /// to the only allowed case. pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, Ty<'tcx>>) { - debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); + if cfg!(debug_assertions) { + match self.def_kind(key) { + DefKind::AnonConst => (), + DefKind::InlineConst => assert!(self.is_type_system_inline_const(key)), + def_kind => bug!("unexpected DefKind in feed_anon_const_type: {def_kind:?}"), + } + } + TyCtxtFeed { tcx: self, key }.type_of(value) } @@ -2085,6 +2092,8 @@ impl<'tcx> TyCtxt<'tcx> { ty::Tuple(params) => *params, _ => bug!(), }; + // Ignore splatting, it is unsupported on closures. + assert!(s.splatted().is_none()); self.mk_fn_sig( params, s.output(), diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 60c2acdb4c4f9..ada3298a6ca6e 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -41,7 +41,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type CoroutineId = DefId; type AdtId = DefId; type ImplId = DefId; - type UnevaluatedConstId = DefId; + type AnonConstId = DefId; type TraitAssocTyId = DefId; type TraitAssocConstId = DefId; type TraitAssocTermId = DefId; @@ -208,23 +208,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.adt_def(adt_def_id) } - fn unevaluated_const_kind_from_def_id( - self, - def_id: Self::DefId, - ) -> ty::UnevaluatedConstKind<'tcx> { + fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind<'tcx> { match self.def_kind(def_id) { DefKind::AssocConst { .. } => { if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(def_id)) { - ty::UnevaluatedConstKind::Inherent { def_id } + ty::AliasConstKind::Inherent { def_id } } else { - ty::UnevaluatedConstKind::Projection { def_id } + ty::AliasConstKind::Projection { def_id } } } - DefKind::Const { .. } => ty::UnevaluatedConstKind::Free { def_id }, + DefKind::Const { .. } => ty::AliasConstKind::Free { def_id }, DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { - ty::UnevaluatedConstKind::Anon { def_id } + ty::AliasConstKind::Anon { def_id } } - kind => bug!("unexpected DefKind in UnevaluatedConst: {kind:?}"), + kind => bug!("unexpected DefKind in AliasConst: {kind:?}"), } } @@ -247,7 +244,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy { def_id }, DefKind::TyAlias => ty::AliasTermKind::FreeTy { def_id }, DefKind::Const { .. } => ty::AliasTermKind::FreeConst { def_id }, - DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => { + DefKind::AnonConst | DefKind::InlineConst | DefKind::Ctor(_, CtorKind::Const) => { ty::AliasTermKind::AnonConst { def_id } } kind => bug!("unexpected DefKind in AliasTy: {kind:?}"), @@ -544,8 +541,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { // only want to consider types that *actually* unify with float/int vars. fn for_each_relevant_impl( self, - trait_def_id: DefId, - self_ty: Ty<'tcx>, + trait_ref: ty::TraitRef<'tcx>, mut f: impl FnMut(DefId) -> R, ) -> R { macro_rules! ret { @@ -557,6 +553,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { }; } + let trait_def_id = trait_ref.def_id; + let self_ty = trait_ref.self_ty(); let tcx = self; let trait_impls = tcx.trait_impls_of(trait_def_id); let mut consider_impls_for_simplified_type = |simp| { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 23b5899b0cc39..208e94270a45e 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -96,6 +96,20 @@ impl<'tcx> TypeError<'tcx> { if values.found { "variadic" } else { "non-variadic" } ) .into(), + TypeError::SplatMismatch(ref values) => format!( + "expected fn with {}, found fn with {}", + if let Some(index) = values.expected { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + }, + if let Some(index) = values.found { + format!("arg {index} splatted") + } else { + "no splatted arg".to_string() + } + ) + .into(), TypeError::ProjectionMismatched(ref values) => format!( "expected `{}`, found `{}`", tcx.alias_term_kind_def_path_str(values.expected), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3ea798ee45fb2..98b44f072075a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -76,9 +76,9 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, Expr, ExprKind, - LitToConstInput, ScalarInt, SimdAlign, UnevaluatedConst, UnevaluatedConstKind, ValTree, - ValTreeKindExt, Value, const_lit_matches_ty, + AliasConst, AliasConstKind, AtomicOrdering, Const, ConstInt, ConstKind, ConstToValTreeResult, + Expr, ExprKind, LitToConstInput, ScalarInt, SimdAlign, ValTree, ValTreeKindExt, Value, + const_lit_matches_ty, }; pub use self::context::{ CtxtInterners, CurrentGcx, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed, tls, @@ -113,7 +113,8 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, - Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, + Rust2024IncompatiblePatInfo, SplattedDef, TypeckResults, UserType, UserTypeAnnotationIndex, + UserTypeKind, }; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::{AmbigModChild, ModChild}; @@ -692,7 +693,7 @@ impl<'tcx> Term<'tcx> { _ => None, }, TermKind::Const(ct) => match ct.kind() { - ConstKind::Unevaluated(_, uv) => Some(uv.into()), + ConstKind::Alias(_, alias_const) => Some(alias_const.into()), _ => None, }, } @@ -705,7 +706,7 @@ impl<'tcx> Term<'tcx> { _ => false, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(ty::IsRigid::No, _) => true, + ty::ConstKind::Alias(ty::IsRigid::No, _) => true, _ => false, }, } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c1f96c3a94fdd..6852c952658e7 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1435,6 +1435,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p.pretty_print_fn_sig( tys, false, + // FIXME(splat): support splatted arguments here? + None, proj.skip_binder().term.as_type().expect("Return type was a const"), )?; resugared = true; @@ -1538,10 +1540,19 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { &mut self, inputs: &[Ty<'tcx>], c_variadic: bool, + splatted: Option, output: Ty<'tcx>, ) -> Result<(), PrintError> { write!(self, "(")?; - self.comma_sep(inputs.iter().copied())?; + let splatted_arg_index = splatted.map(usize::from); + let mut input_iter = inputs.iter().copied(); + if let Some(index) = splatted_arg_index { + self.comma_sep((&mut input_iter).take(usize::from(index)))?; + write!(self, ", #[splat]")?; + self.comma_sep(input_iter)?; + } else { + self.comma_sep(input_iter)?; + } if c_variadic { if !inputs.is_empty() { write!(self, ", ")?; @@ -1568,14 +1579,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } match ct.kind() { - ty::ConstKind::Unevaluated(_, ty::UnevaluatedConst { kind, args, .. }) => { + ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => { match kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } => { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } => { self.pretty_print_value_path(def_id, args)?; } - ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Anon { def_id } => { if def_id.is_local() && let span = self.tcx().def_span(def_id) && let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) @@ -3150,7 +3161,7 @@ define_print! { } write!(p, "fn")?; - p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.output())?; + p.pretty_print_fn_sig(self.inputs(), self.c_variadic(), self.splatted(), self.output())?; } ty::TraitRef<'tcx> { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 0bcc10a760725..49bac6a130231 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -634,8 +634,8 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { folder: &mut F, ) -> Result { let kind = match self.kind() { - ConstKind::Unevaluated(is_rigid, uv) => { - ConstKind::Unevaluated(is_rigid, uv.try_fold_with(folder)?) + ConstKind::Alias(is_rigid, alias_const) => { + ConstKind::Alias(is_rigid, alias_const.try_fold_with(folder)?) } ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), @@ -651,8 +651,8 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { fn super_fold_with>>(self, folder: &mut F) -> Self { let kind = match self.kind() { - ConstKind::Unevaluated(is_rigid, uv) => { - ConstKind::Unevaluated(is_rigid, uv.fold_with(folder)) + ConstKind::Alias(is_rigid, alias_const) => { + ConstKind::Alias(is_rigid, alias_const.fold_with(folder)) } ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), @@ -670,7 +670,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { match self.kind() { - ConstKind::Unevaluated(_, uv) => uv.visit_with(visitor), + ConstKind::Alias(_, alias_const) => alias_const.visit_with(visitor), ConstKind::Value(v) => v.visit_with(visitor), ConstKind::Expr(e) => e.visit_with(visitor), ConstKind::Error(e) => e.visit_with(visitor), diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 1287047581196..77820beef0228 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -36,6 +36,9 @@ pub struct TypeckResults<'tcx> { /// method calls, including those of overloaded operators. type_dependent_defs: ItemLocalMap>, + /// Resolved definitions for splatted function calls. + splatted_defs: ItemLocalMap>, + /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`) /// or patterns (`S { field }`). The index is often useful by itself, but to learn more /// about the field you also need definition of the variant to which the field @@ -229,6 +232,7 @@ impl<'tcx> TypeckResults<'tcx> { TypeckResults { hir_owner, type_dependent_defs: Default::default(), + splatted_defs: Default::default(), field_indices: Default::default(), user_provided_types: Default::default(), user_provided_sigs: Default::default(), @@ -287,6 +291,21 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.type_dependent_defs } } + pub fn splatted_defs(&self) -> LocalTableInContext<'_, Result> { + LocalTableInContext { hir_owner: self.hir_owner, data: &self.splatted_defs } + } + + pub fn splatted_def(&self, id: HirId) -> Option { + validate_hir_id_for_typeck_results(self.hir_owner, id); + self.splatted_defs.get(&id.local_id).cloned().and_then(|r| r.ok()) + } + + pub fn splatted_defs_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Result> { + LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.splatted_defs } + } + pub fn field_indices(&self) -> LocalTableInContext<'_, FieldIdx> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.field_indices } } @@ -407,6 +426,10 @@ impl<'tcx> TypeckResults<'tcx> { matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } + pub fn is_splatted_call(&self, expr: &hir::Expr<'_>) -> bool { + matches!(self.splatted_defs().get(expr.hir_id), Some(Ok(SplattedDef { .. }))) + } + /// Returns the computed binding mode for a `PatKind::Binding` pattern /// (after match ergonomics adjustments). pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> BindingMode { @@ -569,6 +592,18 @@ impl<'tcx> TypeckResults<'tcx> { } } +/// A resolved splatted function call. +#[derive(Debug, Copy, Clone, PartialEq, Eq, StableHash, TyEncodable, TyDecodable)] +pub struct SplattedDef { + /// The function DefId, if available (FnPtrs don't have DefIds) + pub def_id: Option, + /// The index of the first argument in the callee's splatted tuple, and the index of the + /// splatted tuple argument in the caller. + pub arg_index: u16, + /// The number of arguments in the splatted tuple. + pub arg_count: u16, +} + /// Validate that the given HirId (respectively its `local_id` part) can be /// safely used as a key in the maps of a TypeckResults. For that to be /// the case, the HirId must have the same `owner` as all the other IDs in diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ca3b3f0bbec5c..2c91555e3835f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -597,7 +597,38 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - self.def_kind(def_id).is_typeck_child() + match self.def_kind(def_id) { + DefKind::InlineConst => !self.is_type_system_inline_const(def_id), + DefKind::Closure | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const { .. } + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst { .. } + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 534629edd7895..620e6da6a8f88 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -200,10 +200,10 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> { fn visit_const(&mut self, c: ty::Const<'tcx>) { // if we are only looking for "constrained" region, we have to - // ignore the inputs of an unevaluated const, as they may not appear + // ignore the inputs of an alias const, as they may not appear // in the normalized form if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.kind() { + if let ty::ConstKind::Alias(..) = c.kind() { return; } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index fa22e07612cfe..d90c2a4e759af 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index f0f7e166e9672..caf4f8d295c8a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -72,12 +72,12 @@ pub(crate) fn as_constant_inner<'tcx>( ExprKind::NamedConst { def_id, args, ref user_ty } => { let user_ty = user_ty.as_ref().and_then(push_cuta); if tcx.is_type_const(def_id) { - let uneval = ty::UnevaluatedConst::new( + let uneval = ty::AliasConst::new( tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, def_id), + ty::AliasConstKind::new_from_def_id(tcx, def_id), args, ); - let ct = ty::Const::new_unevaluated(tcx, ty::IsRigid::No, uneval); + let ct = ty::Const::new_alias(tcx, ty::IsRigid::No, uneval); let const_ = Const::Ty(ty, ct); return ConstOperand { span, user_ty, const_ }; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 8352ec92e88b0..f70416b2cf867 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -9,7 +9,7 @@ use std::borrow::Borrow; use std::sync::Arc; use std::{debug_assert_matches, mem}; -use itertools::{Itertools, Position}; +use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -551,9 +551,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163). let target_block = self.cfg.start_new_block(); for (pos, sub_branch) in branch.sub_branches.into_iter().rev().with_position() { - debug_assert!(pos != Position::Only); + debug_assert!(!pos.is_exactly_one()); let schedule_drops = - if pos == Position::Last { ScheduleDrops::Yes } else { ScheduleDrops::No }; + if pos.is_last() { ScheduleDrops::Yes } else { ScheduleDrops::No }; let binding_end = self.bind_and_guard_matched_candidate( sub_branch, fake_borrow_temps, diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 854a378bb993d..378e2e618fb93 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -547,6 +547,7 @@ fn construct_fn<'tcx>( body.spread_arg = if abi == ExternAbi::RustCall { // RustCall pseudo-ABI untuples the last argument. + // FIXME(splat): splat can untuple any argument, set spread_arg here Some(Local::new(arguments.len())) } else { None diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b71793c157621..c286c0819aea2 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -18,8 +18,8 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ - self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, TyCtxt, - UpvarArgs, + self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, SplattedDef, Ty, + TyCtxt, UpvarArgs, }; use rustc_middle::{bug, span_bug}; use rustc_span::{DesugaringKind, Span}; @@ -369,19 +369,26 @@ impl<'tcx> ThirBuildCx<'tcx> { let kind = match expr.kind { // Here comes the interesting stuff: hir::ExprKind::MethodCall(segment, receiver, args, fn_span) => { - // Rewrite a.b(c) into UFCS form like Trait::b(a, c) - let expr = self.method_callee(expr, segment.ident.span, None); - info!("Using method span: {:?}", expr.span); - let args = std::iter::once(receiver) - .chain(args.iter()) - .map(|expr| self.mirror_expr(expr)) - .collect(); - ExprKind::Call { - ty: expr.ty, - fun: self.thir.exprs.push(expr), - args, - from_hir_call: true, - fn_span, + if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fn_span, args, Some(receiver)) + } else { + // Rewrite a.b(c) into UFCS form like Trait::b(a, c) + let expr = self.method_callee(expr, segment.ident.span, None); + info!("Using method span: {:?}", expr.span); + + let args = std::iter::once(receiver) + .chain(args.iter()) + .map(|expr| self.mirror_expr(expr)) + .collect(); + ExprKind::Call { + ty: expr.ty, + fun: self.thir.exprs.push(expr), + args, + from_hir_call: true, + fn_span, + } } } @@ -412,6 +419,10 @@ impl<'tcx> ThirBuildCx<'tcx> { from_hir_call: true, fn_span: expr.span, } + } else if self.typeck_results.is_splatted_call(expr) { + // The callee has a splatted tuple argument. + // rewrite `f(a, u, v)` into `f(a, #[splat] (u, v))` + self.convert_splatted_callee(expr, fun.span, args, None) } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind @@ -1205,6 +1216,116 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + span: Span, + ) -> (Expr<'tcx>, u16 /* arg_index */, u16 /* arg_count */) { + let SplattedDef { def_id, arg_index, arg_count } = + self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_id = def_id.unwrap_or_else(|| { + span_bug!(expr.span, "no splatted def for function or method callee") + }); + let def_kind = self.tcx.def_kind(def_id); + let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count + ); + + ( + Expr { + temp_scope_id: expr.hir_id.local_id, + ty: Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + }, + arg_index, + arg_count, + ) + } + + /// The callee has a splatted tuple argument. + /// Rewrite a splatted call `receiver.f(a, u, v)` into `receiver.f(a, #[splat] (u, v))`. + /// The receiver is optional. + fn convert_splatted_callee( + &mut self, + expr: &hir::Expr<'_>, + fn_span: Span, + args: &'tcx [hir::Expr<'tcx>], + receiver: Option<&'tcx hir::Expr<'tcx>>, + ) -> ExprKind<'tcx> { + let tcx = self.tcx; + + // The callee has a splatted tuple argument. + let (func, tupled_arg_index, tupled_args_count) = self.splatted_callee(expr, fn_span); + let tupled_arg_index = usize::from(tupled_arg_index); + let tupled_args_count = usize::from(tupled_args_count); + + // Splatting an empty tuple is permitted: `a.f() -> Trait::f(a, #[splat] ())`. + // In that case, the tupled arg index is one past the end of the args. + if tupled_arg_index + tupled_args_count > args.len() { + span_bug!( + expr.span, + "splatted arg index out of bounds of function args: {:?} + {:?} > {:?} for function call: receiver {:?}, args {:?}", + tupled_arg_index, + tupled_args_count, + args.len(), + receiver, + args, + ); + } + + info!("Using splatted function span: {:?}", func.span); + + // Split into non-tupled and tupled arguments + let initial_non_tupled_args = + args.iter().take(tupled_arg_index).map(|e| self.mirror_expr(e)).collect_vec(); + let tupled_args = if tupled_arg_index == args.len() || tupled_args_count == 0 { + // Splatting an empty tuple, in the ABI this gets ignored + Default::default() + } else { + &args[tupled_arg_index..(tupled_arg_index + tupled_args_count)] + }; + let final_non_tupled_args = args + .iter() + .skip(tupled_arg_index + tupled_args_count) + .map(|e| self.mirror_expr(e)) + .collect_vec(); + + let tupled_arg_tys = tupled_args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); + + let temp_scope_id = + if receiver.is_some() { func.temp_scope_id } else { expr.hir_id.local_id }; + let tupled_args = Expr { + ty: Ty::new_tup_from_iter(tcx, tupled_arg_tys), + temp_scope_id, + span: expr.span, + kind: ExprKind::Tuple { fields: self.mirror_exprs(tupled_args) }, + }; + + let tupled_args = self.thir.exprs.push(tupled_args); + + let mut args = + if let Some(receiver) = receiver { vec![self.mirror_expr(receiver)] } else { vec![] }; + args.extend(initial_non_tupled_args); + args.push(tupled_args); + args.extend(final_non_tupled_args); + + // We need the tupled arguments in HIR/MIR for type checking, but codegen can + // de-tuple them for performance + let fn_span = if receiver.is_some() { func.span } else { expr.span }; + ExprKind::Call { + ty: func.ty, + fun: self.thir.exprs.push(func), + args: args.into_boxed_slice(), + from_hir_call: true, + fn_span, + } + } + fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId { let arm = Arm { pattern: self.pattern_from_hir(&arm.pat), diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index a9c194959b28a..7c714fd7281d5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -49,7 +49,7 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let mut convert = ConstToPat::new(self, id, span, c); match c.kind() { - ty::ConstKind::Unevaluated(_, uv) => convert.unevaluated_to_pat(uv, ty), + ty::ConstKind::Alias(_, alias_const) => convert.alias_to_pat(alias_const, ty), ty::ConstKind::Value(value) => convert.valtree_to_pat(value), _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), } @@ -77,17 +77,17 @@ impl<'tcx> ConstToPat<'tcx> { /// We errored. Signal that in the pattern, so that follow up errors can be silenced. fn mk_err(&self, mut err: Diag<'_>, ty: Ty<'tcx>) -> Box> { - if let ty::ConstKind::Unevaluated(_, uv) = self.c.kind() { - if let ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } = uv.kind + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() { + if let ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } = alias_const.kind && let Some(def_id) = def_id.as_local() { // Include the container item in the output. err.span_label(self.tcx.def_span(self.tcx.local_parent(def_id)), ""); } - if let ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } = uv.kind + if let ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } = alias_const.kind { err.span_label(self.tcx.def_span(def_id), msg!("constant defined here")); } @@ -95,11 +95,7 @@ impl<'tcx> ConstToPat<'tcx> { Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) } - fn unevaluated_to_pat( - &mut self, - uv: ty::UnevaluatedConst<'tcx>, - ty: Ty<'tcx>, - ) -> Box> { + fn alias_to_pat(&mut self, alias_const: ty::AliasConst<'tcx>, ty: Ty<'tcx>) -> Box> { // It's not *technically* correct to be revealing opaque types here as borrowcheck has // not run yet. However, CTFE itself uses `TypingMode::PostAnalysis` unconditionally even // during typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). @@ -109,13 +105,13 @@ impl<'tcx> ConstToPat<'tcx> { // instead of having this logic here let typing_env = self.tcx.erase_and_anonymize_regions(self.typing_env).with_codegen_normalized(self.tcx); - let uv = self.tcx.erase_and_anonymize_regions(uv); + let alias_const = self.tcx.erase_and_anonymize_regions(alias_const); // FIXME(gca): This will become insufficient once associated constants can be // implemented as `type` consts (project-const-generics#76). At that point it'll // become necessary to just use type system normalization for all const patterns // but that's not yet possible. - let mut thir_pat = if uv.kind.is_type_const(self.tcx) { + let mut thir_pat = if alias_const.kind.is_type_const(self.tcx) { let Ok(normalize) = self .tcx .try_normalize_erasing_regions(self.typing_env, Unnormalized::new_wip(self.c)) @@ -132,67 +128,68 @@ impl<'tcx> ConstToPat<'tcx> { } else { // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. - let valtree = match self.tcx.const_eval_resolve_for_typeck(typing_env, uv, self.span) { - Ok(Ok(c)) => c, - Err(ErrorHandled::Reported(_, _)) => { - // Let's tell the use where this failing const occurs. - let mut err = - self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); - // We've emitted an error on the original const, it would be redundant to complain - // on its use as well. - if let ty::ConstKind::Unevaluated(_, uv) = self.c.kind() - && let ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - | ty::UnevaluatedConstKind::Free { .. } = uv.kind - { - err.downgrade_to_delayed_bug(); - } - return self.mk_err(err, ty); - } - Err(ErrorHandled::TooGeneric(_)) => { - let mut e = self - .tcx - .dcx() - .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); - for arg in uv.args { - if let ty::GenericArgKind::Type(ty) = arg.kind() - && let ty::Param(param_ty) = ty.kind() + let valtree = + match self.tcx.const_eval_resolve_for_typeck(typing_env, alias_const, self.span) { + Ok(Ok(c)) => c, + Err(ErrorHandled::Reported(_, _)) => { + // Let's tell the use where this failing const occurs. + let mut err = + self.tcx.dcx().create_err(CouldNotEvalConstPattern { span: self.span }); + // We've emitted an error on the original const, it would be redundant to complain + // on its use as well. + if let ty::ConstKind::Alias(_, alias_const) = self.c.kind() + && let ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + | ty::AliasConstKind::Free { .. } = alias_const.kind { - let def_id = self.tcx.hir_enclosing_body_owner(self.id); - let generics = self.tcx.generics_of(def_id); - let param = generics.type_param(*param_ty, self.tcx); - let span = self.tcx.def_span(param.def_id); - e.span_label(span, "constant depends on this generic parameter"); - if let Some(ident) = self.tcx.def_ident_span(def_id) - && self.tcx.sess.source_map().is_multiline(ident.between(span)) + err.downgrade_to_delayed_bug(); + } + return self.mk_err(err, ty); + } + Err(ErrorHandled::TooGeneric(_)) => { + let mut err = self + .tcx + .dcx() + .create_err(ConstPatternDependsOnGenericParameter { span: self.span }); + for arg in alias_const.args { + if let ty::GenericArgKind::Type(ty) = arg.kind() + && let ty::Param(param_ty) = ty.kind() { - // Display the `fn` name as well in the diagnostic, as the generic isn't - // in the same line and it could be confusing otherwise. - e.span_label(ident, ""); + let def_id = self.tcx.hir_enclosing_body_owner(self.id); + let generics = self.tcx.generics_of(def_id); + let param = generics.type_param(*param_ty, self.tcx); + let span = self.tcx.def_span(param.def_id); + err.span_label(span, "constant depends on this generic parameter"); + if let Some(ident) = self.tcx.def_ident_span(def_id) + && self.tcx.sess.source_map().is_multiline(ident.between(span)) + { + // Display the `fn` name as well in the diagnostic, as the generic isn't + // in the same line and it could be confusing otherwise. + err.span_label(ident, ""); + } } } + return self.mk_err(err, ty); } - return self.mk_err(e, ty); - } - Ok(Err(bad_ty)) => { - // The pattern cannot be turned into a valtree. - let e = match bad_ty.kind() { - ty::Adt(def, ..) => { - assert!(def.is_union()); - self.tcx.dcx().create_err(UnionPattern { span: self.span }) - } - ty::FnPtr(..) | ty::RawPtr(..) => { - self.tcx.dcx().create_err(PointerPattern { span: self.span }) - } - _ => self.tcx.dcx().create_err(InvalidPattern { - span: self.span, - non_sm_ty: bad_ty, - prefix: bad_ty.prefix_string(self.tcx).to_string(), - }), - }; - return self.mk_err(e, ty); - } - }; + Ok(Err(bad_ty)) => { + // The pattern cannot be turned into a valtree. + let e = match bad_ty.kind() { + ty::Adt(def, ..) => { + assert!(def.is_union()); + self.tcx.dcx().create_err(UnionPattern { span: self.span }) + } + ty::FnPtr(..) | ty::RawPtr(..) => { + self.tcx.dcx().create_err(PointerPattern { span: self.span }) + } + _ => self.tcx.dcx().create_err(InvalidPattern { + span: self.span, + non_sm_ty: bad_ty, + prefix: bad_ty.prefix_string(self.tcx).to_string(), + }), + }; + return self.mk_err(e, ty); + } + }; // Lower the valtree to a THIR pattern. self.valtree_to_pat(ty::Value { ty, valtree }) @@ -209,7 +206,7 @@ impl<'tcx> ConstToPat<'tcx> { // Mark the pattern to indicate that it is the result of lowering a named // constant. This is used for diagnostics. - thir_pat.extra.get_or_insert_default().expanded_const = uv.kind.opt_def_id(); + thir_pat.extra.get_or_insert_default().expanded_const = alias_const.kind.opt_def_id(); thir_pat } @@ -253,7 +250,7 @@ impl<'tcx> ConstToPat<'tcx> { // then error about that instead, because `TypeNotStructural` gives advice that is // relevant only when the problem is that `ty` does not derive `PartialEq`. // - // Note that this is a duplicate of a check in `unevaluated_to_pat()`, + // Note that this is a duplicate of a check in `alias_to_pat()`, // which we would run later if we weren’t emitting an error now. if possibly_inapplicable_derived_partial_eq && !has_impl { let mut err = diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 4bf69b3a85520..11b31cb3976c9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -657,12 +657,12 @@ impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { let args = self.typeck_results.node_args(id); // FIXME(mgca): we will need to special case IACs here to have type system compatible // generic args, instead of how we represent them in body expressions. - let c = ty::Const::new_unevaluated( + let c = ty::Const::new_alias( self.tcx, ty::IsRigid::No, - ty::UnevaluatedConst::new( + ty::AliasConst::new( self.tcx, - ty::UnevaluatedConstKind::new_from_def_id(self.tcx, def_id), + ty::AliasConstKind::new_from_def_id(self.tcx, def_id), args, ), ); diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 395127edd1e0a..f94b3f49ff6d1 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index d099fc3b9ddeb..cc216c39b8599 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -44,7 +44,7 @@ pub(crate) fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool !p.has_type_flags( // Only consider global clauses to simplify. TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. + // Clauses that refer to alias constants as they cause cycles. | TypeFlags::HAS_CT_PROJECTION, ) }); diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 7c8bef2aa3fe6..c36b687111c01 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1026,7 +1026,7 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( }); // Copy required constants from the callee_body into the caller_body. Although we are only - // pushing unevaluated consts to `required_consts`, here they may have been evaluated + // pushing constants that still need evaluation to `required_consts`, here they may have been evaluated // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. caller_body.required_consts.as_mut().unwrap().extend( callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), diff --git a/compiler/rustc_mir_transform/src/trivial_const.rs b/compiler/rustc_mir_transform/src/trivial_const.rs index 25d15c12a7b3b..4b66c757729d3 100644 --- a/compiler/rustc_mir_transform/src/trivial_const.rs +++ b/compiler/rustc_mir_transform/src/trivial_const.rs @@ -52,11 +52,10 @@ where F: FnOnce() -> B, B: Deref>, { - if !matches!( - tcx.def_kind(def), - DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst - ) { - return None; + match tcx.def_kind(def) { + DefKind::AssocConst { .. } | DefKind::Const { .. } | DefKind::AnonConst => (), + DefKind::InlineConst if tcx.is_type_system_inline_const(def) => (), + _ => return None, } // If there are impossible predicates then MIR passes will replace the body with diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index c7697e5b78ecb..5aa09e1ba7932 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -565,7 +565,7 @@ impl, I: Interner> TypeFolder for Canonicaliz }, // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) - | ty::ConstKind::Unevaluated(_, _) + | ty::ConstKind::Alias(_, _) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => return c.super_fold_with(self), diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 8fd7d6d0471c7..2ebb0fb851a4c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -37,7 +37,7 @@ pub trait SolverDelegate: Deref + Sized { fn evaluate_const( &self, param_env: ::ParamEnv, - uv: ty::UnevaluatedConst, + alias_const: ty::AliasConst, ) -> Option<::Const>; // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`! diff --git a/compiler/rustc_next_trait_solver/src/normalize.rs b/compiler/rustc_next_trait_solver/src/normalize.rs index 1f9c4407af4c5..761b141202d98 100644 --- a/compiler/rustc_next_trait_solver/src/normalize.rs +++ b/compiler/rustc_next_trait_solver/src/normalize.rs @@ -231,7 +231,7 @@ where // With eager normalization, we should normalize the args of alias before // normalizing the alias itself. let ct = ct.try_super_fold_with(self)?; - let ty::ConstKind::Unevaluated(orig_is_rigid, uv) = ct.kind() else { return Ok(ct) }; + let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return Ok(ct) }; // We support ambiguous aliases inside rigid alias. So we still recognize // the rigidness of the outer alias. if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { @@ -239,10 +239,10 @@ where } let normalized = if ct.has_escaping_bound_vars() { - let (uv, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); + let (alias_const, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, alias_const); let Some(result) = ensure_sufficient_stack(|| { - self.normalize_alias_term(uv.into(), HasEscapingBoundVars::Yes) + self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::Yes) })? else { return Ok(ct); @@ -257,7 +257,7 @@ where ) } else { ensure_sufficient_stack(|| { - self.normalize_alias_term(uv.into(), HasEscapingBoundVars::No) + self.normalize_alias_term(alias_const.into(), HasEscapingBoundVars::No) })? .map(|term| term.expect_const()) .unwrap_or(ct) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 0421bd7aaedc0..db1a8228c43cb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -539,28 +539,24 @@ where candidates: &mut Vec>, ) -> Result<(), RerunNonErased> { let cx = self.cx(); - cx.for_each_relevant_impl( - goal.predicate.trait_def_id(cx), - goal.predicate.self_ty(), - |impl_def_id| -> Result<_, _> { - // For every `default impl`, there's always a non-default `impl` - // that will *also* apply. There's no reason to register a candidate - // for this impl, since it is *not* proof that the trait goal holds. - if cx.impl_is_default(impl_def_id) { - return Ok(()); - } - match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { - ecx.evaluate_added_goals_and_make_canonical_response(certainty) - }) - .map_err_to_rerun()? - { - Ok(candidate) => candidates.push(candidate), - Err(NoSolution) => {} - } + cx.for_each_relevant_impl(goal.predicate.trait_ref(cx), |impl_def_id| -> Result<_, _> { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return Ok(()); + } + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) + .map_err_to_rerun()? + { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => {} + } - Ok(()) - }, - ) + Ok(()) + }) } #[instrument(level = "trace", skip_all)] diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 05532cdb8924b..fe573025c87f4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -462,7 +462,7 @@ where Ok(i) => Ok(i), Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), Err(NoSolutionOrRerunNonErased::RerunNonErased(_)) => { - // check th t the opaque_accesses state mirrors the result we got. + // Check that the opaque_accesses state mirrors the result we got. assert!(opaque_accesses.should_bail().is_err()); Err(NoSolution) } @@ -1439,13 +1439,13 @@ where pub(super) fn evaluate_const( &mut self, param_env: I::ParamEnv, - uv: ty::UnevaluatedConst, + alias_const: ty::AliasConst, ) -> Result, RerunNonErased> { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)?; + match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {} } - Ok(self.delegate.evaluate_const(param_env, uv)) + Ok(self.delegate.evaluate_const(param_env, alias_const)) } pub(super) fn evaluate_const_and_instantiate_projection_term( @@ -1453,9 +1453,9 @@ where param_env: I::ParamEnv, projection_term: ty::AliasTerm, expected_term: I::Term, - uv: ty::UnevaluatedConst, + alias_const: ty::AliasConst, ) -> QueryResultOrRerunNonErased { - match self.evaluate_const(param_env, uv)? { + match self.evaluate_const(param_env, alias_const)? { Some(evaluated) => { self.eq(param_env, expected_term, evaluated.into())?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -1468,11 +1468,11 @@ where // Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and // HasInfers or something, make evaluate_const return that, and make this branch be // based on that, rather than checking `has_non_region_infer`. - if self.resolve_vars_if_possible(uv).has_non_region_infer() { + if self.resolve_vars_if_possible(alias_const).has_non_region_infer() { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } else { - // We do not instantiate to the `uv` passed in, but rather - // `goal.predicate.alias`. The `uv` passed in might correspond to the `impl` + // We do not instantiate to the `alias_const` passed in, but rather + // `goal.predicate.alias`. The `alias_const` passed in might correspond to the `impl` // form of a constant (with generic arguments corresponding to the impl block), // however, we want to structurally instantiate to the original, non-rebased, // trait `Self` form of the constant (with generic arguments being the trait @@ -1515,7 +1515,7 @@ where symbol: I::Symbol, ) -> Result { if self.typing_mode().is_erased_not_coherence() { - self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)?; + match self.opaque_accesses.rerun_always(RerunReason::MayUseUnstableFeature)? {} } Ok(may_use_unstable_feature(&**self.delegate, param_env, symbol)) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index 1c5d6e0b14c65..d9d18bdeea7e7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -98,10 +98,8 @@ where outer.opaque_accesses.update(nested.opaque_accesses)?; - let r = match r.map_err_to_rerun()? { - Ok(i) => Ok(i), - Err(NoSolution) => Err(NoSolution), - }; + // Unwrap is unreachable, we would have returned on the line above. + let r = r.map_err_to_rerun().unwrap(); if !nested.inspect.is_noop() { let probe_kind = probe_kind(&r); diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 27efb582dafcf..8b838f3567c84 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -196,7 +196,7 @@ where Goal { param_env, predicate: ct }: Goal, ) -> QueryResultOrRerunNonErased { match ct.kind() { - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, _) + ty::ConstKind::Alias(ty::IsRigid::Yes, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { @@ -207,7 +207,7 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - ty::ConstKind::Unevaluated(ty::IsRigid::No, uv) => { + ty::ConstKind::Alias(ty::IsRigid::No, alias_const) => { // We never return `NoSolution` here as `evaluate_const` emits an // error itself when failing to evaluate, so emitting an additional fulfillment // error in that case is unnecessary noise. This may change in the future once @@ -216,7 +216,7 @@ where // FIXME(generic_const_exprs): Implement handling for generic // const expressions here. - if let Some(_normalized) = self.evaluate_const(param_env, uv)? { + if let Some(_normalized) = self.evaluate_const(param_env, alias_const)? { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) @@ -252,10 +252,10 @@ where .evaluate_added_goals_and_make_canonical_response(Certainty::Yes) .map_err(Into::into); } - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, uv) => { - uv.type_of(self.cx()).skip_norm_wip() + ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => { + alias_const.type_of(self.cx()).skip_norm_wip() } - ty::ConstKind::Unevaluated(ty::IsRigid::No, _) => unimplemented!( + ty::ConstKind::Alias(ty::IsRigid::No, _) => unimplemented!( "non-rigid unevaluated constant for compute_const_arg_has_type_goal: {ct:?}" ), ty::ConstKind::Expr(_) => unimplemented!( diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs index e0290dfbdebdf..3427044915f88 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs @@ -81,15 +81,10 @@ where None }, |ecx| { - ecx.probe(|&result| ProbeKind::RigidAlias { result }) - .enter(|this| { - this.structurally_instantiate_normalizes_to_term( - goal, - goal.predicate.alias, - ); - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - .map_err(Into::into) + ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { + this.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) }, ) } @@ -351,11 +346,9 @@ where GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous), )?; - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, + ); } // Outside of coherence, we treat the associated item as rigid instead. ty::TypingMode::Typeck { .. } @@ -367,11 +360,9 @@ where goal, goal.predicate.alias, ); - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::Yes, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Yes, + ); } }; } @@ -401,10 +392,10 @@ where // This is not the case here and we only prefer adding an ambiguous // nested goal for consistency. ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous))?; - return then(ecx, Certainty::Yes).map_err(Into::into); + return then(ecx, Certainty::Yes); } else { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return then(ecx, Certainty::Yes).map_err(Into::into); + return then(ecx, Certainty::Yes); } } else { return error_response(ecx, cx.delay_bug("missing item")); @@ -454,9 +445,9 @@ where c.into() } ty::AliasTermKind::ProjectionConst { .. } => { - let uv = ty::UnevaluatedConst::new( + let alias_const = ty::AliasConst::new( cx, - ty::UnevaluatedConstKind::Projection { + ty::AliasConstKind::Projection { def_id: target_item_def_id.into().try_into().unwrap(), }, target_args, @@ -465,14 +456,14 @@ where goal.param_env, goal.predicate.alias, goal.predicate.term, - uv, + alias_const, ); } kind => panic!("expected projection, found {kind:?}"), }; ecx.instantiate_normalizes_to_term(goal, term)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -572,7 +563,6 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) - .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( @@ -759,8 +749,9 @@ where // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't // exist. Instead, `Pointee` should be a supertrait of `Sized`. - let alias_bound_result = - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let alias_bound_result = ecx + .probe_builtin_trait_candidate(BuiltinImplSource::Misc) + .enter(|ecx| { let sized_predicate = ty::TraitRef::new( cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), @@ -769,12 +760,8 @@ where ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate))?; ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into())?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - - let alias_bound_result = match alias_bound_result.map_err_to_rerun()? { - Ok(i) => Ok(i), - Err(NoSolution) => Err(NoSolution), - }; + }) + .map_err_to_rerun()?; // In case the dummy alias-bound candidate does not apply, we instead treat this projection // as rigid. @@ -900,7 +887,6 @@ where // but that's already proven by the generator being WF. [], ) - .map_err(Into::into) } fn consider_builtin_fused_iterator_candidate( diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs index f1baa608798fa..fc0c34e63e187 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/anon_const.rs @@ -15,12 +15,12 @@ where &mut self, goal: Goal>, ) -> QueryResultOrRerunNonErased { - let uv = goal.predicate.projection_term.expect_ct(); + let alias_const = goal.predicate.projection_term.expect_ct(); self.evaluate_const_and_instantiate_projection_term( goal.param_env, goal.predicate.projection_term, goal.predicate.term, - uv, + alias_const, ) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 549d5d903c045..778826ba60aee 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use rustc_type_ir::data_structures::ensure_sufficient_stack; use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{ - AccessedOpaques, CanonicalInput, Certainty, NoSolution, NoSolutionOrRerunNonErased, QueryResult, + AccessedOpaques, CanonicalInput, Certainty, NoSolution, QueryResult, RerunResultExt, }; use rustc_type_ir::{Interner, MayBeErased, TypingMode}; @@ -141,17 +141,8 @@ where ) -> (QueryResult, AccessedOpaques) { ensure_sufficient_stack(|| { EvalCtxt::enter_canonical(cx, search_graph, input, inspect, |ecx, goal| { - let result = ecx.compute_goal(goal); - - // if we're in `RerunNonErased`, don't even bother with inspect, - // and immediately return - let result = match result { - Ok(i) => Ok(i), - Err(NoSolutionOrRerunNonErased::NoSolution(NoSolution)) => Err(NoSolution), - Err(NoSolutionOrRerunNonErased::RerunNonErased(e)) => { - return Err(e.into()); - } - }; + // if we're in `RerunNonErased`, don't even bother with inspect, and immediately return + let result = ecx.compute_goal(goal).map_err_to_rerun()?; ecx.inspect.query_result(result); result.map_err(Into::into) diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 79e0af72710fb..09d96e3b3a143 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -119,7 +119,7 @@ where .map(|pred| goal.with(cx, pred)), )?; - then(ecx, maximal_certainty).map_err(Into::into) + then(ecx, maximal_certainty) }) } @@ -399,7 +399,6 @@ where pred, [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))], ) - .map_err(Into::into) } fn consider_builtin_async_fn_trait_candidates( @@ -450,7 +449,6 @@ where .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred))) .map(|goal| (GoalSource::ImplWhereBound, goal)), ) - .map_err(Into::into) } fn consider_builtin_async_fn_kind_helper_candidate( @@ -696,7 +694,7 @@ where goal.predicate.trait_ref.args.type_at(1), assume, )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(certainty) }, ) } @@ -1085,7 +1083,6 @@ where ecx.try_evaluate_added_goals() }, ) - .map_err(Into::into) }) .is_ok() }; @@ -1124,11 +1121,9 @@ where return Err(NoSolution.into()); }; if matching_projections.next().is_some() { - return ecx - .evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ) - .map_err(Into::into); + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); } ecx.enter_forall_with_assumptions( target_projection, @@ -1156,7 +1151,7 @@ where Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)), )?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1251,13 +1246,9 @@ where let self_ty = goal.predicate.self_ty(); let check_impls = || { let mut disqualifying_impl = None; - self.cx().for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| { - disqualifying_impl = Some(impl_def_id); - }, - ); + self.cx().for_each_relevant_impl(goal.predicate.trait_ref, |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }); if let Some(def_id) = disqualifying_impl { trace!(?def_id, ?goal, "disqualified auto-trait implementation"); // No need to actually consider the candidate here, diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index fbceb185c190c..7040fc60e4f1a 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -602,7 +602,9 @@ impl<'a> Parser<'a> { // Look for usages of '=>' where '>=' was probably intended if self.token == token::FatArrow && expected.iter().any(|tok| matches!(tok, TokenType::Operator | TokenType::Le)) - && !expected.iter().any(|tok| matches!(tok, TokenType::FatArrow | TokenType::Comma)) + && !expected + .iter() + .any(|tok| matches!(tok, TokenType::FatArrow | TokenType::CloseBrace)) { err.span_suggestion( self.token.span, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 701f39e7c0e39..7ae6005e9bc98 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -204,9 +204,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecated { span: attr_span, .. } => { self.check_deprecated(hir_id, *attr_span, target) } - AttributeKind::TargetFeature { attr_span, .. } => { - self.check_target_feature(hir_id, *attr_span, target, attrs) - } AttributeKind::RustcDumpObjectLifetimeDefaults => { self.check_dump_object_lifetime_defaults(hir_id); } @@ -220,7 +217,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::NonExhaustive(attr_span) => { self.check_non_exhaustive(*attr_span, span, target, item) } - &AttributeKind::FfiPure(attr_span) => self.check_ffi_pure(attr_span, attrs), AttributeKind::MayDangle(attr_span) => self.check_may_dangle(hir_id, *attr_span), AttributeKind::Link(_, attr_span) => self.check_link(hir_id, *attr_span, target), AttributeKind::MacroExport { span, .. } => { @@ -272,6 +268,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ExportStable => (), AttributeKind::Feature(..) => (), AttributeKind::FfiConst => (), + AttributeKind::FfiPure(..) => (), AttributeKind::Fundamental => (), AttributeKind::Ignore { .. } => (), AttributeKind::InstructionSet(..) => (), @@ -406,6 +403,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ShouldPanic { .. } => (), AttributeKind::Splat(..) => (), AttributeKind::Stability { .. } => (), + AttributeKind::TargetFeature { .. } => {} AttributeKind::TestRunner(..) => (), AttributeKind::ThreadLocal => (), AttributeKind::TypeLengthLimit { .. } => (), @@ -799,37 +797,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the `#[target_feature]` attribute on `item` is valid. - fn check_target_feature( - &self, - hir_id: HirId, - attr_span: Span, - target: Target, - attrs: &[Attribute], - ) { - match target { - Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Fn => { - // `#[target_feature]` is not allowed in lang items. - if let Some(lang_item) = find_attr!(attrs, Lang(lang ) => lang) - // Calling functions with `#[target_feature]` is - // not unsafe on WASM, see #84988 - && !self.tcx.sess.target.is_like_wasm - && !self.tcx.sess.opts.actually_rustdoc - { - let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); - - self.dcx().emit_err(diagnostics::LangItemWithTargetFeature { - attr_span, - name: lang_item.name(), - sig_span: sig.span, - }); - } - } - _ => {} - } - } - fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) { if let Some(location) = match target { Target::AssocTy => { @@ -1113,13 +1080,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) { - if find_attr!(attrs, FfiConst) { - // `#[ffi_const]` functions cannot be `#[ffi_pure]` - self.dcx().emit_err(diagnostics::BothFfiConstAndPure { attr_span }); - } - } - /// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl. fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) { let hir::Node::GenericParam( diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index a4ba014d2f6aa..92562cc462b87 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -152,13 +152,6 @@ pub(crate) struct DocMaskedNotExternCrateSelf { pub item_span: Span, } -#[derive(Diagnostic)] -#[diag("`#[ffi_const]` function cannot be `#[ffi_pure]`", code = E0757)] -pub(crate) struct BothFfiConstAndPure { - #[primary_span] - pub attr_span: Span, -} - #[derive(Diagnostic)] #[diag("`#[optimize(none)]` cannot be used with `#[inline]` attributes")] pub(crate) struct BothOptimizeNoneAndInline { @@ -369,26 +362,6 @@ pub(crate) struct LangItemWithTrackCaller { pub sig_span: Span, } -#[derive(Diagnostic)] -#[diag( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" -)] -pub(crate) struct LangItemWithTargetFeature { - #[primary_span] - pub attr_span: Span, - pub name: Symbol, - #[label( - "{$name -> - [panic_impl] `#[panic_handler]` - *[other] `{$name}` lang item - } function is not allowed to have `#[target_feature]`" - )] - pub sig_span: Span, -} - #[derive(Diagnostic)] #[diag("duplicate diagnostic item in crate `{$crate_name}`: `{$name}`")] pub(crate) struct DuplicateDiagnosticItemInCrate { diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 1686b4d595933..a5a835d4a78c3 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -27,6 +27,11 @@ pub(crate) enum Duplicate { CrateDepends, } +enum CollectWeak { + Allowed, + Ignore, +} + struct LanguageItemCollector<'ast, 'tcx> { items: LanguageItems, tcx: TyCtxt<'tcx>, @@ -58,6 +63,7 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { attrs: &'ast [ast::Attribute], item_span: Span, generics: Option<&'ast ast::Generics>, + collect_weak: CollectWeak, ) { if let Some((name, attr_span)) = extract_ast(attrs) { match LangItem::from_name(name) { @@ -69,14 +75,18 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> { .delayed_bug("lang item target is checked in attribute parser"); return; } - self.collect_item_extended( - lang_item, - def_id, - item_span, - attr_span, - generics, - actual_target, - ); + // Weak lang items are handled separately + // Weak only lang items are always handled here + if !lang_item.is_weak() || matches!(collect_weak, CollectWeak::Allowed) { + self.collect_item_extended( + lang_item, + def_id, + item_span, + attr_span, + generics, + actual_target, + ); + } } // Unknown lang item. _ => { @@ -295,6 +305,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &i.attrs, i.span, i.opt_generics(), + CollectWeak::Allowed, ); let parent_item = self.parent_item.replace(i); @@ -302,6 +313,17 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { self.parent_item = parent_item; } + fn visit_foreign_item(&mut self, i: &'ast ast::ForeignItem) { + self.check_for_lang( + Target::Fn, + self.resolver.owners[&i.id].def_id, + &i.attrs, + i.span, + None, + CollectWeak::Ignore, + ); + } + fn visit_variant(&mut self, variant: &'ast ast::Variant) { self.check_for_lang( Target::Variant, @@ -309,6 +331,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { &variant.attrs, variant.span, None, + CollectWeak::Allowed, ); } @@ -342,7 +365,14 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { } }; - self.check_for_lang(target, self.resolver.owners[&i.id].def_id, &i.attrs, i.span, generics); + self.check_for_lang( + target, + self.resolver.owners[&i.id].def_id, + &i.attrs, + i.span, + generics, + CollectWeak::Allowed, + ); visit::walk_assoc_item(self, i, ctxt); } diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 81b32f4da10f6..181498443073d 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -312,6 +312,7 @@ impl RustcInternal for FnSig { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { + // FIXME(splat): When `#[splat]` is complete (or stable), add splatted to the public FnSig let fn_sig_kind = rustc_ty::FnSigKind::default() .set_abi(self.abi.internal(tables, tcx)) .set_safety(self.safety.internal(tables, tcx)) diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 4d92660a1b8bb..2f280ef9fe4c0 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -552,16 +552,14 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { } } ty::ConstKind::Param(param) => crate::ty::TyConstKind::Param(param.stable(tables, cx)), - ty::ConstKind::Unevaluated(_, uv) => { - let Some(def_id) = uv.kind.opt_def_id() else { - // FIXME: implement (both AliasTy and UnevaluatedConst will be needing this soon) - panic!( - "non-defid unevaluated constants are not supported by rustc_public at the moment" - ) + ty::ConstKind::Alias(_, alias_const) => { + let Some(def_id) = alias_const.kind.opt_def_id() else { + // FIXME: implement (both AliasTy and AliasConst will be needing this soon) + panic!("non-defid alias consts are not supported by rustc_public at the moment") }; crate::ty::TyConstKind::Unevaluated( tables.const_def(def_id), - uv.args.stable(tables, cx), + alias_const.args.stable(tables, cx), ) } ty::ConstKind::Error(_) => unreachable!(), diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index d4156ade79bd7..7e7ed3ab3b525 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -105,7 +105,7 @@ pub(crate) fn variances_of<'tcx>( err: Diag<'_>, ) -> &'tcx [ty::Variance] { let _guar = err.delay_as_bug(); - let n = tcx.generics_of(def_id).own_params.len(); + let n = tcx.generics_of(def_id).count(); tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n)) } diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index cb52c21c87ae9..a1d61d0d9f2f0 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start indexmap = "2.4.0" -itertools = "0.12" +itertools = "0.15" pulldown-cmark = { version = "0.11", features = [ "html", ], default-features = false } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 32e4bb9262fa8..4c5c591ad94e3 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -10,9 +10,9 @@ // // Checking for unused imports is split into three steps: // -// - `UnusedImportCheckVisitor` walks the AST to find all the unused imports -// inside of `UseTree`s, recording their `NodeId`s and grouping them by -// the parent `use` item +// - `UnusedImportCheckVisitor` visits the `use` items collected during late +// resolution to find all the unused imports inside of `UseTree`s, recording +// their `NodeId`s and grouping them by the parent `use` item // // - `calc_unused_spans` then walks over all the `use` items marked in the // previous step to collect the spans associated with the `NodeId`s and to @@ -410,7 +410,7 @@ fn calc_unused_spans( } impl Resolver<'_, '_> { - pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { + pub(crate) fn check_unused(&mut self, use_items: Vec<&ast::Item>) { let tcx = self.tcx; let mut maybe_unused_extern_crates = FxHashMap::default(); @@ -465,7 +465,10 @@ impl Resolver<'_, '_> { base_id: ast::DUMMY_NODE_ID, item_span: DUMMY_SP, }; - visit::walk_crate(&mut visitor, krate); + // `use_items` is in crate DFS order, so diagnostics and side effects are unchanged. + for item in use_items { + visitor.visit_item(item); + } visitor.report_unused_extern_crate_items(maybe_unused_extern_crates); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a5049339382f7..de3f8c380fc44 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -17,8 +17,7 @@ use tracing::{debug, instrument}; use crate::macros::MacroRulesScopeRef; use crate::{ - ConstArgContext, ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, - with_owner_tables, + ImplTraitContext, InvocationParent, ParentScope, Resolver, with_owner, with_owner_tables, }; pub(crate) fn collect_definitions<'ra>( @@ -115,12 +114,6 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.invocation_parent.impl_trait_context = orig_itc; } - fn with_const_arg(&mut self, ctxt: ConstArgContext, f: F) { - let orig = mem::replace(&mut self.invocation_parent.const_arg_context, ctxt); - f(self); - self.invocation_parent.const_arg_context = orig; - } - fn collect_field(&mut self, field: &'a FieldDef, index: Option) { let index = |this: &Self| { index.unwrap_or_else(|| { @@ -430,6 +423,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an + // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. + // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so // to avoid affecting stable we have to feature gate the not creating // anon consts @@ -441,16 +437,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => self.with_const_arg(ConstArgContext::Direct, |this| { - visit::walk_anon_const(this, constant); - }), + MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), MgcaDisambiguation::AnonConst => { - self.with_const_arg(ConstArgContext::NonDirect, |this| { - let parent = this - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - this.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - }) + let parent = self + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } }; } @@ -459,60 +451,28 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_expr(&mut self, expr: &'a Expr) { debug!(?self.invocation_parent); - let parent_def = match &expr.kind { + match &expr.kind { ExprKind::MacCall(..) => { self.visit_macro_invoc(expr.id); self.visit_invoc(expr.id); - return; } ExprKind::Closure(..) | ExprKind::Gen(..) => { - self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id() + let def = self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id(); + self.with_parent(def, |this| visit::walk_expr(this, expr)); } ExprKind::ConstBlock(constant) => { - // Under `min_generic_const_args` a `const { }` block sometimes - // corresponds to an anon const rather than an inline const. - let def_kind = match self.invocation_parent.const_arg_context { - ConstArgContext::Direct => DefKind::AnonConst, - ConstArgContext::NonDirect => DefKind::InlineConst, - }; - - return self.with_const_arg(ConstArgContext::NonDirect, |this| { - for attr in &expr.attrs { - visit::walk_attribute(this, attr); - } - - let def = - this.create_def(constant.id, None, def_kind, constant.value.span).def_id(); - this.with_parent(def, |this| visit::walk_anon_const(this, constant)); - }); - } - - // Avoid overwriting `const_arg_context` as we may want to treat const blocks - // as being anon consts if we are inside a const argument. - ExprKind::Struct(_) | ExprKind::Call(..) | ExprKind::Tup(..) | ExprKind::Array(..) => { - return visit::walk_expr(self, expr); - } - // FIXME(mgca): we may want to handle block labels in some manner - ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() => match stmt.kind { - // FIXME(mgca): this probably means that mac calls that expand - // to semi'd const blocks are handled differently to just writing - // out a semi'd const block. - StmtKind::Expr(..) | StmtKind::MacCall(..) => return visit::walk_expr(self, expr), - - // Fallback to normal behaviour - StmtKind::Let(..) | StmtKind::Item(..) | StmtKind::Semi(..) | StmtKind::Empty => { - self.invocation_parent.parent_def + for attr in &expr.attrs { + visit::walk_attribute(self, attr); } - }, - _ => self.invocation_parent.parent_def, - }; - - self.with_const_arg(ConstArgContext::NonDirect, |this| { - // Note in some cases the `parent_def` here may be the existing parent - // and this is actually a no-op `with_parent` call. - this.with_parent(parent_def, |this| visit::walk_expr(this, expr)) - }) + let def = self + .create_def(constant.id, None, DefKind::InlineConst, constant.value.span) + .def_id(); + // use specifically walk_anon_const, not walk_expr, to skip self.visit_anon_const + self.with_parent(def, |this| visit::walk_anon_const(this, constant)); + } + _ => visit::walk_expr(self, expr), + } } fn visit_ty(&mut self, ty: &'a Ty) { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 9d08e8b98cb31..f5b38e750a544 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -16,7 +16,7 @@ use tracing::{debug, instrument}; use crate::diagnostics::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; use crate::hygiene::Macros20NormalizedSyntaxContext; -use crate::imports::{Import, NameResolution}; +use crate::imports::{Import, NameResolution, cycle_detection}; use crate::late::{ ConstantHasGenerics, DiagMetadata, NoConstantGenericsReason, PathSource, Rib, RibKind, }; @@ -1148,13 +1148,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, ControlFlow> { let key = BindingKey::new(ident, ns); - // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding - // doesn't need to be mutable. It will fail when there is a cycle of imports, and without - // the exclusive access infinite recursion will crash the compiler with stack overflow. - let resolution = &*self - .resolution_or_default(module.to_module(), key, orig_ident_span) - .try_borrow_mut_unchecked() - .map_err(|_| ControlFlow::Continue(Determined))?; + let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span); + let resolution = resolution_ref.borrow(); let binding = resolution.non_glob_decl.filter(|b| Some(*b) != ignore_decl); @@ -1175,6 +1170,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } + // We need to detect resolution cycles to avoid infinite recursion. The guard ensures + // the resolution is removed when this resolve call ends. + let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref) + .map_err(|_| ControlFlow::Continue(Determined))?; + // Check if one of single imports can still define the name, block if it can. if self.reborrow().single_import_can_define_name( &resolution, @@ -1210,13 +1210,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_import: Option>, ) -> Result, ControlFlow> { let key = BindingKey::new(ident, ns); - // `try_borrow_mut` is required to ensure exclusive access, even if the resulting binding - // doesn't need to be mutable. It will fail when there is a cycle of imports, and without - // the exclusive access infinite recursion will crash the compiler with stack overflow. - let resolution = &*self - .resolution_or_default(module.to_module(), key, orig_ident_span) - .try_borrow_mut_unchecked() - .map_err(|_| ControlFlow::Continue(Determined))?; + let resolution_ref = self.resolution_or_default(module.to_module(), key, orig_ident_span); + let resolution = resolution_ref.borrow(); let binding = resolution.glob_decl.filter(|b| Some(*b) != ignore_decl); @@ -1231,6 +1226,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ); } + // We need to detect resolution cycles to avoid infinite recursion. The guard ensures + // the resolution is removed when this resolve call ends. + let _cycle_guard = cycle_detection::enter_cycle_detector(resolution_ref) + .map_err(|_| ControlFlow::Continue(Determined))?; + // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. if self.reborrow().single_import_can_define_name( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index a3cb526e60a87..08807616d5097 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -32,7 +32,7 @@ use crate::diagnostics::{ ConsiderMarkingAsPubCrate, }; use crate::error_helper::{OnUnknownData, Suggestion}; -use crate::ref_mut::CmCell; +use crate::ref_mut::{CmCell, CmRefCell}; use crate::{ AmbiguityError, BindingKey, CmResolver, Decl, DeclData, DeclKind, Determinacy, Finalize, IdentKey, ImportSuggestion, ImportSummary, LocalModule, ModuleOrUniformRoot, ParentScope, @@ -292,6 +292,8 @@ pub(crate) struct NameResolution<'ra> { pub orig_ident_span: Span, } +pub(crate) type NameResolutionRef<'ra> = Interned<'ra, CmRefCell>>; + impl<'ra> NameResolution<'ra> { pub(crate) fn new(orig_ident_span: Span) -> Self { NameResolution { single_imports: FxIndexSet::default(), orig_ident_span, .. } @@ -320,6 +322,57 @@ impl<'ra> NameResolution<'ra> { } } +// module to keep the TLS private and only accessible through the function `enter_cycle_detector`. +pub(crate) mod cycle_detection { + use std::ptr; + + use crate::CacheRefCell; + use crate::imports::NameResolutionRef; + + thread_local!( + /// During import resolution, recursive imports can form cycles. + /// This set stores the active resolution stack for the current thread. + /// So it's essentially a recursion stack. + /// + /// The key is the interned address of a `RefCell>` allocated + /// in the `Resolver Arenas` (lifetime `'ra`), it is thus stable and allows casting + /// to a `*const ()` for comparison. This is done because we can't use lifetimes + /// other than `'static` in thread local storage. + static ACTIVE_RESOLUTIONS: CacheRefCell> = Default::default(); + ); + + pub(crate) struct ActiveResolutionGuard { + key: *const (), + } + + impl Drop for ActiveResolutionGuard { + fn drop(&mut self) { + ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| { + // Only this guard is allowed to remove this key. + assert!( + Some(self.key) == ar.pop(), + "This guard should be the only one removing this key" + ); + }); + } + } + + /// Returns `Err(())` if a cycle is detected, otherwise this returns a + /// guard that will remove the resolution when dropped. + pub(crate) fn enter_cycle_detector<'ra>( + resolution: NameResolutionRef<'ra>, + ) -> Result { + let key = ptr::from_ref(resolution.0).cast::<()>(); + ACTIVE_RESOLUTIONS.with_borrow_mut(|ar| { + if ar.contains(&key) { + return Err(()); + } + ar.push(key); + Ok(ActiveResolutionGuard { key }) + }) + } +} + /// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved /// import errors within the same use tree into a single diagnostic. #[derive(Debug, Clone)] @@ -640,6 +693,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (binding, t) = { let resolution = &mut *self .resolution_or_default(module.to_module(), key, orig_ident_span) + .0 .borrow_mut(self); let old_decl = resolution.determined_decl(); let old_vis = old_decl.map(|d| d.vis()); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 479af1582fd6f..c94f2f3ffb85d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5623,12 +5623,15 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of -/// lifetime generic parameters and function parameters. -struct ItemInfoCollector<'a, 'ra, 'tcx> { +/// lifetime generic parameters and function parameters. Also collects all `use` and +/// `extern crate` items so that `check_unused` doesn't need to walk the crate again. +struct ItemInfoCollector<'a, 'ast, 'ra, 'tcx> { r: &'a mut Resolver<'ra, 'tcx>, + /// All `use` and `extern crate` items, in the order in which they are visited. + use_items: Vec<&'ast Item>, } -impl ItemInfoCollector<'_, '_, '_> { +impl ItemInfoCollector<'_, '_, '_, '_> { fn collect_fn_info(&mut self, decl: &FnDecl, id: NodeId) { self.r .delegation_fn_sigs @@ -5662,7 +5665,7 @@ fn required_generic_args_suggestion(generics: &ast::Generics) -> Option if required.is_empty() { None } else { Some(format!("<{}>", required.join(", "))) } } -impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { +impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, 'ast, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { ItemKind::TyAlias(TyAlias { generics, .. }) @@ -5695,11 +5698,13 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } } + ItemKind::Use(..) | ItemKind::ExternCrate(..) => { + self.use_items.push(item); + } + ItemKind::Mod(..) | ItemKind::Static(..) | ItemKind::ConstBlock(..) - | ItemKind::Use(..) - | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(..) | ItemKind::MacCall(..) @@ -5730,9 +5735,12 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { } impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) { + /// Returns the `use` and `extern crate` items of the crate, for use by `check_unused`. + pub(crate) fn late_resolve_crate<'ast>(&mut self, krate: &'ast Crate) -> Vec<&'ast Item> { with_owner(self, CRATE_NODE_ID, |this| { - visit::walk_crate(&mut ItemInfoCollector { r: this }, krate); + let mut info_collector = ItemInfoCollector { r: this, use_items: Vec::new() }; + visit::walk_crate(&mut info_collector, krate); + let use_items = info_collector.use_items; let mut late_resolution_visitor = LateResolutionVisitor::new(this); late_resolution_visitor .resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID)); @@ -5745,6 +5753,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { crate::diagnostics::UnusedLabel, ); } + use_items }) } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 1f1884ea0cbe2..1b12f9c4a6f7e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -77,6 +77,7 @@ use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use crate::error_helper::OnUnknownData; +use crate::imports::NameResolutionRef; use crate::ref_mut::{CmCell, CmRefCell}; mod build_reduced_graph; @@ -186,7 +187,6 @@ struct InvocationParent { parent_def: LocalDefId, impl_trait_context: ImplTraitContext, in_attr: bool, - const_arg_context: ConstArgContext, owner: NodeId, } @@ -195,7 +195,6 @@ impl InvocationParent { parent_def: CRATE_DEF_ID, impl_trait_context: ImplTraitContext::Existential, in_attr: false, - const_arg_context: ConstArgContext::NonDirect, owner: CRATE_NODE_ID, }; } @@ -207,13 +206,6 @@ enum ImplTraitContext { InBinding, } -#[derive(Copy, Clone, Debug)] -enum ConstArgContext { - Direct, - /// Either inside of an `AnonConst` or not inside a const argument at all. - NonDirect, -} - /// Used for tracking import use types which will be used for redundant import checking. /// /// ### Used::Scope Example @@ -639,7 +631,7 @@ impl BindingKey { } } -type Resolutions<'ra> = CmRefCell>>>; +type Resolutions<'ra> = CmRefCell>>; /// One node in the tree of modules. /// @@ -1597,11 +1589,10 @@ impl<'ra> ResolverArenas<'ra> { fn alloc_import(&'ra self, import: ImportData<'ra>) -> Import<'ra> { Interned::new_unchecked(self.imports.alloc(import)) } - fn alloc_name_resolution( - &'ra self, - orig_ident_span: Span, - ) -> &'ra CmRefCell> { - self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))) + fn alloc_name_resolution(&'ra self, orig_ident_span: Span) -> NameResolutionRef<'ra> { + Interned::new_unchecked( + self.name_resolutions.alloc(CmRefCell::new(NameResolution::new(orig_ident_span))), + ) } fn alloc_macro_rules_scope(&'ra self, scope: MacroRulesScope<'ra>) -> MacroRulesScopeRef<'ra> { self.dropless.alloc(CacheCell::new(scope)) @@ -2082,17 +2073,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.tcx .sess .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); - self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); + let use_items = + self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); - self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate)); + self.tcx.sess.time("resolve_check_unused", || self.check_unused(use_items)); self.tcx.sess.time("resolve_report_errors", || self.report_errors(krate)); self.tcx .sess .time("resolve_postprocess", || self.cstore_mut().postprocess(self.tcx, krate)); }); - // Make sure we don't mutate the cstore from here on. - self.tcx.untracked().cstore.freeze(); + // Don't mutate the cstore or stable crate id map from here on. + self.tcx.untracked().freeze_cstore(); } fn traits_in_scope( @@ -2212,7 +2204,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: Module<'ra>, key: BindingKey, ) -> Option>> { - self.resolutions(module).borrow().get(&key).map(|resolution| resolution.borrow()) + self.resolutions(module).borrow().get(&key).map(|resolution| resolution.0.borrow()) } fn resolution_or_default( @@ -2220,8 +2212,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { module: Module<'ra>, key: BindingKey, orig_ident_span: Span, - ) -> &'ra CmRefCell> { - self.resolutions(module) + ) -> NameResolutionRef<'ra> { + *self + .resolutions(module) .borrow_mut_unchecked() .entry(key) .or_insert_with(|| self.arenas.alloc_name_resolution(orig_ident_span)) @@ -2832,8 +2825,6 @@ type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; // parallel name resolution. use std::cell::{Cell as CacheCell, RefCell as CacheRefCell}; -// FIXME: `*_unchecked` methods in the module below should be eliminated in the process -// of migration to parallel name resolution. mod ref_mut { use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; use std::fmt; @@ -2941,6 +2932,8 @@ mod ref_mut { } #[track_caller] + // FIXME: this should be eliminated in the process of migration + // to parallel name resolution. pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { self.0.borrow_mut() } @@ -2953,10 +2946,6 @@ mod ref_mut { self.0.borrow_mut() } - pub(crate) fn try_borrow_mut_unchecked(&self) -> Result, BorrowMutError> { - self.0.try_borrow_mut() - } - #[track_caller] pub(crate) fn try_borrow_mut<'ra, 'tcx>( &self, diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 39fe9c80923ec..94237132bd393 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -224,3 +224,13 @@ pub struct Untracked { /// The interned [StableCrateId]s. pub stable_crate_ids: FreezeLock, } + +impl Untracked { + /// Freezes the cstore and, with it, the `StableCrateId` map, making reads of + /// both lock-free. The cstore is frozen first so any in-flight crate loading + /// (which writes the map) finishes before the map is frozen. + pub fn freeze_cstore(&self) { + self.cstore.freeze(); + self.stable_crate_ids.freeze(); + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 827e33dcd6632..c75da250efd5f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -515,6 +515,7 @@ symbols! { await_macro, backchain, backend_repr, + bcmp_fn, begin_panic, bench, bevy_ecs, @@ -1141,6 +1142,7 @@ symbols! { irrefutable_let_patterns, is, is_auto, + is_splatted, is_val_statically_known, isa_attribute, isize, @@ -1280,7 +1282,11 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, + memcmp_fn, + memcpy_fn, + memmove_fn, memory, + memset_fn, memtag, message, meta, @@ -1996,6 +2002,7 @@ symbols! { speed, spirv, splat, + splatted_index, spotlight, sqrtf16, sqrtf32, @@ -2036,6 +2043,7 @@ symbols! { strict_provenance_lints, string_deref_patterns, stringify, + strlen_fn, struct_field_attributes, struct_inherit, struct_variant, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index c3b02eb46ba1e..d61437212a266 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -92,7 +92,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mono::{InstantiationMode, MonoItem}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Instance, TyCtxt}; +use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt}; use rustc_session::config::SymbolManglingVersion; use tracing::debug; @@ -149,29 +149,22 @@ pub fn typeid_for_trait_ref<'tcx>( v0::mangle_typeid_for_trait_ref(tcx, trait_ref) } -/// Computes the symbol name for the given instance. This function will call -/// `compute_instantiating_crate` if it needs to factor the instantiating crate -/// into the symbol name. -fn compute_symbol_name<'tcx>( +pub fn symbol_name_from_attrs<'tcx>( tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - compute_instantiating_crate: impl FnOnce() -> CrateNum, -) -> String { - let def_id = instance.def_id(); - let args = instance.args; - - debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + instance_kind: InstanceKind<'tcx>, +) -> Option { + let def_id = instance_kind.def_id(); if let Some(def_id) = def_id.as_local() { if tcx.proc_macro_decls_static(()) == Some(def_id) { let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); - return rustc_session::generate_proc_macro_decls_symbol(stable_crate_id); + return Some(rustc_session::generate_proc_macro_decls_symbol(stable_crate_id)); } } // FIXME(eddyb) Precompute a custom symbol name based on attributes. let attrs = if tcx.def_kind(def_id).has_codegen_attrs() { - &tcx.codegen_instance_attrs(instance.def) + &tcx.codegen_instance_attrs(instance_kind) } else { CodegenFnAttrs::EMPTY }; @@ -197,7 +190,7 @@ fn compute_symbol_name<'tcx>( // legacy symbol mangling scheme. let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) }; - return v0::mangle_internal_symbol(tcx, name.as_str()); + return Some(v0::mangle_internal_symbol(tcx, name.as_str())); } let wasm_import_module_exception_force_mangling = { @@ -225,15 +218,35 @@ fn compute_symbol_name<'tcx>( if !wasm_import_module_exception_force_mangling { if let Some(name) = attrs.symbol_name { // Use provided name - return name.to_string(); + return Some(name.to_string()); } if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { // Don't mangle - return tcx.item_name(def_id).to_string(); + return Some(tcx.item_name(def_id).to_string()); } } + None +} + +/// Computes the symbol name for the given instance. This function will call +/// `compute_instantiating_crate` if it needs to factor the instantiating crate +/// into the symbol name. +fn compute_symbol_name<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + compute_instantiating_crate: impl FnOnce() -> CrateNum, +) -> String { + let def_id = instance.def_id(); + let args = instance.args; + + debug!("symbol_name(def_id={:?}, args={:?})", def_id, args); + + if let Some(symbol) = symbol_name_from_attrs(tcx, instance.def) { + return symbol; + } + // If we're dealing with an instance of a function that's inlined from // another crate but we're marking it as globally shared to our // compilation (aka we're not making an internal copy in each of our diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index ed4489636f2d8..b26545fa2b4b0 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -696,13 +696,13 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { return Ok(()); } - // We may still encounter unevaluated consts due to the printing + // We may still encounter alias consts due to the printing // logic sometimes passing identity-substituted impl headers. - ty::ConstKind::Unevaluated(_, ty::UnevaluatedConst { kind, args, .. }) => match kind { - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => match kind { + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { return self.print_def_path(def_id, args); } }, diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 3ce76d78e6c8a..0cd9128762cbc 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_trait_selection/src/diagnostics.rs b/compiler/rustc_trait_selection/src/diagnostics.rs index 662480587fdd2..ba89b0987659e 100644 --- a/compiler/rustc_trait_selection/src/diagnostics.rs +++ b/compiler/rustc_trait_selection/src/diagnostics.rs @@ -21,11 +21,11 @@ use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlig pub(crate) mod note_and_explain; #[derive(Diagnostic)] -#[diag("unable to construct a constant value for the unevaluated constant {$unevaluated}")] +#[diag("unable to construct a constant value for the alias const {$alias_const}")] pub(crate) struct UnableToConstructConstantValue<'a> { #[primary_span] pub span: Span, - pub unevaluated: ty::UnevaluatedConst<'a>, + pub alias_const: ty::AliasConst<'a>, } pub(crate) struct NegativePositiveConflict<'tcx> { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 3aaf17095a6ce..9822e8cdef8b2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -823,9 +823,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); + let splatted_arg_index1 = sig1.splatted().map(usize::from); + let splatted_arg_index2 = sig2.splatted().map(usize::from); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { self.push_comma(&mut values.0, &mut values.1, i); + if Some(i) == splatted_arg_index1 { + values.0.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.0.push_normal(" "); + } + if Some(i) == splatted_arg_index2 { + values.1.push("#[splat]", splatted_arg_index1 != splatted_arg_index2); + values.1.push_normal(" "); + } let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 333f6f01c0ef1..624aef89911f7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1079,9 +1079,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { } } GenericArgKind::Const(ct) => { - if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) { + if matches!(ct.kind(), ty::ConstKind::Alias(..)) { // You can't write the generic arguments for - // unevaluated constants. + // alias constants. walker.skip_current_subtree(); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d85a9c9d57ef8..973a088a7f5d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -3769,12 +3769,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, alias_const) => { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); - let const_span = uv.kind.def_span(self.tcx); + let const_span = alias_const.kind.def_span(self.tcx); - let const_ty = uv.type_of(self.tcx).skip_norm_wip(); + let const_ty = alias_const.type_of(self.tcx).skip_norm_wip(); let cast = if const_ty != self.tcx.types.usize { " as usize" } else { "" }; let msg = "try adding a `where` bound"; if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(const_span) { @@ -3812,7 +3812,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Ok(err) } _ => { - bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + bug!("const evaluatable failed for non-alias const `{ct:?}`"); } }, _ => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index d7dfce166742b..3fdb1d6defdf6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2556,6 +2556,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let mut span = obligation.cause.span; + let mut is_async_fn_return = false; if let DefKind::Closure = self.tcx.def_kind(obligation.cause.body_id) && let parent = self.tcx.local_parent(obligation.cause.body_id) && let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(parent) @@ -2571,10 +2572,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // and // async fn foo() -> dyn Display Box span = fn_sig.decl.output.span(); + is_async_fn_return = true; err.span(span); } let body = self.tcx.hir_body_owned_by(obligation.cause.body_id); + if !is_async_fn_return + && let Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = + self.tcx.hir_node_by_def_id(obligation.cause.body_id) + && matches!(closure.fn_decl.output, hir::FnRetTy::DefaultReturn(_)) + { + return true; + } + let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); @@ -4962,6 +4972,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let fn_sig @ ty::FnSig { .. } = fn_ty.fn_sig(tcx).skip_binder() + // FIXME(splat): this might need to change if the Fn* traits start using/supporting splat && fn_sig.abi() == ExternAbi::Rust && !fn_sig.c_variadic() && fn_sig.safety() == hir::Safety::Safe diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f907b4ba19122..e0877829b25f3 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -218,9 +218,9 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn evaluate_const( &self, param_env: ty::ParamEnv<'tcx>, - uv: ty::UnevaluatedConst<'tcx>, + alias_const: ty::AliasConst<'tcx>, ) -> Option> { - let ct = ty::Const::new_unevaluated(self.tcx, ty::IsRigid::No, uv); + let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const); match crate::traits::try_evaluate_const(&self.0, ct, param_env) { Ok(ct) => Some(ct), diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 0d3e2500d0e05..098dcfb30a7b6 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -34,7 +34,9 @@ pub(super) fn fulfillment_error_for_no_solution<'tcx>( } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { let ct_ty = match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } ty::ConstKind::Param(param_ct) => { param_ct.find_const_ty_from_env(obligation.param_env) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 56c89773a6285..a1a7fdc6f6b82 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -146,18 +146,21 @@ impl<'me, 'tcx> TypeFolder> for ReplaceAliasWithInfer<'me, 'tcx> { } let ct = ct.super_fold_with(self); - let ty::ConstKind::Unevaluated(orig_is_rigid, uv) = ct.kind() else { return ct }; + let ty::ConstKind::Alias(orig_is_rigid, alias_const) = ct.kind() else { return ct }; if !self.cx().renormalize_rigid_aliases() && orig_is_rigid == ty::IsRigid::Yes { return ct; } if ct.has_escaping_bound_vars() { - let (replaced, ..) = - BoundVarReplacer::replace_bound_vars(self.at.infcx, &mut self.universes, uv); + let (replaced, ..) = BoundVarReplacer::replace_bound_vars( + self.at.infcx, + &mut self.universes, + alias_const, + ); let _ = self.term_to_infer(replaced.into()); ct } else { - self.term_to_infer(uv.into()).expect_const() + self.term_to_infer(alias_const.into()).expect_const() } } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 6d9c11789f2b7..a39f498e36fd8 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -774,15 +774,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_, unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { let ct = super::try_evaluate_const(selcx.infcx, c, obligation.param_env); if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct { - let span = unevaluated.kind.def_span(self.tcx); + let span = alias_const.kind.def_span(self.tcx); self.tcx .dcx() - .emit_err(UnableToConstructConstantValue { span, unevaluated }); + .emit_err(UnableToConstructConstantValue { span, alias_const }); } ct diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 16009e5295f27..4b54f9c3a2c26 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -30,7 +30,7 @@ pub fn is_const_evaluatable<'tcx>( ) -> Result<(), NotConstEvaluatable> { let tcx = infcx.tcx; match tcx.expand_abstract_consts(unexpanded_ct).kind() { - ty::ConstKind::Unevaluated(_, _) | ty::ConstKind::Expr(_) => (), + ty::ConstKind::Alias(_, _) | ty::ConstKind::Expr(_) => (), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -44,10 +44,7 @@ pub fn is_const_evaluatable<'tcx>( let is_anon_ct = matches!( ct.kind(), - ty::ConstKind::Unevaluated( - _, - ty::UnevaluatedConst { kind: ty::UnevaluatedConstKind::Anon { .. }, .. } - ) + ty::ConstKind::Alias(_, ty::AliasConst { kind: ty::AliasConstKind::Anon { .. }, .. }) ); if !is_anon_ct { @@ -69,7 +66,7 @@ pub fn is_const_evaluatable<'tcx>( // here. tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported"); } - ty::ConstKind::Unevaluated(_, _) => { + ty::ConstKind::Alias(_, _) => { match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) { Err(EvaluateConstErr::HasGenericsOrInfers) => { Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( @@ -93,8 +90,8 @@ pub fn is_const_evaluatable<'tcx>( crate::traits::evaluate_const(infcx, unexpanded_ct, param_env); Ok(()) } else { - let uv = match unexpanded_ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv, + let alias_const = match unexpanded_ct.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, ty::ConstKind::Expr(_) => { bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled") } @@ -117,7 +114,7 @@ pub fn is_const_evaluatable<'tcx>( tcx.dcx() .struct_span_fatal( // Slightly better span than just using `span` alone - if span == DUMMY_SP { uv.kind.def_span(tcx) } else { span }, + if span == DUMMY_SP { alias_const.kind.def_span(tcx) } else { span }, "failed to evaluate generic const expression", ) .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") @@ -131,9 +128,9 @@ pub fn is_const_evaluatable<'tcx>( } Err(EvaluateConstErr::HasGenericsOrInfers) => { - let err = if uv.has_non_region_infer() { + let err = if alias_const.has_non_region_infer() { NotConstEvaluatable::MentionsInfer - } else if uv.has_non_region_param() { + } else if alias_const.has_non_region_param() { NotConstEvaluatable::MentionsParam } else { let guar = infcx.dcx().span_delayed_bug( diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 36a562ab862fa..7b45dfe48a7fe 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -886,13 +886,9 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { let ct = self.tcx.expand_abstract_consts(ct); match ct.kind() { - ty::ConstKind::Unevaluated( + ty::ConstKind::Alias( _, - ty::UnevaluatedConst { - kind: ty::UnevaluatedConstKind::Projection { def_id }, - args, - .. - }, + ty::AliasConst { kind: ty::AliasConstKind::Projection { def_id }, args, .. }, ) if self.tcx.features().min_generic_const_args() => { match self.allow_self_projections { AllowSelfProjections::Yes => { diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index ea2f17d266b78..12b9940a83f44 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -559,7 +559,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { return ProcessResult::Changed(PendingPredicateObligations::new()); } ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(infcx.tcx).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(infcx.tcx).skip_norm_wip() + } // FIXME(generic_const_exprs): we should construct an alias like // `>::Output` when this is an `Expr` representing // `lhs + rhs`. @@ -677,10 +679,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { match const_evaluatable::is_const_evaluatable( self.selcx.infcx, - uv, + alias_const, obligation.param_env, obligation.cause.span, ) { @@ -688,7 +690,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( - uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), + alias_const + .walk() + .filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); ProcessResult::Unchanged } @@ -717,15 +721,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); match (c1.kind(), c2.kind()) { - ( - ty::ConstKind::Unevaluated(_, a), - ty::ConstKind::Unevaluated(_, b), - ) if a.kind == b.kind - && matches!( - a.kind, - ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - ) => + (ty::ConstKind::Alias(_, a), ty::ConstKind::Alias(_, b)) + if a.kind == b.kind + && matches!( + a.kind, + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + ) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) @@ -743,8 +745,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { )); } } - (_, ty::ConstKind::Unevaluated(_, _)) - | (ty::ConstKind::Unevaluated(_, _), _) => (), + (_, ty::ConstKind::Alias(_, _)) | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(new_obligations) = infcx .at(&obligation.cause, obligation.param_env) @@ -764,7 +765,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let stalled_on = &mut pending_obligation.stalled_on; let mut evaluate = |c: Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_, unevaluated) = c.kind() { + if let ty::ConstKind::Alias(_, alias_const) = c.kind() { match super::try_evaluate_const( self.selcx.infcx, c, @@ -773,7 +774,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { Ok(val) => Ok(val), e @ Err(EvaluateConstErr::HasGenericsOrInfers) => { stalled_on.extend( - unevaluated + alias_const .args .iter() .filter_map(TyOrConstInferVar::maybe_from_generic_arg), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c062198861ebe..a98d06351f258 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -369,8 +369,8 @@ pub fn normalize_param_env_or_error<'tcx>( // should actually be okay since without `feature(generic_const_exprs)` the only // const arguments that have a non-empty param env are array repeat counts. These // do not appear in the type system though. - if let ty::ConstKind::Unevaluated(_, uv) = c.kind() - && matches!(uv.kind, ty::UnevaluatedConstKind::Anon { .. }) + if let ty::ConstKind::Alias(_, alias_const) = c.kind() + && matches!(alias_const.kind, ty::AliasConstKind::Anon { .. }) { let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis()); let c = evaluate_const(&infcx, c, ty::ParamEnv::empty()); @@ -401,10 +401,10 @@ pub fn normalize_param_env_or_error<'tcx>( // we cannot prove `T: Trait`. // // The same thing is true for const generics- attempting to prove - // `T: Trait` with the same thing as a where clauses + // `T: Trait` with the same thing as a where clauses // will fail. After normalization we may be attempting to prove `T: Trait<4>` with - // the unnormalized where clause `T: Trait`. In order - // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)` + // the unnormalized where clause `T: Trait`. In order + // for the obligation to hold `4` must be equal to `ConstKind::Alias(...)` // but as we do not have lazy norm implemented, equating the two consts fails outright. // // Ideally we would not normalize consts here at all but it is required for backwards @@ -547,7 +547,7 @@ pub fn deeply_normalize_param_env_ignoring_regions<'tcx>( #[derive(Debug)] pub enum EvaluateConstErr { /// The constant being evaluated was either a generic parameter or inference variable, *or*, - /// some unevaluated constant with either generic parameters or inference variables in its + /// some alias const with either generic parameters or inference variables in its /// generic arguments. HasGenericsOrInfers, /// The type this constant evaluated to is not valid for use in const generics. This should @@ -560,7 +560,7 @@ pub enum EvaluateConstErr { } // FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine -// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own // normalization scheme /// Evaluates a type system constant returning a `ConstKind::Error` in cases where CTFE failed and /// returning the passed in constant if it was not fully concrete (i.e. depended on generic parameters @@ -583,7 +583,7 @@ pub fn evaluate_const<'tcx>( } // FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine -// FIXME(generic_const_exprs): Consider accepting a `ty::UnevaluatedConst` when we are not rolling our own +// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own // normalization scheme /// Evaluates a type system constant making sure to not allow constants that depend on generic parameters /// or inference variables to succeed in evaluating. @@ -608,11 +608,9 @@ pub fn try_evaluate_const<'tcx>( | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), - ty::ConstKind::Unevaluated(_, uv) => { - let opt_anon_const_kind = match uv.kind { - ty::UnevaluatedConstKind::Anon { def_id } => { - Some((def_id, tcx.anon_const_kind(def_id))) - } + ty::ConstKind::Alias(_, alias_const) => { + let opt_anon_const_kind = match alias_const.kind { + ty::AliasConstKind::Anon { def_id } => Some((def_id, tcx.anon_const_kind(def_id))), _ => None, }; @@ -632,14 +630,14 @@ pub fn try_evaluate_const<'tcx>( // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason // about if you have to consider gce whatsoever. Some((def_id, ty::AnonConstKind::GCE)) => { - if uv.has_non_region_infer() || uv.has_non_region_param() { + if alias_const.has_non_region_infer() || alias_const.has_non_region_param() { // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause // inference variables and generic parameters to show up in `ty::Const` even though the anon const // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. match tcx.thir_abstract_const(def_id) { Ok(Some(ct)) => { let ct = tcx.expand_abstract_consts( - ct.instantiate(tcx, uv.args).skip_norm_wip(), + ct.instantiate(tcx, alias_const.args).skip_norm_wip(), ); if let Err(e) = ct.error_reported() { return Err(EvaluateConstErr::EvaluationFailure(e)); @@ -648,8 +646,10 @@ pub fn try_evaluate_const<'tcx>( // the generic arguments provided for it, then we should *not* attempt to evaluate it. return Err(EvaluateConstErr::HasGenericsOrInfers); } else { - let args = - replace_param_and_infer_args_with_placeholder(tcx, uv.args); + let args = replace_param_and_infer_args_with_placeholder( + tcx, + alias_const.args, + ); let typing_env = infcx .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); @@ -666,11 +666,11 @@ pub fn try_evaluate_const<'tcx>( let typing_env = infcx .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); - (uv.args, typing_env) + (alias_const.args, typing_env) } } Some((def_id, ty::AnonConstKind::RepeatExprCount)) => { - if uv.has_non_region_infer() { + if alias_const.has_non_region_infer() { // Diagnostics will sometimes replace the identity args of anon consts in // array repeat expr counts with inference variables so we have to handle this // even though it is not something we should ever actually encounter. @@ -706,9 +706,9 @@ pub fn try_evaluate_const<'tcx>( // logic does not go through type system normalization. If it did this would // be a backwards compatibility problem as we do not enforce "syntactic" non- // usage of generic parameters like we do here. - if uv.args.has_non_region_param() - || uv.args.has_non_region_infer() - || uv.args.has_non_region_placeholders() + if alias_const.args.has_non_region_param() + || alias_const.args.has_non_region_infer() + || alias_const.args.has_non_region_placeholders() { return Err(EvaluateConstErr::HasGenericsOrInfers); } @@ -717,19 +717,21 @@ pub fn try_evaluate_const<'tcx>( // to prevent query cycle. let typing_env = ty::TypingEnv::fully_monomorphized(); - (uv.args, typing_env) + (alias_const.args, typing_env) } }; - let uv = ty::UnevaluatedConst::new(tcx, uv.kind, args); - let erased_uv = tcx.erase_and_anonymize_regions(uv); + let alias_const = ty::AliasConst::new(tcx, alias_const.kind, args); + let erased_alias_const = tcx.erase_and_anonymize_regions(alias_const); use rustc_middle::mir::interpret::ErrorHandled; // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at // where it gets used as a const generic. - let span = uv.kind.def_span(tcx); - match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, span) { - Ok(Ok(val)) => Ok(ty::Const::new_value(tcx, val, uv.type_of(tcx).skip_norm_wip())), + let span = alias_const.kind.def_span(tcx); + match tcx.const_eval_resolve_for_typeck(typing_env, erased_alias_const, span) { + Ok(Ok(val)) => { + Ok(ty::Const::new_value(tcx, val, alias_const.type_of(tcx).skip_norm_wip())) + } Ok(Err(_)) => { let e = tcx.dcx().delayed_bug( "Type system constant with non valtree'able type evaluated but no error emitted", diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index f212079c76fbe..2a28fab776a12 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -469,14 +469,14 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx if tcx.features().generic_const_exprs() // Normalize type_const items even with feature `generic_const_exprs`. - && !matches!(ct.kind(), ty::ConstKind::Unevaluated(_, uv) if uv.kind.is_type_const(tcx)) + && !matches!(ct.kind(), ty::ConstKind::Alias(_, alias_const) if alias_const.kind.is_type_const(tcx)) || !needs_normalization(self.selcx.infcx, &ct) { return ct; } - let uv = match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv, + let alias_const = match ct.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, _ => return ct.super_fold_with(self), }; @@ -484,20 +484,20 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // unless a `min_generic_const_args` feature gate error has already // been emitted earlier in compilation. // - // That's because we can only end up with an Unevaluated ty::Const for a const item + // That's because we can only end up with an Alias ty::Const for a const item // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. - let ct = match uv.kind { - ty::UnevaluatedConstKind::Projection { .. } => { - self.normalize_trait_projection(uv.into()).expect_const() + let ct = match alias_const.kind { + ty::AliasConstKind::Projection { .. } => { + self.normalize_trait_projection(alias_const.into()).expect_const() } - ty::UnevaluatedConstKind::Inherent { .. } => { - self.normalize_inherent_projection(uv.into()).expect_const() + ty::AliasConstKind::Inherent { .. } => { + self.normalize_inherent_projection(alias_const.into()).expect_const() } - ty::UnevaluatedConstKind::Free { .. } => { - self.normalize_free_alias(uv.into()).expect_const() + ty::AliasConstKind::Free { .. } => { + self.normalize_free_alias(alias_const.into()).expect_const() } - ty::UnevaluatedConstKind::Anon { .. } => { + ty::AliasConstKind::Anon { .. } => { let ct = ct.super_fold_with(self); super::with_replaced_escaping_bound_vars( self.selcx.infcx, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 45a3b5c85cdc8..e8371a3c0f257 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -272,21 +272,19 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { return Ok(constant); } - let uv = match constant.kind() { - ty::ConstKind::Unevaluated(_, uv) => uv, + let alias_const = match constant.kind() { + ty::ConstKind::Alias(_, alias_const) => alias_const, _ => return constant.try_super_fold_with(self), }; - let constant = match uv.kind { - ty::UnevaluatedConstKind::Anon { .. } => { - crate::traits::with_replaced_escaping_bound_vars( - self.infcx, - &mut self.universes, - constant, - |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), - ) - } - _ => self.try_fold_free_or_assoc(uv.into())?.expect_const(), + let constant = match alias_const.kind { + ty::AliasConstKind::Anon { .. } => crate::traits::with_replaced_escaping_bound_vars( + self.infcx, + &mut self.universes, + constant, + |constant| crate::traits::evaluate_const(&self.infcx, constant, self.param_env), + ), + _ => self.try_fold_free_or_assoc(alias_const.into())?.expect_const(), }; debug!(?constant, ?self.param_env); constant.try_super_fold_with(self) @@ -373,10 +371,10 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { result.normalized_term }; // `tcx.normalize_canonicalized_projection` may normalize to a type that - // still has unevaluated consts, so keep normalizing here if that's the case. + // still has alias consts, so keep normalizing here if that's the case. // Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer // of type/const and we need to continue folding it to reveal the TAIT behind it - // or further normalize nested unevaluated consts. + // or further normalize nested alias consts. if res != term.to_term(tcx, ty::IsRigid::No) && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || matches!( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e33311ed07719..73dc9bff6343d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -771,12 +771,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(EvaluatedToOkModuloRegions) } - ty::PredicateKind::DynCompatible(trait_def_id) => { - if self.tcx().is_dyn_compatible(trait_def_id) { - Ok(EvaluatedToOk) - } else { - Ok(EvaluatedToErr) - } + ty::PredicateKind::DynCompatible(_) => { + bug!( + "Obligation {obligation:?} should have been handled by fulfillment already." + ) } ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { @@ -852,10 +850,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { match const_evaluatable::is_const_evaluatable( self.infcx, - uv, + alias_const, obligation.param_env, obligation.cause.span, ) { @@ -882,15 +880,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); match (c1.kind(), c2.kind()) { - ( - ty::ConstKind::Unevaluated(_, a), - ty::ConstKind::Unevaluated(_, b), - ) if a.kind == b.kind - && matches!( - a.kind, - ty::UnevaluatedConstKind::Projection { .. } - | ty::UnevaluatedConstKind::Inherent { .. } - ) => + (ty::ConstKind::Alias(_, a), ty::ConstKind::Alias(_, b)) + if a.kind == b.kind + && matches!( + a.kind, + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::Inherent { .. } + ) => { if let Ok(InferOk { obligations, value: () }) = self .infcx @@ -909,8 +905,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } } - (_, ty::ConstKind::Unevaluated(_, _)) - | (ty::ConstKind::Unevaluated(_, _), _) => (), + (_, ty::ConstKind::Alias(_, _)) | (ty::ConstKind::Alias(_, _), _) => (), (_, _) => { if let Ok(InferOk { obligations, value: () }) = self .infcx @@ -929,7 +924,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let evaluate = |c: ty::Const<'tcx>| { - if let ty::ConstKind::Unevaluated(_, _) = c.kind() { + if let ty::ConstKind::Alias(_, _) = c.kind() { match crate::traits::try_evaluate_const( self.infcx, c, @@ -989,7 +984,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), ty::ConstKind::Value(cv) => cv.ty, - ty::ConstKind::Unevaluated(_, uv) => uv.type_of(self.tcx()).skip_norm_wip(), + ty::ConstKind::Alias(_, alias_const) => { + alias_const.type_of(self.tcx()).skip_norm_wip() + } // FIXME(generic_const_exprs): See comment in `fulfill.rs` ty::ConstKind::Expr(_) => return Ok(EvaluatedToOk), ty::ConstKind::Placeholder(_) => { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 8b1b2acc1f5a3..a47f933f5c25e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1064,10 +1064,11 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { let tcx = self.tcx(); match c.kind() { - ty::ConstKind::Unevaluated(_, uv) => { + ty::ConstKind::Alias(_, alias_const) => { if !c.has_escaping_bound_vars() { // Skip type consts as mGCA doesn't support evaluatable clauses - if !uv.kind.is_type_const(tcx) && !tcx.features().generic_const_args() { + if !alias_const.kind.is_type_const(tcx) && !tcx.features().generic_const_args() + { let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::ConstEvaluatable(c), )); @@ -1081,15 +1082,15 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); } - match uv.kind { - ty::UnevaluatedConstKind::Inherent { .. } => { - self.add_wf_preds_for_inherent_projection(uv.into()); + match alias_const.kind { + ty::AliasConstKind::Inherent { .. } => { + self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function } - ty::UnevaluatedConstKind::Projection { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { - let obligations = self.nominal_obligations(def_id, uv.args); + ty::AliasConstKind::Projection { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { + let obligations = self.nominal_obligations(def_id, alias_const.args); self.out.extend(obligations); } } @@ -1111,7 +1112,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::ConstKind::Expr(_) => { // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the // trait bound `typeof(N): Add` holds. This is currently unnecessary - // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Alias` // which means that the `DefId` would have been typeck'd elsewhere. However in // the future we may allow directly lowering to `ConstKind::Expr` in which case // we would not be proving bounds we should. diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml index e61717e5e9ce2..5da9435e77e63 100644 --- a/compiler/rustc_transmute/Cargo.toml +++ b/compiler/rustc_transmute/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1" [dev-dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index 682cf941561fc..45e71d8a38860 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -itertools = "0.12" +itertools = "0.15" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 52964ae72f015..6f7a17c1e4a92 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -70,12 +70,9 @@ fn recurse_build<'tcx>( } &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { - let uneval = ty::UnevaluatedConst::new( - tcx, - ty::UnevaluatedConstKind::new_from_def_id(tcx, def_id), - args, - ); - ty::Const::new_unevaluated(tcx, ty::IsRigid::No, uneval) + let uneval = + ty::AliasConst::new(tcx, ty::AliasConstKind::new_from_def_id(tcx, def_id), args); + ty::Const::new_alias(tcx, ty::IsRigid::No, uneval) } ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 4126f9c887194..cdf280381247a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -177,7 +177,7 @@ fn extract_const_value<'tcx>( } Err(error(cx, LayoutError::TooGeneric(ty))) } - ty::ConstKind::Unevaluated(_, _) => { + ty::ConstKind::Alias(_, _) => { let err = if ct.has_param() { LayoutError::TooGeneric(ty) } else { diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index a8fa2ed87bb87..bac5d47e98676 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -317,6 +317,13 @@ fn opaque_types_defined_by<'tcx>( tcx: TyCtxt<'tcx>, item: LocalDefId, ) -> &'tcx ty::List { + // Closures and coroutines are type checked with their parent + // Note that we also support `SyntheticCoroutineBody` since we create + // a MIR body for the def kind, and some MIR passes (like promotion) + // may require doing analysis using its typing env. + if tcx.is_typeck_child(item.to_def_id()) { + return tcx.opaque_types_defined_by(tcx.local_parent(item)); + } let kind = tcx.def_kind(item); trace!(?kind); let mut collector = OpaqueTypeCollector::new(tcx, item); @@ -329,16 +336,13 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { + | DefKind::AnonConst + | DefKind::InlineConst => { + // Non-type-system inline consts should be caught by `if tcx.is_typeck_child` above + debug_assert!(kind != DefKind::InlineConst || tcx.is_type_system_inline_const(item)); + collector.collect_taits_declared_in_body(); } - // Closures and coroutines are type checked with their parent - // Note that we also support `SyntheticCoroutineBody` since we create - // a MIR body for the def kind, and some MIR passes (like promotion) - // may require doing analysis using its typing env. - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => { - collector.opaques.extend(tcx.opaque_types_defined_by(tcx.local_parent(item))); - } DefKind::AssocTy | DefKind::TyAlias | DefKind::GlobalAsm => {} DefKind::OpaqueTy | DefKind::Mod @@ -349,6 +353,8 @@ fn opaque_types_defined_by<'tcx>( | DefKind::Trait | DefKind::ForeignTy | DefKind::TraitAlias + | DefKind::Closure + | DefKind::SyntheticCoroutineBody | DefKind::TyParam | DefKind::ConstParam | DefKind::Ctor(_, _) diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index ebec1888e6a95..f60a03fa71b26 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -21,6 +21,24 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( ) -> V::Result { let kind = tcx.def_kind(item); trace!(?kind); + let mut visit_alias = || { + if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { + // If the type of the item uses `_`, we're gonna error out anyway, but + // typeck (which type_of invokes below), will call back into opaque_types_defined_by + // causing a cycle. So we just bail out in this case. + if ty.is_suggestable_infer_ty() { + return V::Result::output(); + } + // Associated types in traits don't necessarily have a type that we can visit + try_visit!( + visitor.visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) + ); + } + for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { + try_visit!(visitor.visit(span, pred.skip_norm_wip())); + } + V::Result::output() + }; match kind { // Walk over the signature of the function DefKind::AssocFn | DefKind::Fn => { @@ -47,24 +65,8 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( | DefKind::Static { .. } | DefKind::Const { .. } | DefKind::AssocConst { .. } - | DefKind::AnonConst => { - if let Some(ty) = tcx.hir_node_by_def_id(item).ty() { - // If the type of the item uses `_`, we're gonna error out anyway, but - // typeck (which type_of invokes below), will call back into opaque_types_defined_by - // causing a cycle. So we just bail out in this case. - if ty.is_suggestable_infer_ty() { - return V::Result::output(); - } - // Associated types in traits don't necessarily have a type that we can visit - try_visit!( - visitor - .visit(ty.span, tcx.type_of(item).instantiate_identity().skip_norm_wip()) - ); - } - for (pred, span) in tcx.explicit_predicates_of(item).instantiate_identity(tcx) { - try_visit!(visitor.visit(span, pred.skip_norm_wip())); - } - } + | DefKind::AnonConst => return visit_alias(), + DefKind::InlineConst if tcx.is_type_system_inline_const(item) => return visit_alias(), DefKind::OpaqueTy => { for (pred, span) in tcx .explicit_item_bounds(item) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 335026f28858a..29bda55bbbaab 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -149,6 +149,9 @@ fn adt_sizedness_constraint<'tcx>( /// See `ParamEnv` struct definition for details. fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { + if tcx.is_typeck_child(def_id) { + return tcx.param_env(tcx.typeck_root_def_id(def_id)); + } // Compute the bounds on Self and the type parameters. let ty::InstantiatedPredicates { predicates, .. } = tcx.predicates_of(def_id).instantiate_identity(tcx); diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index f353b156757d9..0645839ef0263 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,7 +9,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::{self as ty, BoundVarIndexKind, Interner, UnevaluatedConst}; +use crate::{self as ty, AliasConst, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] @@ -34,7 +34,7 @@ pub enum ConstKind { /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this /// should - Unevaluated(ty::IsRigid, ty::UnevaluatedConst), + Alias(ty::IsRigid, ty::AliasConst), /// Used to hold computed value. Value(I::ValueConst), @@ -43,7 +43,7 @@ pub enum ConstKind { /// propagated to avoid useless error messages. Error(I::ErrorGuaranteed), - /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent + /// A non-const-item expression awaiting evaluation, used by `feature(generic_const_exprs)` to represent /// const arguments such as `N + 1` or `foo(N)` Expr(I::ExprConst), } @@ -59,7 +59,9 @@ impl fmt::Debug for ConstKind { Infer(var) => write!(f, "{var:?}"), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), - Unevaluated(is_rigid, uv) => write!(f, "Unevaluated({is_rigid:?}, {uv:?})"), + Alias(is_rigid, alias_const) => { + write!(f, "AliasConst({is_rigid:?}, {alias_const:?})") + } Value(val) => write!(f, "{val:?}"), Error(_) => write!(f, "{{const error}}"), Expr(expr) => write!(f, "{expr:?}"), @@ -67,46 +69,42 @@ impl fmt::Debug for ConstKind { } } -impl UnevaluatedConst { +impl AliasConst { #[inline] - pub fn new( - interner: I, - kind: UnevaluatedConstKind, - args: I::GenericArgs, - ) -> UnevaluatedConst { + pub fn new(interner: I, kind: AliasConstKind, args: I::GenericArgs) -> AliasConst { if cfg!(debug_assertions) { let def_id = match kind { - ty::UnevaluatedConstKind::Projection { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Inherent { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Free { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), }; interner.debug_assert_args_compatible(def_id, args); } - UnevaluatedConst { kind, args, _use_alias_new_instead: () } + AliasConst { kind, args, _use_alias_new_instead: () } } pub fn type_of(self, interner: I) -> ty::Unnormalized { let def_id = match self.kind { - ty::UnevaluatedConstKind::Projection { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Inherent { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Free { def_id } => def_id.into(), - ty::UnevaluatedConstKind::Anon { def_id } => def_id.into(), + ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::Inherent { def_id } => def_id.into(), + ty::AliasConstKind::Free { def_id } => def_id.into(), + ty::AliasConstKind::Anon { def_id } => def_id.into(), }; interner.type_of(def_id).instantiate(interner, self.args) } } -/// UnevaluatedConstKind is extremely similar to AliasTyKind, and likely should be reasoned about +/// AliasConstKind is extremely similar to AliasTyKind, and likely should be reasoned about /// and handled in very similar ways. The documentation for AliasTyKind/etc. may be helpful when -/// learning about UnevaluatedConstKind. +/// learning about AliasConstKind. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] -pub enum UnevaluatedConstKind { +pub enum AliasConstKind { /// A projection `::AssocConst` Projection { def_id: I::TraitAssocConstId }, /// An associated constant in an inherent `impl` @@ -114,38 +112,38 @@ pub enum UnevaluatedConstKind { /// A free constant, outside an impl block. Free { def_id: I::FreeConstAliasId }, /// Anonymous constant, e.g. the `1 + 2` in `[u8; 1 + 2]`. - Anon { def_id: I::UnevaluatedConstId }, + Anon { def_id: I::AnonConstId }, } -impl UnevaluatedConstKind { +impl AliasConstKind { pub fn new_from_def_id(interner: I, def_id: I::DefId) -> Self { - interner.unevaluated_const_kind_from_def_id(def_id) + interner.alias_const_kind_from_def_id(def_id) } pub fn is_type_const(self, interner: I) -> bool { match self { - UnevaluatedConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Free { def_id } => interner.is_type_const(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Projection { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Inherent { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Free { def_id } => interner.is_type_const(def_id.into()), + AliasConstKind::Anon { def_id } => interner.is_type_const(def_id.into()), } } pub fn def_span(self, interner: I) -> I::Span { match self { - UnevaluatedConstKind::Projection { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Free { def_id } => interner.def_span(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Projection { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Inherent { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Free { def_id } => interner.def_span(def_id.into()), + AliasConstKind::Anon { def_id } => interner.def_span(def_id.into()), } } pub fn opt_def_id(self) -> Option { match self { - UnevaluatedConstKind::Projection { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Inherent { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Free { def_id } => Some(def_id.into()), - UnevaluatedConstKind::Anon { def_id } => Some(def_id.into()), + AliasConstKind::Projection { def_id } => Some(def_id.into()), + AliasConstKind::Inherent { def_id } => Some(def_id.into()), + AliasConstKind::Free { def_id } => Some(def_id.into()), + AliasConstKind::Anon { def_id } => Some(def_id.into()), } } } diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index c08cec21e5b1f..74abfad6e5abc 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -41,6 +41,7 @@ pub enum TypeError { ArgumentSorts(ExpectedFound, usize), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), + SplatMismatch(ExpectedFound>), /// Instantiating a type variable with the given type would have /// created a cycle (because it appears somewhere within that @@ -76,7 +77,7 @@ impl TypeError { match self { CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | ArraySize(_) | ArgumentSorts(..) | Sorts(_) - | VariadicMismatch(_) | TargetFeatureCast(_) => false, + | VariadicMismatch(_) | SplatMismatch(_) | TargetFeatureCast(_) => false, Mutability | ArgumentMutability(_) diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 14ebffb70b8bf..a514cf9d9052f 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -256,13 +256,12 @@ impl { + ty::Param(_) | ty::Alias(ty::IsRigid::Yes, _) => { if INSTANTIATE_RHS_WITH_INFER { return true; } } - // FIXME(#155345): we should fast-path for rigid aliases here. - ty::Error(_) | ty::Alias(..) | ty::Bound(..) => return true, + ty::Error(_) | ty::Alias(ty::IsRigid::No, _) | ty::Bound(..) => return true, ty::Infer(var) => return self.var_and_ty_may_unify(var, lhs), // These types only unify with inference variables or their own @@ -339,12 +338,22 @@ impl self.var_and_ty_may_unify(var, rhs), + // Since we ensure that the rhs is not non-rigid alias, + // lhs rigid alias can only unify with it if it's a rigid alias of the same kind. + ty::Alias(ty::IsRigid::Yes, lhs_alias) => { + INSTANTIATE_LHS_WITH_INFER + || match rhs.kind() { + ty::Alias(ty::IsRigid::Yes, rhs_alias) => { + lhs_alias.kind == rhs_alias.kind + && self.args_may_unify_inner(lhs_alias.args, rhs_alias.args, depth) + } + _ => false, + } + } // As we're walking the whole type, it may encounter projections // inside of binders and what not, so we're just going to assume that - // projections can unify with other stuff. - // - // Looking forward to lazy normalization this is the safer strategy anyways. - ty::Alias(..) => true, + // non-rigid alias can unify with anything. + ty::Alias(ty::IsRigid::No, _) => true, ty::Int(_) | ty::Uint(_) @@ -469,7 +478,7 @@ impl { @@ -500,9 +509,7 @@ impl { - true - } + ty::ConstKind::Expr(_) | ty::ConstKind::Alias(_, _) | ty::ConstKind::Error(_) => true, ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) => true, } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 1749ec99cad4e..da7d8b5acbc1e 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -79,10 +79,10 @@ bitflags::bitflags! { const HAS_TY_OPAQUE = 1 << 12; /// Does this have `Inherent`? const HAS_TY_INHERENT = 1 << 13; - /// Does this have `ConstKind::Unevaluated`? + /// Does this have `ConstKind::Alias`? const HAS_CT_PROJECTION = 1 << 14; - /// Does this have `Alias` or `ConstKind::Unevaluated`? + /// Does this have `Alias` or `ConstKind::Alias`? /// /// Rephrased, could this term be normalized further? const HAS_ALIAS = TypeFlags::HAS_TY_PROJECTION.bits() @@ -432,8 +432,8 @@ impl FlagComputation { self.add_term(term); } ty::PredicateKind::DynCompatible(_def_id) => {} - ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { - self.add_const(uv); + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(alias_const)) => { + self.add_const(alias_const); } ty::PredicateKind::ConstEquate(expected, found) => { self.add_const(expected); @@ -477,9 +477,9 @@ impl FlagComputation { fn add_const_kind(&mut self, c: &ty::ConstKind) { match *c { - ty::ConstKind::Unevaluated(is_rigid, uv) => { + ty::ConstKind::Alias(is_rigid, alias_const) => { self.add_is_rigid(is_rigid); - self.add_args(uv.args.as_slice()); + self.add_args(alias_const.args.as_slice()); self.add_flags(TypeFlags::HAS_CT_PROJECTION); } ty::ConstKind::Infer(infer) => match infer { diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 99a1028209e2d..8b1401c0609f9 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -601,9 +601,9 @@ impl TypeFolder for RigidnessFolder { } match c.kind() { - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, uv) => { - let uv = uv.fold_with(self); - I::Const::new_unevaluated(self.cx, ty::IsRigid::No, uv) + ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => { + let alias_const = alias_const.fold_with(self); + I::Const::new_alias(self.cx, ty::IsRigid::No, alias_const) } _ => c.super_fold_with(self), } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 6263f4436abd4..77e5fcac07b3f 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -280,7 +280,7 @@ pub trait Const>: fn new_placeholder(interner: I, param: ty::PlaceholderConst) -> Self; - fn new_unevaluated(interner: I, is_rigid: ty::IsRigid, uv: ty::UnevaluatedConst) -> Self; + fn new_alias(interner: I, is_rigid: ty::IsRigid, alias_const: ty::AliasConst) -> Self; fn new_expr(interner: I, expr: I::ExprConst) -> Self; @@ -411,7 +411,7 @@ pub trait Term>: _ => None, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_, uv) => Some(uv.into()), + ty::ConstKind::Alias(_, alias_const) => Some(alias_const.into()), _ => None, }, } @@ -424,7 +424,7 @@ pub trait Term>: _ => false, }, ty::TermKind::Const(ct) => match ct.kind() { - ty::ConstKind::Unevaluated(is_rigid, _) => is_rigid == ty::IsRigid::No, + ty::ConstKind::Alias(is_rigid, _) => is_rigid == ty::IsRigid::No, _ => false, }, } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 1c1fbcff0d928..815890c989d93 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -16,7 +16,7 @@ use crate::solve::{ AccessedOpaques, CanonicalInput, Certainty, ExternalConstraintsData, QueryResult, inspect, }; use crate::visit::{Flags, TypeVisitable}; -use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; +use crate::{self as ty, CanonicalParamEnvCacheEntry, TraitRef, search_graph}; #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "type_ir_interner")] pub trait Interner: @@ -56,13 +56,12 @@ pub trait Interner: type CoroutineId: SpecificDefId; type AdtId: SpecificDefId; type ImplId: SpecificDefId; - type UnevaluatedConstId: SpecificDefId; + type AnonConstId: SpecificDefId; type TraitAssocTyId: SpecificDefId + Into + TryFrom; type TraitAssocConstId: SpecificDefId + Into - + Into + TryFrom; type TraitAssocTermId: SpecificDefId; type OpaqueTyId: SpecificDefId; @@ -75,19 +74,13 @@ pub trait Interner: + Into + TypeFoldable; type FreeTyAliasId: SpecificDefId + Into; - type FreeConstAliasId: SpecificDefId - + Into - + Into; + type FreeConstAliasId: SpecificDefId + Into; type FreeTermAliasId: SpecificDefId; type ImplOrTraitAssocTyId: SpecificDefId + Into; - type ImplOrTraitAssocConstId: SpecificDefId - + Into - + Into; + type ImplOrTraitAssocConstId: SpecificDefId + Into; type ImplOrTraitAssocTermId: SpecificDefId; type InherentAssocTyId: SpecificDefId + Into; - type InherentAssocConstId: SpecificDefId - + Into - + Into; + type InherentAssocConstId: SpecificDefId + Into; type InherentAssocTermId: SpecificDefId; type Span: Span; @@ -244,10 +237,7 @@ pub trait Interner: type AdtDef: AdtDef; fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; - fn unevaluated_const_kind_from_def_id( - self, - def_id: Self::DefId, - ) -> ty::UnevaluatedConstKind; + fn alias_const_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasConstKind; // FIXME: remove in favor of explicit construction fn alias_term_kind_from_def_id(self, def_id: Self::DefId) -> ty::AliasTermKind; @@ -408,8 +398,7 @@ pub trait Interner: fn for_each_relevant_impl( self, - trait_def_id: Self::TraitId, - self_ty: Self::Ty, + trait_ref: TraitRef, f: impl FnMut(Self::ImplId) -> R, ) -> R; fn for_each_blanket_impl( @@ -519,7 +508,7 @@ declare_lift_into! { TraitId, Ty, Tys, - UnevaluatedConstId, + AnonConstId, } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index c6ab804d81c9f..f3bacff256d19 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,12 +1,12 @@ use std::fmt; +#[cfg(feature = "nightly")] +use crate::{AliasConst, ClosureKind}; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; -#[cfg(feature = "nightly")] -use crate::{ClosureKind, UnevaluatedConst}; pub trait IrPrint { fn print(t: &T, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; @@ -100,7 +100,7 @@ mod into_diag_arg_impls { } } - impl IntoDiagArg for UnevaluatedConst { + impl IntoDiagArg for AliasConst { fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { format!("{self:?}").into_diag_arg(path) } diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 857738d207b4f..ea92699958094 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -45,6 +45,7 @@ TrivialTypeTraversalImpls! { (), bool, usize, + u8, u16, u32, u64, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index d64cc2450eb19..1922cbd954cf8 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -169,6 +169,10 @@ impl Relate for ty::FnSig { return Err(TypeError::AbiMismatch(ExpectedFound::new(a.abi(), b.abi()))); }; + if a.splatted() != b.splatted() { + return Err(TypeError::SplatMismatch(ExpectedFound::new(a.splatted(), b.splatted()))); + } + let a_inputs = a.inputs(); let b_inputs = b.inputs(); if a_inputs.len() != b_inputs.len() { @@ -227,17 +231,17 @@ impl Relate for ty::AliasTy { } } -impl Relate for ty::UnevaluatedConst { +impl Relate for ty::AliasConst { fn relate>( relation: &mut R, - a: ty::UnevaluatedConst, - b: ty::UnevaluatedConst, - ) -> RelateResult> { + a: ty::AliasConst, + b: ty::AliasConst, + ) -> RelateResult> { let cx = relation.cx(); if a.kind != b.kind { Err(TypeError::ConstMismatch(ExpectedFound::new( - Const::new_unevaluated(cx, ty::IsRigid::yes_if_next_solver(cx), a), - Const::new_unevaluated(cx, ty::IsRigid::yes_if_next_solver(cx), b), + Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), a), + Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), b), ))) } else { // FIXME(mgca): remove this @@ -245,7 +249,7 @@ impl Relate for ty::UnevaluatedConst { let args = relate_args_invariantly(relation, a.args, b.args)?; - Ok(ty::UnevaluatedConst::new(cx, a.kind, args)) + Ok(ty::AliasConst::new(cx, a.kind, args)) } } } @@ -546,7 +550,7 @@ pub fn structurally_relate_tys>( } /// Relates `a` and `b` structurally, calling the relation for all nested values. -/// Any semantic equality, e.g. of unevaluated consts, and inference variables have +/// Any semantic equality, e.g. of alias consts, and inference variables have /// to be handled by the caller. /// /// FIXME: This is not totally structural, which probably should be fixed. @@ -614,14 +618,11 @@ pub fn structurally_relate_consts>( // While this is slightly incorrect, it shouldn't matter for `min_const_generics` // and is the better alternative to waiting until `generic_const_exprs` can // be stabilized. - ( - ty::ConstKind::Unevaluated(is_rigid_a, au), - ty::ConstKind::Unevaluated(is_rigid_b, bu), - ) => { + (ty::ConstKind::Alias(is_rigid_a, au), ty::ConstKind::Alias(is_rigid_b, bu)) => { // Users shouldn't know about this so the mismatch should be caught // during development rather than presented as type error. debug_assert_eq!(is_rigid_a, is_rigid_b, "{a:?} != {b:?}"); - return Ok(Const::new_unevaluated(cx, is_rigid_a, relation.relate(au, bu)?)); + return Ok(Const::new_alias(cx, is_rigid_a, relation.relate(au, bu)?)); } (ty::ConstKind::Expr(ae), ty::ConstKind::Expr(be)) => { let expr = relation.relate(ae, be)?; diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 76fa888147971..d5f9df2249df7 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -239,14 +239,13 @@ where Ok(a) } - ( - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, _), - ty::ConstKind::Unevaluated(ty::IsRigid::Yes, _), - ) if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => { + (ty::ConstKind::Alias(ty::IsRigid::Yes, _), ty::ConstKind::Alias(ty::IsRigid::Yes, _)) + if (infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver()) => + { structurally_relate_consts(relation, a, b) } - (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) + (ty::ConstKind::Alias(..), _) | (_, ty::ConstKind::Alias(..)) if infcx.cx().features().generic_const_exprs() || infcx.next_trait_solver() => { match relation.structurally_relate_aliases() { diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index 8a6c2c8231adf..aed634d4f3a21 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -60,9 +60,9 @@ pub enum AliasTermKind { /// Can always be normalized away. FreeTy { def_id: I::FreeTyAliasId }, - /// An unevaluated anonymous constants. - AnonConst { def_id: I::UnevaluatedConstId }, - /// An unevaluated const coming from an associated const. + /// An anonymous constant. + AnonConst { def_id: I::AnonConstId }, + /// A const alias coming from an associated const. ProjectionConst { def_id: I::TraitAssocConstId }, /// A top level const item not part of a trait or impl. FreeConst { def_id: I::FreeConstAliasId }, @@ -79,8 +79,8 @@ impl AliasTermKind { AliasTermKind::InherentConst { .. } => "inherent associated const", AliasTermKind::OpaqueTy { .. } => "opaque type", AliasTermKind::FreeTy { .. } => "type alias", - AliasTermKind::FreeConst { .. } => "unevaluated constant", - AliasTermKind::AnonConst { .. } => "unevaluated constant", + AliasTermKind::FreeConst { .. } => "const alias", + AliasTermKind::AnonConst { .. } => "anonymous constant", } } @@ -122,17 +122,13 @@ impl From> for AliasTermKind { } } -impl From> for AliasTermKind { - fn from(value: ty::UnevaluatedConstKind) -> Self { +impl From> for AliasTermKind { + fn from(value: ty::AliasConstKind) -> Self { match value { - ty::UnevaluatedConstKind::Projection { def_id } => { - AliasTermKind::ProjectionConst { def_id } - } - ty::UnevaluatedConstKind::Inherent { def_id } => { - AliasTermKind::InherentConst { def_id } - } - ty::UnevaluatedConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, - ty::UnevaluatedConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, + ty::AliasConstKind::Projection { def_id } => AliasTermKind::ProjectionConst { def_id }, + ty::AliasConstKind::Inherent { def_id } => AliasTermKind::InherentConst { def_id }, + ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, + ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, } } } @@ -189,24 +185,20 @@ impl AliasTerm { ty::AliasTy { kind, args: self.args, _use_alias_new_instead: () } } - pub fn expect_ct(self) -> ty::UnevaluatedConst { + pub fn expect_ct(self) -> ty::AliasConst { let kind = match self.kind { - AliasTermKind::InherentConst { def_id } => { - ty::UnevaluatedConstKind::Inherent { def_id } - } - AliasTermKind::FreeConst { def_id } => ty::UnevaluatedConstKind::Free { def_id }, - AliasTermKind::AnonConst { def_id } => ty::UnevaluatedConstKind::Anon { def_id }, - AliasTermKind::ProjectionConst { def_id } => { - ty::UnevaluatedConstKind::Projection { def_id } - } + AliasTermKind::InherentConst { def_id } => ty::AliasConstKind::Inherent { def_id }, + AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, + AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, + AliasTermKind::ProjectionConst { def_id } => ty::AliasConstKind::Projection { def_id }, kind @ (AliasTermKind::ProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } | AliasTermKind::FreeTy { .. }) => { - panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) + panic!("Cannot turn `{}` into `AliasConst`", kind.descr()) } }; - ty::UnevaluatedConst { kind, args: self.args, _use_alias_new_instead: () } + ty::AliasConst { kind, args: self.args, _use_alias_new_instead: () } } pub fn to_term(self, interner: I, is_rigid: ty::IsRigid) -> I::Term { @@ -214,26 +206,18 @@ impl AliasTerm { Ty::new_alias(interner, is_rigid, ty::AliasTy::new_from_args(interner, kind, self.args)) .into() }; - let unevaluated_const = |kind| { - I::Const::new_unevaluated( - interner, - is_rigid, - ty::UnevaluatedConst::new(interner, kind, self.args), - ) - .into() + let alias_const = |kind| { + I::Const::new_alias(interner, is_rigid, ty::AliasConst::new(interner, kind, self.args)) + .into() }; match self.kind { - AliasTermKind::FreeConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Free { def_id }) - } + AliasTermKind::FreeConst { def_id } => alias_const(ty::AliasConstKind::Free { def_id }), AliasTermKind::InherentConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Inherent { def_id }) - } - AliasTermKind::AnonConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Anon { def_id }) + alias_const(ty::AliasConstKind::Inherent { def_id }) } + AliasTermKind::AnonConst { def_id } => alias_const(ty::AliasConstKind::Anon { def_id }), AliasTermKind::ProjectionConst { def_id } => { - unevaluated_const(ty::UnevaluatedConstKind::Projection { def_id }) + alias_const(ty::AliasConstKind::Projection { def_id }) } AliasTermKind::ProjectionTy { def_id } => alias_ty(ty::Projection { def_id }), AliasTermKind::InherentTy { def_id } => alias_ty(ty::Inherent { def_id }), @@ -367,8 +351,8 @@ impl From> for AliasTerm { } } -impl From> for AliasTerm { - fn from(ty: ty::UnevaluatedConst) -> Self { +impl From> for AliasTerm { + fn from(ty: ty::AliasConst) -> Self { AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () } } } diff --git a/compiler/rustc_type_ir/src/ty/alias.rs b/compiler/rustc_type_ir/src/ty/alias.rs index 0f99c8d787ee7..10fd4c6a25394 100644 --- a/compiler/rustc_type_ir/src/ty/alias.rs +++ b/compiler/rustc_type_ir/src/ty/alias.rs @@ -5,7 +5,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::{AliasTermKind, AliasTyKind, Interner, UnevaluatedConstKind}; +use crate::{AliasConstKind, AliasTermKind, AliasTyKind, Interner}; /// Represents an alias of a type, constant, or other term-like item. /// @@ -55,4 +55,4 @@ pub type ProjectionAliasTy = Alias::TraitAssocTyId>; pub type InherentAliasTy = Alias::InherentAssocTyId>; pub type OpaqueAliasTy = Alias::OpaqueTyId>; pub type FreeAliasTy = Alias::FreeTyAliasId>; -pub type UnevaluatedConst = Alias>; +pub type AliasConst = Alias>; diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 89fac9d200214..31824b61cfb27 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -888,8 +888,19 @@ pub struct TypeAndMut { impl Eq for TypeAndMut {} +/// Error type for splatted argument index errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SplattedArgIndexError { + /// The splatted argument index is invalid. + /// A `u8::MAX` argument index used to indicate that no argument is splatted. + /// Higher values are also not supported, for performance reasons. + InvalidIndex { splatted_arg_index: u8 }, + + /// The splatted argument index is outside the bounds of the function arguments. + OutOfBounds { splatted_arg_index: u8, args_len: u16 }, +} + /// Contains the packed non-type fields of a function signature. -// FIXME(splat): add the splatted argument index as a u16 #[derive_where(Copy, Clone, PartialEq, Eq, Hash; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( @@ -903,6 +914,15 @@ pub struct FnSigKind { #[type_visitable(ignore)] #[type_foldable(identity)] flags: u8, + + /// Which function argument is splatted into multiple arguments in callers, if any? + /// Splatting functions with `>= u8::MAX` arguments is not supported, for performance reasons. + /// (And spending an extra byte on an edge case is not worth the perf.) + #[lift(identity)] + #[type_visitable(ignore)] + #[type_foldable(identity)] + splatted: u8, + #[type_visitable(ignore)] #[type_foldable(identity)] _marker: PhantomData I>, @@ -922,12 +942,28 @@ impl fmt::Debug for FnSigKind { if self.c_variadic() { f.field(&"CVariadic"); - }; + } + + if let Some(index) = self.splatted() { + f.field(&format!("Splatted({})", index)); + } f.finish() } } +impl Default for FnSigKind { + /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic or splatted arguments. + /// To modify these flags, use the `set_*` methods, for readability. + fn default() -> Self { + Self { flags: 0, splatted: 0, _marker: PhantomData } + .set_abi(ExternAbi::Rust) + .set_safety(I::Safety::unsafe_mode()) + .set_c_variadic(false) + .set_no_splatted_args() + } +} + impl FnSigKind { /// Mask for the `ExternAbi` variant, including the unwind flag. const EXTERN_ABI_MASK: u8 = 0b111111; @@ -938,19 +974,34 @@ impl FnSigKind { /// Bitflag for a trailing C-style variadic argument. const C_VARIADIC_FLAG: u8 = 1 << 7; - /// Create a new FnSigKind with the "Rust" ABI, "Unsafe" safety, and no C-style variadic argument. - /// To modify these flags, use the `set_*` methods, for readability. - // FIXME: use Default instead when that trait is const stable. - pub fn default() -> Self { - Self { flags: 0, _marker: PhantomData } - .set_abi(ExternAbi::Rust) - .set_safety(I::Safety::unsafe_mode()) - .set_c_variadic(false) - } + /// The marker index for "no splatted arguments". Higher values are also not supported, for + /// performance reasons. + /// + /// Must have the same value as `FnDeclFlags::NO_SPLATTED_ARG_INDEX` and + /// `rustc_ast::FnDecl::NO_SPLATTED_ARG_INDEX`. + /// + /// This is an implementation detail, which should only be used in low-level encoding. + pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; - /// Create a new FnSigKind with the given ABI, safety, and C-style variadic flag. - pub fn new(abi: ExternAbi, safety: I::Safety, c_variadic: bool) -> Self { - Self::default().set_abi(abi).set_safety(safety).set_c_variadic(c_variadic) + /// Create a new FnSigKind with the given ABI, safety, C-style variadic, and splatted argument + /// index. + pub fn new( + abi: ExternAbi, + safety: I::Safety, + c_variadic: bool, + splatted: Option, + args_len: usize, + ) -> Result { + Self::default() + .set_abi(abi) + .set_safety(safety) + .set_c_variadic(c_variadic) + .set_splatted(splatted, args_len) + } + + /// Create a new safe FnSigKind with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. + pub fn dummy() -> Self { + Self::default().set_safety(I::Safety::safe()) } /// Set the ABI, including the unwind flag. @@ -989,6 +1040,41 @@ impl FnSigKind { self } + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + mut self, + splatted: Option, + args_len: usize, + ) -> Result { + if let Some(splatted_arg_index) = splatted { + if splatted_arg_index == Self::NO_SPLATTED_ARG_INDEX { + // This index value is used as a marker for "no splatting", so it is unsupported. + // Higher values are also not supported, for performance reasons. + return Err(SplattedArgIndexError::InvalidIndex { splatted_arg_index }); + } else if usize::from(splatted_arg_index) >= args_len { + return Err(SplattedArgIndexError::OutOfBounds { + splatted_arg_index, + args_len: args_len as u16, + }); + } + + self.splatted = splatted_arg_index; + } else { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + } + + Ok(self) + } + + /// Set the splatted argument index to "no splatted arguments". + #[must_use = "this method does not modify the receiver"] + pub fn set_no_splatted_args(mut self) -> Self { + self.splatted = Self::NO_SPLATTED_ARG_INDEX; + self + } + /// Get the ABI, including the unwind flag. pub fn abi(self) -> ExternAbi { let abi_index = self.flags & Self::EXTERN_ABI_MASK; @@ -1009,6 +1095,11 @@ impl FnSigKind { pub fn c_variadic(self) -> bool { self.flags & Self::C_VARIADIC_FLAG != 0 } + + /// Get the index of the splatted argument, if any. + pub fn splatted(self) -> Option { + if self.splatted == Self::NO_SPLATTED_ARG_INDEX { None } else { Some(self.splatted) } + } } #[derive_where(Clone, Copy, PartialEq, Hash; I: Interner)] @@ -1039,8 +1130,21 @@ impl FnSig { !self.c_variadic() && self.safety().is_safe() && self.abi() == ExternAbi::Rust } + /// Set the safety flag. + #[must_use = "this method does not modify the receiver"] pub fn set_safety(self, safety: I::Safety) -> Self { - Self { fn_sig_kind: FnSigKind::new(self.abi(), safety, self.c_variadic()), ..self } + Self { fn_sig_kind: self.fn_sig_kind.set_safety(safety), ..self } + } + + /// Set the splatted argument index. + /// The number of function arguments is used for error checking. + #[must_use = "this method does not modify the receiver"] + pub fn set_splatted( + self, + splatted: Option, + args_len: usize, + ) -> Result { + Ok(Self { fn_sig_kind: self.fn_sig_kind.set_splatted(splatted, args_len)?, ..self }) } pub fn safety(self) -> I::Safety { @@ -1055,11 +1159,14 @@ impl FnSig { self.fn_sig_kind.c_variadic() } + pub fn splatted(self) -> Option { + self.fn_sig_kind.splatted() + } + + /// Create a new safe FnSig with no arguments or return type, using the `extern "Rust"` ABI, + /// that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { - inputs_and_output: Default::default(), - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false), - } + Self { inputs_and_output: Default::default(), fn_sig_kind: FnSigKind::dummy() } } } @@ -1092,6 +1199,10 @@ impl ty::Binder> { self.skip_binder().c_variadic() } + pub fn splatted(self) -> Option { + self.skip_binder().splatted() + } + pub fn safety(self) -> I::Safety { self.skip_binder().safety() } @@ -1127,6 +1238,9 @@ impl fmt::Debug for FnSig { if i > 0 { write!(f, ", ")?; } + if Some(i) == fn_sig_kind.splatted().map(usize::from) { + write!(f, "#[splat] ")?; + } write!(f, "{ty:?}")?; } if fn_sig_kind.c_variadic() { @@ -1288,8 +1402,9 @@ impl FnHeader { self.fn_sig_kind.abi() } + /// Create a new safe FnHeader with the `extern "Rust"` ABI, that isn't C-style variadic or splatted. pub fn dummy() -> Self { - Self { fn_sig_kind: FnSigKind::new(ExternAbi::Rust, I::Safety::safe(), false) } + Self { fn_sig_kind: FnSigKind::dummy() } } } diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index b688a001f73fa..9453d30ce91c9 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -364,7 +364,7 @@ pub struct CoroutineClosureSignature { // Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types // never actually differ. But we save them rather than recreating them // from scratch just for good measure. - /// Always safe, RustCall, non-c-variadic + /// Always safe, RustCall, non-c-variadic, non-splatted #[type_visitable(ignore)] #[type_foldable(identity)] pub fn_sig_kind: FnSigKind, diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 196c14d5197eb..d2442358b13cb 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -160,7 +160,7 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg ty::ConstKind::Value(cv) => stack.push(cv.ty().into()), ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), - ty::ConstKind::Unevaluated(_, ct) => { + ty::ConstKind::Alias(_, ct) => { stack.extend(ct.args.iter().rev()); } }, diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 5a900db9d1ce8..065cc128101ae 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -267,7 +267,7 @@ impl VecDeque { /// /// - Ranges must not overlap: `src.abs_diff(dst) >= count`. /// - Ranges must be in bounds of the logical buffer: `src + count <= self.capacity()` and `dst + count <= self.capacity()`. - /// - `head` must be in bounds: `head < self.capacity()`. + /// - `head` must be in bounds: `head < self.capacity()`, unless `self.capacity() == 0`, in which case `head == 0`. #[cfg(not(no_global_oom_handling))] unsafe fn nonoverlapping_ranges( &mut self, diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs new file mode 100644 index 0000000000000..eb02664ee7e46 --- /dev/null +++ b/library/alloc/src/io/error.rs @@ -0,0 +1,277 @@ +use core::io::Custom; +#[cfg_attr(no_global_oom_handling, expect(unused_imports))] +use core::io::CustomOwner; +use core::{error, result}; + +use crate::boxed::Box; +#[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))] +use crate::io::const_error; +use crate::io::{Error, ErrorKind}; + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this [`Error`]. + /// + /// Note that this function allocates memory on the heap. + /// If no extra payload is required, use the `From` conversion from + /// `ErrorKind`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// + /// // creating an error without payload (and without memory allocation) + /// let eof_error = Error::from(ErrorKind::UnexpectedEof); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] + #[inline(never)] + #[rustc_allow_incoherent_impl] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + let custom = custom_owner_from_box(kind, error.into()); + + // SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate. + unsafe { Self::from_custom_owner(custom) } + } + + /// Creates a new I/O error from an arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. It is a shortcut for [`Error::new`][new] + /// with [`ErrorKind::Other`]. + /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method + /// [new]: struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::Error; + /// + /// // errors can be created from strings + /// let custom_error = Error::other("oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::other(custom_error); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[stable(feature = "io_error_other", since = "1.74.0")] + #[rustc_allow_incoherent_impl] + pub fn other(error: E) -> Error + where + E: Into>, + { + Self::new(ErrorKind::Other, error) + } + + /// Consumes the `Error`, returning its inner error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] or [`other`][other], + /// then this function will return [`Some`], + /// otherwise it will return [`None`]. + /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method + /// [new]: struct.Error.html#method.new + /// [other]: struct.Error.html#method.other + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// if let Some(inner_err) = err.into_inner() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn into_inner(self) -> Option> { + let custom_owner = self.into_custom_owner().ok()?; + + let ptr = custom_owner.into_raw().as_ptr(); + + // SAFETY: + // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`. + let custom = unsafe { Box::::from_raw(ptr) }; + + let ptr = custom.into_raw().as_ptr(); + + // SAFETY: + // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate + // to contain a `Custom` which itself was constructed with `Box::into_raw`. + Some(unsafe { Box::from_raw(ptr) }) + } + + /// Attempts to downcast the custom boxed error to `E`. + /// + /// If this [`Error`] contains a custom boxed error, + /// then it would attempt downcasting on the boxed error, + /// otherwise it will return [`Err`]. + /// + /// If the custom boxed error has the same type as `E`, it will return [`Ok`], + /// otherwise it will also return [`Err`]. + /// + /// This method is meant to be a convenience routine for calling + /// `Box::downcast` on the custom boxed error, returned by + /// [`Error::into_inner`][into_inner]. + /// + // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method + /// [into_inner]: struct.Error.html#method.into_inner + /// + /// # Examples + /// + /// ``` + /// use std::fmt; + /// use std::io; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum E { + /// Io(io::Error), + /// SomeOtherVariant, + /// } + /// + /// impl fmt::Display for E { + /// // ... + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # todo!() + /// # } + /// } + /// impl Error for E {} + /// + /// impl From for E { + /// fn from(err: io::Error) -> E { + /// err.downcast::() + /// .unwrap_or_else(E::Io) + /// } + /// } + /// + /// impl From for io::Error { + /// fn from(err: E) -> io::Error { + /// match err { + /// E::Io(io_error) => io_error, + /// e => io::Error::new(io::ErrorKind::Other, e), + /// } + /// } + /// } + /// + /// # fn main() { + /// let e = E::SomeOtherVariant; + /// // Convert it to an io::Error + /// let io_error = io::Error::from(e); + /// // Cast it back to the original variant + /// let e = E::from(io_error); + /// assert!(matches!(e, E::SomeOtherVariant)); + /// + /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); + /// // Convert it to E + /// let e = E::from(io_error); + /// // Cast it back to the original variant + /// let io_error = io::Error::from(e); + /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); + /// assert!(io_error.get_ref().is_none()); + /// assert!(io_error.raw_os_error().is_none()); + /// # } + /// ``` + #[stable(feature = "io_error_downcast", since = "1.79.0")] + #[rustc_allow_incoherent_impl] + pub fn downcast(self) -> result::Result + where + E: error::Error + Send + Sync + 'static, + { + if let Some(e) = self.get_ref() + && e.is::() + { + if let Some(b) = self.into_inner() + && let Ok(err) = b.downcast::() + { + Ok(*err) + } else { + // Safety: We have just checked that the condition is true + unsafe { core::hint::unreachable_unchecked() } + } + } else { + Err(self) + } + } +} + +#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Error { + /// Converts a [`crate::ffi::NulError`] into a [`Error`]. + fn from(_: crate::ffi::NulError) -> Error { + const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + } +} + +#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] +impl From for Error { + /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. + /// + /// `TryReserveError` won't be available as the error `source()`, + /// but this may change in the future. + fn from(_: crate::collections::TryReserveError) -> Error { + // ErrorData::Custom allocates, which isn't great for handling OOM errors. + ErrorKind::OutOfMemory.into() + } +} + +#[cfg(not(no_global_oom_handling))] +fn custom_owner_from_box( + kind: ErrorKind, + error: Box, +) -> CustomOwner { + /// # Safety + /// + /// `ptr` must be valid to pass into `Box::from_raw`. + unsafe fn drop_box_raw(ptr: *mut T) { + // SAFETY + // Caller ensures `ptr` is valid to pass into `Box::from_raw`. + drop(unsafe { Box::from_raw(ptr) }) + } + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let error = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(error)) }; + + // SAFETY: + // * `error` is valid up to a static lifetime, and owns its pointee. + // * `drop_box_raw` is safe to call for the pointer `error` exactly once. + // * `drop_box_raw` is safe to call on a pointer to this instance of `Custom`, + // and will be stored in a `CustomOwner`. + let custom = unsafe { Custom::from_raw(kind, error, drop_box_raw, drop_box_raw) }; + + // SAFETY: the pointer returned by Box::into_raw is non-null. + let custom = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(custom))) }; + + // SAFETY: the `outer_drop` provided to `custom` is valid for itself. + unsafe { CustomOwner::from_raw(custom) } +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..58e5ffa39d0c4 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,20 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +mod error; + +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use core::io::RawOsError; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use core::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use core::io::const_error; +#[unstable(feature = "core_io_borrowed_buf", issue = "117693")] +pub use core::io::{BorrowedBuf, BorrowedCursor}; +#[unstable(feature = "alloc_io", issue = "154046")] +pub use core::io::{ + Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty, + repeat, sink, +}; +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub use core::io::{OsFunctions, chain, take}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index b0e8039263ffe..3bd05eec07974 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -65,15 +65,17 @@ issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables, duplicate_features), deny(warnings))) )] -#![doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr")))] +#![doc(auto_cfg( + hide(no_global_oom_handling, no_rc, no_sync), + hide(target_has_atomic, values("ptr")), +))] #![doc(rust_logo)] #![feature(rustdoc_internals)] #![no_std] #![needs_allocator] // Lints: #![deny(unsafe_op_in_unsafe_fn)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![warn(deprecated_in_future)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] @@ -114,6 +116,9 @@ #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] +#![feature(core_io)] +#![feature(core_io_borrowed_buf)] +#![feature(core_io_internals)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] #![feature(diagnostic_on_move)] @@ -131,6 +136,8 @@ #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] +#![feature(io_const_error)] +#![feature(io_const_error_internals)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] @@ -145,6 +152,7 @@ #![feature(ptr_cast_slice)] #![feature(ptr_internals)] #![feature(ptr_metadata)] +#![feature(raw_os_error_ty)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] #![feature(share_trait)] @@ -234,6 +242,8 @@ pub mod collections; pub mod ffi; pub mod fmt; pub mod intrinsics; +#[unstable(feature = "alloc_io", issue = "154046")] +pub mod io; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b37f865020fd9..c3b86a635bd0f 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1760,7 +1760,11 @@ impl Vec { #[must_use] pub fn into_array(self) -> Result, Self> { if self.len() == N { - Ok(self.into_boxed_slice().into_array().ok().unwrap()) + // SAFETY: `Box::into_array` is guaranteed to return `Ok` if the + // length of the slice is equal to `N`. + // `self.into_boxed_slice().len()` is equal to `self.len()`, + // which we just checked. + Ok(unsafe { self.into_boxed_slice().into_array().unwrap_unchecked() }) } else { Err(self) } diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 2be7a24d2de7b..4b7139d943593 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -6,8 +6,7 @@ #![feature(slice_partition_dedup)] #![feature(strict_provenance_lints)] #![feature(test)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] extern crate test; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f7feb3bbbca85..f89b974cd290c 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -43,8 +43,7 @@ #![feature(vec_try_remove)] #![feature(ptr_cast_slice)] #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] extern crate alloc; diff --git a/library/compiler-builtins/compiler-builtins/src/arm.rs b/library/compiler-builtins/compiler-builtins/src/arm.rs index 0c15b37df1dc1..f26fce2d912b6 100644 --- a/library/compiler-builtins/compiler-builtins/src/arm.rs +++ b/library/compiler-builtins/compiler-builtins/src/arm.rs @@ -84,7 +84,11 @@ intrinsics! { /// /// Usual `memcpy` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // SAFETY: memcpy preconditions apply. unsafe { crate::mem::memcpy(dst, src, n) }; } @@ -96,7 +100,11 @@ intrinsics! { /// Usual `memcpy` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy4(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy4( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // We are guaranteed 4-alignment, so accessing at u32 is okay. let mut dst = dst.cast::(); let mut src = src.cast::(); @@ -121,7 +129,7 @@ intrinsics! { } // SAFETY: `dst` and `src` will still be valid for `n` bytes - unsafe { __aeabi_memcpy(dst.cast::(), src.cast::(), n) }; + unsafe { __aeabi_memcpy(dst.cast::(), src.cast::(), n) }; } /// `memcpy` for 8-byte alignment. @@ -131,7 +139,11 @@ intrinsics! { /// Usual `memcpy` requirements apply. Additionally, `dest` and `src` must be aligned to /// eight bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memcpy8(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memcpy8( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(8)); debug_assert!(src.addr().is_multiple_of(8)); @@ -145,7 +157,11 @@ intrinsics! { /// /// Usual `memmove` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memmove(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { // SAFETY: memmove preconditions apply. unsafe { crate::mem::memmove(dst, src, n) }; } @@ -157,7 +173,11 @@ intrinsics! { /// Usual `memmove` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memmove4(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove4( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(4)); debug_assert!(src.addr().is_multiple_of(4)); @@ -172,7 +192,11 @@ intrinsics! { /// Usual `memmove` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memmove8(dst: *mut u8, src: *const u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memmove8( + dst: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) { debug_assert!(dst.addr().is_multiple_of(8)); debug_assert!(src.addr().is_multiple_of(8)); @@ -186,7 +210,11 @@ intrinsics! { /// /// Usual `memset` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { // Note the different argument order // SAFETY: memset preconditions apply. unsafe { crate::mem::memset(dst, c, n) }; @@ -199,7 +227,11 @@ intrinsics! { /// Usual `memset` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset4(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset4( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { let mut dst = dst.cast::(); debug_assert!(dst.is_aligned()); let mut n = n; @@ -222,7 +254,7 @@ intrinsics! { } // SAFETY: `dst` will still be valid for `n` bytes - unsafe { __aeabi_memset(dst.cast::(), n, byte as i32) }; + unsafe { __aeabi_memset(dst.cast::(), n, byte as i32) }; } /// `memset` for 8-byte alignment. @@ -232,7 +264,11 @@ intrinsics! { /// Usual `memset` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memset8(dst: *mut u8, n: usize, c: i32) { + pub unsafe extern "aapcs" fn __aeabi_memset8( + dst: *mut core::ffi::c_void, + n: usize, + c: i32 + ) { debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memset preconditions apply, less strict alignment. @@ -245,7 +281,7 @@ intrinsics! { /// /// Usual `memclr` requirements apply. #[cfg(not(target_vendor = "apple"))] - pub unsafe extern "aapcs" fn __aeabi_memclr(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr(dst: *mut core::ffi::c_void, n: usize) { // SAFETY: memclr preconditions apply, less strict alignment. unsafe { __aeabi_memset(dst, n, 0) }; } @@ -257,7 +293,7 @@ intrinsics! { /// Usual `memclr` requirements apply. Additionally, `dest` and `src` must be aligned to /// four bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr4(dst: *mut core::ffi::c_void, n: usize) { debug_assert!(dst.addr().is_multiple_of(4)); // SAFETY: memclr preconditions apply, less strict alignment. @@ -271,7 +307,7 @@ intrinsics! { /// Usual `memclr` requirements apply. Additionally, `dst` and `src` must be aligned to /// eight bytes. #[cfg(not(any(target_vendor = "apple", target_env = "msvc")))] - pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut u8, n: usize) { + pub unsafe extern "aapcs" fn __aeabi_memclr8(dst: *mut core::ffi::c_void, n: usize) { debug_assert!(dst.addr().is_multiple_of(8)); // SAFETY: memclr preconditions apply, less strict alignment. diff --git a/library/compiler-builtins/compiler-builtins/src/mem/mod.rs b/library/compiler-builtins/compiler-builtins/src/mem/mod.rs index ac41cd33416f6..ef0f4da228e96 100644 --- a/library/compiler-builtins/compiler-builtins/src/mem/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/mem/mod.rs @@ -9,37 +9,57 @@ mod impls; intrinsics! { #[mem_builtin] - pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { - impls::copy_forward(dest, src, n); + pub unsafe extern "C" fn memcpy( + dest: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) -> *mut core::ffi::c_void { + impls::copy_forward(dest.cast(), src.cast(), n); dest } #[mem_builtin] - pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + pub unsafe extern "C" fn memmove( + dest: *mut core::ffi::c_void, + src: *const core::ffi::c_void, + n: usize + ) -> *mut core::ffi::c_void { let delta = (dest as usize).wrapping_sub(src as usize); if delta >= n { // We can copy forwards because either dest is far enough ahead of src, // or src is ahead of dest (and delta overflowed). - impls::copy_forward(dest, src, n); + impls::copy_forward(dest.cast(), src.cast(), n); } else { - impls::copy_backward(dest, src, n); + impls::copy_backward(dest.cast(), src.cast(), n); } dest } #[mem_builtin] - pub unsafe extern "C" fn memset(s: *mut u8, c: core::ffi::c_int, n: usize) -> *mut u8 { - impls::set_bytes(s, c as u8, n); + pub unsafe extern "C" fn memset( + s: *mut core::ffi::c_void, + c: core::ffi::c_int, + n: usize + ) -> *mut core::ffi::c_void { + impls::set_bytes(s.cast(), c as u8, n); s } #[mem_builtin] - pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> core::ffi::c_int { - impls::compare_bytes(s1, s2, n) + pub unsafe extern "C" fn memcmp( + s1: *const core::ffi::c_void, + s2: *const core::ffi::c_void, + n: usize + ) -> core::ffi::c_int { + impls::compare_bytes(s1.cast(), s2.cast(), n) } #[mem_builtin] - pub unsafe extern "C" fn bcmp(s1: *const u8, s2: *const u8, n: usize) -> core::ffi::c_int { + pub unsafe extern "C" fn bcmp( + s1: *const core::ffi::c_void, + s2: *const core::ffi::c_void, + n: usize + ) -> core::ffi::c_int { memcmp(s1, s2, n) } diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 8f435dd72d7a1..5421b1d2a2652 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -40,5 +40,8 @@ check-cfg = [ 'cfg(target_has_reliable_f128)', 'cfg(target_has_reliable_f128_math)', 'cfg(llvm_enzyme)', + # Prevents use of a static variable for providing platform specific RawOsError + # functionality + 'cfg(no_io_statics)', ] diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 3f1aa54050a31..df6396b84264d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -85,3 +85,30 @@ impl fmt::Debug for c_void { )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] unsafe extern "C" {} + +// Used by rustc for checking the definitions of other function with the same symbol names +// +// See the `invalid_runtime_symbols_definitions` lint. +mod runtime_symbols { + use crate::ffi::{c_char, c_int, c_void}; + + unsafe extern "C" { + #[lang = "memcpy_fn"] + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memmove_fn"] + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + #[lang = "memset_fn"] + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + #[lang = "memcmp_fn"] + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "bcmp_fn"] + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + #[lang = "strlen_fn"] + fn strlen(s: *const c_char) -> usize; + } +} diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 838dab90a6de0..4671e04062fb0 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -332,9 +332,7 @@ mod flags { } impl FormattingOptions { - /// Construct a new `FormatterBuilder` with the supplied `Write` trait - /// object for output that is equivalent to the `{}` formatting - /// specifier: + /// Construct a new `FormattingOptions` representing the plain `{}` formatting specifier: /// /// - no flags, /// - filled with spaces, @@ -518,7 +516,7 @@ impl FormattingOptions { pub const fn get_precision(&self) -> Option { if self.flags & flags::PRECISION_FLAG != 0 { Some(self.precision) } else { None } } - /// Returns the current precision. + /// Returns the current `x?` or `X?` flag. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn get_debug_as_hex(&self) -> Option { if self.flags & flags::DEBUG_LOWER_HEX_FLAG != 0 { diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 6c639a8b092b3..050822da8f12a 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -361,7 +361,7 @@ macro_rules! impl_Display { #[cfg(feature = "optimize_for_size")] fn ${concat($fmt_fn, _small)}(n: $T, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const MAX_DEC_N: usize = $T::MAX.ilog(10) as usize + 1; + const MAX_DEC_N: usize = $T::MAX.ilog10() as usize + 1; let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; let offset = ${concat($fmt_fn, _in_buf_small)}(n, &mut buf); diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index af8acda6633c5..624a953062332 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -17,13 +17,13 @@ macro_rules! impl_NumBufferTrait { #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] impl NumBufferTrait for $signed { // `+ 2` and not `+ 1` to include the `-` character. - const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $signed::MAX.ilog(10) as usize + 2]; - type Buf = [MaybeUninit; $signed::MAX.ilog(10) as usize + 2]; + const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $signed::MAX.ilog10() as usize + 2]; + type Buf = [MaybeUninit; $signed::MAX.ilog10() as usize + 2]; } #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] impl NumBufferTrait for $unsigned { - const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $unsigned::MAX.ilog(10) as usize + 1]; - type Buf = [MaybeUninit; $unsigned::MAX.ilog(10) as usize + 1]; + const DEFAULT: Self::Buf = [MaybeUninit::::uninit(); $unsigned::MAX.ilog10() as usize + 1]; + type Buf = [MaybeUninit; $unsigned::MAX.ilog10() as usize + 1]; } )* } @@ -74,7 +74,6 @@ impl NumBuffer { #[stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "int_format_into", since = "CURRENT_RUSTC_VERSION")] pub const fn new() -> Self { - // FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40. NumBuffer { buf: T::DEFAULT, phantom: core::marker::PhantomData } } } diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index ebbec6c5e8210..24d3bfc67b988 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -16,6 +16,7 @@ /// code, but use an in-memory buffer in our tests. We can do this with /// `Cursor`: /// +// FIXME(#74481): Hard-links required to link from `core` to `std` /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html @@ -80,6 +81,7 @@ impl Cursor { /// is not empty. So writing to cursor starts with overwriting [`Vec`] /// content, not with appending to it. /// + // FIXME(#74481): Hard-links required to link from `core` to `alloc` /// [`Vec`]: ../../alloc/vec/struct.Vec.html /// /// # Examples diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 961e3c7281200..2d15307daf153 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -1,6 +1,714 @@ #![unstable(feature = "core_io", issue = "154046")] -use crate::fmt; +// On 64-bit platforms, `io::Error` may use a bit-packed representation to +// reduce size. However, this representation assumes that error codes are +// always 32-bit wide. +// +// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. +// Therefore, the packed representation is explicitly disabled for UEFI +// targets, and the unpacked representation must be used instead. +#[cfg_attr( + all(target_pointer_width = "64", not(target_os = "uefi")), + path = "error/repr_bitpacked.rs" +)] +#[cfg_attr( + not(all(target_pointer_width = "64", not(target_os = "uefi"))), + path = "error/repr_unpacked.rs" +)] +mod repr; + +#[cfg_attr( + all(target_has_atomic_load_store = "ptr", not(no_io_statics)), + path = "error/os_functions_atomic.rs" +)] +#[cfg_attr( + not(all(target_has_atomic_load_store = "ptr", not(no_io_statics))), + path = "error/os_functions.rs" +)] +mod os_functions; + +use self::os_functions::{decode_error_kind, format_os_error, is_interrupted, set_functions}; +use self::repr::Repr; +use crate::{error, fmt, result}; + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This type alias is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`core::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// of [`core::result::Result`][`Result`]. +/// +// FIXME(#74481): Hard-links required to link from `core` to `std` +/// [`std::io`]: ../../std/io/index.html +/// [`io::Error`]: Error +/// [`Result`]: crate::result::Result +/// [prelude]: crate::prelude +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(search_unbox)] +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`][Read], [`Write`][Write], [`Seek`][Seek], and +/// associated traits. +/// +/// Errors mostly originate from the underlying OS, but custom instances of +/// `Error` can be created with crafted error messages and a particular value of +/// [`ErrorKind`]. +/// +// FIXME(#74481): Hard-links required to link from `core` to `std` +/// [Read]: ../../std/io/trait.Read.html +/// [Write]: ../../std/io/trait.Write.html +/// [Seek]: ../../std/io/trait.Seek.html +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] +pub struct Error { + repr: Repr, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +/// Common errors constants for use in std +#[doc(hidden)] +impl Error { + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const INVALID_UTF8: Self = + const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const READ_EXACT_EOF: Self = + const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( + ErrorKind::NotFound, + "the number of hardware threads is not known for the target platform", + ); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const UNSUPPORTED_PLATFORM: Self = + const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const WRITE_ALL_EOF: Self = + const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const ZERO_TIMEOUT: Self = + const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); +} + +// Only derive debug in tests, to make sure it +// doesn't accidentally get printed. +#[cfg_attr(test, derive(Debug))] +enum ErrorData { + Os(RawOsError), + Simple(ErrorKind), + SimpleMessage(&'static SimpleMessage), + Custom(C), +} + +// `#[repr(align(4))]` is probably redundant, it should have that value or +// higher already. We include it just because repr_bitpacked.rs's encoding +// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the +// alignment required by the struct, only increase it). +// +// If we add more variants to ErrorData, this can be increased to 8, but it +// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or +// whatever cfg we're using to enable the `repr_bitpacked` code, since only the +// that version needs the alignment, and 8 is higher than the alignment we'll +// have on 32 bit platforms. +// +// (For the sake of being explicit: the alignment requirement here only matters +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// matter at all) +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +#[repr(align(4))] +#[derive(Debug)] +pub struct SimpleMessage { + pub kind: ErrorKind, + pub message: &'static str, +} + +/// Creates a new I/O error from a known kind of error and a string literal. +/// +/// Contrary to [`Error::new`][new], this macro does not allocate and can be used in +/// `const` contexts. +/// +// FIXME(#74481): Hard-links required to link from `core` to `alloc` for incoherent method +/// [new]: ../../alloc/io/struct.Error.html#method.new +/// +/// # Example +/// ``` +/// #![feature(io_const_error)] +/// use std::io::{const_error, Error, ErrorKind}; +/// +/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); +/// +/// fn not_here() -> Result<(), Error> { +/// Err(FAIL) +/// } +/// ``` +#[rustc_macro_transparency = "semiopaque"] +#[unstable(feature = "io_const_error", issue = "133448")] +#[allow_internal_unstable(core_io, hint_must_use, io_const_error_internals)] +pub macro const_error($kind:expr, $message:expr $(,)?) { + $crate::hint::must_use($crate::io::Error::from_static_message( + const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, + )) +} + +/// Intended for use for errors not exposed to the user, where allocating onto +/// the heap (for normal construction via Error::new) is too costly. +#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +impl From for Error { + /// Converts an [`ErrorKind`] into an [`Error`]. + /// + /// This conversion creates a new error with a simple representation of error kind. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// let not_found = ErrorKind::NotFound; + /// let error = Error::from(not_found); + /// assert_eq!("entity not found", format!("{error}")); + /// ``` + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { repr: Repr::new_simple(kind) } + } +} + +impl Error { + /// # Safety + /// + /// The provided `CustomOwner` must have been constructed from a `Box` from the `alloc` crate. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_custom_owner(custom: CustomOwner) -> Error { + Error { repr: Repr::new_custom(custom) } + } + + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub fn into_custom_owner(self) -> result::Result { + if matches!(self.repr.data(), ErrorData::Custom(..)) { + let ErrorData::Custom(c) = self.repr.into_data() else { + // SAFETY: Checked above using `matches!`. + unsafe { crate::hint::unreachable_unchecked() } + }; + Ok(c) + } else { + Err(self) + } + } + + /// Creates a new I/O error from a known kind of error as well as a constant + /// message. + /// + /// This function does not allocate. + /// + /// You should not use this directly, and instead use the `const_error!` + /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. + /// + /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. + #[inline] + #[doc(hidden)] + #[unstable(feature = "io_const_error_internals", issue = "none")] + pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { + Self { repr: Repr::new_simple_message(msg) } + } + + /// # Safety + /// + /// `functions` must point to data that is entirely constant; it must + /// not be created during runtime. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_with_functions( + code: RawOsError, + functions: &'static OsFunctions, + ) -> Error { + // SAFETY: Caller ensures `functions` is a constant not created at runtime. + unsafe { + set_functions(functions); + } + Error { repr: Repr::new_os(code) } + } + + /// Returns the OS error that this error represents (if any). + /// + /// If this [`Error`] was constructed via [`last_os_error`][last_os_error] or + /// [`from_raw_os_error`][from_raw_os_error], then this function will return [`Some`], otherwise + /// it will return [`None`]. + /// + // FIXME(#74481): Hard-links required to link from `core` to `std` for incoherent method + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// [from_raw_os_error]: ../../std/io/struct.Error.html#method.from_raw_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_os_error(err: &Error) { + /// if let Some(raw_os_err) = err.raw_os_error() { + /// println!("raw OS error: {raw_os_err:?}"); + /// } else { + /// println!("Not an OS error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "raw OS error: ...". + /// print_os_error(&Error::last_os_error()); + /// // Will print "Not an OS error". + /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn raw_os_error(&self) -> Option { + match self.repr.data() { + ErrorData::Os(i) => Some(i), + ErrorData::Custom(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + } + } + + /// Returns a reference to the inner error wrapped by this error (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err:?}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_ref()), + } + } + + /// Returns a mutable reference to the inner error wrapped by this error + /// (if any). + /// + /// If this [`Error`] was constructed via [`new`][new] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + // FIXME(#74481): Hard-links required to link from `core` to `std` + /// [new]: ../../alloc/io/struct.Error.html#method.new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// use std::{error, fmt}; + /// use std::fmt::Display; + /// + /// #[derive(Debug)] + /// struct MyError { + /// v: String, + /// } + /// + /// impl MyError { + /// fn new() -> MyError { + /// MyError { + /// v: "oh no!".to_string() + /// } + /// } + /// + /// fn change_message(&mut self, new_message: &str) { + /// self.v = new_message.to_string(); + /// } + /// } + /// + /// impl error::Error for MyError {} + /// + /// impl Display for MyError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "MyError: {}", self.v) + /// } + /// } + /// + /// fn change_error(mut err: Error) -> Error { + /// if let Some(inner_err) = err.get_mut() { + /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); + /// } + /// err + /// } + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&change_error(Error::last_os_error())); + /// // Will print "Inner error: ...". + /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + match self.repr.data_mut() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.error_mut()), + } + } + + /// Returns the corresponding [`ErrorKind`] for this error. + /// + /// This may be a value set by Rust code constructing custom `io::Error`s, + /// or if this `io::Error` was sourced from the operating system, + /// it will be a value inferred from the system's error encoding. + /// See [`last_os_error`][last_os_error] for more details. + /// + // FIXME(#74481): Hard-links required to link from `core` to `std` + /// [last_os_error]: ../../std/io/struct.Error.html#method.last_os_error + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // As no error has (visibly) occurred, this may print anything! + /// // It likely prints a placeholder for unidentified (non-)errors. + /// print_error(Error::last_os_error()); + /// // Will print "AddrInUse". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn kind(&self) -> ErrorKind { + match self.repr.data() { + ErrorData::Os(code) => decode_error_kind(code), + ErrorData::Custom(c) => c.kind, + ErrorData::Simple(kind) => kind, + ErrorData::SimpleMessage(m) => m.kind, + } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + #[doc(hidden)] + #[inline] + pub fn is_interrupted(&self) -> bool { + match self.repr.data() { + ErrorData::Os(code) => is_interrupted(code), + ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, + ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, + ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, + } + } +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data() { + ErrorData::Os(code) => fmt + .debug_struct("Os") + .field("code", &code) + .field("kind", &decode_error_kind(code)) + .field( + "message", + &fmt::from_fn(|fmt| { + write!(fmt, "\"{}\"", fmt::from_fn(|fmt| format_os_error(code, fmt))) + }), + ) + .finish(), + ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), + ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + ErrorData::SimpleMessage(msg) => fmt + .debug_struct("Error") + .field("kind", &msg.kind) + .field("message", &msg.message) + .finish(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.repr.data() { + ErrorData::Os(code) => { + let detail = fmt::from_fn(|fmt| format_os_error(code, fmt)); + write!(fmt, "{detail} (os error {code})") + } + ErrorData::Custom(c) => fmt::Display::fmt(c.error_ref(), fmt), + ErrorData::Simple(kind) => kind.fmt(fmt), + ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for Error { + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn error::Error> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().cause(), + } + } + + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error_ref().source(), + } + } +} + +fn _assert_error_is_sync_send() { + fn _is_sync_send() {} + _is_sync_send::(); +} + +#[doc(hidden)] +#[derive(Debug)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct OsFunctions { + pub format_os_error: fn(_: RawOsError, _: &mut fmt::Formatter<'_>) -> fmt::Result, + pub decode_error_kind: fn(_: RawOsError) -> ErrorKind, + pub is_interrupted: fn(_: RawOsError) -> bool, +} + +impl OsFunctions { + const DEFAULT: &'static OsFunctions = &OsFunctions { + format_os_error: |_, _| Ok(()), + decode_error_kind: |_| ErrorKind::Uncategorized, + is_interrupted: |_| false, + }; +} + +// As with `SimpleMessage`: `#[repr(align(4))]` here is just because +// repr_bitpacked's encoding requires it. In practice it almost certainly be +// already be this high or higher. +#[doc(hidden)] +#[repr(align(4))] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub struct Custom { + kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), +} + +// SAFETY: All members of `Custom` are `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for Custom {} + +// SAFETY: All members of `Custom` are `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for Custom {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl fmt::Debug for Custom { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Custom").field("kind", &self.kind).field("error", self.error_ref()).finish() + } +} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for Custom { + fn drop(&mut self) { + // SAFETY: `Custom::from_raw` ensures this call is safe. + unsafe { + (self.error_drop)(self.error.as_ptr()); + } + } +} + +impl Custom { + /// # Safety + /// + /// * `error` must be valid for up to a static lifetime, and own its pointee. + /// * `error_drop` must be safe to call for the pointer `error` exactly once. + /// * `outer_drop` must be safe to call on a pointer to this instance of `Custom` + /// if it were stored within a [`CustomOwner`]. + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw( + kind: ErrorKind, + error: crate::ptr::NonNull, + error_drop: unsafe fn(*mut (dyn error::Error + Send + Sync)), + outer_drop: unsafe fn(*mut Self), + ) -> Custom { + Custom { kind, error, error_drop, outer_drop } + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.error; + core::mem::forget(self); + ptr + } + + fn error_ref(&self) -> &(dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_ref() } + } + + fn error_mut(&mut self) -> &mut (dyn error::Error + Send + Sync + 'static) { + // SAFETY: + // `from_raw` ensures `error` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.error.as_mut() } + } +} + +#[derive(Debug)] +#[repr(transparent)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +pub struct CustomOwner(crate::ptr::NonNull); + +// SAFETY: Custom is `Send` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Send for CustomOwner {} + +// SAFETY: Custom is `Sync` +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +unsafe impl Sync for CustomOwner {} + +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +impl Drop for CustomOwner { + fn drop(&mut self) { + // SAFETY: `CustomOwner::from_raw` ensures this call is safe. + unsafe { + (self.0.as_ref().outer_drop)(self.0.as_ptr()); + } + } +} + +impl CustomOwner { + /// # Safety + /// + /// * The `outer_drop` of the provided `custom` must be safe to call exactly once. + #[doc(hidden)] + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub unsafe fn from_raw(custom: crate::ptr::NonNull) -> CustomOwner { + CustomOwner(custom) + } + + #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] + pub fn into_raw(self) -> crate::ptr::NonNull { + let ptr = self.0; + core::mem::forget(self); + ptr + } + + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_ref(&self) -> &Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_ref() } + } + + #[allow(dead_code, reason = "only used for unpacked representation")] + fn custom_mut(&mut self) -> &mut Custom { + // SAFETY: + // `from_raw` ensures `0` is a valid pointer up to a static lifetime + // and is owned by `self` + unsafe { self.0.as_mut() } + } +} /// The type of raw OS error codes. /// @@ -23,7 +731,7 @@ pub type RawOsError = cfg_select! { /// /// It is used with the [`io::Error`][error] type. /// -/// [error]: ../../std/io/struct.Error.html +/// [error]: Error /// /// # Handling errors and matching on `ErrorKind` /// @@ -258,7 +966,7 @@ pub enum ErrorKind { /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. /// New [`ErrorKind`]s might be added in the future for some of those. /// - /// [error]: ../../std/io/struct.Error.html + /// [error]: Error #[stable(feature = "rust1", since = "1.0.0")] Other, @@ -273,7 +981,7 @@ pub enum ErrorKind { } impl ErrorKind { - pub(crate) const fn as_str(&self) -> &'static str { + const fn as_str(&self) -> &'static str { use ErrorKind::*; match *self { // tidy-alphabetical-start @@ -328,9 +1036,8 @@ impl ErrorKind { // unsafe, or to hard-code max ErrorKind or its size in a way the compiler // couldn't verify. #[inline] - #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] - #[doc(hidden)] - pub const fn from_prim(ek: u32) -> Option { + #[allow(dead_code, reason = "only used for packed representation")] + const fn from_prim(ek: u32) -> Option { macro_rules! from_prim { ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ // Force a compile error if the list gets out of date. diff --git a/library/core/src/io/error/os_functions.rs b/library/core/src/io/error/os_functions.rs new file mode 100644 index 0000000000000..b34b7c7dc7926 --- /dev/null +++ b/library/core/src/io/error/os_functions.rs @@ -0,0 +1,30 @@ +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { + // FIXME: externally implementable items may allow for weak linkage, allowing + // these methods to be overridden even when atomic pointers are not supported. +} + +#[inline] +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = OsFunctions::DEFAULT; + (f.format_os_error)(errno, fmt) +} + +#[inline] +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = OsFunctions::DEFAULT; + (f.decode_error_kind)(errno) +} + +#[inline] +pub(super) fn is_interrupted(errno: RawOsError) -> bool { + let f = OsFunctions::DEFAULT; + (f.is_interrupted)(errno) +} diff --git a/library/core/src/io/error/os_functions_atomic.rs b/library/core/src/io/error/os_functions_atomic.rs new file mode 100644 index 0000000000000..dfaf59ae742de --- /dev/null +++ b/library/core/src/io/error/os_functions_atomic.rs @@ -0,0 +1,61 @@ +//! OS-dependent functions +//! +//! `Error` needs OS functionalities to work interpret raw OS errors, but +//! we can't link to anythink here in `alloc`. Therefore, we restrict +//! creation of `Error` from raw OS errors in `std`, and require providing +//! a vtable of operations when creating one. + +// FIXME: replace this with externally implementable items once they are more stable + +use super::{ErrorKind, OsFunctions, RawOsError}; +use crate::fmt; +use crate::sync::atomic; + +/// These default functions are not reachable, but have them just to be safe. +static OS_FUNCTIONS: atomic::AtomicPtr = + atomic::AtomicPtr::new(OsFunctions::DEFAULT as *const _ as *mut _); + +fn get_os_functions() -> &'static OsFunctions { + // SAFETY: + // * `OS_FUNCTIONS` is initially a pointer to `OsFunctions::DEFAULT`, which is valid for a static lifetime. + // * `OS_FUNCTIONS` can only be changed by `set_functions`, which only accepts `&'static OsFunctions`. + // * Therefore, `OS_FUNCTIONS` must always contain a valid non-null pointer with a static lifetime. + // * `Relaxed` ordering is sufficient as the only way to write to `OS_FUNCTIONS` is through + // `set_functions`, which has as a safety precondition that any value passed in must + // be constant and not created during runtime. + unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) } +} + +/// # Safety +/// +/// The provided reference must point to data that is entirely constant; it must +/// not be created during runtime. +#[inline] +pub(super) unsafe fn set_functions(f: &'static OsFunctions) { + #[cold] + fn set_functions_inner(f: &'static OsFunctions) { + OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); + } + + if OS_FUNCTIONS.load(atomic::Ordering::Relaxed) != f as *const _ as *mut _ { + set_functions_inner(f); + } +} + +#[inline] +pub(super) fn format_os_error(errno: RawOsError, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = get_os_functions(); + (f.format_os_error)(errno, fmt) +} + +#[inline] +pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = get_os_functions(); + (f.decode_error_kind)(errno) +} + +#[inline] +pub(super) fn is_interrupted(errno: RawOsError) -> bool { + let f = get_os_functions(); + (f.is_interrupted)(errno) +} diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/core/src/io/error/repr_bitpacked.rs similarity index 88% rename from library/std/src/io/error/repr_bitpacked.rs rename to library/core/src/io/error/repr_bitpacked.rs index 7c237825459af..22cb331924081 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/core/src/io/error/repr_bitpacked.rs @@ -106,7 +106,7 @@ use core::marker::PhantomData; use core::num::NonZeroUsize; use core::ptr::NonNull; -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; // The 2 least-significant bits are used as tag. const TAG_MASK: usize = 0b11; @@ -126,15 +126,15 @@ const TAG_SIMPLE: usize = 0b11; /// ``` #[repr(transparent)] #[rustc_insignificant_dtor] -pub(super) struct Repr(NonNull<()>, PhantomData>>); +pub(super) struct Repr(NonNull<()>, PhantomData>); // All the types `Repr` stores internally are Send + Sync, and so is it. unsafe impl Send for Repr {} unsafe impl Sync for Repr {} impl Repr { - pub(super) fn new_custom(b: Box) -> Self { - let p = Box::into_raw(b).cast::(); + pub(super) fn new_custom(b: CustomOwner) -> Self { + let p = b.into_raw().as_ptr().cast::(); // Should only be possible if an allocator handed out a pointer with // wrong alignment. debug_assert_eq!(p.addr() & TAG_MASK, 0); @@ -146,7 +146,8 @@ impl Repr { // (rather than `ptr::wrapping_add`), but it's unclear this would give // any benefit, so we just use `wrapping_add` instead. let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); - // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, + // SAFETY: + // `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, // because `p`'s alignment means it isn't allowed to have any of the // `TAG_BITS` set (you can verify that addition and bitwise-or are the // same when the operands have no bits in common using a truth table). @@ -156,7 +157,8 @@ impl Repr { // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the // `new_unchecked` is safe. - let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); + let ptr = unsafe { NonNull::new_unchecked(tagged) }; + let res = Self(ptr, PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); @@ -166,11 +168,9 @@ impl Repr { #[inline] pub(super) fn new_os(code: RawOsError) -> Self { let utagged = ((code as usize) << 32) | TAG_OS; - // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -183,11 +183,9 @@ impl Repr { #[inline] pub(super) fn new_simple(kind: ErrorKind) -> Self { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; - // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. - let res = Self( - NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), - PhantomData, - ); + // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + let utagged = unsafe { NonZeroUsize::new_unchecked(utagged) }; + let res = Self(NonNull::without_provenance(utagged), PhantomData); // quickly smoke-check we encoded the right thing (This generally will // only run in std's tests, unless the user uses -Zbuild-std) debug_assert!( @@ -200,38 +198,43 @@ impl Repr { #[inline] pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { - // Safety: References are never null. - Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) + // SAFETY: References are never null. + let ptr = unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }; + Self(ptr, PhantomData) } #[inline] pub(super) fn data(&self) -> ErrorData<&Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &*c) } } #[inline] pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { - // Safety: We're a Repr, decode_repr is fine. + // SAFETY: We're a Repr, decode_repr is fine. unsafe { decode_repr(self.0, |c| &mut *c) } } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { let this = core::mem::ManuallyDrop::new(self); - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we prevent double-drop using `ManuallyDrop`. - unsafe { decode_repr(this.0, |p| Box::from_raw(p)) } + unsafe { + decode_repr(this.0, |p| CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p))) + } } } impl Drop for Repr { #[inline] fn drop(&mut self) { - // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is // safe because we're being dropped. unsafe { - let _ = decode_repr(self.0, |p| Box::::from_raw(p)); + let _ = decode_repr(self.0, |p| { + CustomOwner::from_raw(core::ptr::NonNull::new_unchecked(p)) + }); } } } @@ -255,7 +258,7 @@ where let kind_bits = (bits >> 32) as u32; let kind = ErrorKind::from_prim(kind_bits).unwrap_or_else(|| { debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); - // This means the `ptr` passed in was not valid, which violates + // SAFETY: This means the `ptr` passed in was not valid, which violates // the unsafe contract of `decode_repr`. // // Using this rather than unwrap meaningfully improves the code @@ -306,7 +309,7 @@ static_assert!(@usize_eq: size_of::>(), size_of::()); // `Custom` and `SimpleMessage` need to be thin pointers. static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); -static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::(), 8); static_assert!((TAG_MASK + 1).is_power_of_two()); // And they must have sufficient alignment. diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/core/src/io/error/repr_unpacked.rs similarity index 76% rename from library/std/src/io/error/repr_unpacked.rs rename to library/core/src/io/error/repr_unpacked.rs index b3e7b5f024ea0..a32ed58ce1b74 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/core/src/io/error/repr_unpacked.rs @@ -2,15 +2,15 @@ //! non-64bit targets, where the packed 64 bit representation wouldn't work, and //! would have no benefit. -use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use super::{Custom, CustomOwner, ErrorData, ErrorKind, RawOsError, SimpleMessage}; -type Inner = ErrorData>; +type Inner = ErrorData; pub(super) struct Repr(Inner); impl Repr { #[inline] - pub(super) fn new_custom(b: Box) -> Self { + pub(super) fn new_custom(b: CustomOwner) -> Self { Self(Inner::Custom(b)) } #[inline] @@ -26,7 +26,7 @@ impl Repr { Self(Inner::SimpleMessage(m)) } #[inline] - pub(super) fn into_data(self) -> ErrorData> { + pub(super) fn into_data(self) -> ErrorData { self.0 } #[inline] @@ -35,7 +35,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&*m), + Inner::Custom(m) => ErrorData::Custom(m.custom_ref()), } } #[inline] @@ -44,7 +44,7 @@ impl Repr { Inner::Os(c) => ErrorData::Os(*c), Inner::Simple(k) => ErrorData::Simple(*k), Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), - Inner::Custom(m) => ErrorData::Custom(&mut *m), + Inner::Custom(m) => ErrorData::Custom(m.custom_mut()), } } } diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 43b6e09bfc4f0..b3c8815cb062c 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,16 +8,22 @@ mod util; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::cursor::Cursor; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::error::ErrorKind; #[unstable(feature = "raw_os_error_ty", issue = "107792")] pub use self::error::RawOsError; +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use self::error::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use self::error::const_error; #[unstable(feature = "core_io", issue = "154046")] -pub use self::io_slice::{IoSlice, IoSliceMut}; -#[unstable(feature = "core_io", issue = "154046")] -pub use self::util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; +pub use self::{ + cursor::Cursor, + error::{Error, ErrorKind, Result}, + io_slice::{IoSlice, IoSliceMut}, + util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, +}; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use self::util::{chain, take}; +pub use self::{ + error::{Custom, CustomOwner, OsFunctions}, + util::{chain, take}, +}; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index a26304c46ecea..6fa1aa3b1f172 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -51,27 +51,16 @@ test(attr(allow(dead_code, deprecated, unused_variables, unused_mut, duplicate_features))) )] #![doc(rust_logo)] -#![doc(auto_cfg(hide( - no_fp_fmt_parse, - target_pointer_width = "16", - target_pointer_width = "32", - target_pointer_width = "64", - target_has_atomic = "8", - target_has_atomic = "16", - target_has_atomic = "32", - target_has_atomic = "64", - target_has_atomic = "ptr", - target_has_atomic_primitive_alignment = "8", - target_has_atomic_primitive_alignment = "16", - target_has_atomic_primitive_alignment = "32", - target_has_atomic_primitive_alignment = "64", - target_has_atomic_primitive_alignment = "ptr", - target_has_atomic_load_store = "8", - target_has_atomic_load_store = "16", - target_has_atomic_load_store = "32", - target_has_atomic_load_store = "64", - target_has_atomic_load_store = "ptr", -)))] +#![doc(auto_cfg( + hide(no_fp_fmt_parse), + hide(target_pointer_width, values("16", "32", "64")), + hide( + target_has_atomic, + target_has_atomic_primitive_alignment, + target_has_atomic_load_store, + values("8", "16", "32", "64", "ptr"), + ), +))] #![no_core] #![rustc_coherence_is_core] #![rustc_preserve_ub_checks] @@ -79,8 +68,7 @@ // Lints: #![deny(rust_2021_incompatible_or_patterns)] #![deny(unsafe_op_in_unsafe_fn)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![warn(deprecated_in_future)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] @@ -108,6 +96,7 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] +#![feature(io_const_error)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 744bd41d1139e..45b8b266a2671 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1066,7 +1066,7 @@ pub const trait Destruct: PointeeSized {} /// /// The implementation of this trait is built-in and cannot be implemented /// for any user type. -#[unstable(feature = "tuple_trait", issue = "none")] +#[unstable(feature = "tuple_trait", issue = "157987")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] #[rustc_deny_explicit_impl] diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 9ec36561839b5..7614adbcc532b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -215,7 +215,7 @@ pub struct Variant { pub name: &'static str, /// All fields of the variant. pub fields: &'static [Field], - /// Whether the enum variant fields is non-exhaustive. + /// Whether the enum variant fields are non-exhaustive. pub non_exhaustive: bool, } @@ -342,6 +342,22 @@ pub struct FnPtr { /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); pub variadic: bool, + + // FIXME(splat): should these fields be private, or merged into an Option? + /// Is any function argument splatted? + pub is_splatted: bool, + + /// The index of the splatted function argument in `inputs`, only valid if `is_splatted` is true. + /// e.g. in `fn overload(a: u8, #[splat] b: (f32, usize))` the index is 1, and it can be called + /// as `overload(a, 1.0, 2)`. + pub splatted_index: u8, +} + +impl FnPtr { + /// Returns the splatted function argument index, or `None` if no argument is splatted. + pub const fn splatted(&self) -> Option { + if self.is_splatted { Some(self.splatted_index) } else { None } + } } #[derive(Debug, Default)] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index f5508b1a4463d..090fea81891ad 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -983,7 +983,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value /// that is too large to represent in the type. /// /// # Examples @@ -1050,7 +1050,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value /// that is too large to represent in the type. /// /// # Examples @@ -1223,7 +1223,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a - /// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts. + /// signed type (where [`MIN`](Self::MIN) is the negative minimal value), which is invalid due to implementation artifacts. /// /// # Examples /// @@ -1289,7 +1289,7 @@ macro_rules! int_impl { /// This function will always panic on overflow, regardless of whether overflow checks are enabled. /// /// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a - /// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts. + /// signed type (where [`MIN`](Self::MIN) is the negative minimal value), which is invalid due to implementation artifacts. /// /// # Examples /// @@ -2259,8 +2259,8 @@ macro_rules! int_impl { /// boundary of the type. /// /// The only case where such wrapping can occur is when one divides `MIN / -1` on a signed type (where - /// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value - /// that is too large to represent in the type. In such a case, this function returns `MIN` itself. + /// [`MIN`](Self::MIN) is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value + /// that is too large to represent in the type. In such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Panics /// @@ -2284,9 +2284,9 @@ macro_rules! int_impl { /// Wrapping Euclidean division. Computes `self.div_euclid(rhs)`, /// wrapping around at the boundary of the type. /// - /// Wrapping will only occur in `MIN / -1` on a signed type (where `MIN` is the negative minimal value + /// Wrapping will only occur in `MIN / -1` on a signed type (where [`MIN`](Self::MIN) is the negative minimal value /// for the type). This is equivalent to `-MIN`, a positive value that is too large to represent in the - /// type. In this case, this method returns `MIN` itself. + /// type. In this case, this method returns [`MIN`](Self::MIN) itself. /// /// # Panics /// @@ -2311,7 +2311,7 @@ macro_rules! int_impl { /// boundary of the type. /// /// Such wrap-around never actually occurs mathematically; implementation artifacts make `x % y` - /// invalid for `MIN / -1` on a signed type (where `MIN` is the negative minimal value). In such a case, + /// invalid for `MIN / -1` on a signed type (where [`MIN`](Self::MIN) is the negative minimal value). In such a case, /// this function returns `0`. /// /// # Panics @@ -2336,8 +2336,8 @@ macro_rules! int_impl { /// Wrapping Euclidean remainder. Computes `self.rem_euclid(rhs)`, wrapping around /// at the boundary of the type. /// - /// Wrapping will only occur in `MIN % -1` on a signed type (where `MIN` is the negative minimal value - /// for the type). In this case, this method returns 0. + /// Wrapping will only occur in `MIN % -1` on a signed type (where [`MIN`](Self::MIN) is + /// the negative minimal value for the type). In this case, this method returns 0. /// /// # Panics /// @@ -2361,9 +2361,9 @@ macro_rules! int_impl { /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary /// of the type. /// - /// The only case where such wrapping can occur is when one negates `MIN` on a signed type (where `MIN` + /// The only case where such wrapping can occur is when one negates [`MIN`](Self::MIN) on a signed type (where [`MIN`](Self::MIN) /// is the negative minimal value for the type); this is a positive value that is too large to represent - /// in the type. In such a case, this function returns `MIN` itself. + /// in the type. In such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Examples /// @@ -2460,7 +2460,7 @@ macro_rules! int_impl { /// /// The only case where such wrapping can occur is when one takes the absolute value of the negative /// minimal value for the type; this is a positive value that is too large to represent in the type. In - /// such a case, this function returns `MIN` itself. + /// such a case, this function returns [`MIN`](Self::MIN) itself. /// /// # Examples /// @@ -2772,7 +2772,7 @@ macro_rules! int_impl { /// /// # Examples /// - /// Please note that this example is shared among integer types, which is why `i32` is used. + /// Please note that this example is shared among integer types, which is why [`i32`] is used. /// /// ``` /// #![feature(signed_bigint_helpers)] @@ -2950,7 +2950,7 @@ macro_rules! int_impl { /// Negates self, overflowing if this is equal to the minimum value. /// /// Returns a tuple of the negated version of self along with a boolean indicating whether an overflow - /// happened. If `self` is the minimum value (e.g., `i32::MIN` for values of type `i32`), then the + /// happened. If `self` is the minimum value (e.g., [`i32::MIN`] for values of type [`i32`]), then the /// minimum value will be returned again and `true` will be returned for an overflow happening. /// /// # Examples @@ -3020,7 +3020,7 @@ macro_rules! int_impl { /// /// Returns a tuple of the absolute version of self along with a boolean indicating whether an overflow /// happened. If self is the minimum value - #[doc = concat!("(e.g., ", stringify!($SelfT), "::MIN for values of type ", stringify!($SelfT), "),")] + #[doc = concat!("(e.g., [`", stringify!($SelfT), "::MIN`] for values of type [`", stringify!($SelfT), "`]),")] /// then the minimum value will be returned again and true will be returned /// for an overflow happening. /// @@ -3177,7 +3177,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3215,7 +3215,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` and + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] and /// `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3262,7 +3262,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3304,7 +3304,7 @@ macro_rules! int_impl { /// /// # Panics /// - /// This function will panic if `rhs` is zero or if `self` is `Self::MIN` + /// This function will panic if `rhs` is zero or if `self` is [`Self::MIN`] /// and `rhs` is -1. This behavior is not affected by the `overflow-checks` flag. /// /// # Examples @@ -3441,8 +3441,8 @@ macro_rules! int_impl { /// rounded down. /// /// This method might not be optimized owing to implementation details; - /// `ilog2` can produce results more efficiently for base 2, and `ilog10` - /// can produce results more efficiently for base 10. + /// [`ilog2`][Self::ilog2] can produce results more efficiently for base 2, + /// and [`ilog10`](Self::ilog10) can produce results more efficiently for base 10. /// /// # Panics /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 4053134d89d43..cfb5eb54fb6bc 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1721,8 +1721,8 @@ macro_rules! uint_impl { /// rounded down. /// /// This method might not be optimized owing to implementation details; - /// `ilog2` can produce results more efficiently for base 2, and `ilog10` - /// can produce results more efficiently for base 10. + /// [`ilog2`](Self::ilog2) can produce results more efficiently for base 2, + /// and [`ilog10`](Self::ilog10) can produce results more efficiently for base 10. /// /// # Panics /// diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 25664a51a1d7b..15b54243c1eed 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -67,7 +67,7 @@ use crate::marker::Tuple; // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` @@ -154,7 +154,7 @@ pub const trait Fn: [const] FnMut { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` @@ -233,7 +233,7 @@ pub const trait FnMut: FnOnce { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{This:resolved}` closure, found `{Self}`", + message = "expected an `{This:resolved}` closure, found `{Self}`", label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` diff --git a/library/core/src/option.rs b/library/core/src/option.rs index c11b2354d1638..163e8bf714b1d 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -581,6 +581,7 @@ use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; +use crate::num::NonZero; use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; @@ -2666,18 +2667,111 @@ impl Iterator for OptionFlatten { type Item = A::Item; fn next(&mut self) -> Option { - self.iter.as_mut()?.next() + match &mut self.iter { + Some(iter) => iter.next(), + None => None, + } } fn size_hint(&self) -> (usize, Option) { - self.iter.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + match &self.iter { + Some(iter) => iter.size_hint(), + None => (0, Some(0)), + } + } + + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth(n), + None => None, + } + } + + fn fold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.fold(init, fold), + None => init, + } + } + + fn try_fold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_fold(init, fold), + None => try { init }, + } + } + + fn count(self) -> usize { + match self.iter { + Some(iter) => iter.count(), + None => 0, + } + } + + fn last(self) -> Option { + match self.iter { + Some(iter) => iter.last(), + None => None, + } } } #[unstable(feature = "option_into_flat_iter", issue = "148441")] impl DoubleEndedIterator for OptionFlatten { fn next_back(&mut self) -> Option { - self.iter.as_mut()?.next_back() + match &mut self.iter { + Some(iter) => iter.next_back(), + None => None, + } + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + match &mut self.iter { + Some(iter) => iter.advance_back_by(n), + None => NonZero::new(n).map_or(Ok(()), Err), + } + } + + fn nth_back(&mut self, n: usize) -> Option { + match &mut self.iter { + Some(iter) => iter.nth_back(n), + None => None, + } + } + + fn rfold(self, init: Acc, fold: Fold) -> Acc + where + Fold: FnMut(Acc, Self::Item) -> Acc, + { + match self.iter { + Some(iter) => iter.rfold(init, fold), + None => init, + } + } + + fn try_rfold(&mut self, init: Acc, fold: Fold) -> R + where + Fold: FnMut(Acc, Self::Item) -> R, + R: Try, + { + match &mut self.iter { + Some(iter) => iter.try_rfold(init, fold), + None => try { init }, + } } } diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 9d53567a26fd9..ee22521cb014c 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -13,7 +13,7 @@ use crate::panic::Location; #[derive(Debug)] pub struct PanicInfo<'a> { message: &'a fmt::Arguments<'a>, - location: &'a Location<'a>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -33,7 +33,7 @@ impl<'a> PanicInfo<'a> { #[inline] pub(crate) fn new( message: &'a fmt::Arguments<'a>, - location: &'a Location<'a>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> Self { @@ -88,10 +88,10 @@ impl<'a> PanicInfo<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { + pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. - Some(&self.location) + Some(self.location) } /// Returns the payload associated with the panic. diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index b7fcd6a5cd3de..db9e60d379f84 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -183,7 +183,7 @@ impl *const T { /// [`with_exposed_provenance`]: with_exposed_provenance #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] - #[expect(lossy_provenance_casts, reason = "this *is* the replacement")] + #[expect(implicit_provenance_casts, reason = "this *is* the replacement")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 593011edecf27..e41ede7498b06 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1000,7 +1000,7 @@ pub const fn dangling_mut() -> *mut T { #[stable(feature = "exposed_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead +#[allow(implicit_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance(addr: usize) -> *const T { addr as *const T } @@ -1041,7 +1041,7 @@ pub const fn with_exposed_provenance(addr: usize) -> *const T { #[stable(feature = "exposed_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "const_exposed_provenance", since = "1.91.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces -#[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead +#[allow(implicit_provenance_casts)] // this *is* the explicit provenance API one should use instead pub const fn with_exposed_provenance_mut(addr: usize) -> *mut T { addr as *mut T } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index fd8dafa18f56b..b333f8217b2c0 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -174,7 +174,7 @@ impl *mut T { /// [`with_exposed_provenance_mut`]: with_exposed_provenance_mut #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] - #[expect(lossy_provenance_casts, reason = "this *is* the replacement")] + #[expect(implicit_provenance_casts, reason = "this *is* the replacement")] pub fn expose_provenance(self) -> usize { self.cast::<()>() as usize } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 51ab9b5fd1eac..b82d79232becc 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -139,6 +139,8 @@ pub impl(crate) const unsafe trait SliceIndex { /// Returns a pointer to the output at this location, without /// performing any bounds checking. /// + /// # Safety + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// @@ -149,6 +151,8 @@ pub impl(crate) const unsafe trait SliceIndex { /// Returns a mutable pointer to the output at this location, without /// performing any bounds checking. /// + /// # Safety + /// /// Calling this method with an out-of-bounds index or a dangling `slice` pointer /// is *[undefined behavior]* even if the resulting pointer is not used. /// diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index ec2201aa7b9da..92fccea38bac9 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -128,8 +128,7 @@ #![feature(unwrap_infallible)] // tidy-alphabetical-end #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] /// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs index 1d50a2552a193..6e7e170917f55 100644 --- a/library/coretests/tests/mem/fn_ptr.rs +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -5,6 +5,7 @@ const STRING_TY: TypeId = const { TypeId::of::() }; const U8_TY: TypeId = const { TypeId::of::() }; const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; const UNIT_TY: TypeId = const { TypeId::of::<()>() }; +const TUPLE_STRING_U8_TY: TypeId = const { TypeId::of::<(String, u8)>() }; #[test] fn test_fn_ptrs() { @@ -14,6 +15,8 @@ fn test_fn_ptrs() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -31,6 +34,8 @@ fn test_ref() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -61,6 +66,8 @@ fn test_unsafe() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -75,6 +82,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -87,6 +96,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -99,6 +110,8 @@ fn test_abi() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -114,6 +127,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -128,6 +143,8 @@ fn test_inputs() { inputs: &[ty1, ty2], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of::().kind }) else { panic!(); @@ -145,6 +162,8 @@ fn test_output() { inputs: &[], output, variadic: false, + is_splatted: false, + splatted_index: _, }) = (const { Type::of:: u8>().kind }) else { panic!(); @@ -160,6 +179,8 @@ fn test_variadic() { inputs: [ty1], output, variadic: true, + is_splatted: false, + splatted_index: _, }) = &(const { Type::of::().kind }) else { panic!(); @@ -167,3 +188,48 @@ fn test_variadic() { assert_eq!(output, &UNIT_TY); assert_eq!(*ty1, U8_TY); } + +#[test] +fn test_splat() { + #[rustfmt::skip] + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: true, + splatted_index: 0, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), Some(0)); +} + +#[test] +fn test_not_splat() { + let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { + panic!(); + }; + let FnPtr { + unsafety: false, + abi: Abi::ExternRust, + inputs: [ty1], + output, + variadic: false, + is_splatted: false, + splatted_index: _, + } = fn_ptr_ty + else { + panic!(); + }; + assert_eq!(output, &UNIT_TY); + assert_eq!(*ty1, TUPLE_STRING_U8_TY); + assert_eq!(fn_ptr_ty.splatted(), None); +} diff --git a/library/std/src/attribute_docs.rs b/library/std/src/attribute_docs.rs index 1d8dddb7ca54a..e8d36483f3139 100644 --- a/library/std/src/attribute_docs.rs +++ b/library/std/src/attribute_docs.rs @@ -85,3 +85,253 @@ /// [`unused_must_use`]: ../rustc/lints/listing/warn-by-default.html#unused-must-use /// [the `must_use` attribute]: ../reference/attributes/diagnostics.html#the-must_use-attribute mod must_use_attribute {} + +#[doc(attribute = "allow")] +// +/// The `allow` attribute suppresses lint diagnostics that would otherwise produce +/// warnings or errors. It can be used on any lint or lint group (except those +/// set to `forbid`). +/// +/// ```rust +/// #[allow(dead_code)] +/// fn unused_function() { +/// // ... +/// } +/// +/// fn main() { +/// // `unused_function` does not generate a compiler warning. +/// } +/// ``` +/// +/// Without `#[allow(dead_code)]`, the example above would emit: +/// +/// ```text +/// warning: function `unused_function` is never used +/// --> main.rs:1:4 +/// | +/// 1 | fn unused_function() { +/// | ^^^^^^^^^^^^^^^ +/// | +/// = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default +/// +/// warning: 1 warning emitted +/// ``` +/// +/// Multiple lints can be set to `allow` at once with commas: +/// +/// ```rust +/// #[allow(unused_variables, unused_mut)] +/// fn main() { +/// let mut x: u32 = 42; +/// } +/// ``` +/// +/// This is mostly used to prevent lint warnings or errors while still under development. +/// +/// It cannot override a lint that has been set to `forbid`. +/// +/// It's also important to consider that overusing `allow` could make code harder to maintain +/// and possibly hide issues. To mitigate this issue, using the `expect` attribute is preferred. +/// +/// `allow` can be overridden by `warn`, `deny`, and `forbid`. +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `allow` attribute]. +/// +/// [the `allow` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod allow_attribute {} + +#[doc(attribute = "cfg")] +// +/// Used for conditional compilation. +/// +/// The `cfg` attribute allows compiling an item under specific conditions, otherwise it +/// will be ignored. +/// +/// ```rust +/// // Only compiles this function for Linux. +/// #[cfg(target_os = "linux")] +/// fn platform_specific() { +/// println!("Running on Linux"); +/// } +/// +/// // Only compiles this function if not for Linux. +/// #[cfg(not(target_os = "linux"))] +/// fn platform_specific() { +/// println!("Running on something else"); +/// } +/// ``` +/// +/// Depending on the platform you're targeting, only one of these two functions will be considered +/// during the compilation. +/// +/// Conditions can also be combined with `all(...)`, `any(...)`, and `not(...)`. +/// +/// * `all`: True if all given predicates are true. +/// * `any`: True if at least one of the given predicates is true. +/// * `not`: True if the predicate is false and false if the predicate is true. +/// +/// ```rust +/// #[cfg(all(unix, target_pointer_width = "64"))] +/// fn unix_64bit() { +/// } +/// ``` +/// +/// If you want to use this mechanism in an `if` condition in your code, you +/// can use the [`cfg!`] macro. To conditionally apply an attribute, +/// see [`cfg_attr`]. +/// +/// For more information, see the Reference on [the `cfg` attribute]. +/// +/// [`cfg_attr`]: ../reference/conditional-compilation.html#the-cfg_attr-attribute +/// [the `cfg` attribute]: ../reference/conditional-compilation.html#the-cfg-attribute +mod cfg_attribute {} + +#[doc(attribute = "deny")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// This is useful for enforcing rules or preventing certain patterns: +/// +/// ```rust,compile_fail +/// #[deny(unused)] +/// fn foo() { +/// let x = 42; // Emits an error because x is unused. +/// } +/// ``` +/// +/// `deny` can be overridden by `allow`, `warn`, and `forbid`: +/// +/// ```rust +/// #![deny(unused)] +/// +/// #[allow(unused)] // We override the `deny` for this function. +/// fn foo() { +/// let x = 42; // No lint emitted even though `x` is unused. +/// } +/// ``` +/// +/// Multiple lints can also be set to `deny` at once: +/// +/// ```rust,compile_fail +/// #![deny(unused_imports, unused_variables)] +/// use std::collections::*; +/// +/// fn main() { +/// let mut x = 10; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `deny` attribute]. +/// +/// [the `deny` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod deny_attribute {} + +#[doc(attribute = "forbid")] +// +/// Emits an error, preventing the compilation from finishing, when a lint check has failed. +/// +/// A lint set to `forbid` cannot be overridden by `allow` or `warn`. +/// Attempting either will result in a compilation error. Writing `#[deny(...)]` on the same lint inside a +/// `forbid` scope is permitted, but has no effect; the lint remains at the `forbid` level. +/// +/// This is useful for enforcing strict policies that should not be relaxed +/// anywhere in the codebase. Example: +/// +/// ```rust +/// #![forbid(unsafe_code)] +/// +/// // This would cause a compilation error if uncommented: +/// // #[allow(unsafe_code)] // error: cannot override `forbid` +/// ``` +/// +/// Multiple lints can be set to `forbid` at once: +/// +/// ```rust +/// #![forbid(unsafe_code, unused)] +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `forbid` attribute]. +/// +/// [the `forbid` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod forbid_attribute {} + +#[doc(attribute = "deprecated")] +// +/// Emits a warning during compilation when an item with this attribute is used. +/// `since` and `note` are optional fields giving more detail about why the item is deprecated. +/// +/// * `since`: the version since when the item is deprecated. +/// * `note`: the reason why an item is deprecated. +/// +/// Example: +/// +/// ```rust +/// #[deprecated(since = "1.0.0", note = "Use bar instead")] +/// struct Foo; +/// struct Bar; +/// ``` +/// +/// `deprecated` attribute helps developers transition away from old code by providing warnings when +/// deprecated items are used. Note that during `Cargo` builds, warnings on dependencies get silenced +/// by default, so you may not see a deprecation warning unless you build that dependency directly. +/// +/// For more information, see the Reference on [the `deprecated` attribute]. +/// +/// [the `deprecated` attribute]: ../reference/attributes/diagnostics.html#the-deprecated-attribute +mod deprecated_attribute {} + +#[doc(attribute = "warn")] +// +/// Emits a warning during compilation when a lint check failed. +/// +/// Unlike `deny` or `forbid`, `warn` does not produce a hard error: the compilation continues, but +/// the compiler emits a warning message. `warn` can be overridden by `allow`, `deny`, and `forbid`. +/// +/// Example: +/// +/// ```rust,compile_fail +/// #![allow(unused)] +/// +/// #[warn(unused)] // We override the allowed `unused` lint. +/// fn foo() { +/// // This lint warns by default even without #[warn(unused)] being explicitly set +/// let x = 42; // warning: unused variable `x` +/// } +/// ``` +/// +/// +/// Many lints, including `unused`, are already set to `warn` by default so this attribute is +/// mainly useful for lints that are normally `allow` by default. +/// +/// Multiple lints can be set to `warn` at once: +/// +/// ```rust,compile_fail +/// #[warn(unused_mut, unused_variables)] +/// fn main() { +/// let mut x = 42; +/// } +/// ``` +/// +/// The lint checks supported by rustc can be found via `rustc -W help`, +/// along with their default settings and are documented in [the `rustc` book]. +/// +/// [the `rustc` book]: ../rustc/lints/listing/index.html +/// +/// For more information, see the Reference on [the `warn` attribute]. +/// +/// [the `warn` attribute]: ../reference/attributes/diagnostics.html#lint-check-attributes +mod warn_attribute {} diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 360ca83c65a91..6dbe37ca9fba8 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,310 +1,20 @@ #[cfg(test)] mod tests; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::ErrorKind; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use core::io::RawOsError; - -// On 64-bit platforms, `io::Error` may use a bit-packed representation to -// reduce size. However, this representation assumes that error codes are -// always 32-bit wide. -// -// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. -// Therefore, the packed representation is explicitly disabled for UEFI -// targets, and the unpacked representation must be used instead. -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -mod repr_bitpacked; -#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] -use repr_bitpacked::Repr; - -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -mod repr_unpacked; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -use repr_unpacked::Repr; - -use crate::{error, fmt, result, sys}; - -/// A specialized [`Result`] type for I/O operations. -/// -/// This type is broadly used across [`std::io`] for any operation which may -/// produce an error. -/// -/// This type alias is generally used to avoid writing out [`io::Error`] directly and -/// is otherwise a direct mapping to [`Result`]. -/// -/// While usual Rust style is to import types directly, aliases of [`Result`] -/// often are not, to make it easier to distinguish between them. [`Result`] is -/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the [prelude]'s import -/// of [`std::result::Result`][`Result`]. -/// -/// [`std::io`]: crate::io -/// [`io::Error`]: Error -/// [`Result`]: crate::result::Result -/// [prelude]: crate::prelude -/// -/// # Examples -/// -/// A convenience function that bubbles an `io::Result` to its caller: -/// -/// ``` -/// use std::io; -/// -/// fn get_string() -> io::Result { -/// let mut buffer = String::new(); -/// -/// io::stdin().read_line(&mut buffer)?; -/// -/// Ok(buffer) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(search_unbox)] -pub type Result = result::Result; - -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and -/// associated traits. -/// -/// Errors mostly originate from the underlying OS, but custom instances of -/// `Error` can be created with crafted error messages and a particular value of -/// [`ErrorKind`]. -/// -/// [`Read`]: crate::io::Read -/// [`Write`]: crate::io::Write -/// [`Seek`]: crate::io::Seek -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Error { - repr: Repr, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.repr, f) - } -} - -/// Common errors constants for use in std -#[allow(dead_code)] -impl Error { - pub(crate) const INVALID_UTF8: Self = - const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - - pub(crate) const READ_EXACT_EOF: Self = - const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( - ErrorKind::NotFound, - "the number of hardware threads is not known for the target platform", - ); - - pub(crate) const UNSUPPORTED_PLATFORM: Self = - const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - - pub(crate) const WRITE_ALL_EOF: Self = - const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - - pub(crate) const ZERO_TIMEOUT: Self = - const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - - pub(crate) const NO_ADDRESSES: Self = - const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From for Error { - /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. - fn from(_: alloc::ffi::NulError) -> Error { - const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") - } -} - -#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] -impl From for Error { - /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. - /// - /// `TryReserveError` won't be available as the error `source()`, - /// but this may change in the future. - fn from(_: alloc::collections::TryReserveError) -> Error { - // ErrorData::Custom allocates, which isn't great for handling OOM errors. - ErrorKind::OutOfMemory.into() - } -} - -// Only derive debug in tests, to make sure it -// doesn't accidentally get printed. -#[cfg_attr(test, derive(Debug))] -enum ErrorData { - Os(RawOsError), - Simple(ErrorKind), - SimpleMessage(&'static SimpleMessage), - Custom(C), -} - -// `#[repr(align(4))]` is probably redundant, it should have that value or -// higher already. We include it just because repr_bitpacked.rs's encoding -// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the -// alignment required by the struct, only increase it). -// -// If we add more variants to ErrorData, this can be increased to 8, but it -// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or -// whatever cfg we're using to enable the `repr_bitpacked` code, since only the -// that version needs the alignment, and 8 is higher than the alignment we'll -// have on 32 bit platforms. -// -// (For the sake of being explicit: the alignment requirement here only matters -// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't -// matter at all) -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -#[repr(align(4))] -#[derive(Debug)] -pub struct SimpleMessage { - pub kind: ErrorKind, - pub message: &'static str, -} - -/// Creates a new I/O error from a known kind of error and a string literal. -/// -/// Contrary to [`Error::new`], this macro does not allocate and can be used in -/// `const` contexts. -/// -/// # Example -/// ``` -/// #![feature(io_const_error)] -/// use std::io::{const_error, Error, ErrorKind}; -/// -/// const FAIL: Error = const_error!(ErrorKind::Unsupported, "tried something that never works"); -/// -/// fn not_here() -> Result<(), Error> { -/// Err(FAIL) -/// } -/// ``` -#[rustc_macro_transparency = "semiopaque"] -#[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] -pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( - const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, - )) -} - -// As with `SimpleMessage`: `#[repr(align(4))]` here is just because -// repr_bitpacked's encoding requires it. In practice it almost certainly be -// already be this high or higher. -#[derive(Debug)] -#[repr(align(4))] -struct Custom { - kind: ErrorKind, - error: Box, -} - -/// Intended for use for errors not exposed to the user, where allocating onto -/// the heap (for normal construction via Error::new) is too costly. -#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] -impl From for Error { - /// Converts an [`ErrorKind`] into an [`Error`]. - /// - /// This conversion creates a new error with a simple representation of error kind. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// let not_found = ErrorKind::NotFound; - /// let error = Error::from(not_found); - /// assert_eq!("entity not found", format!("{error}")); - /// ``` - #[inline] - fn from(kind: ErrorKind) -> Error { - Error { repr: Repr::new_simple(kind) } - } -} - +#[cfg_attr( + test, + expect(unused, reason = "only used in implementation for non-test compilation") +)] +use crate::{ + io::{Error, OsFunctions, RawOsError}, + sys::io::{decode_error_kind, errno, error_string, is_interrupted}, +}; + +// Because std is linked in during testing, these incoherent implementations would +// be duplicated if this was unconditionally included. +// See #2912 for details. +#[cfg(not(test))] impl Error { - /// Creates a new I/O error from a known kind of error as well as an - /// arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. The `error` argument is an arbitrary - /// payload which will be contained in this [`Error`]. - /// - /// Note that this function allocates memory on the heap. - /// If no extra payload is required, use the `From` conversion from - /// `ErrorKind`. - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// // errors can be created from strings - /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); - /// - /// // creating an error without payload (and without memory allocation) - /// let eof_error = Error::from(ErrorKind::UnexpectedEof); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")] - #[inline(never)] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - Self::_new(kind, error.into()) - } - - /// Creates a new I/O error from an arbitrary error payload. - /// - /// This function is used to generically create I/O errors which do not - /// originate from the OS itself. It is a shortcut for [`Error::new`] - /// with [`ErrorKind::Other`]. - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// // errors can be created from strings - /// let custom_error = Error::other("oh no!"); - /// - /// // errors can also be created from other errors - /// let custom_error2 = Error::other(custom_error); - /// ``` - #[stable(feature = "io_error_other", since = "1.74.0")] - pub fn other(error: E) -> Error - where - E: Into>, - { - Self::_new(ErrorKind::Other, error.into()) - } - - fn _new(kind: ErrorKind, error: Box) -> Error { - Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) } - } - - /// Creates a new I/O error from a known kind of error as well as a constant - /// message. - /// - /// This function does not allocate. - /// - /// You should not use this directly, and instead use the `const_error!` - /// macro: `io::const_error!(ErrorKind::Something, "some_message")`. - /// - /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. - #[inline] - #[doc(hidden)] - #[unstable(feature = "io_const_error_internals", issue = "none")] - pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { - Self { repr: Repr::new_simple_message(msg) } - } - /// Returns an error representing the last OS error which occurred. /// /// This function reads the value of `errno` for the target platform (e.g. @@ -324,13 +34,14 @@ impl Error { /// let os_error = Error::last_os_error(); /// println!("last OS error: {os_error:?}"); /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "GetLastError")] #[doc(alias = "errno")] #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::io::errno()) + Error::from_raw_os_error(errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -358,405 +69,18 @@ impl Error { /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); /// # } /// ``` + #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] pub fn from_raw_os_error(code: RawOsError) -> Error { - Error { repr: Repr::new_os(code) } - } + const FUNCTIONS: &'static OsFunctions = &OsFunctions { + format_os_error: |code, fmt| fmt.write_str(&error_string(code)), + decode_error_kind, + is_interrupted, + }; - /// Returns the OS error that this error represents (if any). - /// - /// If this [`Error`] was constructed via [`last_os_error`] or - /// [`from_raw_os_error`], then this function will return [`Some`], otherwise - /// it will return [`None`]. - /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::from_raw_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {raw_os_err:?}"); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn raw_os_error(&self) -> Option { - match self.repr.data() { - ErrorData::Os(i) => Some(i), - ErrorData::Custom(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - } + // SAFETY: `FUNCTIONS` is a constant and not created at runtime. + unsafe { Error::from_raw_os_error_with_functions(code, FUNCTIONS) } } - - /// Returns a reference to the inner error wrapped by this error (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err:?}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&*c.error), - } - } - - /// Returns a mutable reference to the inner error wrapped by this error - /// (if any). - /// - /// If this [`Error`] was constructed via [`new`] then this function will - /// return [`Some`], otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// use std::{error, fmt}; - /// use std::fmt::Display; - /// - /// #[derive(Debug)] - /// struct MyError { - /// v: String, - /// } - /// - /// impl MyError { - /// fn new() -> MyError { - /// MyError { - /// v: "oh no!".to_string() - /// } - /// } - /// - /// fn change_message(&mut self, new_message: &str) { - /// self.v = new_message.to_string(); - /// } - /// } - /// - /// impl error::Error for MyError {} - /// - /// impl Display for MyError { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "MyError: {}", self.v) - /// } - /// } - /// - /// fn change_error(mut err: Error) -> Error { - /// if let Some(inner_err) = err.get_mut() { - /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); - /// } - /// err - /// } - /// - /// fn print_error(err: &Error) { - /// if let Some(inner_err) = err.get_ref() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(&change_error(Error::last_os_error())); - /// // Will print "Inner error: ...". - /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use] - #[inline] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { - match self.repr.data_mut() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(&mut *c.error), - } - } - - /// Consumes the `Error`, returning its inner error (if any). - /// - /// If this [`Error`] was constructed via [`new`] or [`other`], - /// then this function will return [`Some`], - /// otherwise it will return [`None`]. - /// - /// [`new`]: Error::new - /// [`other`]: Error::other - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// if let Some(inner_err) = err.into_inner() { - /// println!("Inner error: {inner_err}"); - /// } else { - /// println!("No inner error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "No inner error". - /// print_error(Error::last_os_error()); - /// // Will print "Inner error: ...". - /// print_error(Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "io_error_inner", since = "1.3.0")] - #[must_use = "`self` will be dropped if the result is not used"] - #[inline] - pub fn into_inner(self) -> Option> { - match self.repr.into_data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => Some(c.error), - } - } - - /// Attempts to downcast the custom boxed error to `E`. - /// - /// If this [`Error`] contains a custom boxed error, - /// then it would attempt downcasting on the boxed error, - /// otherwise it will return [`Err`]. - /// - /// If the custom boxed error has the same type as `E`, it will return [`Ok`], - /// otherwise it will also return [`Err`]. - /// - /// This method is meant to be a convenience routine for calling - /// `Box::downcast` on the custom boxed error, returned by - /// [`Error::into_inner`]. - /// - /// - /// # Examples - /// - /// ``` - /// use std::fmt; - /// use std::io; - /// use std::error::Error; - /// - /// #[derive(Debug)] - /// enum E { - /// Io(io::Error), - /// SomeOtherVariant, - /// } - /// - /// impl fmt::Display for E { - /// // ... - /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// # todo!() - /// # } - /// } - /// impl Error for E {} - /// - /// impl From for E { - /// fn from(err: io::Error) -> E { - /// err.downcast::() - /// .unwrap_or_else(E::Io) - /// } - /// } - /// - /// impl From for io::Error { - /// fn from(err: E) -> io::Error { - /// match err { - /// E::Io(io_error) => io_error, - /// e => io::Error::new(io::ErrorKind::Other, e), - /// } - /// } - /// } - /// - /// # fn main() { - /// let e = E::SomeOtherVariant; - /// // Convert it to an io::Error - /// let io_error = io::Error::from(e); - /// // Cast it back to the original variant - /// let e = E::from(io_error); - /// assert!(matches!(e, E::SomeOtherVariant)); - /// - /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists); - /// // Convert it to E - /// let e = E::from(io_error); - /// // Cast it back to the original variant - /// let io_error = io::Error::from(e); - /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists); - /// assert!(io_error.get_ref().is_none()); - /// assert!(io_error.raw_os_error().is_none()); - /// # } - /// ``` - #[stable(feature = "io_error_downcast", since = "1.79.0")] - pub fn downcast(self) -> result::Result - where - E: error::Error + Send + Sync + 'static, - { - if let ErrorData::Custom(c) = self.repr.data() - && c.error.is::() - { - if let ErrorData::Custom(b) = self.repr.into_data() - && let Ok(err) = b.error.downcast::() - { - Ok(*err) - } else { - // Safety: We have just checked that the condition is true - unsafe { crate::hint::unreachable_unchecked() } - } - } else { - Err(self) - } - } - - /// Returns the corresponding [`ErrorKind`] for this error. - /// - /// This may be a value set by Rust code constructing custom `io::Error`s, - /// or if this `io::Error` was sourced from the operating system, - /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`] for more details. - /// - /// [`last_os_error`]: Error::last_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_error(err: Error) { - /// println!("{:?}", err.kind()); - /// } - /// - /// fn main() { - /// // As no error has (visibly) occurred, this may print anything! - /// // It likely prints a placeholder for unidentified (non-)errors. - /// print_error(Error::last_os_error()); - /// // Will print "AddrInUse". - /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[must_use] - #[inline] - pub fn kind(&self) -> ErrorKind { - match self.repr.data() { - ErrorData::Os(code) => sys::io::decode_error_kind(code), - ErrorData::Custom(c) => c.kind, - ErrorData::Simple(kind) => kind, - ErrorData::SimpleMessage(m) => m.kind, - } - } - - #[inline] - pub(crate) fn is_interrupted(&self) -> bool { - match self.repr.data() { - ErrorData::Os(code) => sys::io::is_interrupted(code), - ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, - ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, - ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, - } - } -} - -impl fmt::Debug for Repr { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.data() { - ErrorData::Os(code) => fmt - .debug_struct("Os") - .field("code", &code) - .field("kind", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) - .finish(), - ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), - ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), - ErrorData::SimpleMessage(msg) => fmt - .debug_struct("Error") - .field("kind", &msg.kind) - .field("message", &msg.message) - .finish(), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.repr.data() { - ErrorData::Os(code) => { - let detail = sys::io::error_string(code); - write!(fmt, "{detail} (os error {code})") - } - ErrorData::Custom(ref c) => c.error.fmt(fmt), - ErrorData::Simple(kind) => kind.fmt(fmt), - ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for Error { - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn error::Error> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.cause(), - } - } - - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.repr.data() { - ErrorData::Os(..) => None, - ErrorData::Simple(..) => None, - ErrorData::SimpleMessage(..) => None, - ErrorData::Custom(c) => c.error.source(), - } - } -} - -fn _assert_error_is_sync_send() { - fn _is_sync_send() {} - _is_sync_send::(); } diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index a8eef06381dae..a3a2f5830ae91 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,4 +1,4 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; +use crate::io::{Error, ErrorKind, const_error}; use crate::sys::io::{decode_error_kind, error_string}; use crate::{assert_matches, error, fmt}; @@ -12,12 +12,7 @@ fn test_debug_error() { let code = 6; let msg = error_string(code); let kind = decode_error_kind(code); - let err = Error { - repr: Repr::new_custom(Box::new(Custom { - kind: ErrorKind::InvalidInput, - error: Box::new(Error { repr: super::Repr::new_os(code) }), - })), - }; + let err = Error::new(ErrorKind::InvalidInput, Error::from_raw_os_error(code)); let expected = format!( "Custom {{ \ kind: InvalidInput, \ @@ -70,10 +65,6 @@ fn test_os_packing() { for code in -20..20 { let e = Error::from_raw_os_error(code); assert_eq!(e.raw_os_error(), Some(code)); - assert_matches!( - e.repr.data(), - ErrorData::Os(c) if c == code, - ); } } @@ -82,28 +73,17 @@ fn test_errorkind_packing() { assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like what we want. - assert_matches!( - Error::from(ErrorKind::OutOfMemory).repr.data(), - ErrorData::Simple(ErrorKind::OutOfMemory), - ); } #[test] fn test_simple_message_packing() { - use super::ErrorKind::*; - use super::SimpleMessage; + use ErrorKind::*; macro_rules! check_simple_msg { ($err:expr, $kind:ident, $msg:literal) => {{ let e = &$err; // Check that the public api is right. assert_eq!(e.kind(), $kind); assert!(format!("{e:?}").contains($msg)); - // and we got what we expected - assert_matches!( - e.repr.data(), - ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) - ); }}; } @@ -128,14 +108,11 @@ impl fmt::Display for Bojji { #[test] fn test_custom_error_packing() { - use super::Custom; let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); + assert_eq!(test.kind(), ErrorKind::Uncategorized); assert_matches!( - test.repr.data(), - ErrorData::Custom(Custom { - kind: ErrorKind::Uncategorized, - error, - }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), + test.get_ref(), + Some(error) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), ); } @@ -181,11 +158,11 @@ fn test_std_io_error_downcast() { assert_eq!(kind, io_error.kind()); // Case 5: simple message - const SIMPLE_MESSAGE: SimpleMessage = - SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; - let io_error = Error::from_static_message(&SIMPLE_MESSAGE); + const KIND: ErrorKind = ErrorKind::Other; + const MESSAGE: &str = "simple message error test"; + let io_error = const_error!(KIND, MESSAGE); let io_error = io_error.downcast::().unwrap_err(); - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); + assert_eq!(KIND, io_error.kind()); + assert_eq!(MESSAGE, format!("{io_error}")); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 4a250281dea6c..dc623a3e82c2c 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,23 +297,27 @@ #[cfg(test)] mod tests; -#[unstable(feature = "read_buf", issue = "78485")] -pub use core::io::{BorrowedBuf, BorrowedCursor}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use core::io::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}; -#[stable(feature = "iovec", since = "1.36.0")] -pub use core::io::{IoSlice, IoSliceMut}; use core::slice::memchr; -#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] -pub use self::buffered::WriterPanicked; +use alloc_crate::io::OsFunctions; #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; +pub use alloc_crate::io::RawOsError; #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; +pub use alloc_crate::io::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; +pub use alloc_crate::io::const_error; +#[unstable(feature = "read_buf", issue = "78485")] +pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc_crate::io::{ + Chain, Empty, Error, ErrorKind, Repeat, Result, Sink, Take, empty, repeat, sink, +}; +#[stable(feature = "iovec", since = "1.36.0")] +pub use alloc_crate::io::{IoSlice, IoSliceMut}; + +#[stable(feature = "bufwriter_into_parts", since = "1.56.0")] +pub use self::buffered::WriterPanicked; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -330,7 +334,6 @@ pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, cursor::Cursor, - error::{Error, ErrorKind, Result}, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1b27be8e2dde3..78e984db6cba5 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -245,8 +245,7 @@ #![allow(explicit_outlives_requirements)] #![allow(unused_lifetimes)] #![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] @@ -391,6 +390,7 @@ // // Library features (alloc): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] @@ -726,13 +726,7 @@ pub mod alloc; mod panicking; #[path = "../../backtrace/src/lib.rs"] -#[allow( - dead_code, - unused_attributes, - fuzzy_provenance_casts, - lossy_provenance_casts, - unsafe_op_in_unsafe_fn -)] +#[allow(dead_code, unused_attributes, implicit_provenance_casts, unsafe_op_in_unsafe_fn)] mod backtrace_rs; #[stable(feature = "cfg_select", since = "1.95.0")] diff --git a/library/std/src/net/ip_addr/tests.rs b/library/std/src/net/ip_addr/tests.rs index 7bed6f8a0f523..666146319d161 100644 --- a/library/std/src/net/ip_addr/tests.rs +++ b/library/std/src/net/ip_addr/tests.rs @@ -1,5 +1,5 @@ use crate::net::Ipv4Addr; -use crate::net::test::{sa4, tsa}; +use crate::net::tests::{sa4, tsa}; #[test] fn to_socket_addr_socketaddr() { diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index 3e4447eb33f2a..2a8b0f8ca9aad 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -43,7 +43,7 @@ mod ip_addr; mod socket_addr; mod tcp; #[cfg(test)] -pub(crate) mod test; +pub(crate) mod tests; mod udp; /// Possible values which can be passed to the [`TcpStream::shutdown`] method. diff --git a/library/std/src/net/socket_addr/tests.rs b/library/std/src/net/socket_addr/tests.rs index 6a065cfba2105..2f3e3add5c22d 100644 --- a/library/std/src/net/socket_addr/tests.rs +++ b/library/std/src/net/socket_addr/tests.rs @@ -1,4 +1,4 @@ -use crate::net::test::{sa4, sa6, tsa}; +use crate::net::tests::{sa4, sa6, tsa}; use crate::net::*; #[test] diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 8a3a7ee7c5cba..88f6af0e33df7 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -3,7 +3,7 @@ use rand::Rng; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; -use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6}; +use crate::net::tests::{LOCALHOST_IP4, LOCALHOST_IP6}; use crate::net::*; use crate::sync::mpsc::channel; use crate::time::{Duration, Instant}; diff --git a/library/std/src/net/test.rs b/library/std/src/net/tests.rs similarity index 83% rename from library/std/src/net/test.rs rename to library/std/src/net/tests.rs index 5002bb5c8e083..3f5d22a910005 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/tests.rs @@ -36,3 +36,13 @@ pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool { _ => a == b, } } + +#[test] +fn hostname_smoketest() { + // Just a smoke test to ensure it can be called. + let name = crate::net::hostname(); + if cfg!(windows) || cfg!(unix) { + // At least on Windows and Unix, this should succeed. + name.unwrap(); + } +} diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index ede0240fbda57..9e9d3bf77e50b 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,5 +1,5 @@ use crate::io::ErrorKind; -use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6, compare_ignore_zoneid}; +use crate::net::tests::{LOCALHOST_IP4, LOCALHOST_IP6, compare_ignore_zoneid}; use crate::net::*; use crate::sync::mpsc::channel; use crate::thread; diff --git a/library/std/src/os/net/linux_ext/tests.rs b/library/std/src/os/net/linux_ext/tests.rs index 02839ee6a2aad..a63524e63b6d8 100644 --- a/library/std/src/os/net/linux_ext/tests.rs +++ b/library/std/src/os/net/linux_ext/tests.rs @@ -1,6 +1,6 @@ #[test] fn quickack() { - use crate::net::test::LOCALHOST_IP4; + use crate::net::tests::LOCALHOST_IP4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; @@ -29,7 +29,7 @@ fn quickack() { #[test] #[cfg(target_os = "linux")] fn deferaccept() { - use crate::net::test::LOCALHOST_IP4; + use crate::net::tests::LOCALHOST_IP4; use crate::net::{TcpListener, TcpStream}; use crate::os::net::linux_ext::tcp::TcpStreamExt; use crate::time::Duration; diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index b0a41502614f6..8a7b94d914ab0 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -102,8 +102,8 @@ pub impl(self) trait CommandExt { /// [POSIX fork() specification]: /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html /// [`std::env`]: mod@crate::env - /// [`Error::new`]: crate::io::Error::new - /// [`Error::other`]: crate::io::Error::other + /// [`Error::new`]: ../../../io/struct.Error.html#method.new + /// [`Error::other`]: ../../../io/struct.Error.html#method.other #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where diff --git a/library/std/src/os/unix/thread.rs b/library/std/src/os/unix/thread.rs index efb349b18d6b5..c19ccaf742da9 100644 --- a/library/std/src/os/unix/thread.rs +++ b/library/std/src/os/unix/thread.rs @@ -34,12 +34,12 @@ impl JoinHandleExt for JoinHandle { // This is an int2ptr cast on some platforms (e.g., *-musl) where RawPthread // is an integer but libc::pthread_t is a pointer. Exposed provenance is the // safe choice here, but `as` also works when it's int2int or ptr2ptr. - #[allow(lossy_provenance_casts)] + #[allow(implicit_provenance_casts)] fn as_pthread_t(&self) -> RawPthread { self.as_inner().id() as RawPthread } - #[allow(lossy_provenance_casts)] // see above for why + #[allow(implicit_provenance_casts)] // see above for why fn into_pthread_t(self) -> RawPthread { self.into_inner().into_id() as RawPthread } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 28e972925e667..ae1b7eaee8d12 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -75,7 +75,7 @@ impl OwnedSocket { } // FIXME(strict_provenance_magic): we defined RawSocket to be a u64 ;-; - #[allow(fuzzy_provenance_casts)] + #[allow(implicit_provenance_casts)] #[cfg(not(target_vendor = "uwp"))] pub(crate) fn set_no_inherit(&self) -> io::Result<()> { cvt(unsafe { diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 658026a8020f9..e8394feb73f5d 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -41,7 +41,7 @@ pub type PanicInfo<'a> = PanicHookInfo<'a>; #[derive(Debug)] pub struct PanicHookInfo<'a> { payload: &'a (dyn Any + Send), - location: &'a Location<'a>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, } @@ -49,7 +49,7 @@ pub struct PanicHookInfo<'a> { impl<'a> PanicHookInfo<'a> { #[inline] pub(crate) fn new( - location: &'a Location<'a>, + location: &'static Location<'static>, payload: &'a (dyn Any + Send), can_unwind: bool, force_no_backtrace: bool, @@ -160,10 +160,10 @@ impl<'a> PanicHookInfo<'a> { #[must_use] #[inline] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn location(&self) -> Option<&Location<'_>> { + pub fn location(&self) -> Option<&'static Location<'static>> { // NOTE: If this is changed to sometimes return None, // deal with that case in std::panicking::default_hook and core::panicking::panic_fmt. - Some(&self.location) + Some(self.location) } /// Returns whether the panic handler is allowed to unwind the stack from diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 18cc4ca74f682..7db6e93e8bd02 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -776,7 +776,7 @@ fn payload_as_str(payload: &dyn Any) -> &str { #[optimize(size)] fn panic_with_hook( payload: &mut dyn PanicPayload, - location: &Location<'_>, + location: &'static Location<'static>, can_unwind: bool, force_no_backtrace: bool, ) -> ! { diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index 9403059e7c607..8c1e07a787716 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -1,5 +1,4 @@ -// FIXME: this module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this module systematically confuses pointers and integers use crate::ffi::OsString; use crate::num::NonZero; diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs index 0c19fcc848f4d..094a7dfa90581 100644 --- a/library/std/src/sys/env/sgx.rs +++ b/library/std/src/sys/env/sgx.rs @@ -1,5 +1,4 @@ -// FIXME: this module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this module systematically confuses pointers and integers pub use super::common::Env; use crate::collections::HashMap; diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index b139376f59768..0ed15b28c36e1 100644 --- a/library/std/src/sys/net/connection/xous/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -43,9 +43,8 @@ impl Iterator for LookupHost { return None; } let mut new_addr = [0u8; 16]; - for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] - .iter() - .zip(new_addr.iter_mut()) + for (src, octet) in + self.data.0[self.offset..(self.offset + 16)].iter().zip(new_addr.iter_mut()) { *octet = *src; } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 7b2c8e5a8024a..6ed6e39c61897 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -3,8 +3,7 @@ //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for Fortanix SGX. #![deny(unsafe_op_in_unsafe_fn)] -// FIXME: this entire module systematically confuses pointers and integers -#![allow(fuzzy_provenance_casts, lossy_provenance_casts)] +#![allow(implicit_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers use crate::io; use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 019d5629d6d6e..0e20a4192b1b9 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -204,6 +204,35 @@ cfg_select! { } } _ => { + #[rustc_force_inline] + unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { + cfg_select! { + all(target_abi = "pauthtest", target_arch = "aarch64") => { + // DWARF register number for SP on AArch64. + const SP_REG: i32 = 31; + + unsafe { + let sp = uw::_Unwind_GetGR(context, SP_REG).addr() as u64; + let mut addr = lpad.addr(); + + // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . + core::arch::asm!( + "pacib {addr}, {sp}", + addr = inout(reg) addr, + sp = in(reg) sp, + options(nostack, preserves_flags) + ); + + lpad.with_addr(addr) + } + } + _ => { + let _ = context; + lpad + } + } + } + /// Default personality routine, which is used directly on most targets /// and indirectly on Windows x86_64 and AArch64 via SEH. unsafe extern "C" fn rust_eh_personality_impl( @@ -239,7 +268,8 @@ cfg_select! { exception_object.cast(), ); uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); + let maybe_signed_lpad = sign_lpad(context, lpad); + uw::_Unwind_SetIP(context, maybe_signed_lpad); uw::_URC_INSTALL_CONTEXT } EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 529cdaf72cc6c..b6ccc6b698fda 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -510,8 +510,8 @@ impl Command { support = SPAWN; } } - Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { - // We're temporarily(?) out of file descriptors. In this case pidfd_spawnp would also fail + Err(e) if matches!(e.raw_os_error(), Some(libc::EMFILE | libc::ENFILE | libc::ENOMEM)) => { + // We're temporarily(?) out of file descriptors or memory. In this case pidfd_spawnp would also fail // Don't update the support flag so we can probe again later. return Err(e) } diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index eb3fcff05ea9e..dd7b7940de510 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -392,6 +392,7 @@ pub fn current_os_id() -> Option { target_os = "vxworks", target_os = "cygwin", target_vendor = "apple", + target_os = "netbsd", ))] fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { let mut result = [0; MAX_WITH_NUL]; @@ -463,7 +464,12 @@ pub fn set_name(name: &CStr) { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { + // See https://github.com/NetBSD/src/blob/8d40872b4c550a802379f3b9c22a40212d5e149d/lib/libpthread/pthread.h#L281 + // FIXME: move to libc. + const PTHREAD_MAX_NAMELEN_NP: usize = 32; + unsafe { + let name = truncate_cstr::<{ PTHREAD_MAX_NAMELEN_NP }>(name); let res = libc::pthread_setname_np( libc::pthread_self(), c"%s".as_ptr(), diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index dad1ea6c57377..9643c3b7bdad8 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -13,10 +13,16 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); + // If a custom `runner` is set up for the current target, we'll be + // executing `./runner ./test`, not just `./test`. For such a case, + // use the same arguments for child to avoid executing `runner` + // without an actual executable. + let args = env::args(); let (rx, tx) = pipe().unwrap(); assert!( process::Command::new(me) + .args(args) .env("I_AM_THE_CHILD", "1") .stdout(tx) .status() diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 93f73ccad3ea4..80e712a2388a1 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,8 +26,16 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - let child_output = - process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + // If a custom `runner` is set up for the current target, we'll be executing `./runner ./test`, + // not just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without an actual executable. + let args = env::args(); + let child_output = process::Command::new("mytest") + .args(args) + .env("PATH", &path) + .arg("child") + .output() + .unwrap(); assert!( child_output.status.success(), diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 98f6b842d135f..6333b32ab8ad4 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -337,6 +337,8 @@ jobs: cargo run --bin=stdarch-gen-loongarch --release -- crates/stdarch-gen-loongarch/lasx.spec git diff --exit-code - name: Check hexagon + env: + STDARCH_GEN_MODE: check run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index a1c31fa9f0cea..c8cfc21a5a1a1 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -268,6 +268,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -282,13 +298,14 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "getrandom" -version = "0.2.17" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "wasi", + "r-efi 5.3.0", + "wasip2", ] [[package]] @@ -299,7 +316,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "rand_core 0.10.0", "wasip2", "wasip3", @@ -445,6 +462,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -457,6 +480,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -529,7 +558,7 @@ checksum = "95c589f335db0f6aaa168a7cd27b1fc6920f5e1470c804f814d9cd6e62a0f70b" dependencies = [ "env_logger 0.11.10", "log", - "rand 0.10.0", + "rand 0.10.1", ] [[package]] @@ -541,6 +570,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "r-efi" version = "6.0.0" @@ -549,20 +584,19 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "libc", "rand_chacha", - "rand_core 0.6.4", + "rand_core 0.9.5", ] [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "getrandom 0.4.2", "rand_core 0.10.0", @@ -570,21 +604,21 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", + "rand_core 0.9.5", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.3.4", ] [[package]] @@ -648,6 +682,19 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.23" @@ -787,11 +834,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "stdarch-gen-common" +version = "0.1.0" +dependencies = [ + "tempfile", +] + [[package]] name = "stdarch-gen-hexagon" version = "0.1.0" dependencies = [ "regex", + "stdarch-gen-common", ] [[package]] @@ -805,7 +860,7 @@ dependencies = [ name = "stdarch-gen-loongarch" version = "0.1.0" dependencies = [ - "rand 0.8.5", + "rand 0.9.4", ] [[package]] @@ -838,7 +893,7 @@ version = "0.0.0" dependencies = [ "core_arch", "quickcheck", - "rand 0.8.5", + "rand 0.9.4", ] [[package]] @@ -864,6 +919,19 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -921,12 +989,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasip2" version = "1.0.2+wasi-0.2.9" diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 0441611f38fd2..8de7a4cfa5221 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -1,13 +1,19 @@ #!/usr/bin/env sh -set -ex - if [ $# -lt 2 ]; then - >&2 echo "Usage: $0 " + >&2 echo "Usage: $0 <..args for \`cargo test\`..>" exit 1 fi -case ${2} in +set -ex + +# Pop both arguments and leave "$@" as containing args to be forwarded to `cargo test` +TARGET="$1" +shift +CC_KIND="$1" +shift + +case ${CC_KIND} in clang) export CC="${CLANG_PATH}" CC_ARG_STYLE=clang @@ -22,7 +28,7 @@ case ${2} in CC_ARG_STYLE=clang ;; *) - >&2 echo "Unknown compiler: ${2}" + >&2 echo "Unknown compiler: ${CC_KIND}" exit 1 ;; esac @@ -35,7 +41,7 @@ echo "PROFILE=${PROFILE}" INTRINSIC_TEST="--manifest-path=crates/intrinsic-test/Cargo.toml" -case ${1} in +case ${TARGET} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" ARCH=aarch64_be @@ -60,24 +66,25 @@ case ${1} in esac -case "${1}" in +case "${TARGET}" in x86_64-unknown-linux-gnu*) env -u CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER \ cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/x86-intel.xml \ --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ - --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ - --target "${1}" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ - --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ - --target "${1}" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${CC_KIND}.txt" \ + --target "${TARGET}" \ --cc-arg-style "${CC_ARG_STYLE}" ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${1}" --profile "${PROFILE}" --tests +cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" \ + --tests "$@" diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index 8d701d9b88056..1b5b17e538bde 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -11903,7 +11903,7 @@ pub unsafe fn vluti4q_lane_s8(a: int8x16_t, b: uint8x8_t) -> in unsafe extern "unadjusted" { #[cfg_attr( any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.vluti4q.lane.v8i8" + link_name = "llvm.aarch64.neon.vluti4q.lane.v16i8" )] fn _vluti4q_lane_s8(a: int8x16_t, b: uint8x8_t, n: i32) -> int8x16_t; } diff --git a/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs b/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs index 4dcbc9549f115..88e5c0987f34d 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/prefetch.rs @@ -2,7 +2,7 @@ use stdarch_test::assert_instr; unsafe extern "unadjusted" { - #[link_name = "llvm.prefetch"] + #[link_name = "llvm.prefetch.p0"] fn prefetch(p: *const i8, rw: i32, loc: i32, ty: i32); } diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index e1157f3440cfb..ac3070918a025 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -1844,7 +1844,7 @@ pub fn svadrd_u64base_u64index(bases: svuint64_t, indices: svuint64_t) -> svuint #[cfg_attr(test, assert_instr(and))] pub fn svand_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.and.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.and.z.nxv16i1")] fn _svand_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svand_b_z(pg, op1, op2) } @@ -2936,7 +2936,7 @@ pub fn svasrd_n_s64_z(pg: svbool_t, op1: svint64_t) -> svint64_ #[cfg_attr(test, assert_instr(bic))] pub fn svbic_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.bic.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.bic.z.nxv16i1")] fn _svbic_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svbic_b_z(pg, op1, op2) } @@ -4560,7 +4560,7 @@ pub fn svcmla_lane_f32( unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fcmla.lane.x.nxv4f32" + link_name = "llvm.aarch64.sve.fcmla.lane.nxv4f32" )] fn _svcmla_lane_f32( op1: svfloat32_t, @@ -7658,7 +7658,10 @@ pub fn svcvt_f64_f32_z(pg: svbool_t, op: svfloat32_t) -> svfloat64_t { #[cfg_attr(test, assert_instr(scvtf))] pub fn svcvt_f32_s32_m(inactive: svfloat32_t, pg: svbool_t, op: svint32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i32")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.scvtf.nxv4f32.nxv4i32" + )] fn _svcvt_f32_s32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; } unsafe { _svcvt_f32_s32_m(inactive, pg.sve_into(), op) } @@ -7682,66 +7685,137 @@ pub fn svcvt_f32_s32_z(pg: svbool_t, op: svint32_t) -> svfloat32_t { svcvt_f32_s32_m(svdup_n_f32(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_m)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool_t, op: svuint32_t) -> svfloat32_t { + unsafe extern "unadjusted" { + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ucvtf.nxv4f32.nxv4i32" + )] + fn _svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; + } + unsafe { _svcvt_f32_u32_m(inactive, pg.sve_into(), op.as_signed()) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_x)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_x(pg: svbool_t, op: svuint32_t) -> svfloat32_t { + unsafe { svcvt_f32_u32_m(transmute_unchecked(op), pg, op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_z)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(ucvtf))] +pub fn svcvt_f32_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat32_t { + svcvt_f32_u32_m(svdup_n_f32(0.0), pg, op) +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool_t, op: svint64_t) -> svfloat32_t { +pub fn svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool_t, op: svint64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i64")] - fn _svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool2_t, op: svint64_t) -> svfloat32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.scvtf.nxv2f64.nxv2i64" + )] + fn _svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; } - unsafe { _svcvt_f32_s64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_f64_s64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_x(pg: svbool_t, op: svint64_t) -> svfloat32_t { - unsafe { svcvt_f32_s64_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_f64_s64_x(pg: svbool_t, op: svint64_t) -> svfloat64_t { + unsafe { svcvt_f64_s64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f32_s64_z(pg: svbool_t, op: svint64_t) -> svfloat32_t { - svcvt_f32_s64_m(svdup_n_f32(0.0), pg, op) +pub fn svcvt_f64_s64_z(pg: svbool_t, op: svint64_t) -> svfloat64_t { + svcvt_f64_s64_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool_t, op: svuint32_t) -> svfloat32_t { +pub fn svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool_t, op: svuint64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.ucvtf.f32i32")] - fn _svcvt_f32_u32_m(inactive: svfloat32_t, pg: svbool4_t, op: svint32_t) -> svfloat32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ucvtf.nxv2f64.nxv2i64" + )] + fn _svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; } - unsafe { _svcvt_f32_u32_m(inactive, pg.sve_into(), op.as_signed()) } + unsafe { _svcvt_f64_u64_m(inactive, pg.sve_into(), op.as_signed()) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_x(pg: svbool_t, op: svuint32_t) -> svfloat32_t { - unsafe { svcvt_f32_u32_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_f64_u64_x(pg: svbool_t, op: svuint64_t) -> svfloat64_t { + unsafe { svcvt_f64_u64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f32_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat32_t { - svcvt_f32_u32_m(svdup_n_f32(0.0), pg, op) +pub fn svcvt_f64_u64_z(pg: svbool_t, op: svuint64_t) -> svfloat64_t { + svcvt_f64_u64_m(svdup_n_f64(0.0), pg, op) +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_m)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool_t, op: svint64_t) -> svfloat32_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f32i64")] + fn _svcvt_f32_s64_m(inactive: svfloat32_t, pg: svbool2_t, op: svint64_t) -> svfloat32_t; + } + unsafe { _svcvt_f32_s64_m(inactive, pg.sve_into(), op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_x)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_x(pg: svbool_t, op: svint64_t) -> svfloat32_t { + unsafe { svcvt_f32_s64_m(transmute_unchecked(op), pg, op) } +} +#[doc = "Floating-point convert"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_s64]_z)"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(scvtf))] +pub fn svcvt_f32_s64_z(pg: svbool_t, op: svint64_t) -> svfloat32_t { + svcvt_f32_s64_m(svdup_n_f32(0.0), pg, op) } #[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f32[_u64]_m)"] @@ -7806,37 +7880,6 @@ pub fn svcvt_f64_s32_z(pg: svbool_t, op: svint32_t) -> svfloat64_t { svcvt_f64_s32_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_m)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool_t, op: svint64_t) -> svfloat64_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.scvtf.f64i64")] - fn _svcvt_f64_s64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; - } - unsafe { _svcvt_f64_s64_m(inactive, pg.sve_into(), op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_x)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_x(pg: svbool_t, op: svint64_t) -> svfloat64_t { - unsafe { svcvt_f64_s64_m(transmute_unchecked(op), pg, op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_s64]_z)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(scvtf))] -pub fn svcvt_f64_s64_z(pg: svbool_t, op: svint64_t) -> svfloat64_t { - svcvt_f64_s64_m(svdup_n_f64(0.0), pg, op) -} -#[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u32]_m)"] #[inline] #[target_feature(enable = "sve")] @@ -7868,190 +7911,202 @@ pub fn svcvt_f64_u32_z(pg: svbool_t, op: svuint32_t) -> svfloat64_t { svcvt_f64_u32_m(svdup_n_f64(0.0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool_t, op: svuint64_t) -> svfloat64_t { +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_m(inactive: svint32_t, pg: svbool_t, op: svfloat32_t) -> svint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.ucvtf.f64i64")] - fn _svcvt_f64_u64_m(inactive: svfloat64_t, pg: svbool2_t, op: svint64_t) -> svfloat64_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzs.nxv4i32.nxv4f32" + )] + fn _svcvt_s32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; } - unsafe { _svcvt_f64_u64_m(inactive, pg.sve_into(), op.as_signed()) } + unsafe { _svcvt_s32_f32_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_x(pg: svbool_t, op: svuint64_t) -> svfloat64_t { - unsafe { svcvt_f64_u64_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_x(pg: svbool_t, op: svfloat32_t) -> svint32_t { + unsafe { svcvt_s32_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_f64[_u64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(ucvtf))] -pub fn svcvt_f64_u64_z(pg: svbool_t, op: svuint64_t) -> svfloat64_t { - svcvt_f64_u64_m(svdup_n_f64(0.0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s32_f32_z(pg: svbool_t, op: svfloat32_t) -> svint32_t { + svcvt_s32_f32_m(svdup_n_s32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_m(inactive: svint32_t, pg: svbool_t, op: svfloat32_t) -> svint32_t { +pub fn svcvt_s64_f64_m(inactive: svint64_t, pg: svbool_t, op: svfloat64_t) -> svint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f32")] - fn _svcvt_s32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzs.nxv2i64.nxv2f64" + )] + fn _svcvt_s64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; } - unsafe { _svcvt_s32_f32_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_s64_f64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_x(pg: svbool_t, op: svfloat32_t) -> svint32_t { - unsafe { svcvt_s32_f32_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_s64_f64_x(pg: svbool_t, op: svfloat64_t) -> svint64_t { + unsafe { svcvt_s64_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f32_z(pg: svbool_t, op: svfloat32_t) -> svint32_t { - svcvt_s32_f32_m(svdup_n_s32(0), pg, op) +pub fn svcvt_s64_f64_z(pg: svbool_t, op: svfloat64_t) -> svint64_t { + svcvt_s64_f64_m(svdup_n_s64(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_m(inactive: svint32_t, pg: svbool_t, op: svfloat64_t) -> svint32_t { +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_m(inactive: svuint32_t, pg: svbool_t, op: svfloat32_t) -> svuint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f64")] - fn _svcvt_s32_f64_m(inactive: svint32_t, pg: svbool2_t, op: svfloat64_t) -> svint32_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzu.nxv4i32.nxv4f32" + )] + fn _svcvt_u32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; } - unsafe { _svcvt_s32_f64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_u32_f32_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_x(pg: svbool_t, op: svfloat64_t) -> svint32_t { - unsafe { svcvt_s32_f64_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint32_t { + unsafe { svcvt_u32_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s32_f64_z(pg: svbool_t, op: svfloat64_t) -> svint32_t { - svcvt_s32_f64_m(svdup_n_s32(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u32_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint32_t { + svcvt_u32_f32_m(svdup_n_u32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_m(inactive: svint64_t, pg: svbool_t, op: svfloat32_t) -> svint64_t { +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_m(inactive: svuint64_t, pg: svbool_t, op: svfloat64_t) -> svuint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f32")] - fn _svcvt_s64_f32_m(inactive: svint64_t, pg: svbool2_t, op: svfloat32_t) -> svint64_t; + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fcvtzu.nxv2i64.nxv2f64" + )] + fn _svcvt_u64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; } - unsafe { _svcvt_s64_f32_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_u64_f64_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_x(pg: svbool_t, op: svfloat32_t) -> svint64_t { - unsafe { svcvt_s64_f32_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_x(pg: svbool_t, op: svfloat64_t) -> svuint64_t { + unsafe { svcvt_u64_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f32_z(pg: svbool_t, op: svfloat32_t) -> svint64_t { - svcvt_s64_f32_m(svdup_n_s64(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzu))] +pub fn svcvt_u64_f64_z(pg: svbool_t, op: svfloat64_t) -> svuint64_t { + svcvt_u64_f64_m(svdup_n_u64(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_m(inactive: svint64_t, pg: svbool_t, op: svfloat64_t) -> svint64_t { +pub fn svcvt_s32_f64_m(inactive: svint32_t, pg: svbool_t, op: svfloat64_t) -> svint32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f64")] - fn _svcvt_s64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i32f64")] + fn _svcvt_s32_f64_m(inactive: svint32_t, pg: svbool2_t, op: svfloat64_t) -> svint32_t; } - unsafe { _svcvt_s64_f64_m(inactive, pg.sve_into(), op) } + unsafe { _svcvt_s32_f64_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_x(pg: svbool_t, op: svfloat64_t) -> svint64_t { - unsafe { svcvt_s64_f64_m(transmute_unchecked(op), pg, op) } +pub fn svcvt_s32_f64_x(pg: svbool_t, op: svfloat64_t) -> svint32_t { + unsafe { svcvt_s32_f64_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f64]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s32[_f64]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] #[cfg_attr(test, assert_instr(fcvtzs))] -pub fn svcvt_s64_f64_z(pg: svbool_t, op: svfloat64_t) -> svint64_t { - svcvt_s64_f64_m(svdup_n_s64(0), pg, op) +pub fn svcvt_s32_f64_z(pg: svbool_t, op: svfloat64_t) -> svint32_t { + svcvt_s32_f64_m(svdup_n_s32(0), pg, op) } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_m)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_m)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_m(inactive: svuint32_t, pg: svbool_t, op: svfloat32_t) -> svuint32_t { +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_m(inactive: svint64_t, pg: svbool_t, op: svfloat32_t) -> svint64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzu.i32f32")] - fn _svcvt_u32_f32_m(inactive: svint32_t, pg: svbool4_t, op: svfloat32_t) -> svint32_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzs.i64f32")] + fn _svcvt_s64_f32_m(inactive: svint64_t, pg: svbool2_t, op: svfloat32_t) -> svint64_t; } - unsafe { _svcvt_u32_f32_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } + unsafe { _svcvt_s64_f32_m(inactive, pg.sve_into(), op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_x)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_x)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint32_t { - unsafe { svcvt_u32_f32_m(transmute_unchecked(op), pg, op) } +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_x(pg: svbool_t, op: svfloat32_t) -> svint64_t { + unsafe { svcvt_s64_f32_m(transmute_unchecked(op), pg, op) } } #[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f32]_z)"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_s64[_f32]_z)"] #[inline] #[target_feature(enable = "sve")] #[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u32_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint32_t { - svcvt_u32_f32_m(svdup_n_u32(0), pg, op) +#[cfg_attr(test, assert_instr(fcvtzs))] +pub fn svcvt_s64_f32_z(pg: svbool_t, op: svfloat32_t) -> svint64_t { + svcvt_s64_f32_m(svdup_n_s64(0), pg, op) } #[doc = "Floating-point convert"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u32[_f64]_m)"] @@ -8115,37 +8170,6 @@ pub fn svcvt_u64_f32_x(pg: svbool_t, op: svfloat32_t) -> svuint64_t { pub fn svcvt_u64_f32_z(pg: svbool_t, op: svfloat32_t) -> svuint64_t { svcvt_u64_f32_m(svdup_n_u64(0), pg, op) } -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_m)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_m(inactive: svuint64_t, pg: svbool_t, op: svfloat64_t) -> svuint64_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fcvtzu.i64f64")] - fn _svcvt_u64_f64_m(inactive: svint64_t, pg: svbool2_t, op: svfloat64_t) -> svint64_t; - } - unsafe { _svcvt_u64_f64_m(inactive.as_signed(), pg.sve_into(), op).as_unsigned() } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_x)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_x(pg: svbool_t, op: svfloat64_t) -> svuint64_t { - unsafe { svcvt_u64_f64_m(transmute_unchecked(op), pg, op) } -} -#[doc = "Floating-point convert"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svcvt_u64[_f64]_z)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(fcvtzu))] -pub fn svcvt_u64_f64_z(pg: svbool_t, op: svfloat64_t) -> svuint64_t { - svcvt_u64_f64_m(svdup_n_u64(0), pg, op) -} #[doc = "Divide"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svdiv[_f32]_m)"] #[inline] @@ -10041,7 +10065,7 @@ pub fn svdupq_n_u8( #[cfg_attr(test, assert_instr(eor))] pub fn sveor_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.eor.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.eor.z.nxv16i1")] fn _sveor_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _sveor_b_z(pg, op1, op2) } @@ -10592,7 +10616,7 @@ pub fn svexpa_f32(op: svuint32_t) -> svfloat32_t { unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fexpa.x.nxv4f32 " + link_name = "llvm.aarch64.sve.fexpa.x.nxv4f32" )] fn _svexpa_f32(op: svint32_t) -> svfloat32_t; } @@ -10608,7 +10632,7 @@ pub fn svexpa_f64(op: svuint64_t) -> svfloat64_t { unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", - link_name = "llvm.aarch64.sve.fexpa.x.nxv2f64 " + link_name = "llvm.aarch64.sve.fexpa.x.nxv2f64" )] fn _svexpa_f64(op: svint64_t) -> svfloat64_t; } @@ -27372,7 +27396,10 @@ pub fn svmls_lane_f64( #[cfg_attr(test, assert_instr(fmmla))] pub fn svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fmmla.nxv4f32")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fmmla.nxv4f32.nxv4f32" + )] fn _svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svfloat32_t; } unsafe { _svmmla_f32(op1, op2, op3) } @@ -27385,7 +27412,10 @@ pub fn svmmla_f32(op1: svfloat32_t, op2: svfloat32_t, op3: svfloat32_t) -> svflo #[cfg_attr(test, assert_instr(fmmla))] pub fn svmmla_f64(op1: svfloat64_t, op2: svfloat64_t, op3: svfloat64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.fmmla.nxv2f64")] + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.fmmla.nxv2f64.nxv2f64" + )] fn _svmmla_f64(op1: svfloat64_t, op2: svfloat64_t, op3: svfloat64_t) -> svfloat64_t; } unsafe { _svmmla_f64(op1, op2, op3) } @@ -30261,7 +30291,7 @@ pub fn svnot_u64_z(pg: svbool_t, op: svuint64_t) -> svuint64_t { #[cfg_attr(test, assert_instr(orn))] pub fn svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orn.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orn.z.nxv16i1")] fn _svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svorn_b_z(pg, op1, op2) } @@ -30274,7 +30304,7 @@ pub fn svorn_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(orr))] pub fn svorr_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orr.z.nvx16i1")] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.orr.z.nxv16i1")] fn _svorr_b_z(pg: svbool_t, op1: svbool_t, op2: svbool_t) -> svbool_t; } unsafe { _svorr_b_z(pg, op1, op2) } @@ -34341,10 +34371,7 @@ pub fn svrecps_f64(op1: svfloat64_t, op2: svfloat64_t) -> svfloat64_t { #[cfg_attr(test, assert_instr(frecpx))] pub fn svrecpx_f32_m(inactive: svfloat32_t, pg: svbool_t, op: svfloat32_t) -> svfloat32_t { unsafe extern "unadjusted" { - #[cfg_attr( - target_arch = "aarch64", - link_name = "llvm.aarch64.sve.frecpx.x.nxv4f32" - )] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.frecpx.nxv4f32")] fn _svrecpx_f32_m(inactive: svfloat32_t, pg: svbool4_t, op: svfloat32_t) -> svfloat32_t; } unsafe { _svrecpx_f32_m(inactive, pg.sve_into(), op) } @@ -34375,10 +34402,7 @@ pub fn svrecpx_f32_z(pg: svbool_t, op: svfloat32_t) -> svfloat32_t { #[cfg_attr(test, assert_instr(frecpx))] pub fn svrecpx_f64_m(inactive: svfloat64_t, pg: svbool_t, op: svfloat64_t) -> svfloat64_t { unsafe extern "unadjusted" { - #[cfg_attr( - target_arch = "aarch64", - link_name = "llvm.aarch64.sve.frecpx.x.nxv2f64" - )] + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.frecpx.nxv2f64")] fn _svrecpx_f64_m(inactive: svfloat64_t, pg: svbool2_t, op: svfloat64_t) -> svfloat64_t; } unsafe { _svrecpx_f64_m(inactive, pg.sve_into(), op) } @@ -35202,19 +35226,6 @@ pub fn svreinterpret_u64_u64(op: svuint64_t) -> svuint64_t { unsafe { crate::intrinsics::transmute_unchecked(op) } } #[doc = "Reverse all elements"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(rev))] -pub fn svrev_b8(op: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv16i1")] - fn _svrev_b8(op: svbool_t) -> svbool_t; - } - unsafe { _svrev_b8(op) } -} -#[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -35222,10 +35233,10 @@ pub fn svrev_b8(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b16(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv8i1")] - fn _svrev_b16(op: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b16")] + fn _svrev_b16(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b16(op.sve_into()).sve_into() } + unsafe { _svrev_b16(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b32)"] @@ -35235,10 +35246,10 @@ pub fn svrev_b16(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b32(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv4i1")] - fn _svrev_b32(op: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b32")] + fn _svrev_b32(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b32(op.sve_into()).sve_into() } + unsafe { _svrev_b32(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev_b64)"] @@ -35248,10 +35259,10 @@ pub fn svrev_b32(op: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(rev))] pub fn svrev_b64(op: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv2i1")] - fn _svrev_b64(op: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.rev.b64")] + fn _svrev_b64(op: svbool_t) -> svbool_t; } - unsafe { _svrev_b64(op.sve_into()).sve_into() } + unsafe { _svrev_b64(op.sve_into()) } } #[doc = "Reverse all elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev[_f32])"] @@ -35367,6 +35378,19 @@ pub fn svrev_u32(op: svuint32_t) -> svuint32_t { pub fn svrev_u64(op: svuint64_t) -> svuint64_t { unsafe { svrev_s64(op.as_signed()).as_unsigned() } } +#[doc = "Reverse all elements"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrev[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(rev))] +pub fn svrev_b8(op: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.vector.reverse.nxv16i1")] + fn _svrev_b8(op: svbool_t) -> svbool_t; + } + unsafe { _svrev_b8(op) } +} #[doc = "Reverse bytes within elements"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svrevb[_s16]_m)"] #[inline] @@ -43312,19 +43336,6 @@ pub fn svusmmla_s32(op1: svint32_t, op2: svuint8_t, op3: svint8_t) -> svint32_t unsafe { _svusmmla_s32(op1, op2.as_signed(), op3) } } #[doc = "Concatenate even elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(uzp1))] -pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv16i1")] - fn _svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svuzp1_b8(op1, op2) } -} -#[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -43332,10 +43343,10 @@ pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv8i1")] - fn _svuzp1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b16")] + fn _svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b32)"] @@ -43345,10 +43356,10 @@ pub fn svuzp1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv4i1")] - fn _svuzp1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b32")] + fn _svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1_b64)"] @@ -43358,10 +43369,10 @@ pub fn svuzp1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp1))] pub fn svuzp1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv2i1")] - fn _svuzp1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.b64")] + fn _svuzp1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1[_f32])"] @@ -43477,6 +43488,19 @@ pub fn svuzp1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svuzp1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Concatenate even elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(uzp1))] +pub fn svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp1.nxv16i1")] + fn _svuzp1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svuzp1_b8(op1, op2) } +} #[doc = "Concatenate even quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp1q[_f32])"] #[inline] @@ -43592,19 +43616,6 @@ pub fn svuzp1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Concatenate odd elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(uzp2))] -pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv16i1")] - fn _svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svuzp2_b8(op1, op2) } -} -#[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -43612,10 +43623,10 @@ pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv8i1")] - fn _svuzp2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b16")] + fn _svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b32)"] @@ -43625,10 +43636,10 @@ pub fn svuzp2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv4i1")] - fn _svuzp2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b32")] + fn _svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2_b64)"] @@ -43638,10 +43649,10 @@ pub fn svuzp2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(uzp2))] pub fn svuzp2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv2i1")] - fn _svuzp2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.b64")] + fn _svuzp2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svuzp2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svuzp2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Concatenate odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2[_f32])"] @@ -43757,6 +43768,19 @@ pub fn svuzp2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svuzp2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svuzp2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Concatenate odd elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(uzp2))] +pub fn svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.uzp2.nxv16i1")] + fn _svuzp2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svuzp2_b8(op1, op2) } +} #[doc = "Concatenate odd quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svuzp2q[_f32])"] #[inline] @@ -44397,19 +44421,6 @@ pub fn svwrffr(op: svbool_t) { unsafe { _svwrffr(op) } } #[doc = "Interleave elements from low halves of two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(zip1))] -pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv16i1")] - fn _svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svzip1_b8(op1, op2) } -} -#[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -44417,10 +44428,10 @@ pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv8i1")] - fn _svzip1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b16")] + fn _svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b32)"] @@ -44430,10 +44441,10 @@ pub fn svzip1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv4i1")] - fn _svzip1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b32")] + fn _svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1_b64)"] @@ -44443,10 +44454,10 @@ pub fn svzip1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip1))] pub fn svzip1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv2i1")] - fn _svzip1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.b64")] + fn _svzip1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1[_f32])"] @@ -44562,6 +44573,19 @@ pub fn svzip1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svzip1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave elements from low halves of two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(zip1))] +pub fn svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip1.nxv16i1")] + fn _svzip1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svzip1_b8(op1, op2) } +} #[doc = "Interleave quadwords from low halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip1q[_f32])"] #[inline] @@ -44677,19 +44701,6 @@ pub fn svzip1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Interleave elements from high halves of two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(zip2))] -pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv16i1")] - fn _svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svzip2_b8(op1, op2) } -} -#[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -44697,10 +44708,10 @@ pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv8i1")] - fn _svzip2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b16")] + fn _svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b32)"] @@ -44710,10 +44721,10 @@ pub fn svzip2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv4i1")] - fn _svzip2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b32")] + fn _svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2_b64)"] @@ -44723,10 +44734,10 @@ pub fn svzip2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(zip2))] pub fn svzip2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv2i1")] - fn _svzip2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.b64")] + fn _svzip2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svzip2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svzip2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave elements from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2[_f32])"] @@ -44842,6 +44853,19 @@ pub fn svzip2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svzip2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svzip2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave elements from high halves of two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(zip2))] +pub fn svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.zip2.nxv16i1")] + fn _svzip2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svzip2_b8(op1, op2) } +} #[doc = "Interleave quadwords from high halves of two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svzip2q[_f32])"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs index a3f70ab61c40f..f11ca660b15e2 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/mod.rs @@ -28,6 +28,14 @@ pub(super) trait SveInto: Sized { unsafe fn sve_into(self) -> T; } +impl SveInto for T { + #[inline] + #[target_feature(enable = "sve")] + unsafe fn sve_into(self) -> T { + self + } +} + macro_rules! impl_sve_type { ($(($v:vis, $elem_type:ty, $name:ident, $elt:literal))*) => ($( #[doc = concat!("Scalable vector of type ", stringify!($elem_type))] @@ -130,7 +138,7 @@ macro_rules! impl_internal_sve_predicate { #[target_feature(enable = "sve")] unsafe fn sve_into(self) -> svbool_t { #[allow(improper_ctypes)] - unsafe extern "C" { + unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", link_name = concat!("llvm.aarch64.sve.convert.to.svbool.nxv", $elt, "i1") @@ -147,7 +155,7 @@ macro_rules! impl_internal_sve_predicate { #[target_feature(enable = "sve")] unsafe fn sve_into(self) -> $name { #[allow(improper_ctypes)] - unsafe extern "C" { + unsafe extern "unadjusted" { #[cfg_attr( target_arch = "aarch64", link_name = concat!("llvm.aarch64.sve.convert.from.svbool.nxv", $elt, "i1") diff --git a/library/stdarch/crates/core_arch/src/hexagon/v128.rs b/library/stdarch/crates/core_arch/src/hexagon/v128.rs index 1f0566af78ef3..1a1826ddda316 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/v128.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/v128.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon HVX 128-byte vector mode intrinsics //! //! This module provides intrinsics for the Hexagon Vector Extensions (HVX) diff --git a/library/stdarch/crates/core_arch/src/hexagon/v64.rs b/library/stdarch/crates/core_arch/src/hexagon/v64.rs index e9b18b2fd8efe..3ac8b47079333 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/v64.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/v64.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon HVX 64-byte vector mode intrinsics //! //! This module provides intrinsics for the Hexagon Vector Extensions (HVX) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index 7e9f45f5e7b04..ffaa4225ba99d 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -62,56 +62,56 @@ unsafe extern "unadjusted" { /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_b_w(a: i8, b: i32) -> i32 { - unsafe { __crc_w_b_w(a.cast_unsigned() as i32, b) } + unsafe { __crc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_h_w(a: i16, b: i32) -> i32 { - unsafe { __crc_w_h_w(a.cast_unsigned() as i32, b) } + unsafe { __crc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_w_w(a: i32, b: i32) -> i32 { unsafe { __crc_w_w_w(a, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crc_w_d_w(a, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_b_w(a: i8, b: i32) -> i32 { - unsafe { __crcc_w_b_w(a.cast_unsigned() as i32, b) } + unsafe { __crcc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_h_w(a: i16, b: i32) -> i32 { - unsafe { __crcc_w_h_w(a.cast_unsigned() as i32, b) } + unsafe { __crcc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { unsafe { __crcc_w_w_w(a, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] +#[stable(feature = "stdarch_loongarch_crc", since = "CURRENT_RUSTC_VERSION")] pub fn crcc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crcc_w_d_w(a, b) } } diff --git a/library/stdarch/crates/core_arch/src/mips/msa.rs b/library/stdarch/crates/core_arch/src/mips/msa.rs index bc601baef9e2a..dab64319ccc3b 100644 --- a/library/stdarch/crates/core_arch/src/mips/msa.rs +++ b/library/stdarch/crates/core_arch/src/mips/msa.rs @@ -45,7 +45,7 @@ types! { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.mips.add.a.b"] fn msa_add_a_b(a: v16i8, b: v16i8) -> v16i8; #[link_name = "llvm.mips.add.a.h"] diff --git a/library/stdarch/crates/core_arch/src/nvptx/mod.rs b/library/stdarch/crates/core_arch/src/nvptx/mod.rs index b63a5d01a7a22..d22f3a25bf70e 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/mod.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/mod.rs @@ -19,9 +19,9 @@ mod packed; pub use packed::*; #[allow(improper_ctypes)] -unsafe extern "C" { - #[link_name = "llvm.nvvm.barrier0"] - fn syncthreads() -> (); +unsafe extern "unadjusted" { + #[link_name = "llvm.nvvm.barrier.cta.sync.aligned.all"] + fn syncthreads(a: u32) -> (); #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.x"] fn block_dim_x() -> u32; #[link_name = "llvm.nvvm.read.ptx.sreg.ntid.y"] @@ -54,7 +54,7 @@ unsafe extern "C" { #[inline] #[unstable(feature = "stdarch_nvptx", issue = "111199")] pub unsafe fn _syncthreads() -> () { - syncthreads() + syncthreads(0) } /// x-th thread-block dimension. diff --git a/library/stdarch/crates/core_arch/src/nvptx/packed.rs b/library/stdarch/crates/core_arch/src/nvptx/packed.rs index 1c7e81268fc99..7e6c14bc993d9 100644 --- a/library/stdarch/crates/core_arch/src/nvptx/packed.rs +++ b/library/stdarch/crates/core_arch/src/nvptx/packed.rs @@ -7,7 +7,7 @@ use crate::intrinsics::simd::*; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.minimum.v2f16"] fn llvm_f16x2_minimum(a: f16x2, b: f16x2) -> f16x2; #[link_name = "llvm.maximum.v2f16"] diff --git a/library/stdarch/crates/core_arch/src/powerpc/altivec.rs b/library/stdarch/crates/core_arch/src/powerpc/altivec.rs index 78ec39f91ff33..ccfed88a93c2a 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/altivec.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/altivec.rs @@ -96,7 +96,7 @@ impl From for m32x4 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.altivec.lvx"] fn lvx(p: *const i8) -> vector_unsigned_int; diff --git a/library/stdarch/crates/core_arch/src/powerpc/vsx.rs b/library/stdarch/crates/core_arch/src/powerpc/vsx.rs index 4a7b561a20c55..60cb2ad44cd4e 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/vsx.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/vsx.rs @@ -52,7 +52,7 @@ impl From for m64x2 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.altivec.vperm"] fn vperm( a: vector_signed_int, diff --git a/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs b/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs index 7b42be8653c55..9032e7795980f 100644 --- a/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs +++ b/library/stdarch/crates/core_arch/src/powerpc64/vsx.rs @@ -17,7 +17,7 @@ use stdarch_test::assert_instr; use crate::mem::transmute; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.ppc.vsx.lxvl"] fn lxvl(a: *const u8, l: usize) -> vector_signed_int; diff --git a/library/stdarch/crates/core_arch/src/wasm32/memory.rs b/library/stdarch/crates/core_arch/src/wasm32/memory.rs index 90e9075e5136b..9d7b7287ede1b 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/memory.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/memory.rs @@ -2,9 +2,11 @@ use stdarch_test::assert_instr; unsafe extern "unadjusted" { - #[link_name = "llvm.wasm.memory.grow"] + #[cfg_attr(target_pointer_width = "32", link_name = "llvm.wasm.memory.grow.i32")] + #[cfg_attr(target_pointer_width = "64", link_name = "llvm.wasm.memory.grow.i64")] fn llvm_memory_grow(mem: u32, pages: usize) -> usize; - #[link_name = "llvm.wasm.memory.size"] + #[cfg_attr(target_pointer_width = "32", link_name = "llvm.wasm.memory.size.i32")] + #[cfg_attr(target_pointer_width = "64", link_name = "llvm.wasm.memory.size.i64")] fn llvm_memory_size(mem: u32) -> usize; } diff --git a/library/stdarch/crates/core_arch/src/x86/aes.rs b/library/stdarch/crates/core_arch/src/x86/aes.rs index d07ab4dc2a01e..ecb09a6a9099f 100644 --- a/library/stdarch/crates/core_arch/src/x86/aes.rs +++ b/library/stdarch/crates/core_arch/src/x86/aes.rs @@ -13,7 +13,7 @@ use crate::core_arch::x86::__m128i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.aesni.aesdec"] fn aesdec(a: __m128i, round_key: __m128i) -> __m128i; #[link_name = "llvm.x86.aesni.aesdeclast"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx.rs b/library/stdarch/crates/core_arch/src/x86/avx.rs index ef434205b52a0..f5ddff6457aea 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx.rs @@ -3292,7 +3292,7 @@ pub const fn _mm256_cvtss_f32(a: __m256) -> f32 { // LLVM intrinsics used in the above functions #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx.round.pd.256"] fn roundpd256(a: __m256d, b: i32) -> __m256d; #[link_name = "llvm.x86.avx.round.ps.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx2.rs b/library/stdarch/crates/core_arch/src/x86/avx2.rs index b49ad9522a412..6925ba8e27028 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx2.rs @@ -3906,7 +3906,7 @@ pub const fn _mm256_extract_epi16(a: __m256i) -> i32 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx2.pmadd.wd"] fn pmaddwd(a: i16x16, b: i16x16) -> i32x8; #[link_name = "llvm.x86.avx2.pmadd.ub.sw"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs b/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs index 8d944f5ba817b..230a4f3754cad 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bf16.rs @@ -9,7 +9,7 @@ use crate::intrinsics::simd::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512bf16.cvtne2ps2bf16.128"] fn cvtne2ps2bf16(a: f32x4, b: f32x4) -> i16x8; #[link_name = "llvm.x86.avx512bf16.cvtne2ps2bf16.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs b/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs index dd211854afbb4..344130fb6e720 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bitalg.rs @@ -27,7 +27,7 @@ use crate::mem::transmute; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpshufbitqmb.512"] fn bitshuffle_512(data: i8x64, indices: i8x64) -> __mmask64; #[link_name = "llvm.x86.avx512.vpshufbitqmb.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512bw.rs b/library/stdarch/crates/core_arch/src/x86/avx512bw.rs index 659d6c3be88e7..50d57b4964489 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512bw.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512bw.rs @@ -12764,7 +12764,7 @@ pub unsafe fn _mm_mask_cvtusepi16_storeu_epi8(mem_addr: *mut i8, k: __mmask8, a: } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.pmul.hr.sw.512"] fn vpmulhrsw(a: i16x32, b: i16x32) -> i16x32; diff --git a/library/stdarch/crates/core_arch/src/x86/avx512cd.rs b/library/stdarch/crates/core_arch/src/x86/avx512cd.rs index 4082433e70759..378bc3617ed63 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512cd.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512cd.rs @@ -563,7 +563,7 @@ pub const fn _mm_maskz_lzcnt_epi64(k: __mmask8, a: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.conflict.d.512"] fn vpconflictd(a: i32x16) -> i32x16; #[link_name = "llvm.x86.avx512.conflict.d.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512dq.rs b/library/stdarch/crates/core_arch/src/x86/avx512dq.rs index 0b322c8b83c7b..5fe40b7541549 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512dq.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512dq.rs @@ -7235,7 +7235,7 @@ pub fn _mm_mask_fpclass_ss_mask(k1: __mmask8, a: __m128) -> __m } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.sitofp.round.v2f64.v2i64"] fn vcvtqq2pd_128(a: i64x2, rounding: i32) -> f64x2; #[link_name = "llvm.x86.avx512.sitofp.round.v4f64.v4i64"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 9b90ad60a39fb..76e7297bcda40 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -44215,7 +44215,7 @@ pub const _MM_PERM_DDDC: _MM_PERM_ENUM = 0xFE; pub const _MM_PERM_DDDD: _MM_PERM_ENUM = 0xFF; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.sqrt.ps.512"] fn vsqrtps(a: f32x16, rounding: i32) -> f32x16; #[link_name = "llvm.x86.avx512.sqrt.pd.512"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs b/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs index 5ce28565d1085..262215e62c43d 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512ifma.rs @@ -347,7 +347,7 @@ pub fn _mm_maskz_madd52lo_epu64(k: __mmask8, a: __m128i, b: __m128i, c: __m128i) } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpmadd52l.uq.128"] fn vpmadd52luq_128(z: __m128i, x: __m128i, y: __m128i) -> __m128i; #[link_name = "llvm.x86.avx512.vpmadd52h.uq.128"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs index d9ad14ef00ddb..141a8c8aec2bc 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi.rs @@ -453,7 +453,7 @@ pub fn _mm_maskz_multishift_epi64_epi8(k: __mmask16, a: __m128i, b: __m128i) -> } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpermi2var.qi.512"] fn vpermi2b(a: i8x64, idx: i8x64, b: i8x64) -> i8x64; #[link_name = "llvm.x86.avx512.vpermi2var.qi.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs index 78a50b90c8614..0a4accc2f70fb 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vbmi2.rs @@ -2383,7 +2383,7 @@ pub const fn _mm_maskz_shrdi_epi16( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.mask.compress.store.w.512"] fn vcompressstorew(mem: *mut i8, data: i16x32, mask: u32); #[link_name = "llvm.x86.avx512.mask.compress.store.w.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs b/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs index 8cd8764f24868..b9084aec59344 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vnni.rs @@ -873,7 +873,7 @@ pub fn _mm256_dpwuuds_epi32(src: __m256i, a: __m256i, b: __m256i) -> __m256i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vpdpwssd.512"] fn vpdpwssd(src: i32x16, a: i16x32, b: i16x32) -> i32x16; #[link_name = "llvm.x86.avx512.vpdpwssd.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs b/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs index 4dd7412e9e330..30fcfce63ccf4 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512vp2intersect.rs @@ -110,7 +110,7 @@ pub unsafe fn _mm512_2intersect_epi64( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vp2intersect.d.128"] fn vp2intersectd_128(a: i32x4, b: i32x4) -> (u8, u8); #[link_name = "llvm.x86.avx512.vp2intersect.q.128"] diff --git a/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs b/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs index 861213eb4257f..b17d3de48b595 100644 --- a/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs +++ b/library/stdarch/crates/core_arch/src/x86/avxneconvert.rs @@ -176,7 +176,7 @@ pub fn _mm256_cvtneps_avx_pbh(a: __m256) -> __m128bh { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.vbcstnebf162ps128"] fn bcstnebf162ps_128(a: *const bf16) -> __m128; #[link_name = "llvm.x86.vbcstnebf162ps256"] diff --git a/library/stdarch/crates/core_arch/src/x86/bmi1.rs b/library/stdarch/crates/core_arch/src/x86/bmi1.rs index 432051abd1cfa..21da03667f849 100644 --- a/library/stdarch/crates/core_arch/src/x86/bmi1.rs +++ b/library/stdarch/crates/core_arch/src/x86/bmi1.rs @@ -131,7 +131,7 @@ pub const fn _mm_tzcnt_32(x: u32) -> i32 { x.trailing_zeros() as i32 } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bextr.32"] fn x86_bmi_bextr_32(x: u32, y: u32) -> u32; } diff --git a/library/stdarch/crates/core_arch/src/x86/bmi2.rs b/library/stdarch/crates/core_arch/src/x86/bmi2.rs index 5320640d96873..dae4133d63bec 100644 --- a/library/stdarch/crates/core_arch/src/x86/bmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86/bmi2.rs @@ -67,7 +67,7 @@ pub fn _pext_u32(a: u32, mask: u32) -> u32 { unsafe { x86_bmi2_pext_32(a, mask) } } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bzhi.32"] fn x86_bmi2_bzhi_32(x: u32, y: u32) -> u32; #[link_name = "llvm.x86.bmi.pdep.32"] diff --git a/library/stdarch/crates/core_arch/src/x86/fxsr.rs b/library/stdarch/crates/core_arch/src/x86/fxsr.rs index 08619efe7c9ef..74a95e192a3af 100644 --- a/library/stdarch/crates/core_arch/src/x86/fxsr.rs +++ b/library/stdarch/crates/core_arch/src/x86/fxsr.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.fxsave"] fn fxsave(p: *mut u8); #[link_name = "llvm.x86.fxrstor"] diff --git a/library/stdarch/crates/core_arch/src/x86/gfni.rs b/library/stdarch/crates/core_arch/src/x86/gfni.rs index e9ee27a7b823b..8cebd04a4b836 100644 --- a/library/stdarch/crates/core_arch/src/x86/gfni.rs +++ b/library/stdarch/crates/core_arch/src/x86/gfni.rs @@ -23,7 +23,7 @@ use crate::mem::transmute; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.vgf2p8affineinvqb.512"] fn vgf2p8affineinvqb_512(x: i8x64, a: i8x64, imm8: u8) -> i8x64; #[link_name = "llvm.x86.vgf2p8affineinvqb.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs b/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs index 0f2769257f958..454785c5e0c89 100644 --- a/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs +++ b/library/stdarch/crates/core_arch/src/x86/pclmulqdq.rs @@ -11,7 +11,7 @@ use crate::core_arch::x86::__m128i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.pclmulqdq"] fn pclmulqdq(a: __m128i, round_key: __m128i, imm8: u8) -> __m128i; } diff --git a/library/stdarch/crates/core_arch/src/x86/rtm.rs b/library/stdarch/crates/core_arch/src/x86/rtm.rs index c88bd6592d784..f09b95d76bdb3 100644 --- a/library/stdarch/crates/core_arch/src/x86/rtm.rs +++ b/library/stdarch/crates/core_arch/src/x86/rtm.rs @@ -16,7 +16,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xbegin"] fn x86_xbegin() -> i32; #[link_name = "llvm.x86.xend"] diff --git a/library/stdarch/crates/core_arch/src/x86/sha.rs b/library/stdarch/crates/core_arch/src/x86/sha.rs index f8a3295d19589..f7d8c3c0b2408 100644 --- a/library/stdarch/crates/core_arch/src/x86/sha.rs +++ b/library/stdarch/crates/core_arch/src/x86/sha.rs @@ -1,7 +1,7 @@ use crate::core_arch::{simd::*, x86::*}; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sha1msg1"] fn sha1msg1(a: i32x4, b: i32x4) -> i32x4; #[link_name = "llvm.x86.sha1msg2"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse.rs b/library/stdarch/crates/core_arch/src/x86/sse.rs index 867387290119b..e31175d48cf94 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse.rs @@ -1987,7 +1987,7 @@ pub const fn _MM_TRANSPOSE4_PS( } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse.rcp.ss"] fn rcpss(a: __m128) -> __m128; #[link_name = "llvm.x86.sse.rcp.ps"] @@ -2040,7 +2040,7 @@ unsafe extern "C" { fn stmxcsr(p: *mut i8); #[link_name = "llvm.x86.sse.ldmxcsr"] fn ldmxcsr(p: *const i8); - #[link_name = "llvm.prefetch"] + #[link_name = "llvm.prefetch.p0"] fn prefetch(p: *const i8, rw: i32, loc: i32, ty: i32); #[link_name = "llvm.x86.sse.cmp.ss"] fn cmpss(a: __m128, b: __m128, imm8: i8) -> __m128; diff --git a/library/stdarch/crates/core_arch/src/x86/sse2.rs b/library/stdarch/crates/core_arch/src/x86/sse2.rs index 62719700ad905..bd7e4df2138d5 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse2.rs @@ -3242,7 +3242,7 @@ pub const fn _mm_unpacklo_pd(a: __m128d, b: __m128d) -> __m128d { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse2.pause"] fn pause(); #[link_name = "llvm.x86.sse2.clflush"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse3.rs b/library/stdarch/crates/core_arch/src/x86/sse3.rs index e4c75702544d9..2f6fa35cb616a 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse3.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse3.rs @@ -178,7 +178,7 @@ pub const fn _mm_moveldup_ps(a: __m128) -> __m128 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse3.ldu.dq"] fn lddqu(mem_addr: *const i8) -> i8x16; } diff --git a/library/stdarch/crates/core_arch/src/x86/sse41.rs b/library/stdarch/crates/core_arch/src/x86/sse41.rs index 4ebf7d3bd39a8..d981166ff08c9 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse41.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse41.rs @@ -1181,7 +1181,7 @@ pub unsafe fn _mm_stream_load_si128(mem_addr: *const __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse41.insertps"] fn insertps(a: __m128, b: __m128, imm8: u8) -> __m128; #[link_name = "llvm.x86.sse41.dppd"] diff --git a/library/stdarch/crates/core_arch/src/x86/sse42.rs b/library/stdarch/crates/core_arch/src/x86/sse42.rs index 55e22592637f1..0534fe9379a18 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse42.rs @@ -569,7 +569,7 @@ pub const fn _mm_cmpgt_epi64(a: __m128i, b: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { // SSE 4.2 string and text comparison ops #[link_name = "llvm.x86.sse42.pcmpestrm128"] fn pcmpestrm128(a: i8x16, la: i32, b: i8x16, lb: i32, imm8: i8) -> u8x16; diff --git a/library/stdarch/crates/core_arch/src/x86/sse4a.rs b/library/stdarch/crates/core_arch/src/x86/sse4a.rs index f36b879a030e3..14e9b56dcb2ed 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse4a.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse4a.rs @@ -6,7 +6,7 @@ use crate::core_arch::{simd::*, x86::*}; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse4a.extrq"] fn extrq(x: i64x2, y: i8x16) -> i64x2; #[link_name = "llvm.x86.sse4a.extrqi"] diff --git a/library/stdarch/crates/core_arch/src/x86/ssse3.rs b/library/stdarch/crates/core_arch/src/x86/ssse3.rs index 1d7a97944a37b..b0152c81596bd 100644 --- a/library/stdarch/crates/core_arch/src/x86/ssse3.rs +++ b/library/stdarch/crates/core_arch/src/x86/ssse3.rs @@ -345,7 +345,7 @@ pub fn _mm_sign_epi32(a: __m128i, b: __m128i) -> __m128i { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.ssse3.pshuf.b.128"] fn pshufb128(a: u8x16, b: u8x16) -> u8x16; diff --git a/library/stdarch/crates/core_arch/src/x86/tbm.rs b/library/stdarch/crates/core_arch/src/x86/tbm.rs index 0ba4572dcd029..2a44579408364 100644 --- a/library/stdarch/crates/core_arch/src/x86/tbm.rs +++ b/library/stdarch/crates/core_arch/src/x86/tbm.rs @@ -13,7 +13,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.tbm.bextri.u32"] fn bextri_u32(a: u32, control: u32) -> u32; } diff --git a/library/stdarch/crates/core_arch/src/x86/vaes.rs b/library/stdarch/crates/core_arch/src/x86/vaes.rs index 864b1d56d1057..72dd387f900d5 100644 --- a/library/stdarch/crates/core_arch/src/x86/vaes.rs +++ b/library/stdarch/crates/core_arch/src/x86/vaes.rs @@ -14,7 +14,7 @@ use crate::core_arch::x86::__m512i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.aesni.aesenc.256"] fn aesenc_256(a: __m256i, round_key: __m256i) -> __m256i; #[link_name = "llvm.x86.aesni.aesenclast.256"] diff --git a/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs b/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs index ad44e59f3ada1..4c39ae6ee7aa3 100644 --- a/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs +++ b/library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs @@ -12,7 +12,7 @@ use crate::core_arch::x86::__m512i; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.pclmulqdq.256"] fn pclmulqdq_256(a: __m256i, round_key: __m256i, imm8: u8) -> __m256i; #[link_name = "llvm.x86.pclmulqdq.512"] diff --git a/library/stdarch/crates/core_arch/src/x86/xsave.rs b/library/stdarch/crates/core_arch/src/x86/xsave.rs index e22d3580ff463..395e2c64be3a0 100644 --- a/library/stdarch/crates/core_arch/src/x86/xsave.rs +++ b/library/stdarch/crates/core_arch/src/x86/xsave.rs @@ -5,7 +5,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xsave"] fn xsave(p: *mut u8, hi: u32, lo: u32); #[link_name = "llvm.x86.xrstor"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 0fd9b09363d4b..e26bfc5c1ca4f 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -527,7 +527,7 @@ pub fn _mm_cvtt_roundss_u64(a: __m128) -> u64 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512.vcvtss2si64"] fn vcvtss2si64(a: f32x4, rounding: i32) -> i64; #[link_name = "llvm.x86.avx512.vcvtss2usi64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs index 2a511328bb382..5baf1c9036e19 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512fp16.rs @@ -211,7 +211,7 @@ pub fn _mm_cvtt_roundsh_u64(a: __m128h) -> u64 { } #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.avx512fp16.vcvtsi642sh"] fn vcvtsi642sh(a: __m128h, b: i64, rounding: i32) -> __m128h; #[link_name = "llvm.x86.avx512fp16.vcvtusi642sh"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/bmi.rs b/library/stdarch/crates/core_arch/src/x86_64/bmi.rs index 8d2b22089ac10..f082953f9df90 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/bmi.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/bmi.rs @@ -122,7 +122,7 @@ pub const fn _mm_tzcnt_64(x: u64) -> i64 { x.trailing_zeros() as i64 } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bextr.64"] fn x86_bmi_bextr_64(x: u64, y: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs b/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs index 6151eee8bdbb5..81ab9e05add96 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/bmi2.rs @@ -69,7 +69,7 @@ pub fn _pext_u64(a: u64, mask: u64) -> u64 { unsafe { x86_bmi2_pext_64(a, mask) } } -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.bmi.bzhi.64"] fn x86_bmi2_bzhi_64(x: u64, y: u64) -> u64; #[link_name = "llvm.x86.bmi.pdep.64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs b/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs index 28bf1951167a5..f50dacb4bee4f 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/fxsr.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.fxsave64"] fn fxsave64(p: *mut u8); #[link_name = "llvm.x86.fxrstor64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse.rs b/library/stdarch/crates/core_arch/src/x86_64/sse.rs index 81e1070b55691..521652c126914 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse.rs @@ -6,7 +6,7 @@ use crate::core_arch::x86::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse.cvtss2si64"] fn cvtss2si64(a: __m128) -> i64; #[link_name = "llvm.x86.sse.cvttss2si64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs index 08dabf053d428..c4768cedbfa6b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs @@ -6,7 +6,7 @@ use crate::core_arch::x86::*; use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse2.cvtsd2si64"] fn cvtsd2si64(a: __m128d) -> i64; #[link_name = "llvm.x86.sse2.cvttsd2si64"] diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs index cd32c149aff5b..307e6167aabae 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse42.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse42.rs @@ -4,7 +4,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.sse42.crc32.64.64"] fn crc32_64_64(crc: u64, v: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/tbm.rs b/library/stdarch/crates/core_arch/src/x86_64/tbm.rs index fe12538b07a06..b4ec50a66fa0b 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/tbm.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/tbm.rs @@ -13,7 +13,7 @@ #[cfg(test)] use stdarch_test::assert_instr; -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.tbm.bextri.u64"] fn bextri_u64(a: u64, control: u64) -> u64; } diff --git a/library/stdarch/crates/core_arch/src/x86_64/xsave.rs b/library/stdarch/crates/core_arch/src/x86_64/xsave.rs index 30a7123315e5f..c307d40073433 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/xsave.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/xsave.rs @@ -6,7 +6,7 @@ use stdarch_test::assert_instr; #[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "unadjusted" { #[link_name = "llvm.x86.xsave64"] fn xsave64(p: *mut u8, hi: u32, lo: u32); #[link_name = "llvm.x86.xrstor64"] diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs deleted file mode 100644 index 87b8ebfa183e7..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub const NOTICE: &str = "\ -// This is a transient test file, not intended for distribution. Some aspects of the -// test are derived from a JSON specification, published under the same license as the -// `intrinsic-test` crate.\n"; - -pub const PLATFORM_RUST_DEFINITIONS: &str = ""; - -pub const PLATFORM_RUST_CFGS: &str = r#" -#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] -#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_dotprod))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] -#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] -#![feature(stdarch_neon_f16)] - -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] -use core_arch::arch::aarch64::*; - -#[cfg(target_arch = "arm")] -use core_arch::arch::arm::*; -"#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index a54e5857192e0..bcbee3503b9ad 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,9 +2,9 @@ use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] -pub struct ArmIntrinsicType(pub IntrinsicType); +pub struct ArmType(pub IntrinsicType); -impl Deref for ArmIntrinsicType { +impl Deref for ArmType { type Target = IntrinsicType; fn deref(&self) -> &Self::Target { @@ -12,7 +12,7 @@ impl Deref for ArmIntrinsicType { } } -impl DerefMut for ArmIntrinsicType { +impl DerefMut for ArmType { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 06cf78a422285..26f861ca64b62 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,4 +1,5 @@ -use super::intrinsic::ArmIntrinsicType; +use super::intrinsic::ArmType; +use crate::arm::Arm; use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; @@ -59,7 +60,7 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec = serde_json::from_reader(reader).expect("Couldn't parse JSON"); @@ -79,10 +80,10 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, -) -> Result, Box> { +) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let result_ty = ArmIntrinsicType(parse_intrinsic_type(&intr.return_type.value)?); + let result_ty = ArmType(parse_intrinsic_type(&intr.return_type.value)?); let args = intr .arguments @@ -120,12 +121,8 @@ fn json_to_intrinsic( } }); - let mut arg = Argument::::new( - i, - String::from(arg_name), - ArmIntrinsicType(arg_ty), - constraint, - ); + let mut arg = + Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); // The JSON doesn't list immediates as const let IntrinsicType { @@ -138,13 +135,14 @@ fn json_to_intrinsic( }) .collect(); - let arguments = ArgumentList:: { args }; + let arguments = ArgumentList:: { args }; Ok(Intrinsic { name, arguments, results: result_ty, arch_tags: intr.architectures, + extension: intr.simd_isa, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 378f23ba7c361..a60e0ff1551a3 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,42 +1,48 @@ -mod config; mod intrinsic; mod json_parser; mod types; -use crate::common::SupportedArchitectureTest; +use crate::common::SupportedArchitecture; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::TypeKind; -use intrinsic::ArmIntrinsicType; +use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; +use intrinsic::ArmType; use json_parser::get_neon_intrinsics; -pub struct ArmArchitectureTest { - intrinsics: Vec>, -} +#[derive(PartialEq)] +pub struct Arm(Vec>); -impl SupportedArchitectureTest for ArmArchitectureTest { - type IntrinsicImpl = ArmIntrinsicType; +impl SupportedArchitecture for Arm { + type Type = ArmType; - fn intrinsics(&self) -> &[Intrinsic] { - &self.intrinsics + fn intrinsics(&self) -> &[Intrinsic] { + &self.0 } - const NOTICE: &str = config::NOTICE; - - const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from a JSON specification, published under the same license as the +// `intrinsic-test` crate. +"#; - const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; - const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; + const C_PRELUDE: &str = r#" +#include +#include +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; - fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name match cli_options.cc_arg_style { CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], + // SVE tests aren't run under GCC so there are no target features added for SVE CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], } } fn create(cli_options: &ProcessedCli) -> Self { + let big_endian = cli_options.target.starts_with("aarch64_be"); let a32 = cli_options.target.starts_with("armv7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); @@ -54,6 +60,53 @@ impl SupportedArchitectureTest for ArmArchitectureTest { // Skip bfloat intrinsics - not currently supported .filter(|i| i.results.kind() != TypeKind::BFloat) .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat)) + // Skip SVE intrinsics that have `f16` - not yet implemented! + .filter(|i| { + let has_f16_arg = i + .arguments + .iter() + .any(|a| a.ty.kind() == TypeKind::Float && a.ty.bit_len == Some(16)); + let has_sve_arg = i + .arguments + .iter() + .any(|a| a.ty.simd_len == Some(SimdLen::Scalable)); + !(has_f16_arg && has_sve_arg) + }) + .filter(|i| { + let has_f16_ret = + i.results.kind() == TypeKind::Float && i.results.bit_len == Some(16); + let has_sve_ret = i.results.simd_len == Some(SimdLen::Scalable); + !(has_f16_ret && has_sve_ret) + }) + // Skip `svqcvtn{u,}n*_x2` intrinsics - not yet implemented! + .filter(|i| !(i.name.starts_with("svqcvtn") && i.name.ends_with("_x2"))) + // Skip `svqrshr{u,}n*_x2` intrinsics - not yet implemented! + .filter(|i| !(i.name.starts_with("svqrshrn") && i.name.ends_with("_x2"))) + .filter(|i| !(i.name.starts_with("svqrshrun") && i.name.ends_with("_x2"))) + // Skip `svclamp*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svclamp")) + // Skip `svdot{_lane,}_{s,u}32_{s,u}16` intrinsics - not yet implemented! + .filter(|i| { + i.name != "svdot_lane_u32_u16" + && i.name != "svdot_lane_s32_s16" + && i.name != "svdot_u32_u16" + && i.name != "svdot_s32_s16" + }) + // Skip `svrevd*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svrevd")) + // Skip `svpsel_lane_b*` intrinsics - not yet implemented! + .filter(|i| !i.name.starts_with("svpsel_lane_b")) + // Skip `svundef*` intrinsics - to avoid undefined behaviour in Rust, these return + // zeroed vectors in Rust, which are inherently going to be different than the + // undefined vectors returned by the C intrinsics. + .filter(|i| !i.name.starts_with("svundef")) + // Skip `sveorv` intrinsics - the code produced by `intrinsic-test` for these + // miscompiles and the Rust intrinsic call gets replaced by a constant zero (see + // llvm/llvm-project#203921). + .filter(|i| !i.name.starts_with("sveorv")) + // These load intrinsics expect each element in the scalable vector `bases` argument to + // be able to be cast to a pointer, which we don't support generating tests for yet. + .filter(|i| !(i.name.starts_with("svld") && i.name.contains("_gather_"))) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) @@ -63,9 +116,36 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .filter(|i| !cli_options.skip.contains(&i.name)) // Skip A64-specific intrinsics on A32 .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()])) + // Skip SVE intrinsics on big endian + .filter(|i| !(big_endian && (i.extension == "SVE" || i.extension == "SVE2"))) + // Skip SVE intrinsics when testing against GCC as our wrappers run into ICEs + // See + .filter(|i| { + !(matches!(cli_options.cc_arg_style, CcArgStyle::Gcc) + && (i.extension == "SVE" || i.extension == "SVE2")) + }) .take(sample_size) .collect::>(); - Self { intrinsics } + Self(intrinsics) } } + +const RUST_PRELUDE: &str = r#" +#![cfg_attr(target_arch = "arm", feature(stdarch_arm_neon_intrinsics))] +#![cfg_attr(target_arch = "arm", feature(stdarch_aarch32_crc32))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fcma))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_i8mm))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_sm4))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] +#![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] +#![feature(stdarch_neon_f16)] + +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] +use core_arch::arch::aarch64::*; + +#[cfg(target_arch = "arm")] +use core_arch::arch::arm::*; +"#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index cd420f10678fc..7754e9ec2d0dd 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,28 +1,37 @@ -use super::intrinsic::ArmIntrinsicType; -use crate::common::intrinsic_helpers::{ - IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, -}; +use super::intrinsic::ArmType; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; -impl IntrinsicTypeDefinition for ArmIntrinsicType { +impl TypeDefinition for ArmType { /// Gets a string containing the typename for this type in C format. fn c_type(&self) -> String { let prefix = self.kind.c_prefix(); - if let Some(bit_len) = self.bit_len { - match (self.simd_len, self.vec_len) { - (None, None) => format!("{prefix}{bit_len}_t"), - (Some(SimdLen::Fixed(simd)), None) => format!("{prefix}{bit_len}x{simd}_t"), - (Some(SimdLen::Fixed(simd)), Some(vec)) => { - format!("{prefix}{bit_len}x{simd}x{vec}_t") - } - (Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), - (Some(SimdLen::Scalable), Some(vec)) => { - format!("sv{prefix}{bit_len}x{vec}_t") - } - (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case + match (self.bit_len, self.simd_len, self.vec_len) { + // e.g. `bool` + (Some(_), None, None) if matches!(self.kind, TypeKind::Bool) => { + format!("{prefix}") } - } else { - todo!("{self:#?}") + // e.g. `float32_t`, `int64_t` + (Some(bit_len), None, None) => format!("{prefix}{bit_len}_t"), + // e.g. `float32x2_t`, `int64x2_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), None) => { + format!("{prefix}{bit_len}x{simd}_t") + } + // e.g. `float32x2x3_t`, `int64x2x3_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{prefix}{bit_len}x{simd}x{vec}_t") + } + // e.g. `svbool_t` + (Some(_), Some(SimdLen::Scalable), None) if matches!(self.kind, TypeKind::Bool) => { + format!("sv{prefix}_t") + } + // e.g. `svfloat32_t`, `svint64_t` + (Some(bit_len), Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), + // e.g. `svfloat32x3_t`, `svint64x3_t` + (Some(bit_len), Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{prefix}{bit_len}x{vec}_t") + } + _ => todo!("{self:#?}"), } } @@ -30,26 +39,39 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { let rust_prefix = self.kind.rust_prefix(); let c_prefix = self.kind.c_prefix(); - if let Some(bit_len) = self.bit_len { - match (self.simd_len, self.vec_len) { - (None, None) => format!("{rust_prefix}{bit_len}"), - (Some(SimdLen::Fixed(simd)), None) => format!("{c_prefix}{bit_len}x{simd}_t"), - (Some(SimdLen::Fixed(simd)), Some(vec)) => { - format!("{c_prefix}{bit_len}x{simd}x{vec}_t") - } - (Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), - (Some(SimdLen::Scalable), Some(vec)) => { - format!("sv{c_prefix}{bit_len}x{vec}_t") - } - (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case + match (self.bit_len, self.simd_len, self.vec_len) { + // e.g. `svpattern` + (None, _, _) => format!("{rust_prefix}"), + // e.g. `bool` + (Some(_), None, None) if matches!(self.kind, TypeKind::Bool) => { + format!("{rust_prefix}") } - } else { - todo!("{self:#?}") + // e.g. `i32` + (Some(bit_len), None, None) => format!("{rust_prefix}{bit_len}"), + // e.g. `int32x2_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), None) => { + format!("{c_prefix}{bit_len}x{simd}_t") + } + // e.g. `int32x2x3_t` + (Some(bit_len), Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{c_prefix}{bit_len}x{simd}x{vec}_t") + } + // e.g. `svbool_t` + (Some(_), Some(SimdLen::Scalable), None) if matches!(self.kind, TypeKind::Bool) => { + format!("sv{c_prefix}_t") + } + // e.g. `svint32_t` + (Some(bit_len), Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), + // e.g. `svint32x3_t` + (Some(bit_len), Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{c_prefix}{bit_len}x{vec}_t") + } + (Some(_), None, Some(_)) => todo!("{self:#?}"), } } /// Determines the load function for this type. - fn get_load_function(&self) -> String { + fn load_function(&self) -> String { if let IntrinsicType { kind: k, bit_len: Some(bl), @@ -73,7 +95,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { len = vec_len.unwrap_or(1), ) } else { - todo!("get_load_function IntrinsicType: {self:#?}") + todo!("load_function IntrinsicType: {self:#?}") } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index eaec5b71c47e3..4d38bce327930 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -1,30 +1,31 @@ use itertools::Itertools; +use crate::common::SupportedArchitecture; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::test_values_array_name; use super::PASSES; use super::constraint::Constraint; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] -pub struct Argument { +pub struct Argument { /// The argument's index in the intrinsic function call. pub pos: usize, /// The argument name. pub name: String, /// The type of the argument. - pub ty: T, + pub ty: A::Type, /// Any constraints that are on this argument pub constraint: Option, } -impl Argument +impl Argument where - T: IntrinsicTypeDefinition, + A: SupportedArchitecture, { - pub fn new(pos: usize, name: String, ty: T, constraint: Option) -> Self { + pub fn new(pos: usize, name: String, ty: A::Type, constraint: Option) -> Self { Argument { pos, name, @@ -63,13 +64,13 @@ where /// Arguments of an intrinsic - including parameters that end up being const generics. #[derive(Debug, PartialEq, Clone)] -pub struct ArgumentList { - pub args: Vec>, +pub struct ArgumentList { + pub args: Vec>, } -impl ArgumentList +impl ArgumentList where - T: IntrinsicTypeDefinition, + A: SupportedArchitecture, { /// Returns a string with the arguments in `self` as a parameter list for a wrapper fn /// definition in C (e.g. `$ty1 $arg1, $ty2 $arg2`). @@ -179,14 +180,14 @@ where .map(|(idx, arg)| { if arg.is_simd() { format!( - "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), - load = arg.ty.get_load_function(), + load = arg.ty.load_function(), ) } else { format!( - "let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", + "let {name} = {vals_name}[(i+{idx}) % {PASSES}];", name = arg.generate_name(), vals_name = test_values_array_name(&arg.ty), ) @@ -196,7 +197,7 @@ where } /// Returns an iterator over the contained arguments - pub fn iter(&self) -> std::slice::Iter<'_, Argument> { + pub fn iter(&self) -> std::slice::Iter<'_, Argument> { self.args.iter() } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 24756324c48e4..21cc5cfa2e597 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -1,8 +1,8 @@ use itertools::Itertools; -use crate::common::intrinsic::Intrinsic; +use crate::common::{SupportedArchitecture, intrinsic::Intrinsic}; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use super::intrinsic_helpers::TypeDefinition; /// Generates a C source file containing wrapper functions around each specialisation of each /// intrinsic (that is, intrinsics with specific values for the the immediate arguments). Each @@ -14,20 +14,15 @@ use super::intrinsic_helpers::IntrinsicTypeDefinition; /// *__dst = __crc32cd(a, b); /// } /// ``` -pub fn write_wrapper_c( +pub fn write_wrapper_c( w: &mut impl std::io::Write, - notice: &str, - platform_headers: &[&str], - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{notice}")?; + write!(w, "{}", A::NOTICE)?; writeln!(w, "#include ")?; writeln!(w, "#include ")?; - - for header in platform_headers { - writeln!(w, "#include <{header}>")?; - } + writeln!(w, "{}", A::C_PRELUDE)?; for intrinsic in intrinsics { intrinsic.iter_specializations(|imm_values| { diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 02f6e40dc06a6..baa11511ec219 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -2,12 +2,11 @@ use std::process::Command; use itertools::Itertools; -use super::intrinsic_helpers::IntrinsicTypeDefinition; -use crate::common::PASSES; +use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; +use crate::common::{PASSES, SupportedArchitecture}; /// Rust definitions that are included verbatim in the generated source. In particular, defines /// a wrapper around float types that defines `NaN`s to be equal reflexively to enable @@ -32,12 +31,6 @@ macro_rules! wrap_partialeq { wrap_partialeq!(NanEqF16(f16), NanEqF32(f32), NanEqF64(f64)); "#; -macro_rules! concatln { - ($($lines:expr),* $(,)?) => { - concat!($( $lines, "\n" ),*) - }; -} - /// Run rustfmt on the generated source code pub fn run_rustfmt(source_path: &str) { let output = Command::new("rustfmt") @@ -65,11 +58,14 @@ pub fn write_bin_cargo_toml( w: &mut impl std::io::Write, module_count: usize, ) -> std::io::Result<()> { - write!(w, concatln!("[workspace]", "members = ["))?; - for i in 0..module_count { - writeln!(w, " \"mod_{i}\",")?; - } - writeln!(w, "]") + write!( + w, + r#" +[workspace] +members = [{members}] +"#, + members = (0..module_count).format_with(",", |i, fmt| fmt(&format_args!("\"mod_{i}\""))) + ) } /// Writes a `Cargo.toml` for a crate with name `name` to `w` that will contain a single Rust source @@ -77,21 +73,20 @@ pub fn write_bin_cargo_toml( pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> { write!( w, - concatln!( - "[package]", - "name = \"{name}\"", - "version = \"{version}\"", - "authors = [{authors}]", - "license = \"{license}\"", - "edition = \"2018\"", - "", - "[dependencies]", - "core_arch = {{ path = \"../../crates/core_arch\" }}", - "", - "[build-dependencies]", - "cc = \"1\"" - ), - name = name, + r#" +[package] +name = "{name}" +version = "{version}" +authors = [{authors}] +license = "{license}" +edition = "2018" + +[dependencies] +core_arch = {{ path = "../../crates/core_arch" }} + +[build-dependencies] +cc = "1" +"#, version = env!("CARGO_PKG_VERSION"), authors = env!("CARGO_PKG_AUTHORS") .split(":") @@ -102,30 +97,30 @@ pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io: /// Writes a Rust source file into `w` with common definitions, static arrays with test values, /// declarations of C wrapper functions for FFI and Rust test functions. -pub fn write_lib_rs( +pub fn write_lib_rs( w: &mut impl std::io::Write, - notice: &str, - cfg: &str, - definitions: &str, i: usize, - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{notice}")?; - - writeln!(w, "#![feature(simd_ffi)]")?; - writeln!(w, "#![feature(f16)]")?; - writeln!(w, "#![allow(unused)]")?; - - // Cargo will spam the logs if these warnings are not silenced. - writeln!(w, "#![allow(non_upper_case_globals)]")?; - writeln!(w, "#![allow(non_camel_case_types)]")?; - writeln!(w, "#![allow(non_snake_case)]")?; - - writeln!(w, "{cfg}")?; - - writeln!(w, "{}", COMMON_RUST_DEFINITIONS)?; - - writeln!(w, "{definitions}")?; + writeln!( + w, + r#" +{notice} +#![feature(simd_ffi)] +#![feature(f16)] +#![allow(unused)] + +// Cargo will spam the logs if these warnings are not silenced. +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +{prelude} +{COMMON_RUST_DEFINITIONS} +"#, + notice = A::NOTICE, + prelude = A::RUST_PRELUDE, + )?; let mut seen = std::collections::HashSet::new(); @@ -157,9 +152,9 @@ pub fn write_lib_rs( /// (first loop) `PASSES` number of times (second loop). For a given iteration of a given /// specialisation, test values are loaded for each argument and passed to the Rust intrinsic /// and the C wrapper function, and the results are compared. -fn generate_rust_test_loop( +fn generate_rust_test_loop( w: &mut impl std::io::Write, - intrinsic: &Intrinsic, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { let intrinsic_name = &intrinsic.name; @@ -199,67 +194,45 @@ fn generate_rust_test_loop( writeln!(w, " ];")?; } - let (cast_prefix, cast_suffix) = if intrinsic.results.is_simd() { - ( - format!( - "std::mem::transmute::<_, [{}; {}]>(", - intrinsic.results.rust_scalar_type().replace("f", "NanEqF"), - intrinsic.results.num_lanes() * intrinsic.results.num_vectors() - ), - ")", - ) - } else if intrinsic.results.kind == TypeKind::Float { - ( - match intrinsic.results.inner_size() { - 16 => format!("NanEqF16("), - 32 => format!("NanEqF32("), - 64 => format!("NanEqF64("), - _ => unimplemented!(), - }, - ")", - ) - } else { - ("".to_string(), "") - }; - write!( w, - concatln!( - " for (id, rust, c) in specializations {{", - " for i in 0..{passes} {{", - " unsafe {{", - "{loaded_args}", - " let __rust_return_value = rust({rust_args});", - "", - " let mut __c_return_value = std::mem::MaybeUninit::uninit();", - " c(__c_return_value.as_mut_ptr(){c_args});", - " let __c_return_value = __c_return_value.assume_init();", - "", - " assert_eq!({cast_prefix}__rust_return_value{cast_suffix}, {cast_prefix}__c_return_value{cast_suffix}, \"{{id}}\");", - " }}", - " }}", - " }}", - ), + r#" +for (id, rust, c) in specializations {{ + for i in 0..{PASSES} {{ + unsafe {{ + {loaded_args} + let __rust_return_value = rust({rust_args}); + + let mut __c_return_value = std::mem::MaybeUninit::uninit(); + c(__c_return_value.as_mut_ptr(){c_args}); + let __c_return_value = __c_return_value.assume_init(); + + {comparison} + }} + }} +}} +"#, loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), - passes = PASSES, - cast_prefix = cast_prefix, - cast_suffix = cast_suffix, + comparison = intrinsic.results.comparison_function(), ) } /// Writes a test function for an given intrinsic to `w`, with a body generated by /// `generate_rust_test_loop`. -fn create_rust_test( +fn create_rust_test( w: &mut impl std::io::Write, - intrinsic: &Intrinsic, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { trace!("generating `{}`", intrinsic.name); write!( w, - concatln!("#[test]", "fn test_{intrinsic_name}() {{"), + r#" +#[test] +fn test_{intrinsic_name}() {{ +"#, intrinsic_name = intrinsic.name, )?; @@ -272,26 +245,25 @@ fn create_rust_test( /// Writes an `extern "C"` block with function declarations for each of the C wrapper functions into /// `w`. -pub fn write_bindings_rust( +pub fn write_bindings_rust( w: &mut impl std::io::Write, i: usize, - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!( w, - concatln!( - "#[allow(improper_ctypes)]", - "#[link(name = \"wrapper_{i}\")]", - "unsafe extern \"C\" {{" - ), - i = i + r#" +#[allow(improper_ctypes)] +#[link(name = "wrapper_{i}")] +unsafe extern "C" {{ +"#, )?; for intrinsic in intrinsics { intrinsic.iter_specializations(|imm_values| { writeln!( w, - " fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", + "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", return_ty = intrinsic.results.rust_type(), name = intrinsic.name, imm_arglist = imm_values @@ -326,32 +298,22 @@ pub fn write_build_rs( write!( w, - concatln!( - "fn main() {{", - " cc::Build::new()", - " .file(\"../../c_programs/wrapper_{i}.c\")", - " .opt_level(2)", - " .flags(&[", - ), - i = i - )?; - - let compiler_specific_flags = match cli_options.cc_arg_style { - CcArgStyle::Gcc => GCC_FLAGS, - CcArgStyle::Clang => CLANG_FLAGS, - }; - - for flag in COMMON_FLAGS - .iter() - .chain(compiler_specific_flags) - .chain(arch_flags) - { - writeln!(w, "\"{flag}\",")?; - } - - write!( - w, - concatln!(" ])", " .compile(\"wrapper_{i}\");", "}}"), - i = i + r#" +fn main() {{ + cc::Build::new() + .file("../../c_programs/wrapper_{i}.c") + .opt_level(2) + .flags(&[{flags}]) + .compile("wrapper_{i}"); +}} +"#, + flags = COMMON_FLAGS + .iter() + .chain(match cli_options.cc_arg_style { + CcArgStyle::Gcc => GCC_FLAGS, + CcArgStyle::Clang => CLANG_FLAGS, + }) + .chain(arch_flags) + .format_with(",", |flag, fmt| fmt(&format_args!("\"{flag}\""))), ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index d69644388a830..0c5bf43069d00 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,22 +1,23 @@ -use crate::common::constraint::Constraint; - use super::argument::ArgumentList; -use super::intrinsic_helpers::IntrinsicTypeDefinition; +use crate::common::{SupportedArchitecture, constraint::Constraint}; /// An intrinsic #[derive(Debug, PartialEq, Clone)] -pub struct Intrinsic { +pub struct Intrinsic { /// The function name of this intrinsic. pub name: String, /// Any arguments for this intrinsic. - pub arguments: ArgumentList, + pub arguments: ArgumentList, /// The return type of this intrinsic. - pub results: T, + pub results: A::Type, /// Any architecture-specific tags. pub arch_tags: Vec, + + /// Specific extension that the intrinsic is from + pub extension: String, } /// Invokes `f` for each combination of the values in the constraint ranges. @@ -40,7 +41,7 @@ fn recurse_specializations<'a, E>( } } -impl Intrinsic { +impl Intrinsic { /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds /// to the value of the `i`th const generic argument of the intrinsic. diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index ca5aeba86dab9..b9f30af7dfd51 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -1,6 +1,6 @@ use std::cmp; use std::fmt; -use std::ops::Deref; +use std::ops::DerefMut; use std::str::FromStr; #[derive(Debug, PartialEq, Copy, Clone)] @@ -90,9 +90,13 @@ impl TypeKind { } } - /// Returns the Rust prefix for this type kind i.e. `i`, `u`, or `f`. + /// Returns the Rust prefix for this type kind (i.e. `i` for `i16`, or `u` for `u16`). For type + /// kinds without any bit length at the end (e.g. `bool`), returns the whole type name. pub fn rust_prefix(&self) -> &str { match self { + Self::Bool => "bool", + Self::SvPattern => "svpattern", + Self::SvPrefetchOp => "svprfop", Self::BFloat => "bf", Self::Float => "f", Self::Int(Sign::Signed) => "i", @@ -101,7 +105,7 @@ impl TypeKind { Self::Char(Sign::Unsigned) => "u", Self::Char(Sign::Signed) => "i", Self::Mask => "u", - _ => unreachable!("Unused type kind: {self:#?}"), + _ => unreachable!("type kind without Rust prefix: {self:#?}"), } } } @@ -195,9 +199,19 @@ impl IntrinsicType { } } -pub trait IntrinsicTypeDefinition: Deref { +pub trait TypeDefinition: Clone + DerefMut { /// Determines the load function for this type. - fn get_load_function(&self) -> String; + fn load_function(&self) -> String; + + /// Determines the comparison function for this type. + fn comparison_function(&self) -> String { + match self.simd_len { + Some(SimdLen::Scalable) => unimplemented!("architecture-specific"), + Some(SimdLen::Fixed(_)) | None => { + default_fixed_vector_comparison(self, self.num_lanes()) + } + } + } /// Gets a string containing the typename for this type in C. fn c_type(&self) -> String; @@ -208,14 +222,49 @@ pub trait IntrinsicTypeDefinition: Deref { /// Gets a string containing the name of the scalar type corresponding to this type if it is a /// vector. fn rust_scalar_type(&self) -> String { - if self.is_simd() { - format!( - "{prefix}{bits}", - prefix = self.kind().rust_prefix(), - bits = self.inner_size() - ) - } else { - self.rust_type() - } + let mut ty = self.clone(); + ty.simd_len = None; + ty.vec_len = None; + ty.rust_type() } } + +/// Returns the default comparison between results of an intrinsic - casting the vectors to arrays +/// and using `assert_eq` - using `NanEqF*` where required for floats. +pub(crate) fn default_fixed_vector_comparison( + ty: &Ty, + num_lanes: u32, +) -> String { + let (cast_prefix, cast_suffix) = if ty.is_simd() { + ( + format!( + "std::mem::transmute::<_, [{}; {}]>(", + ty.rust_scalar_type().replace("f", "NanEqF"), + num_lanes * ty.num_vectors() + ), + ")", + ) + } else if ty.kind == TypeKind::Float { + ( + match ty.inner_size() { + 16 => format!("NanEqF16("), + 32 => format!("NanEqF32("), + 64 => format!("NanEqF64("), + _ => unimplemented!(), + }, + ")", + ) + } else { + ("".to_string(), "") + }; + + format!( + r#" +assert_eq!( + {cast_prefix}__rust_return_value{cast_suffix}, + {cast_prefix}__c_return_value{cast_suffix}, + "{{id}}" +); +"#, + ) +} diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b577491454e44..73daabbd6661a 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -10,7 +10,7 @@ use crate::common::{ run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, }, intrinsic::Intrinsic, - intrinsic_helpers::IntrinsicTypeDefinition, + intrinsic_helpers::TypeDefinition, }; pub mod argument; @@ -29,21 +29,19 @@ pub(crate) const PASSES: u32 = 20; /// Architectures must support this trait /// to be successfully tested. -pub trait SupportedArchitectureTest { - type IntrinsicImpl: IntrinsicTypeDefinition + Sync; +pub trait SupportedArchitecture: Sized { + type Type: TypeDefinition + std::fmt::Debug + PartialEq + Sync; - fn intrinsics(&self) -> &[Intrinsic]; + fn intrinsics(&self) -> &[Intrinsic]; fn create(cli_options: &ProcessedCli) -> Self; const NOTICE: &str; - const PLATFORM_C_HEADERS: &[&str]; + const C_PRELUDE: &str; + const RUST_PRELUDE: &str; - const PLATFORM_RUST_CFGS: &str; - const PLATFORM_RUST_DEFINITIONS: &str; - - fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; + fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { let (max_chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); @@ -55,14 +53,14 @@ pub trait SupportedArchitectureTest { .map(|(i, chunk)| { let c_filename = format!("c_programs/wrapper_{i}.c"); let mut file = File::create(&c_filename).unwrap(); - write_wrapper_c(&mut file, Self::NOTICE, Self::PLATFORM_C_HEADERS, chunk) + write_wrapper_c(&mut file, chunk) }) .collect::>() .unwrap(); } fn generate_rust_file(&self, cli_options: &ProcessedCli) { - let arch_flags = self.arch_flags(cli_options); + let arch_flags = self.c_compiler_flags(cli_options); std::fs::create_dir_all("rust_programs").unwrap(); @@ -81,14 +79,7 @@ pub trait SupportedArchitectureTest { trace!("generating `{rust_filename}`"); let mut file = File::create(&rust_filename)?; - write_lib_rs( - &mut file, - Self::NOTICE, - Self::PLATFORM_RUST_CFGS, - Self::PLATFORM_RUST_DEFINITIONS, - i, - chunk, - )?; + write_lib_rs(&mut file, i, chunk)?; run_rustfmt(&rust_filename); let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 4c3dd078e034f..8c549346ce6ed 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -2,7 +2,7 @@ use itertools::Itertools as _; use crate::common::{ PASSES, - intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind}, + intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}, }; /// Maximum size of a SVE vector @@ -18,13 +18,13 @@ pub const MAX_SVE_BITS: u32 = 2048; /// 0x80, 0x3b, 0xff, /// ]; /// ``` -pub fn test_values_array_static( +pub fn test_values_array_static( w: &mut impl std::io::Write, ty: &T, ) -> std::io::Result<()> { writeln!( w, - "static {name}: [{ty}; {load_size}] = {values};\n", + "static {name}: [{ty}; {load_size}] = {values};", name = test_values_array_name(ty), ty = ty.rust_scalar_type(), load_size = test_values_array_length(&ty), @@ -34,7 +34,7 @@ pub fn test_values_array_static( /// Returns a string with the name of the static variable containing test values for intrinsic /// arguments of this type. -pub fn test_values_array_name(ty: &T) -> String { +pub fn test_values_array_name(ty: &T) -> String { format!( "{ty}_{load_size}", ty = ty.rust_scalar_type().to_uppercase(), diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 4c0136041fc35..e25eb48a456d1 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -5,10 +5,10 @@ mod arm; mod common; mod x86; -use arm::ArmArchitectureTest; -use common::SupportedArchitectureTest; +use arm::Arm; +use common::SupportedArchitecture; use common::cli::{Cli, ProcessedCli}; -use x86::X86ArchitectureTest; +use x86::X86; fn main() { pretty_env_logger::init(); @@ -18,21 +18,15 @@ fn main() { if processed_cli_options.target.starts_with("arm") | processed_cli_options.target.starts_with("aarch64") { - run( - ArmArchitectureTest::create(&processed_cli_options), - processed_cli_options, - ) + run(Arm::create(&processed_cli_options), processed_cli_options) } else if processed_cli_options.target.starts_with("x86") { - run( - X86ArchitectureTest::create(&processed_cli_options), - processed_cli_options, - ) + run(X86::create(&processed_cli_options), processed_cli_options) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) } } -fn run(test_environment: impl SupportedArchitectureTest, processed_cli_options: ProcessedCli) { +fn run(test_environment: impl SupportedArchitecture, processed_cli_options: ProcessedCli) { info!("building C binaries"); test_environment.generate_c_file(); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/config.rs b/library/stdarch/crates/intrinsic-test/src/x86/config.rs deleted file mode 100644 index 68737ab5ac4c8..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/x86/config.rs +++ /dev/null @@ -1,138 +0,0 @@ -pub const NOTICE: &str = "\ -// This is a transient test file, not intended for distribution. Some aspects of the -// test are derived from an XML specification, published under the same license as the -// `intrinsic-test` crate.\n"; - -pub const PLATFORM_RUST_DEFINITIONS: &str = r#" -use core_arch::arch::x86_64::*; - -#[inline] -unsafe fn _mm_loadu_ph_to___m128i(mem_addr: *const f16) -> __m128i { - _mm_castph_si128(_mm_loadu_ph(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_ph_to___m256i(mem_addr: *const f16) -> __m256i { - _mm256_castph_si256(_mm256_loadu_ph(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_ph_to___mm512i(mem_addr: *const f16) -> __m512i { - _mm512_castph_si512(_mm512_loadu_ph(mem_addr)) -} - - -#[inline] -unsafe fn _mm_loadu_ps_to___m128h(mem_addr: *const f32) -> __m128h { - _mm_castps_ph(_mm_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_ps_to___m256h(mem_addr: *const f32) -> __m256h { - _mm256_castps_ph(_mm256_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_ps_to___m512h(mem_addr: *const f32) -> __m512h { - _mm512_castps_ph(_mm512_loadu_ps(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi16_to___m128d(mem_addr: *const i16) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi16_to___m256d(mem_addr: *const i16) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi16_to___m512d(mem_addr: *const i16) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi32_to___m128d(mem_addr: *const i32) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi32_to___m256d(mem_addr: *const i32) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi32_to___m512d(mem_addr: *const i32) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi64_to___m128d(mem_addr: *const i64) -> __m128d { - _mm_castsi128_pd(_mm_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi64_to___m256d(mem_addr: *const i64) -> __m256d { - _mm256_castsi256_pd(_mm256_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi64_to___m512d(mem_addr: *const i64) -> __m512d { - _mm512_castsi512_pd(_mm512_loadu_epi64(mem_addr)) -} - -// === -#[inline] -unsafe fn _mm_loadu_epi16_to___m128(mem_addr: *const i16) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi16_to___m256(mem_addr: *const i16) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi16_to___m512(mem_addr: *const i16) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi16(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi32_to___m128(mem_addr: *const i32) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi32_to___m256(mem_addr: *const i32) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi32_to___m512(mem_addr: *const i32) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi32(mem_addr)) -} - -#[inline] -unsafe fn _mm_loadu_epi64_to___m128(mem_addr: *const i64) -> __m128 { - _mm_castsi128_ps(_mm_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm256_loadu_epi64_to___m256(mem_addr: *const i64) -> __m256 { - _mm256_castsi256_ps(_mm256_loadu_epi64(mem_addr)) -} - -#[inline] -unsafe fn _mm512_loadu_epi64_to___m512(mem_addr: *const i64) -> __m512 { - _mm512_castsi512_ps(_mm512_loadu_epi64(mem_addr)) -} - -"#; - -pub const PLATFORM_RUST_CFGS: &str = r#" -#![feature(stdarch_x86_avx512_bf16)] -#![feature(stdarch_x86_avx512_f16)] -#![feature(stdarch_x86_rtm)] -#![feature(x86_amx_intrinsics)] -"#; diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index 288bd8bdf8961..ce4955032916d 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -1,35 +1,38 @@ -mod config; mod constraint; mod intrinsic; mod types; mod xml_parser; -use crate::common::SupportedArchitectureTest; +use crate::common::SupportedArchitecture; use crate::common::cli::ProcessedCli; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use intrinsic::X86IntrinsicType; use xml_parser::get_xml_intrinsics; -pub struct X86ArchitectureTest { - intrinsics: Vec>, +pub struct X86 { + intrinsics: Vec>, } -impl SupportedArchitectureTest for X86ArchitectureTest { - type IntrinsicImpl = X86IntrinsicType; +impl SupportedArchitecture for X86 { + type Type = X86IntrinsicType; - fn intrinsics(&self) -> &[Intrinsic] { + fn intrinsics(&self) -> &[Intrinsic] { &self.intrinsics } - const NOTICE: &str = config::NOTICE; + const NOTICE: &str = r#" +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from an XML specification, published under the same license as the +// `intrinsic-test` crate. +"#; - const PLATFORM_C_HEADERS: &[&str] = &["immintrin.h"]; + const C_PRELUDE: &str = r#" +#include +"#; + const RUST_PRELUDE: &str = RUST_PRELUDE; - const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; - const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - - fn arch_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { + fn c_compiler_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ "-maes", "-mf16c", @@ -97,3 +100,134 @@ impl SupportedArchitectureTest for X86ArchitectureTest { Self { intrinsics } } } + +const RUST_PRELUDE: &str = r#" +#![feature(stdarch_x86_avx512_bf16)] +#![feature(stdarch_x86_avx512_f16)] +#![feature(stdarch_x86_rtm)] +#![feature(x86_amx_intrinsics)] + +use core_arch::arch::x86_64::*; + +#[inline] +unsafe fn _mm_loadu_ph_to___m128i(mem_addr: *const f16) -> __m128i { + _mm_castph_si128(_mm_loadu_ph(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_ph_to___m256i(mem_addr: *const f16) -> __m256i { + _mm256_castph_si256(_mm256_loadu_ph(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_ph_to___mm512i(mem_addr: *const f16) -> __m512i { + _mm512_castph_si512(_mm512_loadu_ph(mem_addr)) +} + + +#[inline] +unsafe fn _mm_loadu_ps_to___m128h(mem_addr: *const f32) -> __m128h { + _mm_castps_ph(_mm_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_ps_to___m256h(mem_addr: *const f32) -> __m256h { + _mm256_castps_ph(_mm256_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_ps_to___m512h(mem_addr: *const f32) -> __m512h { + _mm512_castps_ph(_mm512_loadu_ps(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi16_to___m128d(mem_addr: *const i16) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi16_to___m256d(mem_addr: *const i16) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi16_to___m512d(mem_addr: *const i16) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi32_to___m128d(mem_addr: *const i32) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi32_to___m256d(mem_addr: *const i32) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi32_to___m512d(mem_addr: *const i32) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi64_to___m128d(mem_addr: *const i64) -> __m128d { + _mm_castsi128_pd(_mm_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi64_to___m256d(mem_addr: *const i64) -> __m256d { + _mm256_castsi256_pd(_mm256_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi64_to___m512d(mem_addr: *const i64) -> __m512d { + _mm512_castsi512_pd(_mm512_loadu_epi64(mem_addr)) +} + +// === +#[inline] +unsafe fn _mm_loadu_epi16_to___m128(mem_addr: *const i16) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi16_to___m256(mem_addr: *const i16) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi16_to___m512(mem_addr: *const i16) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi16(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi32_to___m128(mem_addr: *const i32) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi32_to___m256(mem_addr: *const i32) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi32_to___m512(mem_addr: *const i32) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi32(mem_addr)) +} + +#[inline] +unsafe fn _mm_loadu_epi64_to___m128(mem_addr: *const i64) -> __m128 { + _mm_castsi128_ps(_mm_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm256_loadu_epi64_to___m256(mem_addr: *const i64) -> __m256 { + _mm256_castsi256_ps(_mm256_loadu_epi64(mem_addr)) +} + +#[inline] +unsafe fn _mm512_loadu_epi64_to___m512(mem_addr: *const i64) -> __m512 { + _mm512_castsi512_ps(_mm512_loadu_epi64(mem_addr)) +} +"#; diff --git a/library/stdarch/crates/intrinsic-test/src/x86/types.rs b/library/stdarch/crates/intrinsic-test/src/x86/types.rs index a0e14c77d6b5e..2cba54a73f767 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/types.rs @@ -3,12 +3,10 @@ use std::str::FromStr; use itertools::Itertools; use super::intrinsic::X86IntrinsicType; -use crate::common::intrinsic_helpers::{ - IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, -}; +use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; use crate::x86::xml_parser::Parameter; -impl IntrinsicTypeDefinition for X86IntrinsicType { +impl TypeDefinition for X86IntrinsicType { /// Gets a string containing the type in C format. /// This function assumes that this value is present in the metadata hashmap. fn c_type(&self) -> String { @@ -50,7 +48,7 @@ impl IntrinsicTypeDefinition for X86IntrinsicType { } /// Determines the load function for this type. - fn get_load_function(&self) -> String { + fn load_function(&self) -> String { let type_value = self.param.type_data.clone(); if type_value.len() == 0 { unimplemented!("the value for key 'type' is not present!"); diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index 6006d7919f875..52f6da786e3d7 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -1,6 +1,7 @@ use crate::common::argument::{Argument, ArgumentList}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; +use crate::x86::X86; use crate::x86::constraint::map_constraints; use regex::Regex; @@ -56,13 +57,13 @@ pub struct Parameter { pub fn get_xml_intrinsics( filename: &Path, -) -> Result>, Box> { +) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let data: Data = quick_xml::de::from_reader(reader).expect("failed to deserialize the source XML file"); - let parsed_intrinsics: Vec> = data + let parsed_intrinsics: Vec> = data .intrinsics .into_iter() .filter(|intrinsic| { @@ -84,9 +85,7 @@ pub fn get_xml_intrinsics( Ok(parsed_intrinsics) } -fn xml_to_intrinsic( - intr: XMLIntrinsic, -) -> Result, Box> { +fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box> { let name = intr.name; let result = X86IntrinsicType::from_param(&intr.return_data); let args_check = intr.parameters.into_iter().enumerate().map(|(i, param)| { @@ -100,12 +99,7 @@ fn xml_to_intrinsic( param.imm_width }; let constraint = map_constraints(&name, ¶m.imm_type, effective_imm_width); - let arg = Argument::::new( - i, - param.var_name.clone(), - ty.unwrap(), - constraint, - ); + let arg = Argument::::new(i, param.var_name.clone(), ty.unwrap(), constraint); Some(arg) } }); @@ -125,8 +119,8 @@ fn xml_to_intrinsic( // if one of the args has etype="MASK" and type="__md", // then set the bit_len and simd_len accordingly let re = Regex::new(r"__m\d+").unwrap(); - let is_mask = |arg: &Argument| arg.ty.param.etype.as_str() == "MASK"; - let is_vector = |arg: &Argument| re.is_match(arg.ty.param.type_data.as_str()); + let is_mask = |arg: &Argument| arg.ty.param.etype.as_str() == "MASK"; + let is_vector = |arg: &Argument| re.is_match(arg.ty.param.type_data.as_str()); let pos = args_test.position(|arg| is_mask(arg) && is_vector(arg)); if let Some(index) = pos { args[index].ty.bit_len = args[0].ty.bit_len; @@ -134,7 +128,7 @@ fn xml_to_intrinsic( args.iter_mut().for_each(|arg| arg.ty.update_simd_len()); - let arguments = ArgumentList:: { args }; + let arguments = ArgumentList:: { args }; if let Err(message) = result { return Err(Box::from(message)); @@ -145,5 +139,6 @@ fn xml_to_intrinsic( arguments, results: result.unwrap(), arch_tags: intr.cpuid, + extension: intr.tech, }) } diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index 03fea5e0a4af3..e5ce77ed8b33f 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -13951,7 +13951,7 @@ intrinsics: - 'b: {neon_type[1]}' - 'n: i32' links: - - link: "llvm.aarch64.neon.vluti4q.lane.{neon_type[1]}" + - link: "llvm.aarch64.neon.vluti4q.lane.{neon_type[0]}" arch: aarch64,arm64ec - FnCall: ['_vluti4{neon_type[0].lane_nox}', [a, b, LANE]] @@ -14002,7 +14002,7 @@ intrinsics: - 'b: {neon_type[1]}' - 'n: i32' links: - - link: "llvm.aarch64.neon.vluti4q.laneq.{neon_type[1]}" + - link: "llvm.aarch64.neon.vluti4q.laneq.{neon_type[0]}" arch: aarch64,arm64ec - FnCall: ['_vluti4{neon_type[0].laneq_nox}', [a, b, LANE]] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 29dd3a095d7e6..138d5ba31175b 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -115,7 +115,7 @@ intrinsics: assert_instr: [[fcmla, "IMM_INDEX = 0, IMM_ROTATION = 90"]] compose: - LLVMLink: - name: fcmla.lane.x.{sve_type} + name: fcmla.lane.{sve_type} arguments: - "op1: {sve_type}" - "op2: {sve_type}" @@ -1021,7 +1021,7 @@ intrinsics: doc: Interleave elements from low halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [zip1] compose: - LLVMLink: { name: "zip1.{sve_type}" } @@ -1031,10 +1031,13 @@ intrinsics: doc: Interleave elements from low halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [zip1] compose: - - LLVMLink: { name: "zip1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.zip1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svzip1q[_{type}] attr: [*sve-unstable] @@ -1052,7 +1055,7 @@ intrinsics: doc: Interleave elements from high halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [zip2] compose: - LLVMLink: { name: "zip2.{sve_type}" } @@ -1062,10 +1065,13 @@ intrinsics: doc: Interleave elements from high halves of two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [zip2] compose: - - LLVMLink: { name: "zip2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.zip2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svzip2q[_{type}] attr: [*sve-unstable] @@ -1083,7 +1089,7 @@ intrinsics: doc: Concatenate even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [uzp1] compose: - LLVMLink: { name: "uzp1.{sve_type}" } @@ -1093,10 +1099,13 @@ intrinsics: doc: Concatenate even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [uzp1] compose: - - LLVMLink: { name: "uzp1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.uzp1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svuzp1q[_{type}] attr: [*sve-unstable] @@ -1114,7 +1123,7 @@ intrinsics: doc: Concatenate odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [uzp2] compose: - LLVMLink: { name: "uzp2.{sve_type}" } @@ -1124,10 +1133,13 @@ intrinsics: doc: Concatenate odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [uzp2] compose: - - LLVMLink: { name: "uzp2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.uzp2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svuzp2q[_{type}] attr: [*sve-unstable] @@ -1207,7 +1219,7 @@ intrinsics: doc: Reverse all elements arguments: ["op: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [rev] compose: - LLVMLink: { name: "llvm.vector.reverse.{sve_type}" } @@ -1217,10 +1229,13 @@ intrinsics: doc: Reverse all elements arguments: ["op: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [rev] compose: - - LLVMLink: { name: "llvm.vector.reverse.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.rev.b{size}" + arguments: ["op: svbool_t"] + return_type: "svbool_t" - name: svrevb[_{type}]{_mxz} attr: [*sve-unstable] @@ -4179,7 +4194,8 @@ intrinsics: ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] return_type: "{sve_type[0]}" types: - - [[f32, f64], [i32, u32, i64, u64]] + - [f32, [i64, u64]] + - [f64, [i32, u32]] zeroing_method: { drop: inactive } substitutions: convert_from: { match_kind: "{type[1]}", default: s, unsigned: u } @@ -4187,6 +4203,23 @@ intrinsics: compose: - LLVMLink: name: "{convert_from}cvtf.{type[0]}{type[1]}" + + - name: svcvt_{type[0]}[_{type[1]}]{_mxz} + attr: [*sve-unstable] + doc: Floating-point convert + arguments: + ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] + return_type: "{sve_type[0]}" + types: + - [f32, [i32, u32]] + - [f64, [i64, u64]] + zeroing_method: { drop: inactive } + substitutions: + convert_from: { match_kind: "{type[1]}", default: s, unsigned: u } + assert_instr: ["{convert_from}cvtf"] + compose: + - LLVMLink: + name: "{convert_from}cvtf.{sve_type[0]}.{sve_type[1]}" - name: svcvt_{type[0]}[_{type[1]}]{_mxz} attr: [*sve-unstable] @@ -4195,13 +4228,30 @@ intrinsics: ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] return_type: "{sve_type[0]}" types: - - [[i32, u32, i64, u64], [f32, f64]] + - [[i32, u32], f64] + - [[i64, u64], f32] zeroing_method: { drop: inactive } substitutions: convert_to: { match_kind: "{type[0]}", default: s, unsigned: u } assert_instr: ["fcvtz{convert_to}"] compose: - LLVMLink: { name: "fcvtz{convert_to}.{type[0]}{type[1]}" } + + - name: svcvt_{type[0]}[_{type[1]}]{_mxz} + attr: [*sve-unstable] + doc: Floating-point convert + arguments: + ["inactive: {sve_type[0]}", "pg: {max_predicate}", "op: {sve_type[1]}"] + return_type: "{sve_type[0]}" + types: + - [[i32, u32], f32] + - [[i64, u64], f64] + zeroing_method: { drop: inactive } + substitutions: + convert_to: { match_kind: "{type[0]}", default: s, unsigned: u } + assert_instr: ["fcvtz{convert_to}"] + compose: + - LLVMLink: { name: "fcvtz{convert_to}.{sve_type[0]}.{sve_type[1]}" } - name: svcvt_{type[0]}[_{type[1]}]{_mxz} attr: [*sve-unstable] @@ -4356,7 +4406,7 @@ intrinsics: return_type: svbool_t assert_instr: [and] compose: - - LLVMLink: { name: "and.z.nvx16i1" } + - LLVMLink: { name: "and.z.nxv16i1" } - name: svmov[_b]_z attr: [*sve-unstable] @@ -4386,7 +4436,7 @@ intrinsics: return_type: svbool_t assert_instr: [bic] compose: - - LLVMLink: { name: "bic.z.nvx16i1" } + - LLVMLink: { name: "bic.z.nxv16i1" } - name: sveor[{_n}_{type}]{_mxz} attr: [*sve-unstable] @@ -4417,7 +4467,7 @@ intrinsics: return_type: svbool_t assert_instr: [eor] compose: - - LLVMLink: { name: "eor.z.nvx16i1" } + - LLVMLink: { name: "eor.z.nxv16i1" } - name: svnot[_{type}]{_mxz} attr: [*sve-unstable] @@ -4497,7 +4547,7 @@ intrinsics: return_type: svbool_t assert_instr: [orr] compose: - - LLVMLink: { name: "orr.z.nvx16i1" } + - LLVMLink: { name: "orr.z.nxv16i1" } - name: svorn[_b]_z attr: [*sve-unstable] @@ -4506,7 +4556,7 @@ intrinsics: return_type: svbool_t assert_instr: [orn] compose: - - LLVMLink: { name: "orn.z.nvx16i1" } + - LLVMLink: { name: "orn.z.nxv16i1" } - name: svlsl[{_n}_{type[0]}]{_mxz} attr: [*sve-unstable] @@ -4749,7 +4799,7 @@ intrinsics: assert_instr: [frecpx] zeroing_method: { drop: inactive } compose: - - LLVMLink: { name: "frecpx.x.{sve_type}" } + - LLVMLink: { name: "frecpx.{sve_type}" } - name: svrsqrte[_{type}] attr: [*sve-unstable] @@ -5115,7 +5165,7 @@ intrinsics: types: [[f32, u32], [f64, u64]] assert_instr: [fexpa] compose: - - LLVMLink: { name: "fexpa.x.{sve_type[0]} " } + - LLVMLink: { name: "fexpa.x.{sve_type[0]}" } - name: svscale[{_n}_{type[0]}]{_mxz} attr: [*sve-unstable] @@ -5139,7 +5189,7 @@ intrinsics: types: [f32] assert_instr: [fmmla] compose: - - LLVMLink: { name: "fmmla.{sve_type}" } + - LLVMLink: { name: "fmmla.{sve_type}.{sve_type}" } - name: svmmla[_{type}] attr: [*sve-unstable] @@ -5150,7 +5200,7 @@ intrinsics: types: [f64] assert_instr: [fmmla] compose: - - LLVMLink: { name: "fmmla.{sve_type}" } + - LLVMLink: { name: "fmmla.{sve_type}.{sve_type}" } - name: svmmla[_{type[0]}] attr: [*sve-unstable] diff --git a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs index 72fb97fee1f08..f96f05dfec680 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs @@ -1604,6 +1604,7 @@ impl Intrinsic { (Some(BaseTypeKind::Float), Some(BaseTypeKind::Float)) => ex, (Some(BaseTypeKind::UInt), Some(BaseTypeKind::UInt)) => ex, (Some(BaseTypeKind::Poly), Some(BaseTypeKind::Poly)) => ex, + (Some(BaseTypeKind::Bool), Some(BaseTypeKind::Bool)) => ex, (None, None) => ex, _ => unreachable!( diff --git a/library/stdarch/crates/stdarch-gen-common/Cargo.toml b/library/stdarch/crates/stdarch-gen-common/Cargo.toml new file mode 100644 index 0000000000000..691be14971a0d --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-common/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "stdarch-gen-common" +version = "0.1.0" +edition = "2024" + +[dependencies] +tempfile = "3" \ No newline at end of file diff --git a/library/stdarch/crates/stdarch-gen-common/src/lib.rs b/library/stdarch/crates/stdarch-gen-common/src/lib.rs new file mode 100644 index 0000000000000..13d788594ca55 --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-common/src/lib.rs @@ -0,0 +1,352 @@ +//! Shared check/bless harness for stdarch generators. + +use std::error::Error as StdError; +use std::fmt; +use std::fs; +use std::io; +use std::io::Read; +use std::path::{Path, PathBuf}; + +/// First-line marker identifying an auto-generated file. Generators emit this +/// as the first line of every file they produce; the harness uses it to +/// discover which files in `committed` are owned by the generator. +pub const GENERATED_MARKER: &str = "// This code is automatically generated. DO NOT MODIFY."; + +/// Controls what `run_generator` does with the generator's output. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Mode { + /// Verify that the `committed` matches the generator's output for owned files. + /// + /// Runs the generator into a temp directory, then compares each produced + /// file against the committed copy. Returns an error on the first mismatch. + Check, + /// Update the `committed` to match the generator's output for owned files. + /// + /// Runs the generator into a temp directory and copies each produced file + /// into `committed`. If the generator no longer produces an owned file, the + /// committed copy is deleted. Files in `committed` that are not owned + /// are left untouched. + Bless, +} + +impl Mode { + /// Read the mode from the `STDARCH_GEN_MODE` environment variable. + /// + /// Recognized values: + /// - `"check"` → [`Mode::Check`] + /// - `"bless"` → [`Mode::Bless`] + /// - unset → [`Mode::Bless`] + /// - any other value → panic + pub fn from_env() -> Self { + match std::env::var("STDARCH_GEN_MODE").as_deref() { + Ok("check") => Mode::Check, + Ok("bless") => Mode::Bless, + Ok(other) => panic!("unknown STDARCH_GEN_MODE value: {other:?}"), + Err(_) => Mode::Bless, + } + } +} + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + Mismatch { path: PathBuf, kind: MismatchKind }, + Generator(Box), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MismatchKind { + /// Owned file produced by the generator but absent from the `committed`. + /// Means the `committed` needs to be regenerated. + MissingInCommitted, + /// Owned file present in the `committed` but the generator no longer + /// produces it. The file must be removed from the `committed` . + ExtraInCommitted, + /// Owned file exists on both sides but contents differ. + ContentsDiffer, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Io(e) => write!(f, "I/O error: {e}"), + Error::Mismatch { path, kind } => match kind { + MismatchKind::MissingInCommitted => { + write!(f, "{}: generated but not committed", path.display()) + } + MismatchKind::ExtraInCommitted => { + write!(f, "{}: committed but no longer generated", path.display()) + } + MismatchKind::ContentsDiffer => write!(f, "{}: contents differ", path.display()), + }, + Error::Generator(e) => write!(f, "generator failed: {e}"), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(e) => Some(e), + Error::Generator(e) => Some(&**e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; + +/// Run a generator under the chosen `mode`, reconciling its output with `committed`. +/// +/// Arguments: +/// - `committed` — the directory holding the in-tree (committed) source files. +/// Files inside `committed` whose first line begins with [`GENERATED_MARKER`] +/// are treated as owned by the generator. Anything else is treated as +/// hand-written and is left untouched by `Bless` and ignored by `Check`. +/// So generated files coexist with hand-written files in the same directory. +/// - `mode` — what to do with the generator's output. +/// - `generate` — closure that writes the generator's output into the +/// directory it is given. Its error is wrapped in [`Error::Generator`]. +/// +/// Behavior per mode: +/// - [`Mode::Check`]: runs the generator into a temp dir and compares owned +/// files against the committed copies. Mismatch returns [`Error::Mismatch`]. +/// - [`Mode::Bless`]: runs the generator into a temp dir and copies owned +/// files into `committed`, or removes `committed`'s copy if the generator no +/// longer produces them. +pub fn run_generator(committed: &Path, mode: Mode, generate: F) -> Result<()> +where + F: FnOnce(&Path) -> std::result::Result<(), E>, + E: Into>, +{ + let scratch = tempfile::tempdir()?; + generate(scratch.path()).map_err(|e| Error::Generator(e.into()))?; + + let owned = discover_owned(committed)?; + let produced = discover_all(scratch.path())?; + + let mut names: Vec<&String> = owned.iter().chain(produced.iter()).collect(); + names.sort(); + names.dedup(); + + for name in names { + match mode { + Mode::Check => compare(scratch.path(), committed, name)?, + Mode::Bless => apply_bless(scratch.path(), committed, name)?, + } + } + Ok(()) +} + +/// Returns the names of files in `dir` whose first line begins with +/// [`GENERATED_MARKER`]. Files without the marker are skipped. +fn discover_owned(dir: &Path) -> Result> { + let mut out = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + if !entry.file_type()?.is_file() { + continue; + } + let Ok(mut file) = fs::File::open(entry.path()) else { + continue; + }; + let mut buf = [0u8; GENERATED_MARKER.len()]; + if file.read_exact(&mut buf).is_err() { + continue; + } + if buf == *GENERATED_MARKER.as_bytes() + && let Some(name) = entry.file_name().to_str() + { + out.push(name.to_owned()); + } + } + out.sort(); + Ok(out) +} + +/// Returns every file name in `dir` (scratch dir — all files are generator output). +fn discover_all(dir: &Path) -> Result> { + let mut out = Vec::new(); + for entry in fs::read_dir(dir)? { + let entry = entry?; + if entry.file_type()?.is_file() + && let Some(name) = entry.file_name().to_str() + { + out.push(name.to_string()); + } + } + Ok(out) +} + +fn compare(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result<()> { + let rel_path = PathBuf::from(filename); + let gen_path = generated_dir.join(&rel_path); + let comm_path = committed_dir.join(&rel_path); + match (gen_path.exists(), comm_path.exists()) { + (true, false) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::MissingInCommitted, + }), + (false, true) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ExtraInCommitted, + }), + (false, false) => Ok(()), + (true, true) => { + if fs::read(&gen_path)? != fs::read(&comm_path)? { + Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ContentsDiffer, + }) + } else { + Ok(()) + } + } + } +} + +fn apply_bless(generated_dir: &Path, committed_dir: &Path, filename: &str) -> Result<()> { + fs::create_dir_all(committed_dir)?; + let rel_path = PathBuf::from(filename); + let from = generated_dir.join(&rel_path); + let to = committed_dir.join(&rel_path); + if from.exists() { + if let Some(parent) = to.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(&from, &to)?; + } else if to.exists() { + fs::remove_file(&to)?; + } + Ok(()) +} + +// Skipped on iOS because these tests fail on the `x86_64-apple-ios-macabi` CI runner +// with `Os { code: 17, kind: AlreadyExists, message: "File exists" }`. +#[cfg(all(test, not(target_os = "ios")))] +mod tests { + use super::*; + + fn write_marker(p: &Path, body: &[u8]) { + if let Some(d) = p.parent() { + fs::create_dir_all(d).unwrap(); + } + let mut bytes = Vec::new(); + bytes.extend_from_slice(GENERATED_MARKER.as_bytes()); + bytes.push(b'\n'); + bytes.extend_from_slice(body); + fs::write(p, bytes).unwrap(); + } + + #[test] + fn check_fails_on_byte_diff() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("a.txt"), b"hi"); + let e = run_generator( + &committed, + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.txt"), b"HI"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ContentsDiffer, + .. + } + )); + } + + #[test] + fn check_fails_when_owned_file_missing_from_generated() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("a.txt"), b"hi"); + let e = run_generator( + &committed, + Mode::Check, + |_| -> std::result::Result<(), io::Error> { Ok(()) }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ExtraInCommitted, + .. + } + )); + } + + #[test] + fn check_fails_when_owned_file_missing_from_committed() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + fs::create_dir_all(&committed).unwrap(); + let e = run_generator( + &committed, + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("a.txt"), b"hi"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::MissingInCommitted, + .. + } + )); + } + + #[test] + fn bless_deletes_files_no_longer_produced() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write_marker(&committed.join("keep.txt"), b""); + write_marker(&committed.join("stale.txt"), b""); + run_generator( + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("keep.txt"), b""); + Ok(()) + }, + ) + .unwrap(); + assert!(committed.join("keep.txt").exists()); + assert!(!committed.join("stale.txt").exists()); + } + + #[test] + fn bless_preserves_unowned_files() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + fs::create_dir_all(&committed).unwrap(); + fs::write(committed.join("mod.rs"), b"hand-written").unwrap(); + fs::write(committed.join("old.txt"), b"old").unwrap(); + run_generator( + &committed, + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write_marker(&out.join("new.txt"), b"new"); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(fs::read(committed.join("mod.rs")).unwrap(), b"hand-written"); + assert_eq!(fs::read(committed.join("old.txt")).unwrap(), b"old"); + assert!(committed.join("new.txt").exists()); + } +} diff --git a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml index 397c7816f8d1e..c7dfce2c0fd7b 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-hexagon/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] regex = "1.10" +stdarch-gen-common = { path = "../stdarch-gen-common" } diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index 7a1c3030c0042..c3ad153ab0a7a 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -21,6 +21,7 @@ use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::{run_generator, Mode, GENERATED_MARKER}; /// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. /// These intrinsics have equivalent semantics and can be lowered to the generic form. @@ -1609,6 +1610,7 @@ fn generate_module_file( let mut output = File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; @@ -1691,23 +1693,21 @@ fn main() -> Result<(), String> { } // Generate output files - let hexagon_dir = std::env::args() - .nth(1) - .map(std::path::PathBuf::from) - .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - - // Generate v64.rs (64-byte vector mode) - let v64_path = hexagon_dir.join("v64.rs"); - println!("\nStep 3: Generating v64.rs (64-byte mode)..."); - generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?; - println!(" Output: {}", v64_path.display()); - - // Generate v128.rs (128-byte vector mode) - let v128_path = hexagon_dir.join("v128.rs"); - println!("\nStep 4: Generating v128.rs (128-byte mode)..."); - generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?; - println!(" Output: {}", v128_path.display()); + let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + + // Either "check" to check the output versus the committed output, or "bless" + // to update the output. + let mode = Mode::from_env(); + println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + println!(" Output: {}", hexagon_dir.join(filename).display()); + } + Ok(()) + }) + .map_err(|e| e.to_string())?; println!("\n=== Results ==="); println!( diff --git a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml index d3ac607c5576c..1a8c052ebe318 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml +++ b/library/stdarch/crates/stdarch-gen-loongarch/Cargo.toml @@ -7,4 +7,4 @@ edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rand = "0.8.5" +rand = "0.9.3" diff --git a/library/stdarch/examples/Cargo.toml b/library/stdarch/examples/Cargo.toml index c4fc4c7e374c8..8752f206526c7 100644 --- a/library/stdarch/examples/Cargo.toml +++ b/library/stdarch/examples/Cargo.toml @@ -13,7 +13,7 @@ default-run = "hex" [dependencies] core_arch = { path = "../crates/core_arch" } quickcheck = "1.0" -rand = "0.8" +rand = "0.9.3" [[bin]] name = "hex" diff --git a/library/stdarch/examples/connect5.rs b/library/stdarch/examples/connect5.rs index f24657b148394..2df294b16b71e 100644 --- a/library/stdarch/examples/connect5.rs +++ b/library/stdarch/examples/connect5.rs @@ -33,8 +33,8 @@ #![cfg_attr(target_arch = "x86_64", feature(stdarch_internal))] #![feature(stmt_expr_attributes)] +use rand::rng; use rand::seq::SliceRandom; -use rand::thread_rng; use std::cmp; use std::time::Instant; @@ -374,7 +374,7 @@ impl List { } pub fn shuffle(&mut self) { - let mut rng = thread_rng(); + let mut rng = rng(); let num = self.p_size as usize; self.p_move[..num].shuffle(&mut rng); diff --git a/library/stdarch/examples/hex.rs b/library/stdarch/examples/hex.rs index 21827b375adaf..6417562074d9c 100644 --- a/library/stdarch/examples/hex.rs +++ b/library/stdarch/examples/hex.rs @@ -354,9 +354,9 @@ mod benches { len: usize, f: for<'a> unsafe fn(&[u8], &'a mut [u8]) -> Result<&'a str, usize>, ) { - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let input = std::iter::repeat(()) - .map(|()| rng.r#gen::()) + .map(|()| rng.r#random::()) .take(len) .collect::>(); let mut dst = vec![0; input.len() * 2]; diff --git a/library/stdarch/intrinsics_data/arm_intrinsics.json b/library/stdarch/intrinsics_data/arm_intrinsics.json index fab6da7f2c16c..7f749fe4d8842 100644 --- a/library/stdarch/intrinsics_data/arm_intrinsics.json +++ b/library/stdarch/intrinsics_data/arm_intrinsics.json @@ -51174,6 +51174,12 @@ "svfloat16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51188,6 +51194,12 @@ "svfloat32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51202,6 +51214,12 @@ "svfloat64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51216,6 +51234,12 @@ "svint16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint16_t" }, @@ -51230,6 +51254,12 @@ "svint32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint32_t" }, @@ -51244,6 +51274,12 @@ "svint64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint64_t" }, @@ -51258,6 +51294,12 @@ "svint8x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint8_t" }, @@ -51272,6 +51314,12 @@ "svuint16x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51286,6 +51334,12 @@ "svuint32x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51300,6 +51354,12 @@ "svuint64x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51314,6 +51374,12 @@ "svuint8x2_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint8_t" }, @@ -51328,6 +51394,12 @@ "svfloat16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51342,6 +51414,12 @@ "svfloat32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51356,6 +51434,12 @@ "svfloat64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51370,6 +51454,12 @@ "svint16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint16_t" }, @@ -51384,6 +51474,12 @@ "svint32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint32_t" }, @@ -51398,6 +51494,12 @@ "svint64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint64_t" }, @@ -51412,6 +51514,12 @@ "svint8x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint8_t" }, @@ -51426,6 +51534,12 @@ "svuint16x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51440,6 +51554,12 @@ "svuint32x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51454,6 +51574,12 @@ "svuint64x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51468,6 +51594,12 @@ "svuint8x3_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint8_t" }, @@ -51482,6 +51614,12 @@ "svfloat16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat16_t" }, @@ -51496,6 +51634,12 @@ "svfloat32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat32_t" }, @@ -51510,6 +51654,12 @@ "svfloat64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat64_t" }, @@ -51524,6 +51674,12 @@ "svint16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint16_t" }, @@ -51538,6 +51694,12 @@ "svint32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint32_t" }, @@ -51552,6 +51714,12 @@ "svint64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint64_t" }, @@ -51566,6 +51734,12 @@ "svint8x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint8_t" }, @@ -51580,6 +51754,12 @@ "svuint16x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint16_t" }, @@ -51594,6 +51774,12 @@ "svuint32x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint32_t" }, @@ -51608,6 +51794,12 @@ "svuint64x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint64_t" }, @@ -51622,6 +51814,12 @@ "svuint8x4_t tuple", "uint64_t imm_index" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint8_t" }, @@ -162999,6 +163197,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat16x2_t" }, @@ -163014,6 +163218,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat32x2_t" }, @@ -163029,6 +163239,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svfloat64x2_t" }, @@ -163044,6 +163260,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint16x2_t" }, @@ -163059,6 +163281,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint32x2_t" }, @@ -163074,6 +163302,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint64x2_t" }, @@ -163089,6 +163323,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svint8x2_t" }, @@ -163104,6 +163344,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint16x2_t" }, @@ -163119,6 +163365,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint32x2_t" }, @@ -163134,6 +163386,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint64x2_t" }, @@ -163149,6 +163407,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 1 + } + }, "return_type": { "value": "svuint8x2_t" }, @@ -163164,6 +163428,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat16x3_t" }, @@ -163179,6 +163449,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat32x3_t" }, @@ -163194,6 +163470,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svfloat64x3_t" }, @@ -163209,6 +163491,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint16x3_t" }, @@ -163224,6 +163512,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint32x3_t" }, @@ -163239,6 +163533,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint64x3_t" }, @@ -163254,6 +163554,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svint8x3_t" }, @@ -163269,6 +163575,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint16x3_t" }, @@ -163284,6 +163596,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint32x3_t" }, @@ -163299,6 +163617,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint64x3_t" }, @@ -163314,6 +163638,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 2 + } + }, "return_type": { "value": "svuint8x3_t" }, @@ -163329,6 +163659,12 @@ "uint64_t imm_index", "svfloat16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat16x4_t" }, @@ -163344,6 +163680,12 @@ "uint64_t imm_index", "svfloat32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat32x4_t" }, @@ -163359,6 +163701,12 @@ "uint64_t imm_index", "svfloat64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svfloat64x4_t" }, @@ -163374,6 +163722,12 @@ "uint64_t imm_index", "svint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint16x4_t" }, @@ -163389,6 +163743,12 @@ "uint64_t imm_index", "svint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint32x4_t" }, @@ -163404,6 +163764,12 @@ "uint64_t imm_index", "svint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint64x4_t" }, @@ -163419,6 +163785,12 @@ "uint64_t imm_index", "svint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svint8x4_t" }, @@ -163434,6 +163806,12 @@ "uint64_t imm_index", "svuint16_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint16x4_t" }, @@ -163449,6 +163827,12 @@ "uint64_t imm_index", "svuint32_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint32x4_t" }, @@ -163464,6 +163848,12 @@ "uint64_t imm_index", "svuint64_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint64x4_t" }, @@ -163479,6 +163869,12 @@ "uint64_t imm_index", "svuint8_t x" ], + "Arguments_Preparation": { + "imm_index": { + "minimum": 0, + "maximum": 3 + } + }, "return_type": { "value": "svuint8x4_t" }, diff --git a/library/stdarch/rust-version b/library/stdarch/rust-version index 59e9e5a0e6ee9..e8cec4e0ee9c9 100644 --- a/library/stdarch/rust-version +++ b/library/stdarch/rust-version @@ -1 +1 @@ -045b17737dab5fcc28e4cbee0cfe2ce4ed363b32 +8e150217bafcaaaa0c45bf685c55fd56cec48598 diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 4e380d8894781..b19694f25c646 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -56,7 +56,14 @@ cfg_select! { } } -#[cfg(target_env = "musl")] +// `target_abi = "pauthtest"` is currently only used by the Linux/musl +// `aarch64-unknown-linux-pauthtest` target. That target only supports unwinding +// via libunwind. +#[cfg(target_abi = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + +#[cfg(all(target_env = "musl", not(target_abi = "pauthtest")))] cfg_select! { all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); diff --git a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md index 11a7a573f38b6..baae63e96bdc7 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md @@ -97,7 +97,7 @@ The best way for drivers to get access to this is by overriding the Within the compiler, for performance reasons, we usually do not register dozens of lint passes. Instead, we have a single lint pass of each variety (e.g., -`BuiltinCombinedModuleLateLintPass`) which will internally call all of the +`BuiltinCombinedLateLintModPass`) which will internally call all of the individual lint passes; this is because then we get the benefits of static over dynamic dispatch for each of the (often empty) trait methods. diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index f16f375a5a84b..cc6cf2f8a648c 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -850,12 +850,63 @@ pub mod futures { Then, the `unix` cfg will never be displayed into the documentation. -Rustdoc currently hides `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. +The syntax of `hide` is as follows: you can list as many `cfg` name as you want: -The attribute accepts only a list of identifiers or key/value items. So you can write: +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, target_os)))] +``` + +With the above example, it means that `#[cfg(feature)]` and `#[cfg(target_os)]` won't be displayed in the docs. However, `#[cfg(target_os = "linux)]` or `#[cfg(feature = "something")]` will be displayed because only the key without values was marked as hidden. if you want to hide some values, you can do: + +```rust,ignore (nightly) +#[doc(auto_cfg(hide(feature, target_os, values("something", "linux"))))] +``` + +In this case, `#[cfg(feature = "linux")]`, `#[cfg(feature = "something")]`, `#[cfg(target_os = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. All listed keys will be impacted by `values(...)`. You can split them by having two `hide`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values("something")), + hide(target_os, values("linux")), +))] +``` + +Now, only `#[cfg(feature = "something")]` and `#[cfg(target_os = "linux")]` will be hidden. If you want to hide a key and all its values, you can use `any()`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values(any())), +))] +``` + +If you want to hide only when there is no value you can use `none()`: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values("something", none())), +))] +``` + +So now, if you want to forbid all values for a key, but allow the key itself, you can do: + +```rust,ignore (nightly) +#[doc(auto_cfg( + hide(feature, values(any())), // We completely hide "feature". + show(feature), // We show again "feature" (but not any value). +))] +``` + +If the previous example, both `#[cfg(feature)]` and `#[cfg(feature = "something")]` will be hidden. + +Rustdoc currently hides `test`, `doc` and `doctest` attributes by default and reserves the right to change the list of "hidden by default" attributes. + +The attribute accepts only a list of identifiers and `values()`. So you can write: ```rust,ignore (nightly) -#[doc(auto_cfg(hide(unix, doctest, feature = "something")))] +#[doc(auto_cfg( + hide(unix, doctest), + hide(feature, values("something")), +))] #[doc(auto_cfg(hide()))] ``` @@ -865,7 +916,7 @@ But you cannot write: #[doc(auto_cfg(hide(not(unix))))] ``` -So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix`: +So if we use `doc(auto_cfg(hide(unix)))`, it means it will hide all mentions of `unix` without a value: ```rust,ignore (nightly) #[cfg(unix)] // nothing displayed @@ -879,14 +930,6 @@ However, it only impacts the `unix` cfg, not the feature: #[cfg(feature = "unix")] // `feature = "unix"` is displayed ``` -If `cfg_auto(show(...))` and `cfg_auto(hide(...))` are used to show/hide a same `cfg` on a same item, it'll emit an error. Example: - -```rust,ignore (nightly) -#[doc(auto_cfg(hide(unix)))] -#[doc(auto_cfg(show(unix)))] // Error! -pub fn foo() {} -``` - Using this attribute will re-enable `auto_cfg` if it was disabled at this location: ```rust,ignore (nightly) @@ -904,14 +947,6 @@ pub mod module { } ``` -However, using `doc(auto_cfg = ...)` and `doc(auto_cfg(...))` on the same item will emit an error: - -```rust,ignore (nightly) -#[doc(auto_cfg = false)] -#[doc(auto_cfg(hide(unix)))] // error -pub fn foo() {} -``` - The reason behind this is that `doc(auto_cfg = ...)` enables or disables the feature, whereas `doc(auto_cfg(...))` enables it unconditionally, making the first attribute to appear useless as it will be overidden by the next `doc(auto_cfg)` attribute. ### `#[doc(auto_cfg(show(...)))]` @@ -919,6 +954,8 @@ The reason behind this is that `doc(auto_cfg = ...)` enables or disables the fea This attribute does the opposite of `#[doc(auto_cfg(hide(...)))]`: if you used `#[doc(auto_cfg(hide(...)))]` and want to revert its effect on an item and its descendants, you can use `#[doc(auto_cfg(show(...)))]`. It only applies to `#[doc(auto_cfg = true)]`, not to `#[doc(cfg(...))]`. +It follows the same syntax rules as for `#[doc(auto_cfg(hide(...)))]`. + For example: ```rust,ignore (nightly) @@ -936,7 +973,10 @@ pub mod futures { The attribute accepts only a list of identifiers or key/value items. So you can write: ```rust,ignore (nightly) -#[doc(auto_cfg(show(unix, doctest, feature = "something")))] +#[doc(auto_cfg( + show(unix, doctest), + show(feature, values("something")), +))] #[doc(auto_cfg(show()))] ``` diff --git a/src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md b/src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md similarity index 100% rename from src/doc/unstable-book/src/compiler-flags/min-recursive-limit.md rename to src/doc/unstable-book/src/compiler-flags/min-recursion-limit.md diff --git a/src/doc/unstable-book/src/language-features/strict-provenance-lints.md b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md index fec908fe3212e..3c7f29dc69f46 100644 --- a/src/doc/unstable-book/src/language-features/strict-provenance-lints.md +++ b/src/doc/unstable-book/src/language-features/strict-provenance-lints.md @@ -5,17 +5,18 @@ The tracking issue for this feature is: [#130351] [#130351]: https://github.com/rust-lang/rust/issues/130351 ----- -The `strict_provenance_lints` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. -These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model. +The `strict_provenance_lints` feature allows to enable the `implicit_provenance_casts` lint. ## Example ```rust #![feature(strict_provenance_lints)] -#![warn(fuzzy_provenance_casts)] +#![warn(implicit_provenance_casts)] fn main() { - let _dangling = 16_usize as *const u8; - //~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8` + let dangling = 16_usize as *const u8; + //~^ WARNING: cast from `usize` to `*const u8` implicitly relies on exposed provenance + let _addr = dangling as usize; + //~^ WARNING: cast from `*const u8` to `usize` implicitly exposes pointer provenance } ``` diff --git a/src/doc/unstable-book/src/library-features/tuple-trait.md b/src/doc/unstable-book/src/library-features/tuple-trait.md new file mode 100644 index 0000000000000..5521850f12bb6 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/tuple-trait.md @@ -0,0 +1,5 @@ +# `tuple_trait` + +The tracking issue for this feature is [#157987]. + +[#157987]: https://github.com/rust-lang/rust/issues/157987 diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index c06b4857e4903..45d084b2f9bf7 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -13,7 +13,7 @@ arrayvec = { version = "0.7", default-features = false } askama = { version = "0.16.0", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } -itertools = "0.12" +itertools = "0.15" minifier = { version = "0.3.5", default-features = false } proc-macro2 = "1.0.103" pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 170f1439ecc9c..2950e3b563c1c 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -8,11 +8,13 @@ use std::sync::Arc; use std::{fmt, mem, ops}; use itertools::Either; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; use rustc_hir as hir; use rustc_hir::Attribute; -use rustc_hir::attrs::{self, AttributeKind, CfgEntry, CfgHideShow, HideOrShow}; +use rustc_hir::attrs::{ + AttributeKind, CfgEntry, CfgHideShow, DocCfgHideShow, DocCfgHideShowValue, HideOrShow, +}; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{DUMMY_SP, Span}; @@ -30,6 +32,87 @@ mod tests; #[cfg_attr(test, derive(PartialEq))] pub(crate) struct Cfg(CfgEntry); +// Similar to `hir::DocCfgHideShow` but allows to handle both `show` and `hide` as with the `except` +// field in `Any` variant. +#[derive(Clone, Debug)] +enum DocCfgHide { + Any { except: ThinVec }, + List(ThinVec), +} + +impl DocCfgHide { + fn new() -> Self { + Self::List([DocCfgHideShowValue::new_none(DUMMY_SP)].into()) + } + + fn contains(&self, value: Option) -> bool { + match self { + // Contains any values except the ones listed in `except`. + Self::Any { except } => !except.iter().any(|e| e.value == value), + Self::List(values) => values.iter().any(|v| v.value == value), + } + } + + fn merge_with(&mut self, other: &DocCfgHideShow) { + match (self, other) { + (Self::Any { except }, DocCfgHideShow::Any(_)) => { + except.clear(); + } + (s, DocCfgHideShow::Any(_)) => { + // We "upgrade" the list values to "all". + *s = Self::Any { except: ThinVec::new() }; + } + (Self::Any { except }, DocCfgHideShow::List(values)) => { + for other in values { + if let Some(index) = except.iter().position(|value| value.value == other.value) + { + except.remove(index); + } + } + } + (Self::List(values), DocCfgHideShow::List(other_values)) => { + for other in other_values { + if !values.iter().any(|value| value.value == other.value) { + values.push(*other); + } + } + } + } + } + + fn remove(&mut self, other: &DocCfgHideShow) { + match (self, other) { + (s, DocCfgHideShow::Any(_)) => { + *s = Self::List(ThinVec::new()); + } + (Self::Any { except }, DocCfgHideShow::List(other_values)) => { + for other in other_values { + if !except.iter().any(|value| value.value == other.value) { + except.push(*other); + } + } + } + (Self::List(values), DocCfgHideShow::List(other_values)) => { + for other in other_values { + if let Some(index) = values.iter().position(|value| value.value == other.value) + { + values.remove(index); + } + } + } + } + } +} + +impl From<&DocCfgHideShow> for DocCfgHide { + fn from(from: &DocCfgHideShow) -> Self { + match from { + DocCfgHideShow::Any(_) => Self::Any { except: ThinVec::new() }, + DocCfgHideShow::List(values) => Self::List(values.clone()), + } + } +} + /// Whether the configuration consists of just `Cfg` or `Not`. fn is_simple_cfg(cfg: &CfgEntry) -> bool { match cfg { @@ -53,14 +136,14 @@ fn is_any_cfg(cfg: &CfgEntry) -> bool { } } -fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashSet) -> Option { +fn strip_hidden(cfg: &CfgEntry, hidden: &FxHashMap) -> Option { match cfg { CfgEntry::Bool(..) => Some(cfg.clone()), - CfgEntry::NameValue { .. } => { - if !hidden.contains(&NameValueCfg::from(cfg)) { - Some(cfg.clone()) - } else { + CfgEntry::NameValue { name, value, .. } => { + if hidden.get(name).is_some_and(|values| values.contains(*value)) { None + } else { + Some(cfg.clone()) } } CfgEntry::Not(cfg, _) => { @@ -653,39 +736,12 @@ fn human_readable_target_env(env: Symbol) -> Option<&'static str> { }) } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -struct NameValueCfg { - name: Symbol, - value: Option, -} - -impl NameValueCfg { - fn new(name: Symbol) -> Self { - Self { name, value: None } - } -} - -impl<'a> From<&'a CfgEntry> for NameValueCfg { - fn from(cfg: &'a CfgEntry) -> Self { - match cfg { - CfgEntry::NameValue { name, value, .. } => NameValueCfg { name: *name, value: *value }, - _ => NameValueCfg { name: sym::empty, value: None }, - } - } -} - -impl<'a> From<&'a attrs::CfgInfo> for NameValueCfg { - fn from(cfg: &'a attrs::CfgInfo) -> Self { - Self { name: cfg.name, value: cfg.value.map(|(value, _)| value) } - } -} - /// This type keeps track of (doc) cfg information as we go down the item tree. #[derive(Clone, Debug)] pub(crate) struct CfgInfo { /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active /// `doc(auto_cfg(show(...)))` cfgs. - hidden_cfg: FxHashSet, + hidden_cfg: FxHashMap, /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while /// taking into account the `hidden_cfg` information. current_cfg: Cfg, @@ -700,10 +756,10 @@ pub(crate) struct CfgInfo { impl Default for CfgInfo { fn default() -> Self { Self { - hidden_cfg: FxHashSet::from_iter([ - NameValueCfg::new(sym::test), - NameValueCfg::new(sym::doc), - NameValueCfg::new(sym::doctest), + hidden_cfg: FxHashMap::from_iter([ + (sym::test, DocCfgHide::new()), + (sym::doc, DocCfgHide::new()), + (sym::doctest, DocCfgHide::new()), ]), current_cfg: Cfg(CfgEntry::Bool(true, DUMMY_SP)), auto_cfg_active: true, @@ -712,51 +768,24 @@ impl Default for CfgInfo { } } -fn show_hide_show_conflict_error( - tcx: TyCtxt<'_>, - item_span: rustc_span::Span, - previous: rustc_span::Span, -) { - let mut diag = tcx.sess.dcx().struct_span_err( - item_span, - format!( - "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item" - ), - ); - diag.span_note(previous, "first change was here"); - diag.emit(); -} - /// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument. /// -/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and -/// `auto_cfg(show(...))` on the same item and emits an error if it's the case. -/// /// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs` /// and in `new_hide_attrs` arguments. -fn handle_auto_cfg_hide_show( - tcx: TyCtxt<'_>, - cfg_info: &mut CfgInfo, - attr: &CfgHideShow, - new_show_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, - new_hide_attrs: &mut FxHashMap<(Symbol, Option), rustc_span::Span>, -) { - for value in &attr.values { - let simple = NameValueCfg::from(value); +fn handle_auto_cfg_hide_show(cfg_info: &mut CfgInfo, attr: &CfgHideShow) { + for (cfg_name, value) in &attr.values { if attr.kind == HideOrShow::Show { - if let Some(span) = new_hide_attrs.get(&(simple.name, simple.value)) { - show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span); - } else { - new_show_attrs.insert((simple.name, simple.value), value.span_for_name_and_value()); - } - cfg_info.hidden_cfg.remove(&simple); + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.remove(value)) + .or_insert_with(|| value.into()); } else { - if let Some(span) = new_show_attrs.get(&(simple.name, simple.value)) { - show_hide_show_conflict_error(tcx, value.span_for_name_and_value(), *span); - } else { - new_hide_attrs.insert((simple.name, simple.value), value.span_for_name_and_value()); - } - cfg_info.hidden_cfg.insert(simple); + cfg_info + .hidden_cfg + .entry(*cfg_name) + .and_modify(|entry| entry.merge_with(value)) + .or_insert_with(|| value.into()); } } } @@ -791,9 +820,6 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator false } - let mut new_show_attrs = FxHashMap::default(); - let mut new_hide_attrs = FxHashMap::default(); - let mut doc_cfg = attrs .clone() .filter_map(|attr| match attr { @@ -844,13 +870,7 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator return None; } for (value, _) in &d.auto_cfg { - handle_auto_cfg_hide_show( - tcx, - cfg_info, - value, - &mut new_show_attrs, - &mut new_hide_attrs, - ); + handle_auto_cfg_hide_show(cfg_info, value); } } } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index ec5c9a66d4b65..554bcb2621ac6 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -350,8 +350,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { - ty::ConstKind::Unevaluated(_, ty::UnevaluatedConst { kind, .. }) => match kind { - ty::UnevaluatedConstKind::Projection { def_id } => { + ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => match kind { + ty::AliasConstKind::Projection { def_id } => { if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) { @@ -360,9 +360,9 @@ pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { n.to_string() } } - ty::UnevaluatedConstKind::Inherent { def_id } - | ty::UnevaluatedConstKind::Free { def_id } - | ty::UnevaluatedConstKind::Anon { def_id } => { + ty::AliasConstKind::Inherent { def_id } + | ty::AliasConstKind::Free { def_id } + | ty::AliasConstKind::Anon { def_id } => { if let Some(local_def_id) = def_id.as_local() && let Some(body_id) = tcx.hir_maybe_body_owned_by(local_def_id) { diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index fee783133133f..ccee062584e01 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -233,6 +233,30 @@ impl Cache { } } +impl CacheBuilder<'_, '_> { + /// Extends `dids` with ones that an impl should be associated with for a type appearing in its + /// `Self` type or trait generic arguments, accounting for references and `#[fundamental]` + /// wrappers. + /// + /// This ensures that impls like `impl Trait> for Foreign`, `impl Trait for + /// Box`, and other variations of these, are documented on `Local`'s page. + fn extend_with_fundamental_dids(&self, ty: &clean::Type, dids: &mut FxIndexSet) { + dids.extend(ty.def_id(self.cache)); + // without_borrowed_ref allows cases like `impl Trait<&Box> for Foreign` to be + // handled by this function. (This is rare in practice, but easy to handle here.) + if let clean::Type::Path { path } = ty.without_borrowed_ref() + && let Some(generics) = path.generics() + && let ty::Adt(adt, _) = + self.tcx.type_of(path.def_id()).instantiate_identity().skip_norm_wip().kind() + && adt.is_fundamental() + { + for inner in generics { + self.extend_with_fundamental_dids(inner, dids); + } + } + } +} + impl DocFolder for CacheBuilder<'_, '_> { fn fold_item(&mut self, item: clean::Item) -> Option { if item.item_id.is_local() { @@ -418,22 +442,9 @@ impl DocFolder for CacheBuilder<'_, '_> { // Note: matching twice to restrict the lifetime of the `i` borrow. let mut dids = FxIndexSet::default(); match i.for_ { - clean::Type::Path { ref path } - | clean::BorrowedRef { type_: clean::Type::Path { ref path }, .. } => { - dids.insert(path.def_id()); - if let Some(generics) = path.generics() - && let ty::Adt(adt, _) = self - .tcx - .type_of(path.def_id()) - .instantiate_identity() - .skip_norm_wip() - .kind() - && adt.is_fundamental() - { - for ty in generics { - dids.extend(ty.def_id(self.cache)); - } - } + clean::Type::Path { .. } + | clean::BorrowedRef { type_: clean::Type::Path { .. }, .. } => { + self.extend_with_fundamental_dids(&i.for_, &mut dids); } clean::DynTrait(ref bounds, _) | clean::BorrowedRef { type_: clean::DynTrait(ref bounds, _), .. } => { @@ -452,7 +463,7 @@ impl DocFolder for CacheBuilder<'_, '_> { && let Some(generics) = trait_.generics() { for bound in generics { - dids.extend(bound.def_id(self.cache)); + self.extend_with_fundamental_dids(bound, &mut dids); } } let impl_item = Impl { impl_item: item }; diff --git a/src/librustdoc/html/render/sorted_template.rs b/src/librustdoc/html/render/sorted_template.rs index 659c5e6093bdd..8501421651ab6 100644 --- a/src/librustdoc/html/render/sorted_template.rs +++ b/src/librustdoc/html/render/sorted_template.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write as _}; use std::marker::PhantomData; use std::str::FromStr; -use itertools::{Itertools as _, Position}; +use itertools::Itertools as _; use serde::{Deserialize, Serialize}; /// Append-only templates for sorted, deduplicated lists of items. @@ -62,7 +62,7 @@ impl fmt::Display for SortedTemplate { write!(f, "{}", self.before)?; for (p, fragment) in self.fragments.iter().with_position() { let mut f = DeltaWriter { inner: &mut f, delta: 0 }; - let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR }; + let sep = if p.is_first() { "" } else { F::SEPARATOR }; f.write_str(sep)?; f.write_str(fragment)?; fragment_lengths.push(f.delta); @@ -95,7 +95,7 @@ impl FromStr for SortedTemplate { let (fragment, rest) = s.split_at_checked(index).ok_or(Error("invalid fragment length: out of bounds"))?; s = rest; - let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR }; + let sep = if p.is_first() { "" } else { F::SEPARATOR }; let fragment = fragment .strip_prefix(sep) .ok_or(Error("invalid fragment length: expected to find separator here"))?; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 4de9c46900a76..7083d730de58c 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -7,7 +7,9 @@ use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; -use rustc_hir::attrs::{self, DeprecatedSince, DocAttribute, DocInline, HideOrShow}; +use rustc_hir::attrs::{ + self, DeprecatedSince, DocAttribute, DocCfgHideShow, DocInline, HideOrShow, +}; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; use rustc_hir::{HeaderSafety, Safety, find_attr}; @@ -268,6 +270,18 @@ impl FromClean for Stability { } } +impl FromClean for Box { + fn from_clean(stab: &hir::DefaultBodyStability, _renderer: &JsonRenderer<'_>) -> Self { + let hir::StabilityLevel::Unstable { .. } = stab.level else { + bug!( + "unexpected stable default-body stability, \ + there's no stable equivalent of `#[rustc_default_body_unstable]`" + ) + }; + Box::new(ProvidedDefaultUnstable { feature: stab.feature.to_string() }) + } +} + impl FromClean for Option> { fn from_clean(generic_args: &clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self { use clean::GenericArgs::*; @@ -351,18 +365,23 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)), VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)), FunctionItem(f) => { - ItemEnum::Function(from_clean_function(f, true, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, true, None, header.unwrap(), renderer)) } ForeignFunctionItem(f, _) => { - ItemEnum::Function(from_clean_function(f, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(f, false, None, header.unwrap(), renderer)) } TraitItem(t) => ItemEnum::Trait(t.into_json(renderer)), TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)), - MethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, true, header.unwrap(), renderer)) - } + MethodItem(m, _) => ItemEnum::Function(from_clean_function( + m, + true, + default_body_stability_for_def_id(renderer.tcx, item.item_id.expect_def_id()) + .map(|stab| stab.into_json(renderer)), + header.unwrap(), + renderer, + )), RequiredMethodItem(m, _) => { - ItemEnum::Function(from_clean_function(m, false, header.unwrap(), renderer)) + ItemEnum::Function(from_clean_function(m, false, None, header.unwrap(), renderer)) } ImplItem(i) => ItemEnum::Impl(i.into_json(renderer)), StaticItem(s) => ItemEnum::Static(from_clean_static(s, rustc_hir::Safety::Safe, renderer)), @@ -383,23 +402,41 @@ fn from_clean_item(item: &clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum }) } // FIXME(generic_const_items): Add support for generic associated consts. - RequiredAssocConstItem(_generics, ty) => { - ItemEnum::AssocConst { type_: ty.into_json(renderer), value: None } - } + RequiredAssocConstItem(_generics, ty) => ItemEnum::AssocConst { + type_: ty.into_json(renderer), + value: None, + default_unstable: None, + }, // FIXME(generic_const_items): Add support for generic associated consts. - ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst { + ProvidedAssocConstItem(ci) => ItemEnum::AssocConst { type_: ci.type_.into_json(renderer), value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), + }, + ImplAssocConstItem(ci) => ItemEnum::AssocConst { + type_: ci.type_.into_json(renderer), + value: Some(ci.kind.expr(renderer.tcx)), + default_unstable: None, }, RequiredAssocTypeItem(g, b) => ItemEnum::AssocType { generics: g.into_json(renderer), bounds: b.into_json(renderer), type_: None, + default_unstable: None, }, AssocTypeItem(t, b) => ItemEnum::AssocType { generics: t.generics.into_json(renderer), bounds: b.into_json(renderer), type_: Some(t.item_type.as_ref().unwrap_or(&t.type_).into_json(renderer)), + default_unstable: default_body_stability_for_def_id( + renderer.tcx, + item.item_id.expect_def_id(), + ) + .map(|stab| stab.into_json(renderer)), }, // `convert_item` early returns `None` for stripped items, keywords, attributes and // "special" macro rules. @@ -813,6 +850,7 @@ impl FromClean for Impl { pub(crate) fn from_clean_function( clean::Function { decl, generics }: &clean::Function, has_body: bool, + default_unstable: Option>, header: rustc_hir::FnHeader, renderer: &JsonRenderer<'_>, ) -> Function { @@ -821,6 +859,7 @@ pub(crate) fn from_clean_function( generics: generics.into_json(renderer), header: header.into_json(renderer), has_body, + default_unstable, } } @@ -970,6 +1009,17 @@ impl FromClean for ItemKind { } } +fn default_body_stability_for_def_id( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> Option { + let stability = tcx.lookup_default_body_stability(def_id)?; + match stability.level { + hir::StabilityLevel::Unstable { .. } => Some(stability), + hir::StabilityLevel::Stable { .. } => None, + } +} + fn const_stability_for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option { if !tcx.is_conditionally_const(def_id) { // The item cannot be conditionally-const. No const stability here. @@ -1038,6 +1088,7 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) AK::Deprecated { .. } => return Vec::new(), // Handled separately into Item::deprecation. AK::Stability { .. } => return Vec::new(), // Handled separately into Item::stability AK::RustcConstStability { .. } => return Vec::new(), // Handled separately into Item::const_stability. + AK::RustcBodyStability { .. } => return Vec::new(), // Handled separately by `default_unstable`. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), @@ -1122,24 +1173,41 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) for sub_cfg in cfg { ret.push(Attribute::Other(format!("#[doc(cfg({sub_cfg}))]"))); } - for (auto_cfg, _) in auto_cfg { - let kind = match auto_cfg.kind { - HideOrShow::Hide => "hide", - HideOrShow::Show => "show", - }; - let mut out = format!("#[doc(auto_cfg({kind}("); - for (pos, value) in auto_cfg.values.iter().enumerate() { - if pos > 0 { + if !auto_cfg.is_empty() { + let mut out = format!("#[doc(auto_cfg("); + for (index, (auto_cfg, _)) in auto_cfg.iter().enumerate() { + let kind = match auto_cfg.kind { + HideOrShow::Hide => "hide", + HideOrShow::Show => "show", + }; + if index > 0 { out.push_str(", "); } - out.push_str(value.name.as_str()); - if let Some((value, _)) = value.value { - // We use `as_str` and debug display to have characters escaped and `"` - // characters surrounding the string. - out.push_str(&format!(" = {:?}", value.as_str())); + out.push_str(&format!("{kind}(")); + for (name, cfgs) in &auto_cfg.values { + out.push_str(&format!("{name}, values(")); + match cfgs { + DocCfgHideShow::Any(_) => { + out.push_str("any()"); + } + DocCfgHideShow::List(values) => { + for (pos, value) in values.iter().enumerate() { + let separator = if pos > 0 { ", " } else { "" }; + if let Some(value) = &value.value { + // We use `as_str` and debug display to have characters escaped + // and `"` characters surrounding the string. + out.push_str(&format!("{separator}{:?}", value.as_str())); + } else { + out.push_str(&format!("{separator}none()")); + } + } + } + } + out.push_str(")"); } + out.push(')'); } - out.push_str(")))]"); + out.push_str("))]"); ret.push(Attribute::Other(out)); } for (change, _) in auto_cfg_change { diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml index 9e18691a9605a..ba6dc2d69e1ff 100644 --- a/src/rustdoc-json-types/Cargo.toml +++ b/src/rustdoc-json-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustdoc-json-types" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] path = "lib.rs" diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index e6af2fa04bb10..be4921d888278 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -114,8 +114,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Add `Item::const_stability`. -pub const FORMAT_VERSION: u32 = 59; +// Latest feature: Add default-body stability metadata. +pub const FORMAT_VERSION: u32 = 60; /// The root of the emitted JSON blob. /// @@ -288,6 +288,9 @@ pub struct Item { /// - `#[stable]` and `#[unstable]` attributes: see the [`Self::stability`] field instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]` attributes: /// see the [`Self::const_stability`] field instead. + /// - `#[rustc_default_body_unstable]` attributes: instead see `default_unstable` fields on + /// item kinds that can have unstable default values, such as [`Function::default_unstable`], + /// [`ItemEnum::AssocConst::default_unstable`], and [`ItemEnum::AssocType::default_unstable`]. /// /// Attributes appear in pretty-printed Rust form, regardless of their formatting /// in the original source code. For example: @@ -367,6 +370,19 @@ pub enum StabilityLevel { Unstable, } +/// Information about an unstable default provided by a trait item. +/// +/// Example unstable defaults include: +/// - a stable trait function or method whose body is not stable +/// - a stable trait associated type or const whose default value is not stable +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] +#[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] +pub struct ProvidedDefaultUnstable { + /// The feature that must be enabled to use the provided default. + pub feature: String, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "rkyv_0_8", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] #[cfg_attr(feature = "rkyv_0_8", rkyv(derive(Debug)))] @@ -379,6 +395,9 @@ pub enum StabilityLevel { /// - `#[stable]` and `#[unstable]`. These are in [`Item::stability`] instead. /// - `#[rustc_const_stable]` and `#[rustc_const_unstable]`. These are in /// [`Item::const_stability`] instead. +/// - `#[rustc_default_body_unstable]`. These are in the `default_unstable` field on the appropriate +/// item kinds: [`Function::default_unstable`], [`ItemEnum::AssocConst::default_unstable`], +/// and [`ItemEnum::AssocType::default_unstable`]. pub enum Attribute { /// `#[non_exhaustive]` NonExhaustive, @@ -875,6 +894,11 @@ pub enum ItemEnum { /// // ^^^^^^^^^^ /// ``` value: Option, + /// Metadata about an unstable default value provided for the associated constant, if any. + /// + /// Empty if the associated constant has no default (see [`ItemEnum::AssocConst::value`]), + /// or if the default value is stable. + default_unstable: Option>, }, /// An associated type of a trait or a type. AssocType { @@ -899,6 +923,11 @@ pub enum ItemEnum { /// ``` #[serde(rename = "type")] type_: Option, + /// Metadata about an unstable default value provided for the associated type, if any. + /// + /// Empty if the associated type has no default (see [`ItemEnum::AssocType::type_`]), + /// or if the default value is stable. + default_unstable: Option>, }, } @@ -1188,6 +1217,12 @@ pub struct Function { pub header: FunctionHeader, /// Whether the function has a body, i.e. an implementation. pub has_body: bool, + /// Metadata about a possible unstable provided default implementation for trait methods. + /// + /// Only populated for function items inside traits. Empty if the trait method + /// does not have a default implementation (see [`Function::has_body`]), + /// or if its default implementation is stable. + pub default_unstable: Option>, } /// Generic parameters accepted by an item and `where` clauses imposed on it and the parameters. diff --git a/src/tools/cargo b/src/tools/cargo index a595d0da21f22..a335d47ff8036 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit a595d0da21f228b7fdae64d3d5c0e527ea66bb59 +Subproject commit a335d47ff8036918d3d548dabd513dc0444096a9 diff --git a/src/tools/clippy/.github/FUNDING.yml b/src/tools/clippy/.github/FUNDING.yml new file mode 100644 index 0000000000000..16ac7b6eb9bc9 --- /dev/null +++ b/src/tools/clippy/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: rustfoundation +custom: [ "rust-lang.org/funding" ] diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 3a99d65233d38..cd90bb0a25f5f 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index b612ea4611a9f..7d313ece05b80 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -34,7 +34,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -112,7 +112,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -168,7 +168,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index f9e882d9757ce..5658e041e03cf 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -24,7 +24,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 872931160c35f..b95cbbddd4b32 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -25,13 +25,13 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 981fecd17c988..210f10b7b8804 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 2 # Unsetting this would make so that any malicious package could get our Github Token @@ -80,7 +80,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false @@ -113,7 +113,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index d4dc80efe79de..532af9dff83f2 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -14,7 +14,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 337c900aaf0c8..689d56d0fb7c6 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6670,6 +6670,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`by_ref_peekable_peek`]: https://rust-lang.github.io/rust-clippy/master/index.html#by_ref_peekable_peek [`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth @@ -6694,6 +6695,7 @@ Released 2018-09-13 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions +[`chunks_exact_to_as_chunks`]: https://rust-lang.github.io/rust-clippy/master/index.html#chunks_exact_to_as_chunks [`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy @@ -7457,6 +7459,7 @@ Released 2018-09-13 [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_trailing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_trailing_comma [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_unwrap_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap_unchecked [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern @@ -7571,6 +7574,7 @@ Released 2018-09-13 [`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish +[`check-grouped-late-init`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-grouped-late-init [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items @@ -7608,7 +7612,6 @@ Released 2018-09-13 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit -[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/book/src/continuous_integration/github_actions.md b/src/tools/clippy/book/src/continuous_integration/github_actions.md index bed0f66bab33a..e198a13b38d24 100644 --- a/src/tools/clippy/book/src/continuous_integration/github_actions.md +++ b/src/tools/clippy/book/src/continuous_integration/github_actions.md @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index e123d7ba5704d..6c6ccd4747c1f 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -452,6 +452,37 @@ For internal testing only, ignores the current `publish` settings in the Cargo m * [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) +## `check-grouped-late-init` +Whether to check for grouped late initializations from multiple `let` statements. + +#### Example +```rust +let a; +let b; +if true { + a = 1; + b = 2; +} else { + a = 3; + b = 4; +} +``` +Use instead: +```rust +let (a, b) = if true { + (1, 2) +} else { + (3, 4) +}; +``` + +**Default Value:** `true` + +--- +**Affected lints:** +* [`needless_late_init`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init) + + ## `check-incompatible-msrv-in-tests` Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. @@ -986,28 +1017,6 @@ The minimum size (in bytes) to consider a type for passing by reference instead * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) -## `profiles` -Named profiles of disallowed items (unrelated to Cargo build profiles). - -#### Example - -```toml -[profiles.persistent] -disallowed-methods = [{ path = "std::env::temp_dir" }] -disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - -[profiles.single_threaded] -disallowed-methods = [{ path = "std::thread::spawn" }] -``` - -**Default Value:** `{}` - ---- -**Affected lints:** -* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) -* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) - - ## `pub-underscore-fields-behavior` Lint "public" fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as "pub". diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 47bc7b572c9d0..d910eb47752b5 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,13 +1,12 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher, - MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -223,74 +222,12 @@ macro_rules! deserialize { }}; } -macro_rules! parse_conf_value { - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - profiles @[$($profiles:expr)?], - disallowed @[$($disallowed:expr)?] - ) => { - parse_conf_value_impl!( - $map, - $ty, - $errors, - $file, - $field_span, - ($($profiles)?), - ($($disallowed)?) - ) - }; -} - -macro_rules! parse_conf_value_impl { - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {{ - let raw_value = $map.next_value::()?; - let value_span = $field_span.clone(); - let toml::Value::Table(table) = raw_value else { - $errors.push(ConfError::spanned( - $file, - "expected table with named profiles", - None, - value_span.clone(), - )); - continue; - }; - - let map = parse_profiles(table, $file, value_span.clone(), &mut $errors); - - (map, value_span) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file, $disallowed) - }}; - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - ($profiles:expr), - ($disallowed:expr) - ) => { - compile_error!("field cannot specify both profiles and disallowed-paths attributes") - }; -} - macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? - $(#[profiles = $profiles:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -345,20 +282,10 @@ macro_rules! define_Conf { match field { $(Field::$name => { - let field_span = name.span(); // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = parse_conf_value!( - map, - $ty, - errors, - self.0, - field_span, - // Disallowed-profile table parsing is special-cased to preserve spans for - // diagnostics in disallowed-path entries. - profiles @[$($profiles)?], - disallowed @[$($replacements_allowed)?] - ); + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -415,121 +342,6 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { ) } -fn parse_profiles( - table: toml::value::Table, - file: &SourceFile, - value_span: Range, - errors: &mut Vec, -) -> FxHashMap { - let mut profiles = FxHashMap::default(); - let config_span = span_from_toml_range(file, value_span.clone()); - - for (profile_name, profile_value) in table { - let toml::Value::Table(mut profile_table) = profile_value else { - errors.push(ConfError::spanned( - file, - format!("invalid profile `{profile_name}`: expected table"), - None, - value_span.clone(), - )); - continue; - }; - - let disallowed_methods = match profile_table - .remove("disallowed-methods") - .or_else(|| profile_table.remove("disallowed_methods")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-methods", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - let disallowed_types = match profile_table - .remove("disallowed-types") - .or_else(|| profile_table.remove("disallowed_types")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-types", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - if !profile_table.is_empty() { - let keys = profile_table.keys().map(String::as_str).collect::>().join(", "); - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}` has unknown keys: {keys}"), - None, - value_span.clone(), - )); - } - - profiles.insert( - profile_name, - DisallowedProfile { - disallowed_methods, - disallowed_types, - }, - ); - } - - profiles -} - -fn parse_profile_list( - file: &SourceFile, - profile_name: &str, - key_name: &str, - value: toml::Value, - value_span: Range, - config_span: Span, - errors: &mut Vec, -) -> Vec { - let toml::Value::Array(entries) = value else { - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}`: `{key_name}` must be an array"), - None, - value_span, - )); - return Vec::new(); - }; - - let mut disallowed = Vec::with_capacity(entries.len()); - for entry in entries { - match DisallowedPath::deserialize(entry.clone()) { - Ok(mut path) => { - path.set_span(config_span); - disallowed.push(path); - }, - Err(err) => errors.push(ConfError::spanned( - file, - format!( - "profile `{profile_name}`: {}", - err.to_string().replace('\n', " ").trim() - ), - None, - value_span.clone(), - )), - } - } - - disallowed -} - define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -742,6 +554,30 @@ define_Conf! { /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. #[lints(cargo_common_metadata)] cargo_ignore_publish: bool = false, + /// Whether to check for grouped late initializations from multiple `let` statements. + /// + /// #### Example + /// ```rust + /// let a; + /// let b; + /// if true { + /// a = 1; + /// b = 2; + /// } else { + /// a = 3; + /// b = 4; + /// } + /// ``` + /// Use instead: + /// ```rust + /// let (a, b) = if true { + /// (1, 2) + /// } else { + /// (3, 4) + /// }; + /// ``` + #[lints(needless_late_init)] + check_grouped_late_init: bool = true, /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, @@ -1029,21 +865,6 @@ define_Conf! { /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] pass_by_value_size_limit: u64 = 256, - /// Named profiles of disallowed items (unrelated to Cargo build profiles). - /// - /// #### Example - /// - /// ```toml - /// [profiles.persistent] - /// disallowed-methods = [{ path = "std::env::temp_dir" }] - /// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - /// - /// [profiles.single_threaded] - /// disallowed-methods = [{ path = "std::thread::spawn" }] - /// ``` - #[profiles = true] - #[lints(disallowed_methods, disallowed_types)] - profiles: FxHashMap = FxHashMap::default(), /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 5eaa44ddd51d7..8d9326a904b1e 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -57,15 +57,6 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, - #[serde(default, alias = "disallowed_types")] - pub disallowed_types: Vec, -} - // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index fd515939dfb8e..cf2059eecc079 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -12,7 +12,7 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for usage of items through absolute paths, like `std::env::current_dir`. + /// Checks for usage of items through absolute paths, like `std::f64::consts::PI`. /// /// ### Why restrict this? /// Many codebases have their own style when it comes to importing, but one that is seldom used diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 6e57d0608bed8..118c3c23d727d 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -52,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, } && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) - && is_const_evaluatable(cx, condition) + // Check if the whole expression can be moved into a const context. + // Note that const eval can evaluate things which cannot be moved (e.g. `false && x`). + && is_const_evaluatable(cx.tcx, cx.typeck_results(), condition) && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs index 7eff5eccfa138..91e2689a14731 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, NON_MINIMAL_CFG}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::{MetaItemInner, MetaItemKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -29,7 +29,7 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) { meta.span, "unneeded sub `cfg` when there is only one condition", |diag| { - if let Some(snippet) = list[0].span().get_source_text(cx) { + if let Some(snippet) = list[0].span().get_text(cx) { diag.span_suggestion( meta.span, "try", diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 6efc931df2429..4c97c03471aa9 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::AttrStyle; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check( return; } if nb_items == clippy_lints.len() { - if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) { + if let Some(snippet) = behind_cfg_attr.span.get_text(cx) { span_lint_and_sugg( cx, UNNECESSARY_CLIPPY_CFG, @@ -48,7 +48,7 @@ pub(super) fn check( ); } } else { - let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(","); + let snippet = clippy_lints.iter().filter_map(|sp| sp.get_text(cx)).join(","); span_lint_and_note( cx, UNNECESSARY_CLIPPY_CFG, diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 2d56086a96024..60cdcd4a85817 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::USELESS_ATTRIBUTE; use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, first_line_of_span}; +use clippy_utils::source::{SpanExt, first_line_of_span}; use clippy_utils::sym; use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { } let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) + if let Some(src) = line_span.get_text(cx) && src.contains("#[") { #[expect(clippy::collapsible_span_lint_calls)] diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 2f2382d9a50ed..efd453b3276cc 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; @@ -157,30 +157,30 @@ fn check_inverted_bool_in_condition( let suggestion = match (left.kind, right.kind) { (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (ExprKind::Unary(UnOp::Not, left_sub), _) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right.span.get_source_text(cx) else { + let Some(right) = right.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (_, ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left.span.get_source_text(cx) else { + let Some(left) = left.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; @@ -392,12 +392,8 @@ impl SuggestContext<'_, '_, '_> { } }, &Term(n) => { - self.output.push_str( - &self.terminals[n as usize] - .span - .source_callsite() - .get_source_text(self.cx)?, - ); + self.output + .push_str(&self.terminals[n as usize].span.source_callsite().get_text(self.cx)?); }, } Some(()) @@ -452,10 +448,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio .map(|arg| simplify_not(cx, curr_msrv, arg)) .collect::>>()? .join(", "); - Some(format!( - "{}.{neg_method}({negated_args})", - receiver.span.get_source_text(cx)? - )) + Some(format!("{}.{neg_method}({negated_args})", receiver.span.get_text(cx)?)) }) }, ExprKind::Closure(closure) => { @@ -463,13 +456,13 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let params = body .params .iter() - .map(|param| param.span.get_source_text(cx).map(|t| t.to_string())) + .map(|param| param.span.get_text(cx).map(|t| t.to_string())) .collect::>>()? .join(", "); let negated = simplify_not(cx, curr_msrv, body.value)?; Some(format!("|{params}| {negated}")) }, - ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()), + ExprKind::Unary(UnOp::Not, expr) => expr.span.get_text(cx).map(|t| t.to_string()), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 947b99696bb1f..13ae80868d9b5 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -1,6 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::{ get_enclosing_closure, get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable, @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // If the new borrow might be itself borrowed mutably and the original reference is not a temporary // value, do not propose to use it directly. && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) - && let Some(deref_text) = deref_target.span.get_source_text(cx) + && let Some(deref_text) = deref_target.span.get_text(cx) { // `&*x` can be needed to shorten the borrow of `x`. Replacing it with `x` can be // incorrect when `x` is a closure-captured upvar (e.g. a closure returning another diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index adc43e282fc5c..f23e789cf5d64 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity().skip_norm_wip() && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() - && let Some(recv) = receiver.span.get_source_text(cx) + && let Some(recv) = receiver.span.get_text(cx) { // `as_mut_ptr` might not exist let applicability = Applicability::MaybeIncorrect; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index c924fba6b5c85..d2bf6db751320 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; @@ -40,7 +40,7 @@ pub(super) fn check( diag.help("an `as` cast can become silently lossy if the types change in the future"); let mut applicability = Applicability::MachineApplicable; let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "", &mut applicability); - let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else { + let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_text(cx) else { return; }; match cast_to_hir.kind { diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index 55b0945f0962b..36d63ce63195b 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs b/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs index 844d4c7acbe7f..5cfbffc6c3f66 100644 --- a/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs @@ -249,7 +249,7 @@ fn can_coerce_to_target_type(expr: &Expr<'_>) -> bool { fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) { let mut usages = Vec::new(); - for_each_expr(cx, body.value, |expr| { + for_each_expr(cx.tcx, body.value, |expr| { if let ExprKind::Path(ref qpath) = expr.kind && !expr.span.from_expansion() && let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 333f31ba00eac..c14bd056de393 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::res::MaybeResPath as _; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym}; @@ -113,7 +113,7 @@ pub(super) fn check<'tcx>( let literal_str = &cast_str; if let LitKind::Int(n, _) = lit.node - && let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(src) = cast_expr.span.get_text(cx) && cast_to.is_floating_point() && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && let from_nbits = 128 - n.get().leading_zeros() @@ -140,7 +140,7 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) + if let Some(src) = cast_expr.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); @@ -232,7 +232,7 @@ fn lint_unnecessary_cast( // (-1).foo() instead of -1.foo()) let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(..) = parent_expr.kind - && literal_str.starts_with('-') + && literal_str.starts_with(['-', '!']) { format!("({literal_str}_{cast_to})") } else { @@ -253,7 +253,7 @@ fn lint_unnecessary_cast( fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option { match expr.kind { ExprKind::Lit(lit) => Some(lit), - ExprKind::Unary(UnOp::Neg, e) => { + ExprKind::Unary(UnOp::Neg | UnOp::Not, e) => { if let ExprKind::Lit(lit) = e.kind { Some(lit) } else { 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 f4738e7b0d51d..90290caf10bf7 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; @@ -21,7 +21,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_> let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs index 4f663f4aa9098..24955ac3c6258 100644 --- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -64,15 +64,8 @@ impl<'a> ClonedRefToSliceRefs<'a> { impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if self.msrv.meets(cx, { - if is_in_const_context(cx) { - msrvs::CONST_SLICE_FROM_REF - } else { - msrvs::SLICE_FROM_REF - } - }) - // `&[foo.clone()]` expressions - && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // `&[foo.clone()]` expressions + if let ExprKind::AddrOf(_, mutability, arr) = &expr.kind // mutable references would have a different meaning && mutability.is_not() @@ -81,10 +74,18 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(path, recv, _, _) = item.kind + + && self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) && let Some(adjustment) = is_needless_clone_or_equivalent(cx, recv, path.ident.name, item.hir_id) // check for immutability or purity - && (!is_mutable(cx, recv) || is_const_evaluatable(cx, recv)) + && (!is_mutable(cx, recv) || is_const_evaluatable(cx.tcx, cx.typeck_results(), recv)) // get appropriate crate for `slice::from_ref` && let Some(builtin_crate) = clippy_utils::std_or_core(cx) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 95b99c1d2435f..c330ed3d5a0a4 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index baa13b2133277..6def41b775d1d 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::source::{IntoSpan as _, SpanExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; @@ -319,7 +319,7 @@ pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { } } -fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Option { +fn span_extract_keyword(cx: &LateContext<'_>, span: Span, keyword: &str) -> Option { span.with_source_text(cx, |snippet| { tokenize_with_text(snippet) .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) @@ -335,7 +335,7 @@ fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Opti } /// Peel the parentheses from an `if` expression, e.g. `((if true {} else {}))`. -pub(super) fn peel_parens(cx: &impl HasSession, mut span: Span) -> (Span, Span, Span) { +pub(super) fn peel_parens(cx: &LateContext<'_>, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index ddd3e8b805d19..4ddd396c50ce4 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -79,7 +79,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI let mut has_read_access = false; // Inspect all expressions and sub-expressions in the block. - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { // Ignore expressions that are not simply `id`. if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 6daa70f390aaa..51a848d022d80 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -361,11 +361,13 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BY_REF_PEEKABLE_PEEK_INFO, crate::methods::BYTES_COUNT_TO_LEN_INFO, crate::methods::BYTES_NTH_INFO, crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CHUNKS_EXACT_TO_AS_CHUNKS_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, @@ -507,6 +509,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNNECESSARY_UNWRAP_UNCHECKED_INFO, crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index c831f96443c60..9e141bd945db0 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_ty_alias; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use hir::ExprKind; use hir::def::Res; use rustc_errors::Applicability; @@ -78,7 +78,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.check_source_text(cx, |s| s.starts_with('<')) { + if expr.span.check_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 58cc318d57ccd..e2fd71b7d990f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,18 +1,13 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -60,19 +55,6 @@ declare_clippy_lint! { /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` - /// - /// Disallowed profiles allow scoping different disallow lists: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-methods = [{ path = "crate::devices::Buffer::copy_to_host", reason = "Forward code must not touch host buffers" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn evaluate() { - /// // Method calls in this function use the `forward_pass` profile. - /// } - /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, style, @@ -82,22 +64,12 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); pub struct DisallowedMethods { - default: DefIdMap<(&'static str, &'static DisallowedPath)>, - /// Lookup per profile that declares a non-empty `disallowed_methods` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_methods` entries are absent here. - profiles: FxHashMap>, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_types`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, } impl DisallowedMethods { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (default, _) = create_disallowed_map( + let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -110,62 +82,7 @@ impl DisallowedMethods { "function", false, ); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_methods.as_slice(); - if paths.is_empty() { - continue; - } - - let (map, _) = create_disallowed_map( - tcx, - paths, - PathNS::Value, - |def_kind| { - matches!( - def_kind, - DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn - ) - }, - "function", - false, - ); - profiles.insert(symbol, map); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_METHODS, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_methods`", - entry.name - ), - ); - } + Self { disallowed } } } @@ -181,43 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, expr.hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, &(path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|map| map.get(&id).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_METHODS, - span, - format!("use of a disallowed method `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some(&(path, disallowed_path)) = self.default.get(&id) { - let diag_amendment = disallowed_path.diag_amendment(span); + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - |diag| diag_amendment(diag), + disallowed_path.diag_amendment(span), ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 37dd605617a7a..2c520d053f439 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,18 +1,15 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -54,17 +51,6 @@ declare_clippy_lint! { /// // A similar type that is allowed by the config /// use std::collections::HashMap; /// ``` - /// - /// Disallowed profiles can scope lists to specific modules: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-types = [{ path = "crate::buffers::HostBuffer", reason = "Prefer device buffers in forward computations" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn forward_step(buffer: crate::buffers::DeviceBuffer) { /* ... */ } - /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, style, @@ -73,127 +59,37 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); -struct TypeLookup { +pub struct DisallowedTypes { def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, prim_tys: FxHashMap, } -impl TypeLookup { - fn from_config(tcx: TyCtxt<'_>, paths: &'static [DisallowedPath]) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, paths, PathNS::Type, def_kind_predicate, "type", true); - Self { def_ids, prim_tys } - } - - fn find(&self, res: &Res) -> Option<(&'static str, &'static DisallowedPath)> { - match res { - Res::Def(_, did) => self.def_ids.get(did).copied(), - Res::PrimTy(prim) => self.prim_tys.get(prim).copied(), - _ => None, - } - } -} - -pub struct DisallowedTypes { - default: TypeLookup, - /// Lookup per profile that declares a non-empty `disallowed_types` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_types` entries are absent here. - profiles: FxHashMap, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_methods`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, -} - impl DisallowedTypes { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let default = TypeLookup::from_config(tcx, &conf.disallowed_types); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_types.as_slice(); - if paths.is_empty() { - continue; - } - profiles.insert(symbol, TypeLookup::from_config(tcx, paths)); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_TYPES, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_types`", - entry.name - ), - ); - } + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); + Self { def_ids, prim_tys } } - fn check_res_emit(&mut self, cx: &LateContext<'_>, hir_id: rustc_hir::HirId, res: &Res, span: Span) { - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, (path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|lookup| lookup.find(res).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some((path, disallowed_path)) = self.default.find(res) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - |diag| diag_amendment(diag), - ); - } + fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { + let (path, disallowed_path) = match res { + Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, + Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, + _ => return, + }; + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + disallowed_path.diag_amendment(span), + ); } } @@ -215,22 +111,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind && let Some(res) = path.res.type_ns { - self.check_res_emit(cx, item.hir_id(), &res, item.span); + self.check_res_emit(cx, &res, item.span); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { - self.check_res_emit(cx, ty.hir_id, &cx.qpath_res(path, ty.hir_id), ty.span); + self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); } } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - self.check_res_emit( - cx, - poly.trait_ref.hir_ref_id, - &poly.trait_ref.path.res, - poly.trait_ref.path.span, - ); + self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); } } 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 b164a9a99782b..4139df9cd7997 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -99,7 +99,7 @@ pub fn check( 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| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body_id), |expr| { if is_inside_always_const_context(cx.tcx, expr.hir_id) { return ControlFlow::::Continue(()); } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 81da571bdd601..c5816cb8b2d63 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1124,6 +1124,9 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range Gap<'a> { let prev_stop = prev_chunk.last()?; let next_stop = next_chunk.first()?; let gap_span = prev_stop.span.between(next_stop.span); - let gap_snippet = gap_span.get_source_text(cx)?; + let gap_snippet = gap_span.get_text(cx)?; let mut has_comment = false; let mut empty_lines = Vec::new(); diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index ee1be48417222..b01745008510c 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -1,6 +1,6 @@ use clippy_utils::attrs::span_contains_cfg; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::span_contains_non_whitespace; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_errors::Applicability; @@ -192,7 +192,7 @@ impl LateLintPass<'_> for EmptyWithBrackets { // Span of the parentheses in variant definition let span = variant.span.with_lo(variant.ident.span.hi()); let span_inner = span - .with_lo(SpanRangeExt::trim_start(span, cx).start + BytePos(1)) + .with_lo(SpanExt::trim_start(span, cx).start + BytePos(1)) .with_hi(span.hi() - BytePos(1)); if span_contains_non_whitespace(cx, span_inner, false) { continue; diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index a24cc957df070..cba40abf04e17 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -604,7 +604,7 @@ fn is_any_expr_in_map_used<'tcx>( map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>, ) -> bool { - for_each_expr(cx, map, |e| { + for_each_expr(cx.tcx, map, |e| { if spanless_eq.eq_expr(ctxt, e, expr) { return ControlFlow::Break(()); } diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 93a6dc59993a6..a6f80da5dab9c 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -102,7 +102,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { fn emit_sugg(&self, spans: Vec, msg: String, help: &'static str) { let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect(); span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| { - diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion(help, suggestions, Applicability::MaybeIncorrect); }); } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs index 888d5b7b762a0..eb96e65638445 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs @@ -91,7 +91,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "multiply and add expressions can be calculated more efficiently and accurately", + "multiply and add expressions may be calculated more efficiently and accurately", |diag| { let maybe_neg_sugg = |expr, app: &mut _| { let sugg = Sugg::hir_with_applicability(cx, expr, "_", app); @@ -120,6 +120,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { }, app, ); + diag.note_once("the performance gain from `mul_add` may vary depending on the target architecture"); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 2691fb4766397..f13278d91ce7f 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, first_node_in_macro, matching_root_macro_call}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), ([], [_]) => { // Simulate macro expansion, converting {{ and }} to { and }. - let Some(snippet) = format_args.span.get_source_text(cx) else { + let Some(snippet) = format_args.span.get_text(cx) else { return; }; let s_expand = snippet.replace("{{", "{").replace("}}", "}"); diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 05145456071c0..9014302fb8ea6 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -10,7 +10,7 @@ use clippy_utils::macros::{ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet, snippet_opt}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, is_in_test, peel_hir_expr_while, sym, trait_ref_of_method}; use itertools::Itertools; @@ -380,7 +380,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { /// Check if there is a comma after the last format macro arg. fn check_trailing_comma(&self) { let span = self.macro_call.span; - if let Some(src) = span.get_source_text(self.cx) + if let Some(src) = span.get_text(self.cx) && let Some(src) = src.strip_suffix([')', ']', '}']) && let src = src.trim_end_matches(|c: char| c.is_whitespace() && c != '\n') && let Some(src) = src.strip_suffix(',') @@ -694,7 +694,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.source_callsite().get_text(cx) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 57c1dd2eb72b0..c64e742592080 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::Item; @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { // If the comment contains a bare CR (not followed by a LF), do not propose an auto-fix // as bare CR are not allowed in doc comments. - if span.check_source_text(cx, contains_bare_cr) { + if span.check_text(cx, contains_bare_cr) { diag.help(msg) .note("bare CR characters are not allowed in doc comments"); return; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 64b5a55deafa6..c2836741b969e 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ @@ -197,8 +197,8 @@ fn convert_to_from( return None; }; - let from = self_ty.span.get_source_text(cx)?; - let into = target_ty.span.get_source_text(cx)?; + let from = self_ty.span.get_text(cx)?; + let into = target_ty.span.get_text(cx)?; let mut suggestions = vec![ // impl Into for U -> impl From for U diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 39b53e02831c4..1dc23df6706ca 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -163,12 +163,11 @@ fn check_needless_must_use( ); } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type - if sig.header.is_async() { - if let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) - && !is_must_use_ty(cx, future_ty) - { - return; - } + if sig.header.is_async() + && let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) + && !is_must_use_ty(cx, future_ty) + { + return; } span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index e49dee4164b88..f3952b2687a56 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -50,7 +50,7 @@ fn check_raw_ptr<'tcx>( if !raw_ptrs.is_empty() { let typeck = cx.tcx.typeck_body(body.id()); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { match e.kind { hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs index 33eede8e65ac9..a5fed9f03a620 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -23,7 +23,7 @@ pub(super) fn check_fn( } let mut line_count: u64 = 0; - let too_many = body.value.span.check_source_text(cx, |src| { + let too_many = body.value.span.check_text(cx, |src| { let mut in_comment = false; let mut code_in_line; diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index ff22ba4fcd0d6..ea1992d471312 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::is_zero_integer_const; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_else_clause; -use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -104,28 +104,27 @@ impl LateLintPass<'_> for IfNotElse { } } -fn make_sugg<'a>( - sess: &impl HasSession, +fn make_sugg( + cx: &LateContext<'_>, expr_span: Span, - cond_kind: &'a ExprKind<'a>, + cond_kind: &ExprKind<'_>, cond_inner: Span, els_span: Span, - default: &'a str, + default: &str, applicability: &mut Applicability, ) -> String { - let (cond_inner_snip, _) = snippet_with_context(sess, cond_inner, expr_span.ctxt(), default, applicability); - let (els_snip, _) = snippet_with_context(sess, els_span, expr_span.ctxt(), default, applicability); - let indent = indent_of(sess, expr_span); - + let (cond_inner_snip, _) = snippet_with_context(cx, cond_inner, expr_span.ctxt(), default, applicability); + let (els_snip, _) = snippet_with_context(cx, els_span, expr_span.ctxt(), default, applicability); + let indent = indent_of(cx, expr_span); let suggestion = match cond_kind { ExprKind::Unary(UnOp::Not, cond_rest) => { let (cond_rest_snip, _) = - snippet_with_context(sess, cond_rest.span, expr_span.ctxt(), default, applicability); + snippet_with_context(cx, cond_rest.span, expr_span.ctxt(), default, applicability); format!("if {cond_rest_snip} {els_snip} else {cond_inner_snip}") }, ExprKind::Binary(_, lhs, rhs) => { - let (lhs_snip, _) = snippet_with_context(sess, lhs.span, expr_span.ctxt(), default, applicability); - let (rhs_snip, _) = snippet_with_context(sess, rhs.span, expr_span.ctxt(), default, applicability); + let (lhs_snip, _) = snippet_with_context(cx, lhs.span, expr_span.ctxt(), default, applicability); + let (rhs_snip, _) = snippet_with_context(cx, rhs.span, expr_span.ctxt(), default, applicability); format!("if {lhs_snip} == {rhs_snip} {els_snip} else {cond_inner_snip}") }, diff --git a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs index 06ebd3ac7f5fd..2701cdaa394da 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 70176c62772b7..bf2c7a007644c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet, snippet_with_context}; use clippy_utils::sym; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 1ae8198a432dd..99e7d202fe46f 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -193,10 +193,11 @@ impl IncompatibleMsrv { } } - if (self.check_in_tests || !is_in_test(cx.tcx, node)) - && let Some(current) = self.msrv.current(cx) + // Check `is_in_test` last as it walks the HIR parent chain. + if let Some(current) = self.msrv.current(cx) && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) && version > current + && (self.check_in_tests || !is_in_test(cx.tcx, node)) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index bc57d9e85478a..aae847031bf64 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs index 3ffa23dcd6046..87e63e8732fe8 100644 --- a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, snippet}; +use clippy_utils::source::snippet; use clippy_utils::sym; use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{HasAttrs as _, NodeId}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index dd63de288b87f..f83bcb3cf4076 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{HirId, Item, ItemKind, Mod}; @@ -99,7 +99,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule { if let Some(prev) = mod_pos.checked_sub(1) && let prev = cx.tcx.hir_item(module.item_ids[prev]) && let items_span = last.span.with_lo(test_mod.span.hi()) - && let Some(items) = items_span.get_source_text(cx) + && let Some(items) = items_span.get_text(cx) { diag.multipart_suggestion_with_style( "move the items to before the test module was defined", diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs index 7cf594384b5a2..dec3f3bcae895 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs @@ -2,7 +2,7 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt}; +use clippy_utils::source::SpanExt; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test}; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; @@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { // TODO: Is there a cleaner, robust way to ask this question? // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", // and that doesn't get us the true name in scope rather than the span text either. - if let Some(name) = local_span.get_source_text(cx) + if let Some(name) = local_span.get_text(cx) && is_ident(&name) { // If the local is an ordinary named variable, @@ -228,11 +228,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { if fn_span.from_expansion() { // Don't lint on the main function generated by `--test` target - if cx.sess().is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { + if cx.tcx.sess.is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { return; } - let is_from_external_macro = fn_span.in_external_macro(cx.sess().source_map()); + let is_from_external_macro = fn_span.in_external_macro(cx.tcx.sess.source_map()); span_lint_and_then( cx, LARGE_STACK_FRAMES, diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 838abc81ce449..48e1414defadf 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_from_proc_macro, sym}; use hir::def_id::DefId; use rustc_errors::Applicability; @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) - && let Some(mod_name) = ty.span.get_source_text(cx) + && let Some(mod_name) = ty.span.get_text(cx) && ty.span.eq_ctxt(last_segment.ident.span) { let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index d4d357ad5d869..9b54da5b020e7 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; use clippy_utils::{parent_item_name, peel_ref_operators, sym}; @@ -261,7 +261,7 @@ impl LenZero { } fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return span; }; if has_enclosing_paren(snippet) { diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 984574c221fb6..3f8bd2223876f 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -162,7 +162,8 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { ); }, ); - } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + } else if local.ty.is_none() + && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 7f2687664c686..3f4b59382ca15 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a0a5558b42eac..a65b27f550995 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -454,7 +454,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. - store.register_pre_expansion_pass( + store.register_pre_expansion_lint_pass( Box::new(move || Box::new(attrs::EarlyAttributes::new(conf))) ); @@ -464,12 +464,12 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co { let format_args = format_args_storage.clone(); let attrs = attr_storage.clone(); - store.early_passes.push(Box::new(move || { + store.register_early_lint_pass(Box::new(move || { Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone())) })); } - store.late_passes.push(Box::new(move |tcx: TyCtxt<'_>| { + store.register_late_lint_pass(Box::new(move |tcx: TyCtxt<'_>| { let skippable_lints = tcx.skippable_lints(()); let is_active = |lints: &rustc_lint::LintVec| is_lint_pass_required(skippable_lints, lints); Box::new(CombinedLateLintPass::new( @@ -531,7 +531,6 @@ rustc_lint::early_lint_methods!( PartialPubFields: partial_pub_fields::PartialPubFields = partial_pub_fields::PartialPubFields, UnderscoreTyped: let_with_type_underscore::UnderscoreTyped = let_with_type_underscore::UnderscoreTyped, ExcessiveNesting: excessive_nesting::ExcessiveNesting = excessive_nesting::ExcessiveNesting::new(conf), - RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, NeedlessElse: needless_else::NeedlessElse = needless_else::NeedlessElse, RawStrings: raw_strings::RawStrings = raw_strings::RawStrings::new(conf), Visibility: visibility::Visibility = visibility::Visibility, @@ -729,7 +728,7 @@ rustc_lint::late_lint_methods!( UndocumentedUnsafeBlocks: undocumented_unsafe_blocks::UndocumentedUnsafeBlocks = undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf), FormatArgs: format_args::FormatArgs<'tcx> = format_args::FormatArgs::new(tcx, conf, format_args.clone()), TrailingEmptyArray: trailing_empty_array::TrailingEmptyArray = trailing_empty_array::TrailingEmptyArray, - NeedlessLateInit: needless_late_init::NeedlessLateInit = needless_late_init::NeedlessLateInit, + NeedlessLateInit: needless_late_init::NeedlessLateInit<'tcx> = needless_late_init::NeedlessLateInit::new(conf), ReturnSelfNotMustUse: return_self_not_must_use::ReturnSelfNotMustUse = return_self_not_must_use::ReturnSelfNotMustUse, NumberedFields: init_numbered_fields::NumberedFields = init_numbered_fields::NumberedFields, ManualBits: manual_bits::ManualBits = manual_bits::ManualBits::new(conf), @@ -859,6 +858,7 @@ rustc_lint::late_lint_methods!( ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, + RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, // add late passes here, used by `cargo dev new_lint` ]] ); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 2b4828872b202..3e58448af5266 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -161,7 +161,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly_trait_ref: &'tcx PolyTraitRef<'tcx>) { - report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + if !poly_trait_ref.span.from_expansion() { + report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 2ec046c8d6ba4..4e7ff879ae590 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -207,7 +207,9 @@ pub struct LiteralDigitGrouping { impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // `NumericLiteral::from_lit_kind` only accepts integer and float literals. if let ExprKind::Lit(lit) = expr.kind + && matches!(lit.kind, token::LitKind::Integer | token::LitKind::Float) && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -226,7 +228,7 @@ impl LiteralDigitGrouping { } fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if let Some(src) = span.get_source_text(cx) + if let Some(src) = span.get_text(cx) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) { @@ -418,7 +420,9 @@ pub struct DecimalLiteralRepresentation { impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // Only integer tokens can produce `LitKind::Int`. if let ExprKind::Lit(lit) = expr.kind + && lit.kind == token::LitKind::Integer && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -436,10 +440,10 @@ impl DecimalLiteralRepresentation { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind - && let Some(src) = span.get_source_text(cx) + && val >= u128::from(self.threshold) + && let Some(src) = span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && num_lit.radix == Radix::Decimal - && val >= u128::from(self.threshold) { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 41022e6a5321b..f4d71d0524b22 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr && let PatKind::Binding(_, binding_id, ..) = pat.kind { // Destructured iterator element `(idx, _)`, look for uses of the binding - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr }); } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { // Bound as a tuple, look for `tup.0` - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index ffc6f7186922a..ceaf681088399 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -13,6 +13,7 @@ use rustc_hir::QPath::Resolved; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::{Spanned, sym}; use super::MANUAL_SLICE_FILL; @@ -84,6 +85,24 @@ pub(super) fn check<'tcx>( { sugg(cx, body, expr, recv_path.span, assignval.span); } + // `for slot in s { *slot = value; }` where `s` is already `&mut [T; N]` + else if let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind + && let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind + && let Res::Local(local) = slice_path.res + && local == pat.hir_id + && !assignval.span.from_expansion() + && switch_to_eager_eval(cx, assignval) + && !is_local_used(cx, assignval, local) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && let ty::Ref(_, inner_ty, rustc_ast::Mutability::Mut) = arg_ty.kind() + && is_slice_like(cx, *inner_ty) + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *inner_ty, clone_trait, &[]) + && msrv.meets(cx, msrvs::SLICE_FILL) + { + sugg(cx, body, expr, arg.span, assignval.span); + } } fn sugg<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index 4ecfadbf9d494..b80efdae9294c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use clippy_utils::{expr_or_init, pat_is_wild, sym}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Pat, PatKind, TyKind}; diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index bee3b19b597c0..161834914b309 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block}; +use clippy_utils::source::{SpanExt, position_before_rarrow, snippet_block}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -76,8 +76,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { "this function can be simplified using the `async fn` syntax", |diag| { if let Some(vis_span) = vis_span_opt - && let Some(vis_snip) = vis_span.get_source_text(cx) - && let Some(header_snip) = header_span.get_source_text(cx) + && let Some(vis_snip) = vis_span.get_text(cx) + && let Some(header_snip) = header_span.get_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) { @@ -185,6 +185,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, Some((sugg, String::new())) } else { let sugg = "return the output of the future directly"; - output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}"))) + output.span.get_text(cx).map(|src| (sugg, format!(" -> {src}"))) } } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 6848af7748f3f..c81fa2e2b3b27 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -140,7 +140,13 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() && !is_in_const_context(cx) { + // Cheap kind check before the costlier const context query. + if matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::MethodCall(..) | ExprKind::Call(..) + ) && !expr.span.from_expansion() + && !is_in_const_context(cx) + { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -155,6 +161,15 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + // Cheap `if`-statement check before the costlier const context query. + if !block + .stmts + .iter() + .any(|stmt| matches!(stmt.kind, StmtKind::Expr(e) if matches!(e.kind, ExprKind::If(..)))) + { + return; + } + if is_in_const_context(cx) || !self.msrv.meets(cx, msrvs::CLAMP) { return; } @@ -293,18 +308,19 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind + // Match method names before the costlier type queries. + && let Some((min, max)) = match (seg_first.ident.name, seg_second.ident.name) { + (sym::min, sym::max) => Some((arg_second, arg_first)), + (sym::max, sym::min) => Some((arg_first, arg_second)), + _ => None, + } && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) - && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); - let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { - (sym::min, sym::max) => (arg_second, arg_first), - (sym::max, sym::min) => (arg_first, arg_second), - _ => return None, - }; Some(ClampSuggestion { params: InputMinMax { input, @@ -367,12 +383,16 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) && let Some(inner_seg) = segment(cx, inner_fn) && let Some(outer_seg) = segment(cx, outer_fn) { - let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + let typeck = cx.typeck_results(); + let (input, inner_arg) = match ( + is_const_evaluatable(cx.tcx, typeck, first), + is_const_evaluatable(cx.tcx, typeck, second), + ) { (true, false) => (second, first), (false, true) => (first, second), _ => return None, }; - let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let is_float = typeck.expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (inner_seg, outer_seg) { (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 4a2784caf4c74..acd47fcd279db 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() || const_1.is_neg_infinity() && const_2.is_pos_infinity()) - && let Some(local_snippet) = first.span.get_source_text(cx) + && let Some(local_snippet) = first.span.get_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 96d69003934fc..33370194d4e7c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; use rustc_errors::Applicability; @@ -105,8 +105,8 @@ impl LateLintPass<'_> for ManualHashOne { finish_expr.span, "manual implementation of `BuildHasher::hash_one`", |diag| { - if let Some(build_hasher) = build_hasher.span.get_source_text(cx) - && let Some(hashed_value) = hashed_value.span.get_source_text(cx) + if let Some(build_hasher) = build_hasher.span.get_text(cx) + && let Some(hashed_value) = hashed_value.span.get_text(cx) { diag.multipart_suggestion( "try", diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index f60b2a1a6729f..94a3f2b1f15f9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns { pat.span, "this OR pattern can be rewritten using a range", |diag| { - if let Some(min) = min.span.get_source_text(cx) - && let Some(max) = max.span.get_source_text(cx) + if let Some(min) = min.span.get_text(cx) + && let Some(max) = max.span.get_text(cx) { diag.span_suggestion( pat.span, diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 67685f803841c..2eb15928f25aa 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::{If, IfLetOrMatch}; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index e3a112d1f7802..e317056e29809 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -160,7 +160,7 @@ fn handle( ); } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) && cx.typeck_results().expr_adjustments(body_some).is_empty() - && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { 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 caef6a0b36377..f500f21246bdb 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 @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; @@ -152,7 +152,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { if let Some(((_, dest), src)) = split && let Some(pat_snippets) = group .iter() - .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .map(|(_, arm)| arm.pat.span.get_text(cx)) .collect::>>() { let suggs = src diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 6b46791eb117a..98530b7808076 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -119,7 +119,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { wildcard_ident.map_or(String::new(), |ident| { ident .span - .get_source_text(cx) + .get_text(cx) .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ ")) }), if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ae0f3ffa0edc9..459c2bf1fef73 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -26,14 +26,15 @@ mod wild_in_or_pats; use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::walk_span_to_context; +use clippy_utils::source::SpanExt; use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, + higher, is_direct_expn_of, is_in_const_context, is_lint_allowed, is_span_match, sym, tokenize_with_text, }; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; +use rustc_lexer::{TokenKind, is_whitespace}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{SpanData, SyntaxContext}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -1084,34 +1085,38 @@ impl<'tcx> LateLintPass<'tcx> for Matches { try_err::check(cx, expr, ex); } - if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { + if !from_expansion + && let mut has_cfg = false + && let mut has_comments = false + && walk_intra_arm_text(cx, expr.span, ex.span, arms, |s| { + let mut iter = tokenize_with_text(s).filter(|(t, ..)| match t { + TokenKind::Whitespace => false, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } => { + has_comments = true; + false + }, + _ => true, + }); + while let Some((t, ..)) = iter.next() { + if matches!(t, TokenKind::Pound) + && matches!(iter.next(), Some((TokenKind::OpenBracket, ..))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _))) + { + has_cfg = true; + } + } + }) + && !has_cfg + { if source == MatchSource::Normal { - if !(self.msrv.meets(cx, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + let is_match_like_matches = self.msrv.meets(cx, msrvs::MATCHES_MACRO) + && match_like_matches::check_match(cx, expr, ex, arms); + if !(is_match_like_matches || is_lint_allowed(cx, MATCH_SAME_ARMS, expr.hir_id)) { match_same_arms::check(cx, arms); } redundant_pattern_match::check_match(cx, expr, ex, arms); - let mut match_comments = span_extract_comments(cx, expr.span); - // We remove comments from inside arms block. - if !match_comments.is_empty() { - for arm in arms { - for comment in span_extract_comments(cx, arm.body.span) { - if let Some(index) = match_comments - .iter() - .enumerate() - .find(|(_, cm)| **cm == comment) - .map(|(index, _)| index) - { - match_comments.remove(index); - } - } - } - } - // If there are still comments, it means they are outside of the arms. Tell the lint - // code about it. - single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); + single_match::check(cx, ex, arms, expr, has_comments); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); @@ -1216,64 +1221,51 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } -/// Checks if there are any arms with a `#[cfg(..)]` attribute. -fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool { - let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else { - // Shouldn't happen, but treat this as though a `cfg` attribute were found - return true; - }; - - let start = scrutinee_span.hi(); - let mut arm_spans = arms.iter().map(|arm| { - let data = arm.span.data(); - (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi)) - }); - let end = e.span.hi(); - - // Walk through all the non-code space before each match arm. The space trailing the final arm is - // handled after the `try_fold` e.g. - // - // match foo { - // _________^- everything between the scrutinee and arm1 - //| arm1 => (), - //|---^___________^ everything before arm2 - //| #[cfg(feature = "enabled")] - //| arm2 => some_code(), - //|---^____________________^ everything before arm3 - //| // some comment about arm3 - //| arm3 => some_code(), - //|---^____________________^ everything after arm3 - //| #[cfg(feature = "disabled")] - //| arm4 = some_code(), - //|}; - //|^ - let found = arm_spans.try_fold(start, |start, range| { - let Some((end, next_start)) = range else { - // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute - // were found. - return Err(()); - }; - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, - } - .span(); - (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(()) - }); - match found { - Ok(start) => { - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, +/// Calls the given function for each segment of the source text within the +/// match block which is not part of any arm. For the purposes of this function +/// attributes on an arm are not considered part of the arm. +/// +/// This will return whether all the relevant source text could be retrieved. If +/// all the source text cannot be retrieved it should be assumed that the match +/// originates from a macro. +#[must_use] +fn walk_intra_arm_text( + cx: &LateContext<'_>, + match_sp: Span, + scrutinee_sp: Span, + arms: &[Arm<'_>], + mut f: impl FnMut(&str), +) -> bool { + if let Some(src) = match_sp.get_source_range(cx) + && let scrutinee_sp = scrutinee_sp.source_callsite().data() + && let block_start = (scrutinee_sp.hi.0 - src.sf.start_pos.0) as usize + && let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) + && let Some(block_text) = src_text.get(block_start..src.range.end) + && let Some(stripped_text) = block_text.trim_start_matches(is_whitespace).strip_prefix('{') + && let arms_start = block_start + (block_text.len() - stripped_text.len()) + && let Some(arms_end) = stripped_text + .trim_end_matches(|c| is_whitespace(c) || c == ')') + .strip_suffix('}') + .map(|s| src.range.end - (stripped_text.len() - s.len())) + && let Some(range) = arms.iter().try_fold(arms_start..arms_end, |range, arm| { + let arm_sp: rustc_span::SpanData = arm.span.source_callsite().data(); + let arm_range = (arm_sp.lo.0 - src.sf.start_pos.0) as usize..(arm_sp.hi.0 - src.sf.start_pos.0) as usize; + if range.start <= arm_range.start + && arm_range.end <= range.end + && let Some(src) = src_text.get(range.start..arm_range.start) + { + f(src); + Some(arm_range.end..range.end) + } else { + None } - .span(); - span_contains_cfg(cx, span) - }, - Err(()) => true, + }) + && let Some(src) = src_text.get(range) + { + f(src); + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index b96c869b5151a..7c740ababb060 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ - SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, + SpanExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, sym}; @@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) + span.check_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } pub(crate) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs b/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..1faaf2c9c8e43 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs @@ -0,0 +1,84 @@ +use crate::clippy_utils::res::MaybeTypeckRes; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath as _}; +use clippy_utils::sugg::Sugg; +use clippy_utils::sym; +use clippy_utils::ty::implements_trait; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LetStmt, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::BY_REF_PEEKABLE_PEEK; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + if let ExprKind::MethodCall(maybe_peekable, peekable_recv, [], _) = recv.kind + && maybe_peekable.ident.name == sym::peekable + && !peekable_recv.span.from_expansion() + && let ExprKind::MethodCall(maybe_by_ref, by_ref_recv, [], _) = peekable_recv.kind + && maybe_by_ref.ident.name == sym::by_ref + && !by_ref_recv.span.from_expansion() + && [peekable_recv, recv] + .into_iter() + .all(|e| cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator)) + { + span_lint_and_then( + cx, + BY_REF_PEEKABLE_PEEK, + expr.span, + "calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output", + |diag| { + let span = by_ref_recv.span.shrink_to_hi().with_hi(expr.span.hi()); + if let ty::Ref(_, iter_ty, _) = cx.typeck_results().expr_ty_adjusted(by_ref_recv).kind() + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *iter_ty, clone_trait, &[]) + { + diag.span_suggestion_verbose( + span, + "to peek the first item without advancing the underlying iterator, use", + ".clone().next().as_ref()", + Applicability::MaybeIncorrect, + ); + } + diag.span_suggestion_verbose( + span, + "to advance the underlying iterator, use", + ".next().as_ref()", + Applicability::MaybeIncorrect, + ); + // If the iterator is a local variable, initialized through a simple binding with an inferred + // initialization expression, suggest making the initialization expression peekable. + if let Some(iter_local_id) = by_ref_recv.res_local_id() + && let Node::LetStmt(LetStmt { + pat: let_pat, + ty: None, + init: Some(init_expr), + els: None, + span: let_stmt_span, + .. + }) = cx.tcx.parent_hir_node(iter_local_id) + && let PatKind::Binding(BindingMode::MUT, _, _, None) = let_pat.kind + && !let_stmt_span.from_expansion() + // Changing the type of the iterator may prevent the code from compiling + && let mut app = Applicability::MaybeIncorrect + && let sugg = + Sugg::hir_with_context(cx, init_expr, let_stmt_span.ctxt(), "_", &mut app).maybe_paren() + { + diag.multipart_suggestion( + "to make the iterator peekable, use", + vec![ + (init_expr.span.source_callsite(), format!("{sugg}.peekable()")), + (recv.span.with_lo(by_ref_recv.span.hi()), String::new()), + ], + app, + ); + } else { + diag.help( + "you might want to transform the iterator itself using `.peekable()` without using `.by_ref()`", + ); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 15f4d91e4bd9e..b6694ad5148ba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sym; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - if let Some(recv_source) = recv.span.get_source_text(cx) { + if let Some(recv_source) = recv.span.get_text(cx) { let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() { recv_source.to_owned() } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..e151cf43a5044 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -0,0 +1,104 @@ +use super::CHUNKS_EXACT_TO_AS_CHUNKS; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{get_expr_use_site, sym}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{DesugaringKind, ExpnKind, Span, Symbol}; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, + call_span: Span, + method_name: Symbol, + msrv: Msrv, +) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + if !matches!(recv_ty.kind(), ty::Ref(_, inner, _) if inner.is_slice()) { + return; + } + + if is_const_evaluatable(cx.tcx, cx.typeck_results(), arg) { + if !msrv.meets(cx, msrvs::AS_CHUNKS) { + return; + } + + let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); + + if use_ctxt.is_ty_unified { + return; + } + + let suggestion_method = if method_name == sym::chunks_exact_mut { + "as_chunks_mut" + } else { + "as_chunks" + }; + + let mut applicability = Applicability::MachineApplicable; + let arg_str = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut applicability).0; + + let as_chunks = format_args!("{suggestion_method}::<{arg_str}>()"); + + span_lint_and_then( + cx, + CHUNKS_EXACT_TO_AS_CHUNKS, + call_span, + format!("using `{method_name}` with a constant chunk size"), + |diag| { + if let Node::Expr(use_expr) = use_ctxt.node { + match use_expr.kind { + ExprKind::Call(_, [recv]) | ExprKind::MethodCall(_, recv, [], _) + if recv.hir_id == use_ctxt.child_id + && matches!( + use_expr.span.ctxt().outer_expn_data().kind, + ExpnKind::Desugaring(DesugaringKind::ForLoop), + ) => + { + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0"), + applicability, + ); + return; + }, + ExprKind::MethodCall(_, recv, ..) + if recv.hir_id == use_ctxt.child_id + && cx + .ty_based_def(use_expr) + .assoc_fn_parent(cx) + .is_diag_item(cx, sym::Iterator) => + { + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0.iter()"), + applicability, + ); + return; + }, + _ => {}, + } + } + + diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); + + if let Node::LetStmt(let_stmt) = use_ctxt.node + && let PatKind::Binding(_, _, ident, _) = let_stmt.pat.kind + { + diag.note(format!( + "you can access the chunks using `{ident}.0.iter()`, and the remainder using `{ident}.1`" + )); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs index cd6b39b15a1b9..0b6ead0747a80 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -16,8 +16,7 @@ const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) { if let Some(arg) = arg { if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { suggest(cx, expr, recv, span); } diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 6c222640e2601..2b4b200904b2e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -1,67 +1,31 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_range_full, std_or_core, sym}; +use clippy_utils::{is_full_collection_range, std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::Ty; -use rustc_span::Symbol; -/// Checks if both types match the given diagnostic item, e.g.: -/// -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^ true -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^^^^^ false -fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool { - if let Some(expr_adt) = expr.ty_adt_def() - && let Some(recv_adt) = recv.ty_adt_def() - { - cx.tcx.is_diagnostic_item(sym, expr_adt.did()) && cx.tcx.is_diagnostic_item(sym, recv_adt.did()) - } else { - false - } -} - -/// Checks `std::{vec::Vec, collections::VecDeque}`. -fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - (types_match_diagnostic_item(cx, expr, recv, sym::Vec) - || types_match_diagnostic_item(cx, expr, recv, sym::VecDeque)) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::string::String` -fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - expr.is_lang_item(cx, LangItem::String) - && recv.is_lang_item(cx, LangItem::String) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`. -fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> { - types_match_diagnostic_item(cx, expr, recv, sym::HashSet) - .then_some("HashSet") - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap")) - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap")) -} - -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) { - let expr_ty = cx.typeck_results().expr_ty(expr); - let recv_ty = cx.typeck_results().expr_ty(recv); - let recv_ty_no_refs = recv_ty.peel_refs(); - - if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind - && let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path) - .then_some("Vec") - .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) - .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) +pub(super) fn check(cx: &LateContext<'_>, arg: Option<&Expr<'_>>, expr: &Expr<'_>, recv: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + let (is_ref, ty) = match *ty.kind() { + ty::Ref(_, ty, _) => (true, ty), + _ => (false, ty), + }; + if cx.typeck_results().expr_ty(expr) == ty + && let Some(did) = ty.opt_def_id() + && (cx.tcx.lang_items().string() == Some(did) + || matches!( + ty.opt_diag_name(cx), + Some(sym::HashMap | sym::HashSet | sym::BinaryHeap | sym::Vec | sym::VecDeque) + )) + && arg.is_none_or(|arg| is_full_collection_range(cx, recv.res_local_id(), arg)) && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); - let sugg = if let ty::Ref(..) = recv_ty.kind() { + let sugg = if is_ref { format!("{exec_context}::mem::take({recv})") } else { format!("{exec_context}::mem::take(&mut {recv})") @@ -71,8 +35,8 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re cx, DRAIN_COLLECT, expr.span, - format!("you seem to be trying to move all elements into a new `{typename}`"), - "consider using `mem::take`", + "draining all elements of a collection into a new collection of the same type", + "use `mem::take` to avoid creating a new allocation", sugg, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 89b37d507b702..97a03f9868b1d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -100,7 +100,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex } fn contains_call<'a>(cx: &LateContext<'a>, arg: &'a hir::Expr<'a>) -> bool { - for_each_expr(cx, arg, |expr| { + for_each_expr(cx.tcx, arg, |expr| { if matches!(expr.kind, hir::ExprKind::MethodCall { .. } | hir::ExprKind::Call { .. }) && !is_inside_always_const_context(cx.tcx, expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index 8e5b271212e67..419a7e0ed877d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,7 +1,7 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks, sym}; use rustc_ast::Mutability; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & .iter() .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) .count() - && let Some(param_snippet) = param.span.get_source_text(cx) + && let Some(param_snippet) = param.span.get_text(cx) { let mut applicability = Applicability::MachineApplicable; let (filter, _) = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs index 9c6b16b88bfd1..3584833bc61cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs @@ -1,15 +1,15 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::snippet; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{path_to_local_with_projections, sym}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use super::FILTER_NEXT; -#[derive(Copy, Clone)] +#[derive(Clone, Copy)] pub(super) enum Direction { Forward, Backward, @@ -17,15 +17,13 @@ pub(super) enum Direction { /// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for /// `DoubleEndedIterator` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - filter_arg: &'tcx hir::Expr<'_>, +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + filter_arg: &Expr<'_>, direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a - // DoubleEndedIterator let (required_trait, next_method, find_method) = match direction { Direction::Forward => (sym::Iterator, "next", "find"), Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), @@ -37,30 +35,31 @@ pub(super) fn check<'tcx>( { return; } - let msg = format!( - "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ - `.{find_method}(..)` instead", - required_trait.as_str() - ); - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + span_lint_and_then( + cx, + FILTER_NEXT, + expr.span, + format!("called `filter(..).{next_method}()` on an `{required_trait}`"), + |diag| { + let mut app = Applicability::MachineApplicable; + let filter_snippet = snippet_with_applicability(cx, filter_arg.span, "..", &mut app); + let iter_snippet = snippet_with_applicability(cx, recv.span, "..", &mut app); + + let pat = if let Some(id) = path_to_local_with_projections(recv) + && let Node::Pat(pat) = cx.tcx.hir_node(id) + && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind { - (Applicability::Unspecified, Some((pat.span, ident))) + app = Applicability::Unspecified; + Some((pat.span, ident)) } else { - (Applicability::MachineApplicable, None) + None }; - diag.span_suggestion( + diag.span_suggestion_verbose( expr.span, - "try", + format!("use `.{find_method}(..)` instead"), format!("{iter_snippet}.{find_method}({filter_snippet})"), - applicability, + app, ); if let Some((pat_span, ident)) = pat { @@ -69,8 +68,6 @@ pub(super) fn check<'tcx>( format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), ); } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + }, + ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 8d1585baac451..2ef09fa3f8d2c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -12,8 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) && matches!(ty_name, sym::Vec | sym::VecDeque) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index a89a656a6bc7e..5ef8450da93bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; use clippy_utils::{ExprUseNode, get_expr_use_site, sym}; @@ -47,7 +47,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: let can_lint = for_each_expr_without_closures(block.stmts, |e| { if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. - let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { + let _: Option = for_each_expr(cx.tcx, cx.tcx.hir_body(c.body).value, |e| { if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, ctxt, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index 48f2c10f97cc6..050b28b3babd7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -27,8 +27,8 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) - && let Some(recv_snippet) = recv.span.get_source_text(cx) - && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) + && let Some(recv_snippet) = recv.span.get_text(cx) + && let Some(err_arg_snippet) = err_arg.span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) { let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str(), true, Some(indent + 4)); diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs b/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs index aa23b9deff45b..538f8a2fe93dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeResPath}; @@ -29,6 +30,8 @@ pub(super) fn check<'tcx>( && let ExprKind::MethodCall(method_path, map_recv, [map_arg], _) = peel_blocks(outer_value).kind && method_path.ident.name == sym::map && cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option) + // `b` is not lazy evaluated + && switch_to_eager_eval(cx, map_recv) // `b` does not reference the outer closure parameter `a`. && !local_used_in(cx, outer_param_id, map_recv) // `|b| (a, b)` diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs index 5f5944d5d4230..6ef7abffb0b2f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -31,14 +31,12 @@ pub(super) fn check<'tcx>( && let ExprKind::Closure(closure) = acc.kind && msrv.meets(cx, msrvs::ITERATOR_TRY_FOLD) && !is_from_proc_macro(cx, expr) - && let Some(args_snip) = closure - .fn_arg_span - .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx)) + && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| fn_arg_span.get_text(cx)) { let init_snip = rest .is_empty() .then_some(first.span) - .and_then(|span| span.get_source_text(cx)) + .and_then(|span| span.get_text(cx)) .map_or_else(|| "...".to_owned(), |src| src.to_owned()); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs index ad950f75f8130..14a9752d3bded 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_expr_identity_function; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub(super) fn check( && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) - && let Some(map_arg) = map_arg.span.get_source_text(cx) + && let Some(map_arg) = map_arg.span.get_text(cx) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs index ac2f991804865..ec6dccd4fe543 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs @@ -128,10 +128,10 @@ pub(super) fn check<'tcx>( (SuggestedKind::AndThen, _) => "and_then", (SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and", (SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and", - (SuggestedKind::Other, _) - if unwrap_arg_ty.peel_refs().is_array() - && cx.typeck_results().expr_ty_adjusted(unwrap_arg).peel_refs().is_slice() => - { + (SuggestedKind::Other, _) if unwrap_arg_ty != cx.typeck_results().expr_ty_adjusted(unwrap_arg) => { + // If the `unwrap_or` argument needs an adjustment, moving it into `map_or`'s + // first argument can make type inference pick the unadjusted type and reject + // the closure return type. Keep the lint, but don't emit a rustfix. return; }, _ => "map_or", diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index b01cd49739431..38a3b1705ad17 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1,4 +1,5 @@ mod bind_instead_of_map; +mod by_ref_peekable_peek; mod bytecount; mod bytes_count_to_len; mod bytes_nth; @@ -9,6 +10,7 @@ mod chars_last_cmp; mod chars_last_cmp_with_unwrap; mod chars_next_cmp; mod chars_next_cmp_with_unwrap; +mod chunks_exact_to_as_chunks; mod clear_with_drain; mod clone_on_copy; mod clone_on_ref_ptr; @@ -141,6 +143,7 @@ mod unnecessary_map_or_else; mod unnecessary_min_or_max; mod unnecessary_sort_by; mod unnecessary_to_owned; +mod unnecessary_unwrap_unchecked; mod unwrap_expect_used; mod useless_asref; mod useless_nonzero_new_unchecked; @@ -201,6 +204,39 @@ declare_clippy_lint! { "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Iterator::by_ref().peekable().peek()`. + /// + /// ### Why is this bad? + /// While it might look like this will allow peeking on the first + /// element of an iterator without consuming it and without consuming + /// the iterator itself, it will in practice consume the first element. + /// + /// The implementation of `Peekable::peek()` produces the first element + /// of the underlying iterator, and stores it internally so that it can + /// be later produced. As a consequence, it advances the underlying + /// iterator, whose `.next()` method will now produce its second element. + /// + /// ### Example + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.by_ref().peekable().peek(); // 1 + /// let y = iter.by_ref().peekable().peek(); // 2 + /// ``` + /// If this does what you intended, use the following instead, which is + /// shorter and clearer: + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.next().as_ref(); // 1 + /// let y = iter.next().as_ref(); // 2 + /// ``` + #[clippy::version = "1.98.0"] + pub BY_REF_PEEKABLE_PEEK, + suspicious, + "Using `.by_ref().peekable().peek()` on an iterator" +} + declare_clippy_lint! { /// ### What it does /// It checks for `str::bytes().count()` and suggests replacing it with @@ -329,6 +365,32 @@ declare_clippy_lint! { "using `.chars().next()` to check if a string starts with a char" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `chunks_exact` or `chunks_exact_mut` with a constant chunk size. + /// + /// ### Why is this bad? + /// `as_chunks` provides better ergonomics and type safety by returning arrays instead of slices. + /// It was stabilized in Rust 1.88. + /// + /// ### Example + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let mut it = slice.chunks_exact(2); + /// for chunk in it {} + /// ``` + /// Use instead: + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let (chunks, remainder) = slice.as_chunks::<2>(); + /// for chunk in chunks {} + /// ``` + #[clippy::version = "1.93.0"] + pub CHUNKS_EXACT_TO_AS_CHUNKS, + style, + "using `chunks_exact` with constant when `as_chunks` is more ergonomic" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container. @@ -4541,6 +4603,34 @@ declare_clippy_lint! { "unnecessary calls to `to_owned`-like functions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `unwrap_unchecked` when an `_unchecked` variant of the function exists. + /// + /// ### Why is this bad? + /// Calling the non-unchecked variant may result in checking that is then discarded + /// if `unwrap_unchecked` is called directly afterwards, whereas the unchecked + /// variant most likely avoids performing the check completely. + /// + /// ### Known problems + /// + /// The unchecked variant is only suggested if it's defined in the same `impl` block + /// as the non-unchecked one + /// + /// ### Example + /// ```rust + /// let s = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + /// ``` + /// Use instead: + /// ```rust + /// let s = unsafe { std::str::from_utf8_unchecked(&[]) }; + /// ``` + #[clippy::version = "1.98.0"] + pub UNNECESSARY_UNWRAP_UNCHECKED, + complexity, + "calling `unwrap_unchecked` on a function which has an `_unchecked` variant" +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of the following functions with an argument that constructs a default value @@ -4835,9 +4925,11 @@ impl_lint_pass!(Methods => [ BIND_INSTEAD_OF_MAP, BYTES_COUNT_TO_LEN, BYTES_NTH, + BY_REF_PEEKABLE_PEEK, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, CHARS_LAST_CMP, CHARS_NEXT_CMP, + CHUNKS_EXACT_TO_AS_CHUNKS, CLEAR_WITH_DRAIN, CLONED_INSTEAD_OF_COPIED, CLONE_ON_COPY, @@ -4979,6 +5071,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, UNNECESSARY_SORT_BY, UNNECESSARY_TO_OWNED, + UNNECESSARY_UNWRAP_UNCHECKED, UNWRAP_OR_DEFAULT, UNWRAP_USED, USELESS_ASREF, @@ -5198,12 +5291,15 @@ impl Methods { _ => {}, } }, + (name @ (sym::chunks_exact | sym::chunks_exact_mut), [arg]) => { + chunks_exact_to_as_chunks::check(cx, recv, arg, expr, call_span, name, self.msrv); + }, (sym::and_then, [arg]) => { manual_option_zip::check(cx, expr, recv, arg, self.msrv); let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { - let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and", true); if !ule_and_linted { return_and_then::check(cx, expr, recv, arg); } @@ -5277,8 +5373,10 @@ impl Methods { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some((sym::drain, recv, args, ..)) => { - drain_collect::check(cx, args, expr, recv); + Some((sym::drain, recv, args, ..)) => match args { + [arg] => drain_collect::check(cx, Some(arg), expr, recv), + [] => drain_collect::check(cx, None, expr, recv), + _ => {}, }, _ => {}, } @@ -5333,7 +5431,11 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { + (sym::unwrap_unchecked, []) => { + unnecessary_unwrap_unchecked::check(cx, expr, recv, call_span); + unnecessary_literal_unwrap::check(cx, expr, recv, name, args); + }, + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, (sym::extend, [arg]) => { @@ -5439,7 +5541,7 @@ impl Methods { get_last_with_len::check(cx, expr, recv, arg); }, (sym::get_or_insert_with, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert", false); }, (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); @@ -5594,16 +5696,19 @@ impl Methods { ptr_offset_by_literal::check(cx, expr, self.msrv); }, (sym::ok_or_else, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or", true); }, (sym::open, [_]) => { open_options::check(cx, expr, recv); }, (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or", false); } }, + (sym::peek, []) => { + by_ref_peekable_peek::check(cx, expr, recv); + }, (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, @@ -5699,7 +5804,7 @@ impl Methods { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } - unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some", true); }, (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); @@ -5789,7 +5894,7 @@ impl Methods { ); }, _ => { - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or", false); }, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 948ed8a25746e..20468784aae7a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { @@ -36,7 +36,7 @@ fn handle_expr( && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, @@ -78,7 +78,7 @@ fn handle_expr( if revert != is_all && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) && peels_expr_ref(arg).res_local_id() == Some(first_param) - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 06e6a3c70b87d..7626cdb5a5fe8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name expr.span, "derefed type is same as origin", "try", - recv.span.get_source_text(cx).unwrap().to_owned(), + recv.span.get_text(cx).unwrap().to_owned(), Applicability::MachineApplicable, ); } 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 a07cd5a8925ae..6f9e817490c12 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 @@ -30,9 +30,26 @@ pub(super) fn check<'tcx>( args: &'tcx [hir::Expr<'_>], msrv: Msrv, ) { + // Bail out early unless the method is one that `check_unwrap_or_default` or + // `check_or_fn_call` can lint, to avoid walking the argument of every method call. + if !matches!( + name, + sym::unwrap_or + | sym::unwrap_or_else + | sym::or_insert + | sym::or_insert_with + | sym::get_or_insert + | sym::map_or + | sym::ok_or + | sym::or + | sym::and + ) { + return; + } + if let [arg] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { // `or_fun_call` lint needs to take nested expr into account, // but `unwrap_or_default` lint doesn't, we don't want something like: // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by @@ -72,7 +89,7 @@ pub(super) fn check<'tcx>( // `map_or` takes two arguments if let [arg, lambda] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { let is_top_most_expr = ex.hir_id == inner_arg.hir_id; match ex.kind { hir::ExprKind::Call(fun, fun_args) => { @@ -112,6 +129,12 @@ fn check_unwrap_or_default( method_span: Span, msrv: Msrv, ) -> bool { + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` @@ -150,12 +173,6 @@ fn check_unwrap_or_default( } }; - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) diff --git a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs index b5d2add65cf19..17f81f547aeb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -92,7 +92,7 @@ fn expr_as_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti } fn format_isize_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { - let text = expr.span.get_source_text(cx)?; + let text = expr.span.get_text(cx)?; let text = peel_parens_str(&text); Some(text.trim_end_matches("isize").trim_end_matches('_').to_string()) } diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index 5fec0c5f2cf7d..b01adbf4d58af 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::source::{SpanExt as _, snippet_with_applicability}; use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; @@ -49,8 +49,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' ), )]; if let Some((left, right)) = invert_bindings - && let Some(snip_left) = left.get_source_text(cx) - && let Some(snip_right) = right.get_source_text(cx) + && let Some(snip_left) = left.get_text(cx) + && let Some(snip_right) = right.get_text(cx) { suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index 8641f7c0abec3..fbb3dd41c7a4e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -22,8 +22,9 @@ pub(super) fn check<'a>( // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], trim_span) = split_recv.kind && trim_method_name.ident.name == sym::trim - && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() - && !is_const_evaluatable(cx, trim_recv) + && let typeck = cx.typeck_results() + && typeck.expr_ty_adjusted(trim_recv).peel_refs().is_str() + && !is_const_evaluatable(cx.tcx, typeck, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && matches!( split_lit.node, diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index fff203296bce9..13782a492cbc5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -214,7 +214,7 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - let _: Option = for_each_expr(cx, init_expr, |e| { + let _: Option = for_each_expr(cx.tcx, init_expr, |e| { if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs index 48e89c2998efa..53c695e378476 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( } && msrv.meets(cx, msrvs::MATCHES_MACRO) && !is_from_proc_macro(cx, expr) - && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx) + && let Some(scrutinee_snip) = scrutinee.span.get_text(cx) { // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then // something like `r"\"` will become `'\'`, which is of course invalid diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs index d322909bef359..43dac249f7440 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -29,8 +29,8 @@ pub(super) fn check( }; let both_calls_span = first_call_span.with_hi(call_span.hi()); - if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx) - && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx) + if let Some(both_calls_snippet) = both_calls_span.get_text(cx) + && let Some(first_caller_snippet) = first_caller.span.get_text(cx) { let (sugg_span, suggestion) = if is_some { ( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs index b2413bb77aa73..b622a219f35dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; @@ -39,11 +39,11 @@ pub(super) fn check( return; }; let both_calls_span = get_call_span.with_hi(call_span.hi()); - if let Some(snippet) = both_calls_span.get_source_text(cx) - && let Some(arg_snippet) = arg.span.get_source_text(cx) + if let Some(snippet) = both_calls_span.get_text(cx) + && let Some(arg_snippet) = arg.span.get_text(cx) { let generics_snippet = if let Some(generics) = path.args - && let Some(generics_snippet) = generics.span_ext.get_source_text(cx) + && let Some(generics_snippet) = generics.span_ext.get_text(cx) { format!("::{generics_snippet}") } else { @@ -64,7 +64,7 @@ pub(super) fn check( suggestion, Applicability::MaybeIncorrect, ); - } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) { + } else if let Some(caller_snippet) = get_caller.span.get_text(cx) { let full_span = get_caller.span.with_hi(call_span.hi()); span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 444d0a1d72422..5b19c43f1b32f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; @@ -42,7 +42,7 @@ pub fn check_for_loop_iter( && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && !clone_or_copy_needed - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) { // Issue 12098 // https://github.com/rust-lang/rust-clippy/issues/12098 @@ -102,7 +102,7 @@ pub fn check_for_loop_iter( && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, sym::Item) && iter_item_ty == into_iter_item_ty - && let Some(collection_snippet) = collection.span.get_source_text(cx) + && let Some(collection_snippet) = collection.span.get_text(cx) { collection_snippet } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 2869547650f31..0b3c8146b9f76 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -18,6 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, simplify_using: &str, + use_turbofish: bool, ) -> bool { let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); @@ -46,23 +47,43 @@ pub(super) fn check<'tcx>( } else { "unnecessary closure used with `bool::then`" }; - let applicability = if body + + let mut applicability = Applicability::MachineApplicable; + if body .params .iter() // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable - } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect + .any(|param| !matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + { + // If the closure parameters have a pattern, + // it might be required for type inferrence. + applicability = Applicability::MaybeIncorrect; + } + let (ascription, turbofish) = match fn_decl.output { + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) => { + // if the closure has no explicit return type, + // then there's nothing to preserve + (String::new(), String::new()) + }, + FnRetTy::Return(ty) => { + // explicit return type was given on the closure + // + // we can preserve this information using `as`, but `as` is + // a somewhat dangerous feature, because it can be used to + // truncate integers + // + // if possible, use turbofish to preserve the type information + let ty = snippet_with_applicability(cx, ty.span, "_", &mut applicability); + if use_turbofish { + (String::new(), format!("::<{ty}>")) + } else { + (format!(" as {ty}"), String::new()) + } + }, }; // This is a duplicate of what's happening in clippy_lints::methods::method_call, @@ -73,7 +94,10 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( span, format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + format!( + "{simplify_using}{turbofish}({}{ascription})", + snippet(cx, body_expr.span, "..") + ), applicability, ); }); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 391209d8c3657..60bc4de69237a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -262,7 +262,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) -> Option< if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Left) { (left_expr, l_pat.span, false) } else if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Right) { - (left_expr, r_pat.span, true) + // Use the right-hand expr (the `a` side) as the key body, peeling any `&` + // introduced by the `.cmp(&rhs)` call so the suggestion doesn't contain a + // spurious borrow. + let right_body = if let ExprKind::AddrOf(_, _, inner) = right_expr.kind { + inner + } else { + right_expr + }; + (right_body, l_pat.span, true) } else { return None; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a56dcd894b6aa..35ae44591c188 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,7 +3,7 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_context}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; @@ -217,7 +217,7 @@ fn check_into_iter_call_arg( && let parent_ty = cx.typeck_results().expr_ty(parent) && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 @@ -313,8 +313,8 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) - && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) + && let Some(arg_snippet) = argument_expr.span.get_text(cx) { // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to @@ -712,7 +712,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx && let arg_ty = arg_ty.peel_refs() // For now we limit this lint to `String` and `Vec`. && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty)) - && let Some(snippet) = caller.span.get_source_text(cx) + && let Some(snippet) = caller.span.get_text(cx) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..3f7f146446361 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,276 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; +use clippy_utils::ty::{option_or_result_arg_ty, same_type_modulo_regions}; +use clippy_utils::{is_from_proc_macro, last_path_segment, over}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Body, Expr, ExprKind, PatKind, Safety}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_span::symbol::Ident; + +use super::UNNECESSARY_UNWRAP_UNCHECKED; + +#[derive(Clone, Copy, Debug)] +enum Variant { + /// Free `fn` in a module + Fn, + /// Associated item from an `impl` + Assoc(AssocKind), +} + +impl Variant { + fn msg(self) -> &'static str { + // Don't use `format!` instead -- it won't be optimized out. + match self { + Variant::Fn => "usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists", + Variant::Assoc(AssocKind::Fn) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists" + }, + Variant::Assoc(AssocKind::Method) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists" + }, + } + } +} + +/// This only exists so the help message shows `associated function` or `method`, depending on +/// whether it has a `self` parameter. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum AssocKind { + /// No `self`: `fn new() -> Self` + Fn, + /// Has `self`: `fn ty<'tcx>(&self) -> Ty<'tcx>` + Method, +} + +impl AssocKind { + fn new(fn_has_self_parameter: bool) -> Self { + if fn_has_self_parameter { Self::Method } else { Self::Fn } + } +} + +fn unchecked_ident(checked_ident: Ident) -> Option { + let checked_ident = checked_ident.to_string(); + // Only add `_unchecked` if it doesn't already end with `_` + (!checked_ident.ends_with('_')).then(|| Ident::from_str(&(checked_ident + "_unchecked"))) +} + +/// Find a function called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_fn( + cx: &LateContext<'_>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(DefId, Ident)> { + // Don't use `parent_module`. We only want to lint if its first parent is a `Mod`, + // i.e. if this is a free-standing function + let parent = cx.tcx.parent(checked_def_id); + if cx.tcx.def_kind(parent) == DefKind::Mod + && let children = parent.as_local().map_or_else( + || cx.tcx.module_children(parent), + // We must use a !query for local modules to prevent an ICE. + |parent| cx.tcx.module_children_local(parent), + ) + // Make sure that there are other functions in this module + // (otherwise there couldn't be an unchecked version) + && children.len() > 1 + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + && let Some(unchecked_def_id) = children.iter().find_map(|child| { + if child.ident == unchecked_ident + && let Res::Def(DefKind::Fn, def_id) = child.res + { + Some(def_id) + } else { + None + } + }) + { + Some((unchecked_def_id, unchecked_ident)) + } else { + None + } +} + +/// Find a method called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_method<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(&'tcx ty::AssocItem, Ident)> { + // Don't use `parent_impl`. We only want to lint if its first parent is an `Impl` + let parent = cx.tcx.parent(checked_def_id); + if matches!(cx.tcx.def_kind(parent), DefKind::Impl { .. }) + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + // Only look in the same impl (to avoid dealing with generics etc.) + && let Some(unchecked) = cx.tcx.associated_items(parent).find_by_ident_and_namespace( + cx.tcx, + unchecked_ident, + Namespace::ValueNS, + parent, + ) + { + Some((unchecked, unchecked_ident)) + } else { + None + } +} + +/// Checks that `checked_def_id` and `unchecked_def_id` refer to functions with: +/// - same visibility +/// - identical signatures, apart from unsafety +/// - "matching" return types: the checked version returns `Option`/`Result`, while the +/// unchecked one returns `T` +fn same_functions_modulo_safety<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + unchecked_def_id: DefId, + unwrapped_ret_ty: Ty<'tcx>, +) -> bool { + let hir_body = |def_id: DefId| -> Option<&'tcx Body<'tcx>> { cx.tcx.hir_maybe_body_owned_by(def_id.as_local()?) }; + let fn_sig = |def_id| cx.tcx.fn_sig(def_id).skip_binder().skip_binder(); + + if match (hir_body(checked_def_id), hir_body(unchecked_def_id)) { + // For local functions, we can get the parameter names. In that case, we want to make sure + // that the latter are equal between the checked and unchecked versions. + (Some(checked_body), Some(unchecked_body)) => { + over(checked_body.params, unchecked_body.params, |p1, p2| { + // We only allow simple params (plain bindings) for now, to stay on the safer side. + if let PatKind::Binding(bm1, _, ident1, None) = p1.pat.kind + && let PatKind::Binding(bm2, _, ident2, None) = p2.pat.kind + { + bm1 == bm2 && ident1 == ident2 + } else { + false + } + }) + }, + // For non-local functions, parameter names are not accessible. Oh well, we'll let it slip + (None, None) => true, + // If only one of the versions is non-local, then something weird happened. Bail just in case + _ => false, + } { + // Check that the functions have identical signatures, apart from safety, and return type (see + // below) + let checked_fn_sig = fn_sig(checked_def_id); + let unchecked_fn_sig = fn_sig(unchecked_def_id); + + (checked_fn_sig.safety() == Safety::Safe && unchecked_fn_sig.safety() == Safety::Unsafe) + && checked_fn_sig.c_variadic() == unchecked_fn_sig.c_variadic() + && checked_fn_sig.abi() == unchecked_fn_sig.abi() + // NOTE: the reason we use `same_type_modulo_regions` all over the place here is that + // the regions of different functions will be distinct, even if they are called the same + && over(checked_fn_sig.inputs(), unchecked_fn_sig.inputs(), |ty1, ty2| { + same_type_modulo_regions(*ty1, *ty2) + }) + // The checked version should return `Option` or `Result`, + // and the unchecked version should return just `T` + && same_type_modulo_regions(unchecked_fn_sig.output(), unwrapped_ret_ty) + && option_or_result_arg_ty(cx, checked_fn_sig.output()) + .is_some_and(|wrapped_ty| same_type_modulo_regions(wrapped_ty, unwrapped_ret_ty)) + // Check that the visibilities are the same (for the purposes of replacing, it would be enough to have + // the former _at least as_ visible as the latter, but we don't bother) + && cx.tcx.visibility(unchecked_def_id) == cx.tcx.visibility(checked_def_id) + } else { + false + } +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, recv: &Expr<'_>, call_span: Span) { + if expr.span.from_expansion() { + return; + } + let expected_ret_ty = cx.typeck_results().expr_ty(expr); + let (variant, checked_span, unchecked_sugg, unchecked_full_path) = match recv.kind { + // Construct `Variant::Fn(_)`, if applicable. This is necessary for us to handle + // functions like `std::str::from_utf8_unchecked`. + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked_def_id, unchecked_ident)) = + find_unchecked_sibling_fn(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked_def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked_def_id); + ( + Variant::Fn, + checked_ident.span, + if checked_ident.span == path.span { + // replacing `bar(x)` with `bar_unchecked(x)` + // `bar_unchecked` might not be in scope, so suggest the full path + unchecked_full_path.clone() + } else { + // replacing `foo::bar(x)` with `foo::bar_unchecked(x)` + // since the path is qualified, we can just replace the final segment + unchecked_ident.to_string() + }, + unchecked_full_path, + ) + }, + // We unfortunately must handle `A::a(&a)` and `a.a()` separately, this handles the + // former + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && let ty::AssocKind::Fn { has_self, .. } = unchecked.kind + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::new(has_self)), + // since this is basically a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + // ... And now the latter ^^ + ExprKind::MethodCall(segment, _, _, _) + if let checked_ident = segment.ident + && let Some(checked_def_id) = cx.typeck_results().type_dependent_def_id(recv.hir_id) + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::Method), + // since this is a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + _ => return, + }; + + if !is_from_proc_macro(cx, expr) { + span_lint_and_then(cx, UNNECESSARY_UNWRAP_UNCHECKED, expr.span, variant.msg(), |diag| { + let sugg = vec![ + // replace the function with the unchecked version + (checked_span, unchecked_sugg), + // remove the call to `.unwrap_unchecked()` + (call_span.with_lo(recv.span.hi()), String::new()), + ]; + diag.multipart_suggestion( + format!("use `{unchecked_full_path}` instead, and remove the call to `.unwrap_unchecked()`"), + sugg, + // TODO: make this `MachineApplicable` when the function comes from std/alloc/core + // The reasoning is that, if the function comes from std/alloc/core, its checked and unchecked are + // pretty likely to have their semantics match. + Applicability::MaybeIncorrect, + ); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index ba62853c74571..74da699e3c006 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -79,14 +79,15 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { + let m = match path.ident.name { + sym::max => MinMax::Max, + sym::min => MinMax::Min, + _ => return None, + }; if cx.typeck_results().expr_ty(receiver).is_floating_point() || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) { - match path.ident.name { - sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), - _ => None, - } + fetch_const(cx, expr.span.ctxt(), Some(receiver), args, m) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index 314004621ce05..79e56b7de41f8 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -333,11 +333,15 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if let ExprKind::Lit(lit) = expr.kind { + // `check_lit` only lints integer literals and suffixed float literals. + if let ExprKind::Lit(lit) = expr.kind + && match lit.kind { + token::LitKind::Integer => true, + token::LitKind::Float => lit.suffix.is_some(), + _ => false, + } + && !expr.span.in_external_macro(cx.sess().source_map()) + { MiscEarlyLints::check_lit(cx, lit, expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 33ab94e00a5c6..5bc437cf24f4c 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; @@ -59,7 +59,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { .iter() .filter_map(|f| match f.pat.kind { PatKind::Wild => None, - _ => f.span.get_source_text(cx), + _ => f.span.get_text(cx), }) .format(", "), )); diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 7dbe39bb099d6..36a2a7f4b406c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::{PathNS, lookup_path_str}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; @@ -78,7 +78,7 @@ impl LateLintPass<'_> for ImportRename { && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') - && let Some(snip) = span_without_semi.get_source_text(cx) + && let Some(snip) = span_without_semi.get_text(cx) && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 634745422204f..657da09f5ca2b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -109,7 +109,7 @@ fn should_lint<'tcx>( // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. let mut has_debug_struct = false; - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); @@ -166,7 +166,7 @@ fn check_struct<'tcx>( let mut has_direct_field_access = false; let mut field_accesses = FxHashSet::default(); - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::Field(target, ident) = expr.kind && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() && target_ty == self_ty diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 93cfed38c43ed..32a86db67c194 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -1,8 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, Attribute, find_attr}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocContainer; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, OwnerId, TraitFn, TraitItem, TraitItemKind, find_attr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::config::CrateType; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -66,134 +64,41 @@ declare_clippy_lint! { declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); -fn check_missing_inline_attrs( - cx: &LateContext<'_>, - attrs: &[Attribute], - sp: Span, - desc: &'static str, - hir_id: Option, -) { - if !find_attr!(attrs, Inline(..)) { - let msg = format!("missing `#[inline]` for {desc}"); - if let Some(hir_id) = hir_id { - span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); - } else { - span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); - } +fn check(cx: &LateContext<'_>, item: OwnerId, sp: Span) { + if cx.effective_visibilities.is_exported(item.def_id) + && !find_attr!(cx.tcx.hir_attrs(item.into()), Inline(..)) + // Rust `inline` doesn't mean anything with external linkage. + && !cx.tcx.codegen_fn_attrs(item.def_id).contains_extern_indicator() + && !cx.tcx.crate_types().iter().any(|&t| matches!(t, CrateType::ProcMacro)) + && !sp.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint( + cx, + MISSING_INLINE_IN_PUBLIC_ITEMS, + sp, + "missing `#[inline]` on a publicly callable function", + ); } } impl<'tcx> LateLintPass<'tcx> for MissingInline { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if it.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) - { - return; - } - - if !cx.effective_visibilities.is_exported(it.owner_id.def_id) { - return; - } - match it.kind { - hir::ItemKind::Fn { .. } => { - if fn_is_externally_exported(cx, it.owner_id.to_def_id()) { - return; - } - - let desc = "a function"; - let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc, None); - }, - hir::ItemKind::Trait { items: trait_items, .. } => { - // note: we need to check if the trait is exported so we can't use - // `LateLintPass::check_trait_item` here. - for &tit in trait_items { - let tit_ = cx.tcx.hir_trait_item(tit); - match tit_.kind { - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, - hir::TraitItemKind::Fn(..) => { - if cx.tcx.defaultness(tit.owner_id).has_value() { - // trait method with default body needs inline in case - // an impl is not provided - let desc = "a default trait method"; - let item = cx.tcx.hir_trait_item(tit); - let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); - } - }, - } - } - }, - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::ExternCrate(..) - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => {}, + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { + if let ItemKind::Fn { .. } = it.kind { + check(cx, it.owner_id, it.span); } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if impl_item.span.in_external_macro(cx.sess().source_map()) - || cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(_, f) = item.kind + && let TraitFn::Provided(_) = f { - return; + check(cx, item.owner_id, item.span); } + } - // If the item being implemented is not exported, then we don't need #[inline] - if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - return; - } - - let desc = match impl_item.kind { - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, - }; - - let assoc_item = cx.tcx.associated_item(impl_item.owner_id); - let container_id = assoc_item.container_id(cx.tcx); - let trait_def_id = match assoc_item.container { - AssocContainer::Trait => Some(container_id), - AssocContainer::TraitImpl(_) => Some(cx.tcx.impl_trait_id(container_id)), - AssocContainer::InherentImpl => None, - }; - - if let Some(trait_def_id) = trait_def_id - && trait_def_id.is_local() - && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) - { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(..) = item.kind { + check(cx, item.owner_id, item.span); } - - let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } - -/// Checks if this function is externally exported, where #[inline] wouldn't have the desired effect -/// and a rustc warning would be triggered, see #15301 -fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { - let attrs = cx.tcx.codegen_fn_attrs(def_id); - attrs.contains_extern_indicator() -} diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 8b02c4865d138..a78f53188ba8b 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index ad44d65b4d663..6d9950579fb24 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs index 807c4cf5da8e1..299860ac1e037 100644 --- a/src/tools/clippy/clippy_lints/src/needless_else.rs +++ b/src/tools/clippy/clippy_lints/src/needless_else.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -43,7 +43,7 @@ impl EarlyLintPass for NeedlessElse { && !else_clause.span.from_expansion() && block.stmts.is_empty() && let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx) - && range.clone().check_source_text(cx, |src| { + && range.clone().check_text(cx, |src| { // Ignore else blocks that contain comments or #[cfg]s !src.contains(['/', '#']) }) diff --git a/src/tools/clippy/clippy_lints/src/needless_ifs.rs b/src/tools/clippy/clippy_lints/src/needless_ifs.rs index cdf9bd91339c6..48d15056f7291 100644 --- a/src/tools/clippy/clippy_lints/src/needless_ifs.rs +++ b/src/tools/clippy/clippy_lints/src/needless_ifs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -48,7 +48,7 @@ impl LateLintPass<'_> for NeedlessIfs { && block.stmts.is_empty() && block.expr.is_none() && !expr.span.in_external_macro(cx.sess().source_map()) - && then.span.check_source_text(cx, |src| { + && then.span.check_text(cx, |src| { // Ignore // - empty macro expansions // - empty reptitions in macro expansions @@ -58,7 +58,7 @@ impl LateLintPass<'_> for NeedlessIfs { .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace() || ch == b'\x0b') }) && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) - && let Some(cond_snippet) = cond_span.get_source_text(cx) + && let Some(cond_snippet) = cond_span.get_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index bc4f6ca401710..69184d1894025 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -1,16 +1,19 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdMap, HirIdSet, LetStmt, LocalSource, MatchSource, Node, Pat, + PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -61,17 +64,126 @@ declare_clippy_lint! { "late initializations that can be replaced by a `let` statement with an initializer" } -declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); +impl_lint_pass!(NeedlessLateInit<'_> => [NEEDLESS_LATE_INIT]); -fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr(cx, stmt, |e| { - if matches!(e.kind, ExprKind::Assign(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) +pub struct NeedlessLateInit<'tcx> { + check_grouped_late_init: bool, + grouped_late_inits: Vec<(HirId, HirIdMap>)>, +} + +impl<'tcx> NeedlessLateInit<'tcx> { + pub fn new(conf: &'static Conf) -> Self { + Self { + check_grouped_late_init: conf.check_grouped_late_init, + grouped_late_inits: Vec::default(), } - }) - .is_some() + } + + fn check_if_or_match( + &mut self, + cx: &LateContext<'tcx>, + local_stmt: &'tcx LetStmt<'tcx>, + block: &'tcx Block<'tcx>, + binding_id: HirId, + usage: Usage<'tcx>, + exprs: impl IntoIterator>, + ) { + let mut assigns: Vec> = Vec::new(); + for expr in exprs { + let ty = cx.typeck_results().expr_ty(expr); + if ty.is_never() { + continue; + } + if !ty.is_unit() { + return; + } + + let Some(assign_group) = LocalAssignGroup::new(cx, expr) else { + return; + }; + + if let Some(last_group) = assigns.last() + && !assign_group.is_parallel(last_group) + { + return; + } + + assigns.push(assign_group); + } + + let Some(first_group) = assigns.first() else { + return; + }; + // If there are multiple assignments grouped together, lazyly check them after processing the block. + if first_group.0.len() > 1 { + if self.check_grouped_late_init { + let late_inits = if let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + { + late_inits + } else { + &mut self.grouped_late_inits.push_mut((block.hir_id, HirIdMap::default())).1 + }; + + let mut decls = HirIdMap::default(); + decls.insert(binding_id, local_stmt); + late_inits.insert(usage.expr.hir_id, GroupedLateInit { usage, assigns, decls }); + } + + return; + } + + if first_group.0[0].lhs_id == binding_id { + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + local_stmt.span, + "unneeded late initialization", + |diag| { + let mut suggestions = vec![]; + for group in assigns { + suggestions.extend( + group + .0 + .iter() + .flat_map(|assign| { + let rhs_span = assign.rhs.span.source_callsite(); + let mut spans = vec![assign.span.until(rhs_span)]; + + if rhs_span.hi() != assign.span.hi() { + spans.push(rhs_span.shrink_to_hi().with_hi(assign.span.hi())); + } + + spans + }) + .map(|span| (span, String::new())), + ); + } + + suggestions.push((local_stmt.span, String::new())); + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local_stmt, &mut applicability); + suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); + if usage.needs_semi { + suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); + } + let binding_name = cx.tcx.hir_name(binding_id); + let descriptor = if matches!(usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the {descriptor}", + ), + suggestions, + applicability, + ); + }, + ); + } + } } fn contains_let(cond: &Expr<'_>) -> bool { @@ -99,112 +211,35 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { } #[derive(Debug)] -struct LocalAssign { +struct LocalAssign<'tcx> { lhs_id: HirId, - rhs_span: Span, + rhs: &'tcx Expr<'tcx>, span: Span, } -impl LocalAssign { - fn from_expr(expr: &Expr<'_>, span: Span) -> Option { +impl<'tcx> LocalAssign<'tcx> { + fn new(expr: &'tcx Expr<'tcx>, span: Span) -> Option { if expr.span.from_expansion() { return None; } - if let ExprKind::Assign(lhs, rhs, _) = expr.kind { - if lhs.span.from_expansion() { - return None; - } - - Some(Self { + if let ExprKind::Assign(lhs, rhs, _) = expr.kind + && !lhs.span.from_expansion() + { + return Some(Self { lhs_id: lhs.res_local_id()?, - rhs_span: rhs.span.source_callsite(), + rhs, span, - }) - } else { - None - } - } - - fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option { - let assign = match expr.kind { - ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span), - ExprKind::Block(block, _) => { - if let Some((last, other_stmts)) = block.stmts.split_last() - && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind - - && let assign = Self::from_expr(expr, last.span)? - - // avoid visiting if not needed - && assign.lhs_id == binding_id - && other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)) - { - Some(assign) - } else { - None - } - }, - ExprKind::Assign(..) => Self::from_expr(expr, expr.span), - _ => None, - }?; - - if assign.lhs_id == binding_id { - Some(assign) - } else { - None - } - } -} - -fn assignment_suggestions<'tcx>( - cx: &LateContext<'tcx>, - binding_id: HirId, - exprs: impl IntoIterator>, -) -> Option<(Applicability, Vec<(Span, String)>)> { - let mut assignments = Vec::new(); - - for expr in exprs { - let ty = cx.typeck_results().expr_ty(expr); - - if ty.is_never() { - continue; - } - if !ty.is_unit() { - return None; + }); } - let assign = LocalAssign::new(cx, expr, binding_id)?; - - assignments.push(assign); - } - - let suggestions = assignments - .iter() - .flat_map(|assignment| { - let mut spans = vec![assignment.span.until(assignment.rhs_span)]; - - if assignment.rhs_span.hi() != assignment.span.hi() { - spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); - } - - spans - }) - .map(|span| (span, String::new())) - .collect::>(); - - match suggestions.len() { - // All of `exprs` are never types - // https://github.com/rust-lang/rust-clippy/issues/8911 - 0 => None, - 1 => Some((Applicability::MachineApplicable, suggestions)), - // multiple suggestions don't work with rustfix in multipart_suggest - // https://github.com/rust-lang/rustfix/issues/141 - _ => Some((Applicability::Unspecified, suggestions)), + None } } +#[derive(Debug)] struct Usage<'tcx> { - stmt: &'tcx Stmt<'tcx>, + span: Span, expr: &'tcx Expr<'tcx>, needs_semi: bool, } @@ -226,20 +261,34 @@ fn first_usage<'tcx>( .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: true, }), StmtKind::Semi(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: false, }), _ => None, }) + .or_else(|| { + block + .expr + .filter(|expr| is_local_used(cx, *expr, binding_id)) + .map(|expr| Usage { + span: expr.span, + expr, + needs_semi: true, + }) + }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option { +fn local_snippet_without_semicolon<'a>( + cx: &LateContext<'_>, + local: &LetStmt<'_>, + applicability: &mut Applicability, +) -> Cow<'a, str> { let span = local.span.with_hi(match local.ty { // let : ; // ~~~~~~~~~~~~~~~ @@ -249,105 +298,70 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> None => local.pat.span.hi(), }); - span.get_source_text(cx) + snippet_with_applicability(cx, span, "..", applicability) } -fn check<'tcx>( - cx: &LateContext<'tcx>, - local: &'tcx LetStmt<'tcx>, - local_stmt: &'tcx Stmt<'tcx>, - block: &'tcx Block<'tcx>, - binding_id: HirId, -) -> Option<()> { - let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?; - let binding_name = cx.tcx.hir_opt_name(binding_id)?; - let let_snippet = local_snippet_without_semicolon(cx, local)?; - - match usage.expr.kind { - ExprKind::Assign(..) => { - let assign = LocalAssign::new(cx, usage.expr, binding_id)?; - let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); - msg_span.push_span_label(local_stmt.span, "created here"); - msg_span.push_span_label(assign.span, "initialised here"); - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - msg_span, - "unneeded late initialization", - |diag| { - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here"), - vec![ - (local_stmt.span, String::new()), - ( - assign.span, - let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), - ), - ], - Applicability::MachineApplicable, - ); - }, - ); - }, - ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); +#[derive(Debug)] +struct LocalAssignGroup<'tcx>(Vec>); + +impl<'tcx> LocalAssignGroup<'tcx> { + fn new(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + match expr.kind { + ExprKind::Block(Block { expr: Some(expr), .. }, _) + if let Some(assign) = LocalAssign::new(expr, expr.span) => + { + Some(LocalAssignGroup(vec![assign])) + }, + ExprKind::Block(Block { expr: None, stmts, .. }, _) => { + let mut assign_group = Vec::new(); + // Avoid cases when the assignee is used or reassigned in the subsequent assignments + let mut used_locals = HirIdSet::default(); + for stmt in stmts.iter().rev() { + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let Some(assign) = LocalAssign::new(expr, stmt.span) + && !used_locals.contains(&assign.lhs_id) + { + used_locals.insert(assign.lhs_id); + for_each_expr(cx.tcx, assign.rhs, |e| { + if let Some(id) = e.res_local_id() { + used_locals.insert(id); + } + ControlFlow::<()>::Continue(()) + }); + assign_group.push(assign); + continue; } - diag.multipart_suggestion( - format!( - "move the declaration `{binding_name}` here and remove the assignments from the branches" - ), - suggestions, - applicability, - ); - }, - ); - }, - ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, mut suggestions) = - assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); - } + break; + } + if assign_group.is_empty() { + None + } else { + Some(LocalAssignGroup(assign_group)) + } + }, + ExprKind::Assign(..) if let Some(assign) = LocalAssign::new(expr, expr.span) => { + Some(LocalAssignGroup(vec![assign])) + }, + _ => None, + } + } - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), - suggestions, - applicability, - ); - }, - ); - }, - _ => {}, + /// Checks if the assignments in `self` and `other` are parallel, i.e. they have the same number + /// of assignments and the same assignees in the same order. + fn is_parallel(&self, other: &Self) -> bool { + self.0.len() == other.0.len() && self.0.iter().zip(other.0.iter()).all(|(a, b)| a.lhs_id == b.lhs_id) } +} - Some(()) +#[derive(Debug)] +struct GroupedLateInit<'tcx> { + usage: Usage<'tcx>, + assigns: Vec>, + decls: HirIdMap<&'tcx LetStmt<'tcx>>, } -impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit<'tcx> { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let mut parents = cx.tcx.hir_parent_iter(local.hir_id); if let LetStmt { @@ -362,8 +376,147 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { } = local && let Some((_, Node::Stmt(local_stmt))) = parents.next() && let Some((_, Node::Block(block))) = parents.next() + && let Some(usage) = first_usage(cx, *binding_id, local_stmt.hir_id, block) { - check(cx, local, local_stmt, block, *binding_id); + if self.check_grouped_late_init + && let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + && let Some(late_init) = late_inits.get_mut(&usage.expr.hir_id) + { + late_init.decls.insert(*binding_id, local); + return; + } + + match usage.expr.kind { + ExprKind::Assign(..) + if let Some(assign) = LocalAssign::new(usage.expr, usage.expr.span) + && assign.lhs_id == *binding_id => + { + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local, &mut applicability); + let binding_name = cx.tcx.hir_name(*binding_id); + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + msg_span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + let rhs_snippet = snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ); + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![ + (local_stmt.span, String::new()), + (assign.span, format!("{let_snippet} = {rhs_snippet}")), + ], + applicability, + ); + }, + ); + }, + ExprKind::If(cond, then_expr, Some(mut else_expr)) if !contains_let(cond) => { + // Flatten multiple if branches + let mut exprs = vec![then_expr]; + while let ExprKind::If(cond, then, Some(else_)) = else_expr.kind { + if contains_let(cond) { + return; + } + exprs.push(then); + else_expr = else_; + } + exprs.push(else_expr); + self.check_if_or_match(cx, local, block, *binding_id, usage, exprs); + }, + ExprKind::Match(_, arms, MatchSource::Normal) => { + self.check_if_or_match(cx, local, block, *binding_id, usage, arms.iter().map(|arm| arm.body)); + }, + _ => {}, + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if self.check_grouped_late_init + && let Some((_, late_inits)) = self.grouped_late_inits.pop_if(|(hir_id, _)| *hir_id == block.hir_id) + { + 'outer: for (_, late_init) in late_inits { + if late_init.decls.len() < late_init.assigns[0].0.len() { + continue; + } + + let mut suggestions = vec![]; + for assign in &late_init.assigns[0].0 { + if let Some(local) = late_init.decls.get(&assign.lhs_id) + // If the local has a type annotation, skip it since removing the annotation might cause type + // inference issues while annotating the tuple makes the suggestion harder to read. + && local.ty.is_none() + { + suggestions.push((local.span, String::new())); + } else { + continue 'outer; + } + } + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + late_init.usage.span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + for group in &late_init.assigns { + let rhs_snippet = group + .0 + .iter() + .rev() + .map(|assign| { + snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ) + }) + .intersperse(", ".into()) + .collect::(); + suggestions.push(( + group.0.last().unwrap().span.to(group.0[0].span), + format!("({rhs_snippet})"), + )); + } + let let_snippet = late_init.assigns[0] + .0 + .iter() + .rev() + .map(|assign| cx.tcx.hir_name(assign.lhs_id).to_string()) + .intersperse(", ".to_owned()) + .collect::(); + suggestions.push((late_init.usage.span.shrink_to_lo(), format!("let ({let_snippet}) = "))); + if late_init.usage.needs_semi { + suggestions.push((late_init.usage.span.shrink_to_hi(), ";".to_owned())); + } + let descriptor = if matches!(late_init.usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!("move the declarations here and remove the assignments from the {descriptor}"), + suggestions, + applicability, + ); + }, + ); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 91358ef77fa1e..0d5b2ee6ba29e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,6 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::HasSession as _; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use core::ops::ControlFlow; @@ -207,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. let mut closures: FxIndexSet = FxIndexSet::default(); - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); } @@ -269,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // If the argument is never used mutably, we emit the warning. let sp = input.span; if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind { - let Some(after_mut_span) = cx.sess().source_map().span_extend_to_prev_str( + let Some(after_mut_span) = cx.tcx.sess.source_map().span_extend_to_prev_str( inner_ty.ty.span.shrink_to_lo(), "mut", true, diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 4ff5b0b0b3c39..a2ad531751288 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), @@ -275,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9e6fc68116d4f..933ad716446f2 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::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; @@ -278,8 +278,8 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let ExprKind::Index(..) = &expr.kind { if !is_inside_always_const_context(cx.tcx, expr.hir_id) && let [arr, func] = &*reduced - && let Some(arr) = arr.span.get_source_text(cx) - && let Some(func) = func.span.get_source_text(cx) + && let Some(arr) = arr.span.get_text(cx) + && let Some(func) = func.span.get_text(cx) { span_lint_hir_and_then( cx, @@ -300,7 +300,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } else { let mut snippet = String::new(); for e in reduced { - if let Some(snip) = e.span.get_source_text(cx) { + if let Some(snip) = e.span.get_text(cx) { snippet.push_str(&snip); snippet.push_str("; "); } else { diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index cb934466bd890..cf19cd9fc81d5 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet_with_applicability}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index 487eaecf18890..2755e3add760e 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -198,7 +198,7 @@ impl LazyInfo { // visit body to collect `Lazy::new` calls let mut new_fn_calls = FxIndexMap::default(); - for_each_expr::<(), ()>(cx, body, |ex| { + for_each_expr::<(), ()>(cx.tcx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index b449681ae2e14..e601d54cc4eda 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanExt}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -129,7 +129,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace if let ExpnKind::Macro(MacroKind::Bang, mac_name) = expn_data.kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = expn_data.call_site.get_source_text(cx) + && let Some(snip) = expn_data.call_site.get_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index 6e7ee727965db..5735c23a62ac8 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::token::LitKind; use rustc_ast::{Expr, ExprKind}; use rustc_errors::Applicability; @@ -86,7 +86,7 @@ impl EarlyLintPass for OctalEscapes { // Last check to make sure the source text matches what we read from the string. // Macros are involved somehow if this doesn't match. - if span.check_source_text(cx, |src| match *src.as_bytes() { + if span.check_text(cx, |src| match *src.as_bytes() { [b'\\', b'0', lo] => lo == c_lo, [b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 5695779425f4a..fa391fad589fe 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::qualify_min_const_fn::is_stable_const_fn; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, is_in_const_context, trait_ref_of_method}; @@ -74,8 +74,8 @@ pub(super) fn check<'tcx>( expr.span, "manual implementation of an assign operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs.span.get_text(cx) { diag.span_suggestion( expr.span, diff --git a/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs b/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs index 8511f2151342d..08e56e4516c52 100644 --- a/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs +++ b/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::numeric_literal; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -40,7 +40,7 @@ fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { if let LitKind::Int(Pu128(val), _) = lit.node && !is_single_digit(val) && !is_power_of_twoish(val) - && let Some(src) = lit.span.get_source_text(cx) + && let Some(src) = lit.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && num_lit.is_decimal() { diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs index f0b6407a141bd..8a2cdf430927b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{eq_expr_value, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -43,8 +43,8 @@ fn lint_misrefactored_assign_op( expr.span, "variable appears on both sides of an assignment operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs_other.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs_other.span.get_text(cx) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index cc147a4a84bdc..fa7a5d8feb380 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index a5e57d97301e3..473718c5419f1 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -73,7 +73,7 @@ impl PathbufPushSearcher<'_> { && let Some(arg) = self.arg && let ExprKind::Lit(x) = arg.kind && let LitKind::Str(_, StrStyle::Cooked) = x.node - && let Some(s) = arg.span.get_source_text(cx) + && let Some(s) = arg.span.get_text(cx) { Some(format!(" = PathBuf::from({s});")) } else { @@ -83,8 +83,8 @@ impl PathbufPushSearcher<'_> { fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option { let arg = self.arg?; - let arg_str = arg.span.get_source_text(cx)?; - let init_val = self.init_val.span.get_source_text(cx)?; + let arg_str = arg.span.get_text(cx)?; + let init_val = self.init_val.span.get_text(cx)?; Some(format!(" = {init_val}.join({arg_str});")) } diff --git a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs index 40bc42dcdcb36..200ab8f8418f1 100644 --- a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs +++ b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs @@ -1,7 +1,7 @@ use super::PTR_ARG; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_expr_use_or_unification_node, is_lint_allowed, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; @@ -55,7 +55,7 @@ pub(super) fn check_body<'tcx>( .chain(result.replacements.iter().map(|r| { ( r.expr_span, - format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), + format!("{}{}", r.self_span.get_text(cx).unwrap(), r.replacement), ) })) .collect(), @@ -156,7 +156,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> { DerefTy::Path => f.write_str("Path"), DerefTy::Slice(hir_ty, ty) => { f.write_char('[')?; - match hir_ty.and_then(|s| s.get_source_text(self.0)) { + match hir_ty.and_then(|s| s.get_text(self.0)) { Some(s) => f.write_str(&s)?, None => ty.fmt(f)?, } @@ -279,7 +279,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - match ty.span().get_source_text(cx) { + match ty.span().get_text(cx) { Some(s) => format!("&{}{s}", mutability.prefix_str()), None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)), }, diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index cbfd1af1907b7..7f95adba870d7 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -629,6 +629,11 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + // Cheap `let` check before the costlier lint level and const context queries. + if !matches!(stmt.kind, StmtKind::Let(..)) { + return; + } + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { return; @@ -646,7 +651,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() + // Cheap `if`/`match` check before the costlier lint level and const context queries. + if matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) + && !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 71965ee1e29f9..2fe368c0639ab 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{ @@ -191,14 +191,21 @@ impl Ranges { impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind + && matches!( + op.node, + BinOpKind::And | BinOpKind::BitAnd | BinOpKind::Or | BinOpKind::BitOr + ) && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + && !is_in_const_context(cx) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } - check_exclusive_range_plus_one(cx, expr); - check_inclusive_range_minus_one(cx, expr); - check_reversed_empty_range(cx, expr); + if let Some(range) = higher::Range::hir(cx, expr) { + check_exclusive_range_plus_one(cx, expr, &range); + check_inclusive_range_minus_one(cx, expr, &range); + check_reversed_empty_range(cx, expr, &range); + } } } @@ -210,10 +217,6 @@ fn check_possible_range_contains( expr: &Expr<'_>, span: Span, ) { - if is_in_const_context(cx) { - return; - } - let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -300,7 +303,7 @@ fn check_possible_range_contains( if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind && op == lhs_op.node && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) - && new_span.check_source_text(cx, |src| { + && new_span.check_text(cx, |src| { // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong src.matches('(').count() == src.matches(')').count() }) @@ -481,54 +484,58 @@ fn can_switch_ranges<'tcx>( } // exclusive range plus one: `x..(y+1)` -fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::HalfOpen, y_plus_one, RANGE_PLUS_ONE, "an inclusive range would be more readable", - "..=", ); } // inclusive range minus one: `x..=(y-1)` -fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::Closed, y_minus_one, RANGE_MINUS_ONE, "an exclusive range would be more readable", - "..", ); } /// Check for a `kind` of range in `expr`, check for `predicate` on the end, -/// and emit the `lint` with `msg` and the `operator`. +/// and emit the `lint` with `msg`, suggesting the opposite range limits. fn check_range_switch<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, + range: &higher::Range<'tcx>, kind: RangeLimits, predicate: impl for<'hir> FnOnce(&Expr<'hir>) -> Option<&'hir Expr<'hir>>, lint: &'static Lint, msg: &'static str, - operator: &str, ) { - if let Some(range) = higher::Range::hir(cx, expr) - && let higher::Range { - start, - end: Some(end), - limits, - span, - } = range + if let higher::Range { + start, + end: Some(end), + limits, + span, + } = *range && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(end) && can_switch_ranges(cx, span.ctxt(), expr, kind, cx.typeck_results().expr_ty(y)) { + // Suggest the opposite range limits to the ones being checked. + let operator = match kind { + RangeLimits::HalfOpen => "..=", + RangeLimits::Closed => "..", + }; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -550,7 +557,7 @@ fn check_range_switch<'tcx>( } } -fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>, range: &higher::Range<'_>) { fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!( get_parent_expr(cx, expr), @@ -580,12 +587,12 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } } - if let Some(higher::Range { + if let higher::Range { start: Some(start), end: Some(end), limits, span, - }) = higher::Range::hir(cx, expr) + } = *range && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() && let ecx = ConstEvalCtxt::new(cx) diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index d8aa88d48b09d..31c90c591b7c2 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet_opt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(format_args) = &expr.kind && !format_args.span.in_external_macro(cx.sess().source_map()) - && format_args.span.check_source_text(cx, |src| src.starts_with('r')) + && format_args.span.check_text(cx, |src| src.starts_with('r')) && let Some(str) = snippet_opt(cx.sess(), format_args.span) && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count() && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1) @@ -97,7 +97,7 @@ impl EarlyLintPass for RawStrings { _ => return, } && !expr.span.in_external_macro(cx.sess().source_map()) - && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) + && expr.span.check_text(cx, |src| src.starts_with(prefix)) { self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr()); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index bfb704dd21719..db3be9e64f147 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use clippy_utils::{fn_has_unsatisfiable_preds, sym}; use rustc_errors::Applicability; @@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .unwrap_crate_local() .lint_root; - if let Some(snip) = span.get_source_text(cx) + if let Some(snip) = span.get_text(cx) && let Some(dot) = snip.rfind('.') { let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 7ebf7ccb7571e..c7f7b47403376 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, UseKind}; @@ -49,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && !is_ignorable_export(item) - && !item.span.in_external_macro(cx.sess().source_map()) + && !item.span.in_external_macro(cx.tcx.sess.source_map()) { let span = item .kind diff --git a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs index 602093259eaec..2dc03328fef86 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs @@ -144,7 +144,7 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: S // Also check that within the body of the function there is also no function call // with the same name (since it will result in recursion) - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Path(qpath) = &expr.kind && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() && let Some(name) = tcx.opt_item_name(def_id) diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs index b6a28571230a6..22d1c21aa43ba 100644 --- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::{BindingMode, Pat, PatKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::in_automatically_derived; +use rustc_hir::{BindingMode, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -29,10 +30,11 @@ declare_clippy_lint! { declare_lint_pass!(RefPatterns => [REF_PATTERNS]); -impl EarlyLintPass for RefPatterns { - fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind +impl<'tcx> LateLintPass<'tcx> for RefPatterns { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'tcx>) { + if let PatKind::Binding(BindingMode::REF, _, _, _) = pat.kind && !pat.span.from_expansion() + && !in_automatically_derived(cx.tcx, pat.hir_id) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 80696f0a81b15..837c29c08851b 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::paths; use clippy_utils::paths::PathLookup; use clippy_utils::res::MaybeQPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -190,7 +190,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape }; if let Some((primary, auxiliary, kind)) = parts - && let Some(literal_snippet) = base.get_source_text(cx) + && let Some(literal_snippet) = base.get_text(cx) && let Some(inner) = literal_snippet.get(offset as usize..) // Only convert to native rustc spans if the parsed regex matches the // source snippet exactly, to ensure the span offsets are correct diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index 2ec921ed21c7d..c1a909fdde4e1 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -67,7 +67,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) } } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index e806123596b85..7fdc03891a889 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -94,7 +94,7 @@ impl SemicolonBlock { // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call // (5); // } - if remove_span.check_source_text(cx, |src| src.contains(')')) { + if remove_span.check_text(cx, |src| src.contains(')')) { return; } diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index 4aba49071ee56..03acfb22c6c82 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -127,7 +127,7 @@ fn find_insert_calls<'tcx>( contains_expr: &OpExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.value, insert_expr.value) diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 4ddf82773d872..5072e39afb8c1 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -199,7 +199,7 @@ pub fn is_local_used_except<'tcx>( id: HirId, except: Option, ) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) } else if e.res_local_id() == Some(id) { diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index aba51114c533a..67c8710b80ec8 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -146,16 +146,24 @@ impl SingleComponentPathImports { // ``` let mut macros = Vec::new(); - let mut import_usage_visitor = ImportUsageVisitor::default(); for item in items { self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros); + } + + // Only walk the module's AST in search of `self::xxx` paths when there are single + // component imports left to lint, as the visitor recurses into every nested item. + single_use_usages.retain(|usage| !imports_reused_with_self.contains(&usage.name)); + if single_use_usages.is_empty() { + return; + } + + let mut import_usage_visitor = ImportUsageVisitor::default(); + for item in items { import_usage_visitor.visit_item(item); } for usage in single_use_usages { - if !imports_reused_with_self.contains(&usage.name) - && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) - { + if !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) { self.found.entry(usage.item_id).or_default().push(usage); } } diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index fc5702a358bbc..3d5de785b790c 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::{Range, VecArgs}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, RangeLimits, UintTy}; @@ -91,7 +91,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return; }; // `is_from_proc_macro` will skip any `vec![]`. Let's not! 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 d519afba61b53..0f7cbd503bcd8 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 @@ -238,20 +238,21 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> /// Does not catch individually moved items fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool { loop { - if let Some(stability) = cx.tcx.lookup_stability(def_id) - && let StabilityLevel::Stable { - since, - allowed_through_unstable_modules: None, - } = stability.level - { - let stable = match since { - StableSince::Version(v) => msrv.meets(cx, v), - StableSince::Current => msrv.current(cx).is_none(), - StableSince::Err(_) => false, - }; - - if !stable { - return false; + if let Some(stability) = cx.tcx.lookup_stability(def_id) { + match stability.level { + // Workaround for items from `core::intrinsics` with a stable export in a different module. + // Not that we ignore the `since` field as we are already accessing the item in question. + StabilityLevel::Stable { + allowed_through_unstable_modules: Some(_), + .. + } => return true, + StabilityLevel::Stable { since, .. } => match since { + StableSince::Version(v) if !msrv.meets(cx, v) => return false, + StableSince::Current if msrv.current(cx).is_none() => return false, + StableSince::Err(_) => return false, + StableSince::Version(_) | StableSince::Current => {}, + }, + StabilityLevel::Unstable { .. } => return false, } } diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index b4eb8977bf0fb..986429ec0829e 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -147,7 +147,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< // We want to retrieve all the comparisons done. // They are ordered in a nested way and so we need to traverse the AST to collect them all. - if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { + if for_each_expr(cx.tcx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { if left.res_local_id() == Some(binding) @@ -230,13 +230,13 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind - && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() - && ty.is_str() && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) && let Some(arg) = args.get(pos) + && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() { check_single_char_pattern_lint(cx, arg); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 7b690b7eb136c..16cf0ecbc0bc7 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; @@ -212,10 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { bounds_span = bounds_span.to(bound.span); } - let fixed_trait_snippet = unique_traits - .iter() - .filter_map(|b| b.span.get_source_text(cx)) - .join(" + "); + let fixed_trait_snippet = unique_traits.iter().filter_map(|b| b.span.get_text(cx)).join(" + "); span_lint_and_sugg( cx, @@ -451,7 +448,7 @@ fn rollup_traits<'cx, 'tcx>( let traits = comparable_bounds .iter() - .filter_map(|&(_, span)| span.get_source_text(cx)) + .filter_map(|&(_, span)| span.get_text(cx)) .join(" + "); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index 28cef60dfd2af..89627eded65f5 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt as _}; +use clippy_utils::source::SpanExt as _; use rustc_errors::Applicability; use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; @@ -114,8 +114,8 @@ fn ty_cannot_be_named(ty: Ty<'_>) -> bool { ) } -fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - span.with_source_text(sess, |name| { +fn maybe_name_by_expr<'a>(cx: &LateContext<'_>, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(cx, |name| { (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) }) .flatten() 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 2bf1d8be46536..d35139f2f6b9b 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -393,7 +393,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( } // this should roughly be the reverse of `block_parents_have_safety_comment` - if for_each_expr(cx, expr, |expr| match expr.kind { + if for_each_expr(cx.tcx, expr, |expr| match expr.kind { hir::ExprKind::Block( Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index 735225a9e2cee..13baa378910d9 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -180,32 +180,21 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { let args = std::iter::once(receiver).chain(args.iter()).collect::>(); let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait); for (i, trait_name) in arg_indices { - match check_arg(cx, args[i]) { - Some((span, None)) => { - span_lint( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ + if let Some((span, last_semi)) = check_arg(cx, args[i]) { + span_lint_and_then( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + format!( + "this closure returns \ the unit type which also implements {trait_name}" - ), - ); - }, - Some((span, Some(last_semi))) => { - span_lint_and_help( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ - the unit type which also implements {trait_name}" - ), - Some(last_semi), - "probably caused by this trailing semicolon", - ); - }, - None => {}, + ), + |diag| { + if let Some(last_semi) = last_semi { + diag.span_help(last_semi, "probably caused by this trailing semicolon"); + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 6df3e7baa1bca..b9d6c81a152b3 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -1,7 +1,7 @@ use std::iter; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::expr_type_is_certain; use clippy_utils::{is_empty_block, is_expr_default, is_from_proc_macro}; @@ -85,7 +85,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ && block.expr.is_none() && let Some(last_stmt) = block.stmts.iter().last() && let StmtKind::Semi(last_expr) = last_stmt.kind - && let Some(snip) = last_expr.span.get_source_text(cx) + && let Some(snip) = last_expr.span.get_text(cx) { Some((last_stmt.span, snip)) } else { @@ -117,7 +117,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg)) .collect(); - if let Some(call_snippet) = expr.span.get_source_text(cx) { + if let Some(call_snippet) = expr.span.get_text(cx) { if arg_snippets_without_redundant_exprs.is_empty() && let suggestions = args_to_recover .iter() diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index 8d4f1a153def8..4112e1b6ca63f 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::Symbol; @@ -81,8 +81,13 @@ impl UnnecessaryBoxReturns { // It's sometimes useful to return Box if T is unsized, so don't lint those. // Also, don't lint if we know that T is very large, in which case returning - // a Box may be beneficial. - if boxed_ty.is_sized(cx.tcx, cx.typing_env()) && approx_ty_size(cx, boxed_ty) <= self.maximum_size { + // a Box may be beneficial. When the size depends on generic parameters + // (e.g. `[T; N]`) it cannot be determined here, so don't lint that either, as + // the `Box` may be a deliberate choice to avoid copying a large value. + if boxed_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(layout) = cx.layout_of(boxed_ty) + && layout.size.bytes() <= self.maximum_size + { span_lint_and_then( cx, UNNECESSARY_BOX_RETURNS, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs index 75b0b7b10687a..60a6688927ab5 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index bfbfd0903bbe8..0a6f3f5992ece 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_def_id_trait_method; -use clippy_utils::source::{HasSession, snippet_with_applicability, walk_span_to_context}; +use clippy_utils::source::{snippet_with_applicability, walk_span_to_context}; use clippy_utils::usage::is_todo_unimplemented_stub; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -303,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // evaluate the expression, to immediately evaluate the expression. let mut app = Applicability::MaybeIncorrect; - let async_span = cx.sess().source_map().span_extend_while_whitespace(async_span); + let async_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_span); let signature_snippet = snippet_with_applicability(cx, signature_span, "_", &mut app); let tail_snippet = snippet_with_applicability(cx, tail_span, "_", &mut app).to_string(); diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index d503bd3379b08..bb2ad9f92157c 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; +use clippy_utils::source::{SpanExt, position_before_rarrow}; use clippy_utils::{is_never_expr, is_unit_expr}; use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 41d06a29d2c25..f487b5777b77a 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -132,8 +132,8 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) { Some(recv) } else { @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { + if name.ident.name == sym::into && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -393,8 +393,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) - && name.ident.name == sym::try_into + if name.ident.name == sym::try_into + && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) && a.is_diag_item(cx, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/useless_vec.rs b/src/tools/clippy/clippy_lints/src/useless_vec.rs index 7b6122213d608..cdb757ed89e23 100644 --- a/src/tools/clippy/clippy_lints/src/useless_vec.rs +++ b/src/tools/clippy/clippy_lints/src/useless_vec.rs @@ -7,7 +7,7 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_parent_expr, higher, is_in_test, span_contains_comment}; @@ -285,10 +285,10 @@ impl SuggestedType { assert!(args_span.is_none_or(|s| !s.from_expansion())); assert!(len_span.is_none_or(|s| !s.from_expansion())); - let maybe_args = args_span.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")); + let maybe_args = args_span.map(|sp| sp.get_text(cx).expect("spans are always crate-local")); let maybe_args = maybe_args.as_deref().unwrap_or_default(); let maybe_len = len_span - .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|sp| sp.get_text(cx).expect("spans are always crate-local")) .map(|st| format!("; {st}")) .unwrap_or_default(); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index c60d84785a229..2bcf369fb3334 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -6,11 +6,11 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_hir::{ - self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, + self as hir, BindingMode, Body, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{FloatTy, IntTy, UintTy}; +use rustc_middle::ty::{FloatTy, IntTy, TypeckResults, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; @@ -137,15 +137,31 @@ impl<'tcx> LateLintPass<'tcx> for Author { fn check_item(cx: &LateContext<'_>, hir_id: HirId) { if let Some(body) = cx.tcx.hir_maybe_body_owned_by(hir_id.expect_owner().def_id) { - check_node(cx, hir_id, |v| { - v.expr(&v.bind("expr", body.value)); - }); + check_node_with_body( + cx, + hir_id, + |v| { + v.expr(&v.bind("expr", body.value)); + }, + Some(body), + ); } } fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { + check_node_with_body(cx, hir_id, f, None); +} + +/// Check the node at `hir_id`, in the context of `body` or the default from `cx` if none is given. +fn check_node_with_body( + cx: &LateContext<'_>, + hir_id: HirId, + f: impl Fn(&PrintVisitor<'_, '_>), + body: Option<&Body<'_>>, +) { if has_attr(cx, hir_id) { - f(&PrintVisitor::new(cx)); + let typeck_results = body.map_or_else(|| cx.typeck_results(), |body| cx.tcx.typeck_body(body.id())); + f(&PrintVisitor::new(cx, typeck_results)); println!("{{"); println!(" // report your lint here"); println!("}}"); @@ -199,6 +215,7 @@ impl Display for OptionPat { struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + typeck_results: &'tcx TypeckResults<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names ids: Cell>, @@ -207,9 +224,10 @@ struct PrintVisitor<'a, 'tcx> { } impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>) -> Self { Self { cx, + typeck_results, ids: Cell::default(), first: Cell::new(true), } @@ -291,7 +309,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn maybe_path<'p>(&self, path: &Binding>) { - if let Some(id) = path.value.res(self.cx).opt_def_id() + if let Some(id) = path.value.res(self.typeck_results).opt_def_id() && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs index 6629a67f78bd4..6401b48f70b05 100644 --- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs @@ -1,18 +1,18 @@ use clippy_utils::macros::FormatArgsStorage; -use clippy_utils::source::SpanRangeExt; -use itertools::Itertools; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, hygiene}; -use std::iter::once; +use rustc_span::source_map::SourceMap; +use rustc_span::{Span, SpanData}; use std::mem; /// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { format_args: FxHashMap, + parent_spans: Vec, storage: FormatArgsStorage, } @@ -20,6 +20,7 @@ impl FormatArgsCollector { pub fn new(storage: FormatArgsStorage) -> Self { Self { format_args: FxHashMap::default(), + parent_spans: Vec::new(), storage, } } @@ -30,7 +31,7 @@ impl_lint_pass!(FormatArgsCollector => []); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(args) = &expr.kind { - if has_span_from_proc_macro(cx, args) { + if self.has_span_from_external_macro(cx.sess().source_map(), expr.span, args) { return; } @@ -43,53 +44,99 @@ impl EarlyLintPass for FormatArgsCollector { } } -/// Detects if the format string or an argument has its span set by a proc macro to something inside -/// a macro callsite, e.g. -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// ``` -/// -/// Where `some_proc_macro` expands to -/// -/// ```ignore -/// println!("output {}", a); -/// ``` -/// -/// But with the span of `"output {}"` set to the macro input -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// // ^^^^^^^^^^ -/// ``` -fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { - let ctxt = args.span.ctxt(); +impl FormatArgsCollector { + /// Detects if the format string or an argument has its span set by a proc macro to something + /// inside a macro callsite, e.g. + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// ``` + /// + /// Where `some_proc_macro` expands to + /// + /// ```ignore + /// println!("output {}", a); + /// ``` + /// + /// But with the span of `"output {}"` set to the macro input + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// // ^^^^^^^^^^ + /// ``` + fn has_span_from_external_macro(&mut self, sm: &SourceMap, fmt_sp: Span, args: &FormatArgs) -> bool { + let mut fmt_sp = fmt_sp.data(); + + // Find the first macro call that contains the format string. + let arg_sp = if let Some(arg_sp) = walk_span_to_context(args.span, fmt_sp.ctxt) { + arg_sp.data() + } else { + // Try to find a common parent for the format call and the format string. + self.parent_spans.clear(); + // `fmt_sp.ctxt` isn't a parent of the format string so don't add it to the + // search. The first iteration will always run since it can't be the root. + while !fmt_sp.ctxt.is_root() { + fmt_sp = fmt_sp.ctxt.outer_expn_data().call_site.data(); + self.parent_spans.push(fmt_sp); + } + let mut arg_sp = args.span.data(); + // Note: A parent span will always eventually be found since the root context + // is an ancestor of all contexts. + loop { + match self.parent_spans.iter().find(|s| s.ctxt == arg_sp.ctxt) { + Some(call_sp) if call_sp.lo <= arg_sp.lo && arg_sp.hi <= call_sp.hi => { + fmt_sp = *call_sp; + break arg_sp; + }, + // If the string isn't within the call span we some macro stuff we can't + // easily interpret. + Some(_) => return true, + None => arg_sp = arg_sp.ctxt.outer_expn_data().call_site.data(), + } + } + }; + if fmt_sp.ctxt.in_external_macro(sm) { + return true; + } + let Some(src) = arg_sp.get_source_range(sm) else { + return true; + }; + let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) else { + return true; + }; - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^^^^ ^^^^^ ^^^^^^^ - let argument_span = args - .arguments - .explicit_args() - .iter() - .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt)); + // Check the spans between the format string and the arguments and between each argument. + args.arguments + .explicit_args() + .iter() + .try_fold(src.range.end, |start, arg| { + let expr_sp = walk_span_to_context(arg.expr.span, fmt_sp.ctxt)?.data(); + let expr_start = (expr_sp.lo.0 - src.sf.start_pos.0) as usize; + let expr_end = (expr_sp.hi.0 - src.sf.start_pos.0) as usize; + let mut tks = tokenize(src_text.get(start..expr_start)?, FrontmatterAllowed::No) + .map(|x| x.kind) + .filter(|x| { + !matches!( + x, + TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { + doc_style: None, + terminated: true + } + | TokenKind::Whitespace + ) + }); - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^ ^^ ^^^^^^ - !once(args.span) - .chain(argument_span) - .tuple_windows() - .map(|(start, end)| start.between(end)) - .all(|sp| { - sp.check_source_text(cx, |src| { - // text should be either `, name` or `, name =` - let mut iter = tokenize(src, FrontmatterAllowed::No).filter(|t| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }); - iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma)) - && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq)) + // `,` or `, ident =` + let matches = matches!(tks.next(), Some(TokenKind::Comma)) + && match tks.next() { + Some(TokenKind::Ident) => matches!(tks.next(), Some(TokenKind::Eq)), + Some(_) => false, + None => true, + } + && tks.next().is_none(); + matches.then_some(expr_end) }) - }) + .is_none() + } } diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 74ffeb629d2be..9cefc54598b78 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -152,5 +152,5 @@ impl EarlyLintPass for Visibility { } fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool { - !span.check_source_text(cx, |src| src.starts_with("pub")) + !span.check_text(cx, |src| src.starts_with("pub")) } diff --git a/src/tools/clippy/clippy_lints/src/write/literal.rs b/src/tools/clippy/clippy_lints/src/write/literal.rs index 699ac7ea7a5cd..df31fad8615ed 100644 --- a/src/tools/clippy/clippy_lints/src/write/literal.rs +++ b/src/tools/clippy/clippy_lints/src/write/literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::format_arg_removal_span; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::token::LitKind; use rustc_ast::{ @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) && let Some(arg) = format_args.arguments.by_index(index) && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() - && let Some(value_string) = arg.expr.span.get_source_text(cx) + && let Some(value_string) = arg.expr.span.get_text(cx) { let (replacement, replace_raw) = match lit.kind { LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) _ => continue, }; - let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { + let Some(format_string_snippet) = format_args.span.get_text(cx) else { continue; }; let format_string_is_raw = format_string_snippet.starts_with('r'); diff --git a/src/tools/clippy/clippy_lints/src/write/with_newline.rs b/src/tools/clippy/clippy_lints/src/write/with_newline.rs index e4b51da3cadcf..15cc29818aec4 100644 --- a/src/tools/clippy/clippy_lints/src/write/with_newline.rs +++ b/src/tools/clippy/clippy_lints/src/write/with_newline.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::MacroCall; -use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::source::{SpanExt, expand_past_previous_comma}; use clippy_utils::sym; use rustc_ast::{FormatArgs, FormatArgsPiece}; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = format_string_span.get_source_text(cx) else { + let Some(format_snippet) = format_string_span.get_text(cx) else { return; }; diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index 8e2166858fec8..f75a9f67d0563 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -63,15 +63,23 @@ static LINTS: &[&Lint] = &[ pub fn register_lints(store: &mut LintStore) { store.register_lints(LINTS); - store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); - store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); - store.register_late_pass(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); - store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); - store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); - store.register_late_pass(|_| Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem)); + store.register_early_pass(Box::new(|| { + Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths) + })); + store.register_early_pass(Box::new(|| Box::new(produce_ice::ProduceIce))); + store.register_late_pass(Box::new(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls))); + store.register_late_pass(Box::new(|_| Box::::default())); + store.register_late_pass(Box::new(|_| { + Box::::default() + })); + store.register_late_pass(Box::new(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath))); + store.register_late_pass(Box::new(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass))); + store.register_late_pass(Box::new(|_| Box::new(msrv_attr_impl::MsrvAttrImpl))); + store.register_late_pass(Box::new(|_| { + Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new()) + })); + store.register_late_pass(Box::new(|_| Box::new(unusual_names::UnusualNames))); + store.register_late_pass(Box::new(|_| { + Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem) + })); } diff --git a/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs b/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs index b300dfa27b0eb..e0148c79477c0 100644 --- a/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs +++ b/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs @@ -541,7 +541,7 @@ fn extract_nested_is_diag_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diag_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { @@ -554,7 +554,7 @@ fn extract_nested_is_diagnostic_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diagnostic_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 95b8bdde5cd91..f322d03f18795 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-2026-06-11 +nightly-2026-06-25 ``` diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 8df82489d6a3f..7718afc5f0222 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,6 +1,6 @@ //! Utility functions for attributes, including Clippy's built-in ones -use crate::source::SpanRangeExt; +use crate::source::SpanExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr::AttributeExt; use rustc_errors::Applicability; @@ -37,8 +37,6 @@ pub fn check_clippy_attr(sess: &Session, attr: &A) { | sym::version | sym::cognitive_complexity | sym::dump - | sym::disallowed_profile - | sym::disallowed_profiles | sym::msrv | sym::has_significant_drop | sym::format_args => {}, @@ -107,7 +105,12 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { /// Checks whether the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { - s.check_source_text(cx, |src| { + s.check_text(cx, |src| { + // PERF: A `#[cfg]` needs a literal `#`, so skip the lexer when the source has none. + if !src.contains('#') { + return false; + } + let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 098be98f3672c..11e6e32ba6c56 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ #![expect(clippy::float_cmp)] use crate::res::MaybeDef; -use crate::source::{SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, sym, unsext}; use rustc_abi::Size; @@ -522,9 +522,9 @@ pub fn eval_int(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { /// /// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, + pub tcx: TyCtxt<'tcx>, + pub typing_env: ty::TypingEnv<'tcx>, + pub typeck: &'tcx TypeckResults<'tcx>, source: Cell, ctxt: Cell, } @@ -885,12 +885,22 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => return None, }; + let args = self.typeck.node_args(id); + + if !args.is_empty() { + let owner_def_id = self.typeck.hir_owner.def_id.to_def_id(); + let identity_args = ty::GenericArgs::identity_for_item(self.tcx, owner_def_id); + // Don't try to fully evaluate consts inside code whose bounds can't be satisfied. + if self + .tcx + .instantiate_and_check_impossible_predicates((owner_def_id, identity_args)) + { + return None; + } + } + self.tcx - .const_eval_resolve( - self.typing_env, - mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), - qpath.span(), - ) + .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(did, args), qpath.span()) .ok() } @@ -934,7 +944,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx) + && let Some(src) = (span.lo..expr_lo).get_source_range(self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; diff --git a/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs b/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs deleted file mode 100644 index 98a498981b3b5..0000000000000 --- a/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::sym; -use rustc_ast::ast::{LitKind, MetaItemInner}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::{Attribute, HirId}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; - -/// One profile name referenced by a `#[clippy::disallowed_profile(...)]` or -/// `#[clippy::disallowed_profiles(...)]` attribute on an item. -/// -/// A single attribute produces one `ProfileEntry` per string argument. The entry records which -/// attribute variant introduced it, the profile name, and the span of that string literal so -/// diagnostics (e.g. "unknown profile") can point at the exact argument. -#[derive(Copy, Clone)] -pub struct ProfileEntry { - pub attr_name: Symbol, - pub name: Symbol, - pub span: Span, -} - -/// The set of profiles active at some `HirId`, obtained by walking up the HIR from that id and -/// collecting the first ancestor that carries a `#[clippy::disallowed_profile(s)]` attribute. -/// -/// An empty selection is represented by `None` at the call site; a `ProfileSelection` is always -/// non-empty. -#[derive(Clone)] -pub struct ProfileSelection { - entries: SmallVec<[ProfileEntry; 2]>, -} - -impl ProfileSelection { - pub fn new(entries: SmallVec<[ProfileEntry; 2]>) -> Self { - Self { entries } - } - - pub fn is_empty(&self) -> bool { - self.entries.is_empty() - } - - pub fn iter(&self) -> impl Iterator { - self.entries.iter() - } -} - -#[derive(Default)] -pub struct ProfileResolver { - cache: FxHashMap>, -} - -impl ProfileResolver { - pub fn active_profiles(&mut self, cx: &LateContext<'_>, hir_id: HirId) -> Option<&ProfileSelection> { - // NOTE: The `contains_key`+`get` dance is intentional: using only `get` here triggers borrowck - // errors because we need to mutate `self.cache` on cache misses. - if self.cache.contains_key(&hir_id) { - return self.cache.get(&hir_id).and_then(|selection| selection.as_ref()); - } - - let (resolved, visited) = self.resolve(cx, hir_id); - - for id in visited { - self.cache.entry(id).or_insert_with(|| resolved.clone()); - } - self.cache.insert(hir_id, resolved); - - self.cache.get(&hir_id).and_then(|selection| selection.as_ref()) - } - - fn resolve(&self, cx: &LateContext<'_>, start: HirId) -> (Option, SmallVec<[HirId; 8]>) { - let mut visited = SmallVec::<[HirId; 8]>::new(); - let mut current = Some(start); - - while let Some(id) = current { - if let Some(cached) = self.cache.get(&id) { - return (cached.clone(), visited); - } - - visited.push(id); - - if let Some(selection) = profiles_from_attrs(cx, cx.tcx.hir_attrs(id)) { - return (Some(selection), visited); - } - - if id == rustc_hir::CRATE_HIR_ID { - current = None; - } else { - current = Some(cx.tcx.parent_hir_id(id)); - } - } - - (None, visited) - } -} - -fn profiles_from_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) -> Option { - let mut entries = SmallVec::<[ProfileEntry; 2]>::new(); - - for attr in attrs { - let path = attr.path(); - if path.len() != 2 || path[0] != sym::clippy { - continue; - } - - let name = path[1]; - if name != sym::disallowed_profile && name != sym::disallowed_profiles { - continue; - } - - let attr_label = if name == sym::disallowed_profiles { - "`clippy::disallowed_profiles`" - } else { - "`clippy::disallowed_profile`" - }; - - let Some(items) = attr.meta_item_list() else { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects string arguments")) - .emit(); - continue; - }; - - if items.is_empty() { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects at least one profile name")) - .emit(); - continue; - } - - if name == sym::disallowed_profile && items.len() != 1 { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), "use `clippy::disallowed_profiles` for multiple profiles") - .emit(); - } - - for item in items { - match literal_symbol(&item) { - Some((symbol, span)) => entries.push(ProfileEntry { - attr_name: name, - name: symbol, - span, - }), - None => emit_string_error(cx, &item), - } - } - } - - if entries.is_empty() { - None - } else { - Some(ProfileSelection::new(entries)) - } -} - -fn literal_symbol(item: &MetaItemInner) -> Option<(Symbol, Span)> { - match item { - MetaItemInner::Lit(lit) => { - let LitKind::Str(symbol, _) = lit.kind else { return None }; - Some((symbol, lit.span)) - }, - MetaItemInner::MetaItem(_) => None, - } -} - -fn emit_string_error(cx: &LateContext<'_>, item: &MetaItemInner) { - let span = match item { - MetaItemInner::Lit(lit) => lit.span, - MetaItemInner::MetaItem(meta) => meta.span, - }; - cx.tcx - .sess - .dcx() - .struct_span_err(span, "expected string literal profile name") - .emit(); -} diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index cd609980b149b..1c133fccdb308 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -11,15 +11,15 @@ use crate::consts::{ConstEvalCtxt, FullInt}; use crate::sym; -use crate::ty::{all_predicates_of, is_copy}; +use crate::ty::all_predicates_of; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -48,17 +48,17 @@ impl ops::BitOrAssign for EagernessSuggestion { } /// Determine the eagerness of the given function call. -fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { +fn fn_eagerness(tcx: TyCtxt<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let ty = match cx.tcx.impl_of_assoc(fn_id) { - Some(id) => cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(), + let ty = match tcx.impl_of_assoc(fn_id) { + Some(id) => tcx.type_of(id).instantiate_identity().skip_norm_wip(), None => return Lazy, }; if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( - cx.tcx.crate_name(fn_id.krate), + tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro ) { Eager @@ -71,22 +71,20 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Due to the limited operations on these types functions should be fairly cheap. if def.variants().iter().flat_map(|v| v.fields.iter()).any(|x| { matches!( - cx.tcx - .type_of(x.did) + tcx.type_of(x.did) .instantiate_identity() .skip_norm_wip() .peel_refs() .kind(), ty::Param(_) ) - }) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + }) && all_predicates_of(tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(pred) => tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) { // Limit the function to either `(self) -> bool` or `(&self) -> bool` - match &**cx - .tcx + match &**tcx .fn_sig(fn_id) .instantiate_identity() .skip_norm_wip() @@ -104,14 +102,12 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } -fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +fn res_has_significant_drop(res: Res, ecx: &ConstEvalCtxt<'_>, e: &Expr<'_>) -> bool { if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _) | Res::SelfCtor(_) | Res::SelfTyAlias { .. } = res { - cx.typeck_results() - .expr_ty(e) - .has_significant_drop(cx.tcx, cx.typing_env()) + ecx.typeck.expr_ty(e).has_significant_drop(ecx.tcx, ecx.typing_env) } else { false } @@ -119,12 +115,12 @@ fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> boo #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, + struct V<'tcx> { + ecx: ConstEvalCtxt<'tcx>, eagerness: EagernessSuggestion, } - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { use EagernessSuggestion::{ForceNoChange, Lazy, NoChange}; if self.eagerness == ForceNoChange { @@ -134,8 +130,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Autoderef through a user-defined `Deref` impl can have side-effects, // so don't suggest changing it. if self - .cx - .typeck_results() + .ecx + .typeck .expr_adjustments(e) .iter() .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) @@ -152,58 +148,62 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .. }, args, - ) => match self.cx.qpath_res(path, hir_id) { + ) => match self.ecx.typeck.qpath_res(path, hir_id) { res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { - if res_has_significant_drop(res, self.cx, e) { + if res_has_significant_drop(res, &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, - Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), + Res::Def(_, id) if self.ecx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did - Res::Def(..) if is_const_evaluatable(self.cx, e) => { + Res::Def(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, Res::Def(_, id) => match path { QPath::Resolved(_, p) => { - self.eagerness |= - fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness( + self.ecx.tcx, + id, + p.segments.last().unwrap().ident.name, + !args.is_empty(), + ); }, QPath::TypeRelative(_, name) => { - self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness(self.ecx.tcx, id, name.ident.name, !args.is_empty()); }, }, _ => self.eagerness = Lazy, }, // No need to walk the arguments here, `is_const_evaluatable` already did - ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => { + ExprKind::MethodCall(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, #[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778 ExprKind::Struct(path, ..) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::Path(ref path) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self - .cx - .typeck_results() + .ecx + .typeck .type_dependent_def_id(e.hir_id) - .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true)); + .map_or(Lazy, |id| fn_eagerness(self.ecx.tcx, id, name.ident.name, true)); }, ExprKind::Index(_, e, _) => { - let ty = self.cx.typeck_results().expr_ty_adjusted(e); - if is_copy(self.cx, ty) && !ty.is_ref() { + let ty = self.ecx.typeck.expr_ty_adjusted(e); + if self.ecx.tcx.type_is_copy_modulo_regions(self.ecx.typing_env, ty) && !ty.is_ref() { self.eagerness |= NoChange; } else { self.eagerness = Lazy; @@ -211,24 +211,19 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, // Custom `Deref` impl might have side effects - ExprKind::Unary(UnOp::Deref, e) - if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => - { + ExprKind::Unary(UnOp::Deref, e) if self.ecx.typeck.expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe. - ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(UnOp::Deref, e) if !self.ecx.typeck.expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange, ExprKind::Unary(_, e) - if matches!( - self.cx.typeck_results().expr_ty(e).kind(), - ty::Bool | ty::Int(_) | ty::Uint(_), - ) => {}, + if matches!(self.ecx.typeck.expr_ty(e).kind(), ty::Bool | ty::Int(_) | ty::Uint(_),) => {}, // `>>` and `<<` panic when the right-hand side is greater than or equal to the number of bits in the // type of the left-hand side, or is negative. @@ -236,18 +231,16 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // overflow with constants, the compiler emits an error for it and the programmer will have to fix it. // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) - if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && ConstEvalCtxt::new(self.cx).eval(right).is_none() => + if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) && self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) - && let right_ty = self.cx.typeck_results().expr_ty(right) - && let ecx = ConstEvalCtxt::new(self.cx) - && let left = ecx.eval(left) - && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) + && let right_ty = self.ecx.typeck.expr_ty(right) + && let left = self.ecx.eval(left) + && let right = self.ecx.eval(right).and_then(|c| c.int_value(self.ecx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -265,16 +258,14 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // error and it's good to have the eagerness warning up front when the user fixes the logic error. ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) - && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && let ecx = ConstEvalCtxt::new(self.cx) - && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => + && !self.ecx.typeck.expr_ty(e).is_floating_point() + && (self.ecx.eval(left).is_none() || self.ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).is_primitive() - && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {}, + if self.ecx.typeck.expr_ty(lhs).is_primitive() && self.ecx.typeck.expr_ty(rhs).is_primitive() => {}, // Can't be moved into a closure ExprKind::Break(..) @@ -322,7 +313,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS } let mut v = V { - cx, + ecx: ConstEvalCtxt::new(cx), eagerness: EagernessSuggestion::Eager, }; v.visit_expr(e); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 38724a1053f65..050e43344d0c4 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,6 +1,6 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, SpanRange, walk_span_to_context}; use crate::{sym, tokenize_with_text}; use core::mem; use rustc_ast::ast; @@ -1036,7 +1036,7 @@ fn reduce_exprkind<'hir>( // `{}` => `()` ([], None) if block.span.ctxt() != eval_ctxt - || block.span.check_source_text(cx, |src| { + || block.span.check_text(cx, |src| { tokenize(src, FrontmatterAllowed::No) .map(|t| t.kind) .filter(|t| { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index e8342e691dbd4..0264c68776085 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -46,7 +46,6 @@ mod check_proc_macro; pub mod comparisons; pub mod consts; pub mod diagnostics; -pub mod disallowed_profiles; pub mod eager_or_lazy; pub mod higher; mod hir_utils; @@ -116,14 +115,14 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, SyntaxContext}; -use source::{SpanRangeExt, walk_span_to_context}; +use source::{SpanExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::ConstEvalCtxt; use crate::higher::Range; use crate::msrvs::Msrv; use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use crate::source::HasSourceMap; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1329,59 +1328,23 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { }) } -/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. -/// -/// For the lower bound, this means that: -/// - either there is none -/// - or it is the smallest value that can be represented by the range's integer type -/// -/// For the upper bound, this means that: -/// - either there is none -/// - or it is the largest value that can be represented by the range's integer type and is -/// inclusive -/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to -/// a method call on that same container (e.g. `v.drain(..v.len())`) -/// -/// If the given `Expr` is not some kind of range, the function returns `false`. -pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); +/// Checks whether the given `Expr` is a range over the entire container. +pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option, expr: &Expr<'_>) -> bool { if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) { - let start_is_none_or_min = start.is_none_or(|start| { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) - { - start_const.is_numeric_min(cx.tcx, bnd_ty) - } else { - false - } - }); - let end_is_none_or_max = end.is_none_or(|end| match limits { - RangeLimits::Closed => { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) - { - end_const.is_numeric_max(cx.tcx, bnd_ty) - } else { - false - } - }, - RangeLimits::HalfOpen => { - if let Some(container_path) = container_path - && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind - && name.ident.name == sym::len - && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + start.is_none_or(|start| is_integer_literal(start, 0)) + && end.is_none_or(|end| { + if limits == RangeLimits::HalfOpen + && let Some(container) = container + && let ExprKind::MethodCall(seg, recv, [], _) = end.kind { - container_path.res == path.res + seg.ident.name == sym::len && recv.res_local_id() == Some(container) } else { false } - }, - }); - return start_is_none_or_min && end_is_none_or_max; + }) + } else { + false } - false } /// Checks whether the given expression is a constant literal of the given value. @@ -2880,8 +2843,8 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + span.check_text(sm, |snippet| { tokenize(snippet, FrontmatterAllowed::No).any(|token| { matches!( token.kind, @@ -2895,8 +2858,8 @@ pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool { /// token, including comments unless `skip_comments` is set. /// This is useful to determine if there are any actual code tokens in the span that are omitted in /// the late pass, such as platform-specific code. -pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool { + span.check_text(sm, |snippet| { tokenize_with_text(snippet).any(|(token, _, _)| match token { TokenKind::Whitespace => false, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, @@ -2904,18 +2867,19 @@ pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, sk }) }) } + /// Returns all the comments a given span contains /// /// Comments are returned wrapped with their relevant delimiters -pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String { - span_extract_comments(cx, span).join("\n") +pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String { + span_extract_comments(sm, span).join("\n") } /// Returns all the comments a given span contains. /// /// Comments are returned wrapped with their relevant delimiters. -pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec { - span.with_source_text(cx, |snippet| { +pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec { + span.with_source_text(sm, |snippet| { tokenize_with_text(snippet) .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) .map(|(_, s, _)| s.to_string()) diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 5151351ed2ade..d9b34ebfe21e4 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -3,8 +3,9 @@ use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; use rustc_attr_parsing::parse_version; use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::RustcVersion; +use rustc_hir::{HirId, RustcVersion}; use rustc_lint::LateContext; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::Symbol; use serde::Deserialize; @@ -26,7 +27,7 @@ msrv_aliases! { 1,97,0 { ISOLATE_LOWEST_ONE } 1,93,0 { VEC_DEQUE_POP_BACK_IF, VEC_DEQUE_POP_FRONT_IF } 1,91,0 { DURATION_FROM_MINUTES_HOURS } - 1,88,0 { LET_CHAINS } + 1,88,0 { LET_CHAINS, AS_CHUNKS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,86,0 { VEC_POP_IF } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL, WAKER_NOOP } @@ -118,16 +119,29 @@ impl Msrv { /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations pub fn current(self, cx: &LateContext<'_>) -> Option { if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { - let start = cx.last_node_with_lint_attrs; - if let Some(msrv_attr) = once(start) - .chain(cx.tcx.hir_parent_id_iter(start)) - .find_map(|id| parse_attrs(cx.tcx.sess, cx.tcx.hir_attrs(id))) - { - return Some(msrv_attr); - } + self.for_attrs(cx.tcx, cx.last_node_with_lint_attrs) + } else { + self.0 } + } - self.0 + /// Returns the MSRV at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn at(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { + self.for_attrs(tcx, node) + } else { + self.0 + } + } + + fn for_attrs(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + once(node) + .chain(tcx.hir_parent_id_iter(node)) + .find_map(|id| parse_attrs(tcx.sess, tcx.hir_attrs(id))) + .or(self.0) } /// Checks if a required version from [this module](self) is met at the current node @@ -138,6 +152,14 @@ impl Msrv { self.current(cx).is_none_or(|msrv| msrv >= required) } + /// Checks if a required version from [this module](self) is met at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn meets_at(self, tcx: TyCtxt<'_>, node: HirId, required: RustcVersion) -> bool { + self.at(tcx, node).is_none_or(|msrv| msrv >= required) + } + pub fn read_cargo(&mut self, sess: &Session) { let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") .ok() diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index e5389ac71d398..5c61f424adf1a 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -6,9 +6,8 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; use rustc_const_eval::check_consts::ConstCx; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::{RustcVersion, StableSince}; +use rustc_hir::{self as hir, HirId, RustcVersion, StableSince}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::LateContext; @@ -420,14 +419,17 @@ fn check_terminator<'tcx>( /// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { - cx.tcx.is_const_fn(def_id) - && cx - .tcx + is_stable_const_fn_at(cx.tcx, cx.last_node_with_lint_attrs, def_id, msrv) +} + +/// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. +pub fn is_stable_const_fn_at(tcx: TyCtxt<'_>, node: HirId, def_id: DefId, msrv: Msrv) -> bool { + tcx.is_const_fn(def_id) + && tcx .lookup_const_stability(def_id) .or_else(|| { - cx.tcx - .trait_of_assoc(def_id) - .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + tcx.trait_of_assoc(def_id) + .and_then(|trait_def_id| tcx.lookup_const_stability(trait_def_id)) }) .is_none_or(|const_stab| { if let rustc_hir::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -441,10 +443,10 @@ pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bo StableSince::Err(_) => return false, }; - msrv.meets(cx, const_stab_rust_version) + msrv.meets_at(tcx, node, const_stab_rust_version) } else { // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + tcx.features().enabled(const_stab.feature) && msrv.at(tcx, node).is_none() } }) } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 02d502f8b67f8..171276f997d39 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -20,27 +20,38 @@ use std::borrow::Cow; use std::fmt; use std::ops::{Deref, Index, Range}; -pub trait HasSession { - fn sess(&self) -> &Session; +pub trait HasSourceMap<'sm>: Copy { + #[must_use] + fn source_map(self) -> &'sm SourceMap; } -impl HasSession for Session { - fn sess(&self) -> &Session { +impl<'sm> HasSourceMap<'sm> for &'sm SourceMap { + #[inline] + fn source_map(self) -> &'sm SourceMap { self } } -impl HasSession for TyCtxt<'_> { - fn sess(&self) -> &Session { - self.sess +impl<'sm> HasSourceMap<'sm> for &'sm Session { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.source_map() } } -impl HasSession for EarlyContext<'_> { - fn sess(&self) -> &Session { - ::rustc_lint::LintContext::sess(self) +impl<'sm> HasSourceMap<'sm> for TyCtxt<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.sess.source_map() } } -impl HasSession for LateContext<'_> { - fn sess(&self) -> &Session { - self.tcx.sess() +impl<'sm> HasSourceMap<'sm> for &'sm EarlyContext<'_> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + ::rustc_lint::LintContext::sess(self).source_map() + } +} +impl<'sm> HasSourceMap<'sm> for &LateContext<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.tcx.sess.source_map() } } @@ -95,39 +106,39 @@ impl IntoSpan for Range { } } -pub trait SpanRangeExt: SpanRange { +pub trait SpanExt: SpanRange { /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, /// or the source text is not accessible. - fn get_source_text(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new) + fn get_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()).and_then(SourceText::new) } /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_range(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()) + fn get_source_range<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { - with_source_text(cx.sess().source_map(), self.into_range(), f) + fn with_source_text<'sm, T>(self, sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { + with_source_text(sm.source_map(), self.into_range(), f) } /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { - self.with_source_text(cx, pred).unwrap_or(false) + fn check_text<'sm>(self, sm: impl HasSourceMap<'sm>, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + self.with_source_text(sm, pred).unwrap_or(false) } /// Calls the given function with the both the text of the source file and the referenced range, /// and returns the value. Returns `None` if the source text cannot be retrieved. - fn with_source_text_and_range( + fn with_source_text_and_range<'sm, T>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str, Range) -> T, ) -> Option { - with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) + with_source_text_and_range(sm.source_map(), self.into_range(), f) } /// Calls the given function with the both the text of the source file and the referenced range, @@ -135,12 +146,12 @@ pub trait SpanRangeExt: SpanRange { /// retrieved, or no result is returned. /// /// The new range must reside within the same source file. - fn map_range( + fn map_range<'sm>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { - map_range(cx.sess().source_map(), self.into_range(), f) + map_range(sm.source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. @@ -156,16 +167,16 @@ pub trait SpanRangeExt: SpanRange { /// /// When the range points to `foo`, suggesting to remove the range after it's been extended will /// cause the `)` to be placed inside the line comment as `( // Some comment)`. - fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { - with_leading_whitespace(cx.sess().source_map(), self.into_range()) + fn with_leading_whitespace<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + with_leading_whitespace(sm.source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl HasSession) -> Range { - trim_start(cx.sess().source_map(), self.into_range()) + fn trim_start<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + trim_start(sm.source_map(), self.into_range()) } } -impl SpanRangeExt for T {} +impl SpanExt for T {} /// Handle to a range of text in a source file. pub struct SourceText(SourceFileRange); @@ -353,15 +364,15 @@ impl SourceFileRange { } /// Like [`snippet_block`], but add braces if the expr is not an `ExprKind::Block` with no label. -pub fn expr_block( - sess: &impl HasSession, +pub fn expr_block<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> String { - let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app); + let (code, from_macro) = snippet_block_with_context(sm, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, None) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -386,13 +397,13 @@ pub fn expr_block( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { - first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +pub fn first_line_of_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + first_char_in_first_line(sm, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } -fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option { - let line_span = line_span(sess, span); - snippet_opt(sess, line_span).and_then(|snip| { +fn first_char_in_first_line<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + let line_span = line_span(sm, span); + snippet_opt(sm, line_span).and_then(|snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -407,9 +418,9 @@ fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option Span { +fn line_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); - let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap(); + let SourceFileAndLine { sf, line } = sm.source_map().lookup_line(span.lo()).unwrap(); let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) @@ -423,13 +434,13 @@ fn line_span(sess: &impl HasSession, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +pub fn indent_of<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).map(|mut s| { +pub fn snippet_indent<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -441,8 +452,8 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // sources that the user has no control over. // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. -pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) +pub fn is_present_in_source<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sm, span) && snippet.is_empty() { return false; @@ -534,8 +545,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, /// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` -pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) +pub fn snippet<'a, 'sm>(sm: impl HasSourceMap<'sm>, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(sm, span).map_or_else(|| Cow::Borrowed(default), From::from) } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -547,17 +558,17 @@ pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow< /// /// If the span might realistically contain a macro call (e.g. `vec![]`), consider using /// [`snippet_with_context`] instead. -pub fn snippet_with_applicability<'a>( - sess: &impl HasSession, +pub fn snippet_with_applicability<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - snippet_with_applicability_sess(sess.sess(), span, default, applicability) + snippet_with_applicability_sm(sm.source_map(), span, default, applicability) } -fn snippet_with_applicability_sess<'a>( - sess: &Session, +fn snippet_with_applicability_sm<'a>( + sm: &SourceMap, span: Span, default: &'a str, applicability: &mut Applicability, @@ -565,7 +576,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - if let Some(t) = snippet_opt(sess, span) { + if let Some(t) = snippet_opt(sm, span) { Cow::Owned(t) } else { if *applicability == Applicability::MachineApplicable { @@ -576,8 +587,8 @@ fn snippet_with_applicability_sess<'a>( } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { - sess.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + sm.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -614,37 +625,42 @@ pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block(sess: &impl HasSession, span: Span, default: &str, indent_relative_to: Option) -> String { - let snip = snippet(sess, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); +pub fn snippet_block<'sm>( + sm: impl HasSourceMap<'sm>, + span: Span, + default: &str, + indent_relative_to: Option, +) -> String { + let snip = snippet(sm, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Same as [`snippet_block`], but adapts the applicability level by the rules of /// [`snippet_with_applicability`]. -pub fn snippet_block_with_applicability( - sess: &impl HasSession, +pub fn snippet_block_with_applicability<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &str, indent_relative_to: Option, applicability: &mut Applicability, ) -> String { - let snip = snippet_with_applicability(sess, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let snip = snippet_with_applicability(sm, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Combination of [`snippet_block`] and [`snippet_with_context`]. -pub fn snippet_block_with_context( - sess: &impl HasSession, +pub fn snippet_block_with_context<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> (String, bool) { - let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let (snip, from_macro) = snippet_with_context(sm, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); (reindent_multiline(&snip, true, indent), from_macro) } @@ -658,18 +674,18 @@ pub fn snippet_block_with_context( /// correctly get a snippet of `vec![]`. /// /// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context<'a>( - sess: &impl HasSession, +pub fn snippet_with_context<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - snippet_with_context_sess(sess.sess(), span, outer, default, applicability) + snippet_with_context_sm(sm.source_map(), span, outer, default, applicability) } -fn snippet_with_context_sess<'a>( - sess: &Session, +fn snippet_with_context_sm<'a>( + sm: &SourceMap, span: Span, outer: SyntaxContext, default: &'a str, @@ -677,10 +693,7 @@ fn snippet_with_context_sess<'a>( ) -> (Cow<'a, str>, bool) { // If it is just range desugaring, use the desugaring span since it may include parenthesis. if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { - return ( - snippet_with_applicability_sess(sess, span, default, applicability), - false, - ); + return (snippet_with_applicability_sm(sm, span, default, applicability), false); } let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( @@ -696,7 +709,7 @@ fn snippet_with_context_sess<'a>( ); ( - snippet_with_applicability_sess(sess, span, default, applicability), + snippet_with_applicability_sm(sm, span, default, applicability), is_macro_call, ) } @@ -759,15 +772,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span { - let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true); +pub fn expand_past_previous_comma<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + let extended = sm.source_map().span_extend_to_prev_char(span, ',', true); extended.with_lo(extended.lo() - BytePos(1)) } /// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// character (or a single byte with `ascii_only`) -pub fn str_literal_to_char_literal( - sess: &impl HasSession, +pub fn str_literal_to_char_literal<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -782,7 +795,7 @@ pub fn str_literal_to_char_literal( } && len == 1 { - let snip = snippet_with_applicability(sess, expr.span, string, applicability); + let snip = snippet_with_applicability(sm, expr.span, string, applicability); let ch = if let StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 59e98cdc7bef8..6053788c82b13 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -176,6 +176,8 @@ generate! { checked_sub, child_id, child_kill, + chunks_exact, + chunks_exact_mut, clamp, clippy_utils, clone_into, @@ -209,8 +211,6 @@ generate! { deprecated_in_future, deref_mut_method, diagnostics, - disallowed_profile, - disallowed_profiles, disallowed_types, drain, dump, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 10b4cb26c392a..ae348651c0d62 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -3,7 +3,7 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; -use rustc_abi::VariantIdx; +use rustc_abi::{BackendRepr, FieldsShape, VariantIdx, Variants}; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -17,7 +17,7 @@ use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; -use rustc_middle::ty::layout::ValidityRequirement; +use rustc_middle::ty::layout::{LayoutError, LayoutOf, TyAndLayout}; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, ProjectionAliasTy, Region, RegionKind, TraitRef, Ty, TyCtxt, @@ -34,7 +34,7 @@ use std::{debug_assert_matches, iter, mem}; use crate::paths::{PathNS, lookup_path_str}; use crate::res::{MaybeDef, MaybeQPath}; -use crate::sym; +use crate::{over, sym}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -486,8 +486,8 @@ pub fn peel_n_ty_refs(mut ty: Ty<'_>, n: usize) -> (Ty<'_>, Option) /// and `false` for: /// - `Result` and `Result` pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { + match (a.kind(), b.kind()) { + (ty::Adt(did_a, args_a), ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } @@ -500,35 +500,112 @@ pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { _ => true, }) }, + (ty::Ref(_, a, mut_a), ty::Ref(_, b, mut_b)) => mut_a == mut_b && same_type_modulo_regions(*a, *b), + (ty::Tuple(as_), ty::Tuple(bs)) => over(as_, bs, |a, b| same_type_modulo_regions(*a, *b)), + (ty::Array(a, na), ty::Array(b, nb)) => na == nb && same_type_modulo_regions(*a, *b), _ => a == b, } } /// Checks if a given type looks safe to be uninitialized. pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); - cx.tcx - .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty))) - .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty)) + match cx.layout_of(ty) { + Ok(layout) => is_uninit_value_valid_for_layout(cx, layout), + // The type layout is either not concrete enough yet or too large, fall back to structural check instead + Err(LayoutError::TooGeneric(_) | LayoutError::SizeOverflow(_)) => is_uninit_value_valid_for_ty_fallback(cx, ty), + Err(_) => false, + } +} + +fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndLayout<'tcx>) -> bool { + // ZSTs contribute no bytes to the vector buffer + if layout.layout.is_zst() { + return true; + } + + match layout.layout.backend_repr { + BackendRepr::Scalar(s) => s.is_uninit_valid(), + BackendRepr::ScalarPair(a, b) => a.is_uninit_valid() && b.is_uninit_valid(), + BackendRepr::SimdVector { element, count } => count == 0 || element.is_uninit_valid(), + BackendRepr::SimdScalableVector { element, .. } => element.is_uninit_valid(), + // Here validity is determined by the structural fields instead. + BackendRepr::Memory { .. } => match &layout.layout.variants { + Variants::Single { .. } => match &layout.layout.fields { + FieldsShape::Primitive => { + debug_assert!(false, "Both Scalar primitives and ! should be handled above."); + false + }, + // Arrays are valid if empty, or if their elements are valid. + FieldsShape::Array { count, .. } => { + if *count == 0 { + true + } else { + is_uninit_value_valid_for_layout(cx, layout.field(cx, 0)) + } + }, + // Structs like types are valid only if all fields are valid. + FieldsShape::Arbitrary { offsets, .. } => { + (0..offsets.len()).all(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + // Unions are valid if at least one field is valid. + FieldsShape::Union(count) => { + (0..count.get()).any(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + }, + // Types with no valid variants must be uninhabited + Variants::Empty => true, + // Enum like with multiple inhabited variants have a discriminant, they cannot be uninitialized. + Variants::Multiple { .. } => false, + }, + } } -/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`. +/// Fallback for polymorphic types where `layout_of` fails fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); + match *ty.kind() { // The array length may be polymorphic, let's try the inner type. - ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), + ty::Array(component, len) => { + // Zero-length arrays are always valid + if len.try_to_target_usize(cx.tcx) == Some(0) { + return true; + } + is_uninit_value_valid_for_ty(cx, component) + }, // Peek through tuples and try their fallbacks. ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), - // Unions are always fine right now. - // This includes MaybeUninit, the main way people use uninitialized memory. - ty::Adt(adt, _) if adt.is_union() => true, + // For Unions, check if any field is uninit + ty::Adt(adt, args) if adt.is_union() => adt.all_fields().any(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + is_uninit_value_valid_for_ty(cx, field_ty) + }), // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit // can themselves be uninit too. - // This purposefully ignores enums as they may have a discriminant that can't be uninit. - ty::Adt(adt, args) if adt.is_struct() => adt - .all_fields() - .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args).skip_norm_wip())), - // For the rest, conservatively assume that they cannot be uninit. + // This also applies for single variant enums, whose validity is determined by their fields. + ty::Adt(adt, args) if adt.is_struct() || adt.variants().len() == 1 => adt.all_fields().all(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + + is_uninit_value_valid_for_ty(cx, field_ty) + }), + // Without a usable whole type layout, + // conservatively reject remaining enum cases + ty::Adt(adt, _) if adt.is_enum() => false, + // Conservatively reject remaining types _ => false, } } @@ -1049,9 +1126,13 @@ pub fn make_projection<'tcx>( assert_generic_args_match(tcx, assoc_item.def_id, args); let kind = if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(assoc_item.def_id)) { - ty::AliasTyKind::Inherent { def_id: assoc_item.def_id } + ty::AliasTyKind::Inherent { + def_id: assoc_item.def_id, + } } else { - ty::AliasTyKind::Projection { def_id: assoc_item.def_id } + ty::AliasTyKind::Projection { + def_id: assoc_item.def_id, + } }; Some(AliasTy::new_from_args(tcx, kind, args)) @@ -1335,6 +1416,14 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option` or a `Result` and return its argument type (`T`) if it is. +pub fn option_or_result_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) if matches!(adt.opt_diag_name(cx), Some(sym::Option | sym::Result)) => Some(args.type_at(0)), + _ => None, + } +} + /// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if /// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` /// types, or `PhantomData` types containing any of the previous. This can be used to check whether diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 4a1d24024915b..d644cd5a4aace 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -195,7 +195,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { - for_each_expr(cx, v, |e| { + for_each_expr(cx.tcx, v, |e| { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { @@ -220,7 +220,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; - for_each_expr(cx, block, |e| { + for_each_expr(cx.tcx, block, |e| { if past_expr { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 28449a75a8fc5..ca1fa57cffc10 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,6 +1,6 @@ use crate::get_enclosing_block; use crate::msrvs::Msrv; -use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::qualify_min_const_fn::is_stable_const_fn_at; use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; use core::ops::ControlFlow; @@ -8,8 +8,8 @@ use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ - self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, - ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, + self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, CRATE_HIR_ID, Expr, ExprKind, HirId, + ItemId, ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -145,7 +145,7 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( /// Calls the given function once for each expression contained. This will enter bodies, but not /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( - cx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, ) -> Option { @@ -188,7 +188,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( ControlFlow::Continue(()) } } - let mut v = V { tcx: cx.tcx, f }; + let mut v = V { tcx, f }; node.visit(&mut v).break_value() } @@ -299,7 +299,7 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body).value, |e| { if let ExprKind::Path(p) = &e.kind && cx.qpath_res(p, e.hir_id) == res { @@ -312,7 +312,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { @@ -322,89 +322,69 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc .is_some() } -/// Checks if the given expression is a constant. -pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { - struct V<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - } - - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = intravisit::nested_filter::None; - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { - match e.kind { - ExprKind::ConstBlock(_) => return ControlFlow::Continue(()), - ExprKind::Call( - &Expr { - kind: ExprKind::Path(ref p), - hir_id, - .. - }, - _, - ) if self - .cx - .qpath_res(p, hir_id) - .opt_def_id() - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, - ExprKind::MethodCall(..) - if self - .cx - .typeck_results() - .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, - 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_raw_ptr() => (), - ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), - ExprKind::Index(base, _, _) - if matches!( - self.cx.typeck_results().expr_ty(base).peel_refs().kind(), - ty::Slice(_) | ty::Array(..) - ) => {}, - ExprKind::Path(ref p) - if matches!( - self.cx.qpath_res(p, e.hir_id), - Res::Def( - DefKind::Const { .. } - | DefKind::AssocConst { .. } - | DefKind::AnonConst - | DefKind::ConstParam - | DefKind::Ctor(..) - | DefKind::Fn - | DefKind::AssocFn, - _ - ) | Res::SelfCtor(_) - ) => {}, - - ExprKind::AddrOf(..) - | ExprKind::Array(_) - | ExprKind::Block(..) - | ExprKind::Cast(..) - | ExprKind::DropTemps(_) - | ExprKind::Field(..) - | ExprKind::If(..) - | ExprKind::Let(..) - | ExprKind::Lit(_) - | ExprKind::Match(..) - | ExprKind::Repeat(..) - | ExprKind::Struct(..) - | ExprKind::Tup(_) - | ExprKind::Type(..) - | ExprKind::UnsafeBinderCast(..) => (), - - _ => { - return ControlFlow::Break(()); +/// Checks if the given expression can be evaluated as a constant at the specified node +pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool { + for_each_expr(tcx, e, move |e| { + match e.kind { + ExprKind::ConstBlock(_) => return ControlFlow::Continue(Descend::No), + ExprKind::Call( + &Expr { + kind: ExprKind::Path(ref p), + hir_id, + .. }, - } + _, + ) if typeck + .qpath_res(p, hir_id) + .opt_def_id() + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::MethodCall(..) + if typeck + .type_dependent_def_id(e.hir_id) + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::Binary(_, lhs, rhs) + if typeck.expr_ty(lhs).peel_refs().is_primitive_ty() + && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() => {}, + ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => (), + ExprKind::Index(base, _, _) + if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) => {}, + ExprKind::Path(ref p) + if matches!( + typeck.qpath_res(p, e.hir_id), + Res::Def( + DefKind::Const { .. } + | DefKind::AssocConst { .. } + | DefKind::AnonConst + | DefKind::ConstParam + | DefKind::Ctor(..) + | DefKind::Fn + | DefKind::AssocFn, + _ + ) | Res::SelfCtor(_) + ) => {}, + + ExprKind::AddrOf(..) + | ExprKind::Array(_) + | ExprKind::Block(..) + | ExprKind::Cast(..) + | ExprKind::DropTemps(_) + | ExprKind::Field(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Lit(_) + | ExprKind::Match(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => {}, - walk_expr(self, e) + _ => return ControlFlow::Break(()), } - } - - let mut v = V { cx }; - v.visit_expr(e).is_continue() + ControlFlow::Continue(Descend::Yes) + }) + .is_none() } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. @@ -785,7 +765,7 @@ pub fn local_used_once<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { let mut expr = None; - let cf = for_each_expr(cx, visitable, |e| { + let cf = for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 3afa9da1f947d..ac044327aafe9 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-2026-06-11" +channel = "nightly-2026-06-25" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml deleted file mode 100644 index c77f5658ae69c..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-methods = [ - { path = "std::mem::drop" } -] - -[profiles.forward_pass] -disallowed-methods = [ - { path = "alloc::vec::Vec::push", reason = "push is forbidden in forward profile" } -] - -[profiles.export] -disallowed-methods = [ - { path = "core::option::Option::unwrap" } -] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs deleted file mode 100644 index 07bb22e05d6f7..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -#![warn(clippy::disallowed_methods)] -#![allow( - unused, - clippy::no_effect, - clippy::needless_borrow, - clippy::vec_init_then_push, - clippy::unnecessary_literal_unwrap -)] - -fn default_violation() { - let value = String::from("test"); - std::mem::drop(value); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[expect(clippy::disallowed_methods)] -fn expected_violation() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -#[clippy::disallowed_profile("unknown_profile")] -//~^ ERROR: unknown profile `unknown_profile` for -//~| ERROR: unknown profile `unknown_profile` for -fn unknown_profile() { - let mut values = Vec::new(); - values.push(1); - // unknown profile falls back to the default list - std::mem::drop(values); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -// `#[expect(clippy::disallowed_methods)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_METHODS`, but not the one tagged under `DISALLOWED_TYPES`. -#[expect(clippy::disallowed_methods)] -#[clippy::disallowed_profile("unknown_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` -fn expect_before_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("unknown_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` -#[expect(clippy::disallowed_methods)] -fn expect_after_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -fn main() { - default_violation(); - expected_violation(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr deleted file mode 100644 index 5f64b1bf4499b..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:12:5 - | -LL | std::mem::drop(value); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:24:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:30:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:40:5 - | -LL | std::mem::drop(values); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:46:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:48:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:54:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:61:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 10 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml deleted file mode 100644 index 3c47b2afe26fe..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-types = [ - { path = "std::rc::Rc" } -] - -[profiles.forward_pass] -disallowed-types = [ - { path = "std::cell::RefCell", reason = "Prefer shared references" } -] - -[profiles.export] -disallowed-types = [ - { path = "std::sync::Mutex" } -] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs deleted file mode 100644 index 0161417dd6022..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![warn(clippy::disallowed_types)] -#![allow(dead_code)] - -use std::rc::Rc; //~ ERROR: use of a disallowed type `std::rc::Rc` -use std::sync::Mutex; - -struct Wrapper; - -fn default_type() { - let _value: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let _value: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -#[clippy::disallowed_profile("unknown_type_profile")] -//~^ ERROR: unknown profile `unknown_type_profile` for -//~| ERROR: unknown profile `unknown_type_profile` for -fn unknown_profile() { - let _other = 1u32; - let _fallback: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - let _other: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -// `#[expect(clippy::disallowed_types)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_TYPES`, but not one tagged under `DISALLOWED_METHODS`. -#[expect(clippy::disallowed_types)] -#[clippy::disallowed_profile("unknown_type_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` -fn expect_before_unknown_profile() { - let _value: Rc = todo!(); -} - -#[clippy::disallowed_profile("unknown_type_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` -#[expect(clippy::disallowed_types)] -fn expect_after_unknown_profile() { - let _value: Rc = todo!(); -} - -fn main() { - default_type(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr deleted file mode 100644 index 68d63e218c0de..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:4:1 - | -LL | use std::rc::Rc; - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:10:17 - | -LL | let _value: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:15:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:20:17 - | -LL | let _value: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:28:20 - | -LL | let _fallback: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:33:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:34:17 - | -LL | let _other: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:40:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:46:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 11 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml b/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml new file mode 100644 index 0000000000000..2ff5a13e893be --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml @@ -0,0 +1 @@ +check-grouped-late-init = false diff --git a/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs b/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs new file mode 100644 index 0000000000000..5a5e657b295c3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs @@ -0,0 +1,64 @@ +//@check-pass + +fn main() {} + +fn issue16330() { + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + let a; + let b; + let c; + match 1 { + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index ba930d094fc19..528e2f1089c04 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -33,6 +33,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -70,7 +71,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -135,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -172,7 +173,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -237,6 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -274,7 +275,6 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/tests/ui/author/issue_17240.rs b/src/tools/clippy/tests/ui/author/issue_17240.rs new file mode 100644 index 0000000000000..591a19907720a --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_17240.rs @@ -0,0 +1,8 @@ +//@ check-pass +// Ensure that a proper body is used when printing paths (`x`) +// if the attribute is placed on an item. +#[clippy::author] +fn main() { + let x = 42i32; + _ = -x; +} diff --git a/src/tools/clippy/tests/ui/author/issue_17240.stdout b/src/tools/clippy/tests/ui/author/issue_17240.stdout new file mode 100644 index 0000000000000..81c927180ce1c --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_17240.stdout @@ -0,0 +1,20 @@ +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 2 + && let StmtKind::Let(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Semi(e) = block.stmts[1].kind + && let ExprKind::Block(block1, None) = e.kind + && block1.stmts.len() == 1 + && let StmtKind::Let(local1) = block1.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Unary(UnOp::Neg, inner) = init1.kind + && let PatKind::Wild = local1.pat.kind + && block1.expr.is_none() + && block.expr.is_none() +{ + // report your lint here +} diff --git a/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs b/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs new file mode 100644 index 0000000000000..4cc9ac80692d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs @@ -0,0 +1,17 @@ +#![allow(unused, clippy::missing_safety_doc)] + +pub fn lol() -> Option { + Some(0) +} + +pub unsafe fn lol_unchecked() -> u32 { + 0 +} + +pub fn kek() -> Option { + Some(0) +} + +unsafe fn kek_unchecked() -> u32 { + 0 +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed new file mode 100644 index 0000000000000..e40b745245568 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.clone().next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed new file mode 100644 index 0000000000000..b9b7cdeaf5922 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed new file mode 100644 index 0000000000000..67256fc86887c --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter().peekable(); + let _: Option<&i32> = iter.peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs b/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..d8ccb8b74fb64 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr b/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr new file mode 100644 index 0000000000000..5a284775e04f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr @@ -0,0 +1,101 @@ +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:19:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` + = note: `-D clippy::by-ref-peekable-peek` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::by_ref_peekable_peek)]` +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:25:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = a.into_iter().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:36:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = mac!().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:42:27 + | +LL | let _: Option<&i32> = iter.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut iter = [1, 2, 3].into_iter().peekable(); +LL ~ let _: Option<&i32> = iter.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:46:27 + | +LL | let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).next().as_ref(); + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..0d5f4d06043d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs @@ -0,0 +1,43 @@ +#![warn(clippy::chunks_exact_to_as_chunks)] +#![allow(unused)] + +fn main() { + let slice = [1, 2, 3, 4, 5, 6, 7, 8]; + + // Should trigger lint - literal constant + let mut it = slice.chunks_exact(4); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should trigger lint - const value + const CHUNK_SIZE: usize = 4; + let mut it = slice.chunks_exact(CHUNK_SIZE); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should NOT trigger - runtime value + let size = 4; + let mut it = slice.chunks_exact(size); + for chunk in it {} + + // Should trigger lint - with remainder + let mut it = slice.chunks_exact(3); + //~^ chunks_exact_to_as_chunks + for chunk in &mut it {} + for e in it.remainder() {} + + // Should trigger - mutable variant + let mut arr = [1, 2, 3, 4, 5, 6, 7, 8]; + let mut it = arr.chunks_exact_mut(4); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should NOT trigger - type must unify with another branch + let condition = true; + let y = 3; + let _ = if condition { + slice.chunks_exact(5) + } else { + slice.chunks_exact(y) + }; +} diff --git a/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr new file mode 100644 index 0000000000000..628752f9bae71 --- /dev/null +++ b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr @@ -0,0 +1,56 @@ +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<3>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact_mut` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks_mut::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/clear_with_drain.fixed b/src/tools/clippy/tests/ui/clear_with_drain.fixed index aaaec700cc9d2..5f8441c0470dc 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.fixed +++ b/src/tools/clippy/tests/ui/clear_with_drain.fixed @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_full() { diff --git a/src/tools/clippy/tests/ui/clear_with_drain.rs b/src/tools/clippy/tests/ui/clear_with_drain.rs index e36384e09c693..59024dcfe93d8 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.rs +++ b/src/tools/clippy/tests/ui/clear_with_drain.rs @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.drain(0..v.len()); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..v.len()); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.drain(0..); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..deque.len()); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..deque.len()); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.drain(0..s.len()); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..s.len()); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.drain(0..); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..); - //~^ clear_with_drain } fn string_range_full() { diff --git a/src/tools/clippy/tests/ui/clear_with_drain.stderr b/src/tools/clippy/tests/ui/clear_with_drain.stderr index 8b09c26e46af2..b4c71bf60be7f 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.stderr +++ b/src/tools/clippy/tests/ui/clear_with_drain.stderr @@ -8,124 +8,88 @@ LL | v.drain(0..v.len()); = help: to override `-D warnings` add `#[allow(clippy::clear_with_drain)]` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:26:7 - | -LL | v.drain(usize::MIN..v.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:46:7 + --> tests/ui/clear_with_drain.rs:41:7 | LL | v.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:51:7 - | -LL | v.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:68:7 + --> tests/ui/clear_with_drain.rs:58:7 | LL | v.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:86:7 + --> tests/ui/clear_with_drain.rs:76:7 | LL | v.drain(..v.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:125:11 + --> tests/ui/clear_with_drain.rs:115:11 | LL | deque.drain(0..deque.len()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:130:11 - | -LL | deque.drain(usize::MIN..deque.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:150:11 + --> tests/ui/clear_with_drain.rs:135:11 | LL | deque.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:155:11 - | -LL | deque.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:172:11 + --> tests/ui/clear_with_drain.rs:152:11 | LL | deque.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:190:11 + --> tests/ui/clear_with_drain.rs:170:11 | LL | deque.drain(..deque.len()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:229:7 + --> tests/ui/clear_with_drain.rs:209:7 | LL | s.drain(0..s.len()); | ^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:234:7 - | -LL | s.drain(usize::MIN..s.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:254:7 + --> tests/ui/clear_with_drain.rs:229:7 | LL | s.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:259:7 - | -LL | s.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:276:7 + --> tests/ui/clear_with_drain.rs:246:7 | LL | s.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:294:7 + --> tests/ui/clear_with_drain.rs:264:7 | LL | s.drain(..s.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashSet` - --> tests/ui/clear_with_drain.rs:333:9 + --> tests/ui/clear_with_drain.rs:303:9 | LL | set.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashMap` - --> tests/ui/clear_with_drain.rs:353:9 + --> tests/ui/clear_with_drain.rs:323:9 | LL | map.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `BinaryHeap` - --> tests/ui/clear_with_drain.rs:373:10 + --> tests/ui/clear_with_drain.rs:343:10 | LL | heap.drain(); | ^^^^^^^ help: try: `clear()` -error: aborting due to 21 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-16950.rs b/src/tools/clippy/tests/ui/crashes/ice-16950.rs new file mode 100644 index 0000000000000..414b34ebed348 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-16950.rs @@ -0,0 +1,27 @@ +//@check-pass +#![feature(trivial_bounds)] + +struct Helper(T); + +trait Unsized { + const SIZE: usize = usize::MAX; +} + +impl Unsized for T {} + +impl Helper { + const SIZE: usize = size_of::(); +} + +struct TrickClippy(str); + +impl TrickClippy { + fn trick_clippy() -> bool + where + Self: Sized, + { + Helper::::SIZE == str::SIZE + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/drain_collect.stderr b/src/tools/clippy/tests/ui/drain_collect.stderr index 46da4e799d2e8..7c1008aebe58a 100644 --- a/src/tools/clippy/tests/ui/drain_collect.stderr +++ b/src/tools/clippy/tests/ui/drain_collect.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `BinaryHeap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:6:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` | note: the lint level is defined here --> tests/ui/drain_collect.rs:1:9 @@ -10,59 +10,59 @@ note: the lint level is defined here LL | #![deny(clippy::drain_collect)] | ^^^^^^^^^^^^^^^^^^^^^ -error: you seem to be trying to move all elements into a new `HashMap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:15:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `HashSet` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:24:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:33:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:42:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:47:5 | LL | b.drain(0..).collect() - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:52:5 | LL | b.drain(..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:57:5 | LL | b.drain(0..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:63:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(&mut b)` -error: you seem to be trying to move all elements into a new `String` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:72:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr index 91b38932fee79..77fcae84d570e 100644 --- a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect_nostd.rs:7:5 | LL | v.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `core::mem::take(v)` | = note: `-D clippy::drain-collect` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs index 2e062208935a2..fa2ebddfde3f2 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -211,4 +211,18 @@ mod proc_macro_generated { } } +mod issue17255 { + + trait AnotherSimpleTrait<'a> {} + + macro_rules! mac { + ($lt:lifetime, $t:ident, $tr:path) => { + impl<$t: for<'lt> $tr> AnotherSimpleTrait<'_> for $t {} + }; + } + + // Do not lint code expanded from macros + mac!('a, T, super::SimplerTrait); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs index 87ce517a0a451..6a20967b7fc50 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs @@ -1,3 +1,5 @@ +//@no-rustfix + #![warn(clippy::extra_unused_type_parameters)] fn unused_where_clause(x: U) @@ -24,4 +26,17 @@ where unimplemented!(); } +// The fix just removes the type parameter from the definition of `unused_ty`, but it doesn't adjust +// its callsites, leading to compilation errors. +mod issue15884 { + fn unused_ty(x: u8) { + //~^ extra_unused_type_parameters + unimplemented!() + } + + fn main() { + unused_ty::(0); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr index 0765c41609368..2c7051886bf45 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr @@ -1,5 +1,5 @@ error: type parameter `T` goes unused in function definition - --> tests/ui/extra_unused_type_parameters_unfixable.rs:3:24 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:5:24 | LL | fn unused_where_clause(x: U) | ^ @@ -9,7 +9,7 @@ LL | fn unused_where_clause(x: U) = help: to override `-D warnings` add `#[allow(clippy::extra_unused_type_parameters)]` error: type parameters go unused in function definition: T, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:11:30 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:13:30 | LL | fn unused_multi_where_clause(x: U) | ^ ^^^^^^^^^^ @@ -17,12 +17,24 @@ LL | fn unused_multi_where_clause(x: U) = help: consider removing the parameters error: type parameters go unused in function definition: T, U, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:19:28 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:21:28 | LL | fn unused_all_where_clause() | ^ ^^^^^^^^^^ ^^^^^^^^^^ | = help: consider removing the parameters -error: aborting due to 3 previous errors +error: type parameter `T` goes unused in function definition + --> tests/ui/extra_unused_type_parameters_unfixable.rs:32:17 + | +LL | fn unused_ty(x: u8) { + | ^^^ + | +help: consider removing the parameter + | +LL - fn unused_ty(x: u8) { +LL + fn unused_ty(x: u8) { + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/filter_next.fixed b/src/tools/clippy/tests/ui/filter_next.fixed new file mode 100644 index 0000000000000..5502fa1fb2733 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.fixed @@ -0,0 +1,63 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().find(|&x| *x < 0); + //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next + + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().find(|&x| { + //~^ filter_next + *x < 0 + }); + + #[rustfmt::skip] + let _ = v.iter().rfind(|&x| { + //~^ filter_next + *x < 0 + }); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/src/tools/clippy/tests/ui/filter_next.rs b/src/tools/clippy/tests/ui/filter_next.rs new file mode 100644 index 0000000000000..c8e9f060cd609 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.rs @@ -0,0 +1,65 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); + //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next + + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next(); + + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/src/tools/clippy/tests/ui/filter_next.stderr b/src/tools/clippy/tests/ui/filter_next.stderr new file mode 100644 index 0000000000000..19c2428c3d17d --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.stderr @@ -0,0 +1,78 @@ +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:16:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next(); +LL + let _ = v.iter().find(|&x| *x < 0); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:19:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next_back(); +LL + let _ = v.iter().rfind(|&x| *x < 0); + | + +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:24:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | +help: use `.find(..)` instead + | +LL ~ let _ = v.iter().find(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:31:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + | +help: use `.rfind(..)` instead + | +LL ~ let _ = v.iter().rfind(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:58:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +LL + let _ = vec![1].into_iter().rfind(|&x| x < 0); + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_next_unfixable.rs b/src/tools/clippy/tests/ui/filter_next_unfixable.rs new file mode 100644 index 0000000000000..07a0db1353500 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next_unfixable.rs @@ -0,0 +1,19 @@ +//@no-rustfix +#![warn(clippy::filter_next)] + +fn main() {} + +// The fixed version doesn't compile, as `iter` isn't `mut`. +// We do emit a note suggesting adding it, but not an autofix +pub fn issue10029() { + { + let iter = (0..10); + let _ = iter.filter(|_| true).next(); + //~^ filter_next + } + { + let iter = (0..10); + let _ = iter.filter(|_| true).next_back(); + //~^ filter_next + } +} diff --git a/src/tools/clippy/tests/ui/filter_next_unfixable.stderr b/src/tools/clippy/tests/ui/filter_next_unfixable.stderr new file mode 100644 index 0000000000000..2c35badec4853 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next_unfixable.stderr @@ -0,0 +1,38 @@ +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next_unfixable.rs:11:17 + | +LL | let _ = iter.filter(|_| true).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you will also need to make `iter` mutable, because `find` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:10:13 + | +LL | let iter = (0..10); + | ^^^^ + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = iter.filter(|_| true).next(); +LL + let _ = iter.find(|_| true); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next_unfixable.rs:16:17 + | +LL | let _ = iter.filter(|_| true).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you will also need to make `iter` mutable, because `rfind` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:15:13 + | +LL | let iter = (0..10); + | ^^^^ +help: use `.rfind(..)` instead + | +LL - let _ = iter.filter(|_| true).next_back(); +LL + let _ = iter.rfind(|_| true); + | + +error: aborting due to 2 previous errors + 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 3f2ca76826f45..b14d0585f2c96 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -1,121 +1,122 @@ -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:19:13 | LL | let _ = a * b + c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` | + = note: the performance gain from `mul_add` may vary depending on the target architecture = note: `-D clippy::suboptimal-flops` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:21:13 | LL | let _ = a * b - c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:23:13 | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:25:13 | LL | let _ = c - a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:27:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:29:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:32:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:34:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:36:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:39:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:41:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:44:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:48:13 | LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may 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 +error: multiply and add expressions may 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 +error: multiply and add expressions may 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 +error: multiply and add expressions may 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 +error: multiply and add expressions may 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: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:122:5 | LL | a += b * c; | ^^^^^^^^^^ help: consider using: `a = b.mul_add(c, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:125:5 | LL | a -= b * c; diff --git a/src/tools/clippy/tests/ui/let_underscore_future.rs b/src/tools/clippy/tests/ui/let_underscore_future.rs index eb1a985a91172..17c73876302dd 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.rs +++ b/src/tools/clippy/tests/ui/let_underscore_future.rs @@ -1,4 +1,5 @@ use std::future::Future; +use std::pin::Pin; async fn some_async_fn() {} @@ -10,6 +11,10 @@ fn custom() -> impl Future { fn do_something_to_future(future: &mut impl Future) {} +fn boxed() -> Pin>> { + Box::pin(async {}) +} + fn main() { let _ = some_async_fn(); //~^ let_underscore_future @@ -21,4 +26,7 @@ fn main() { do_something_to_future(&mut future); let _ = future; //~^ let_underscore_future + + // Typed bindings are an intentional discard, see also `let_underscore_untyped`. + let _: Pin>> = boxed(); } diff --git a/src/tools/clippy/tests/ui/let_underscore_future.stderr b/src/tools/clippy/tests/ui/let_underscore_future.stderr index baa489551d4a3..12cbfff133e9c 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_future.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:14:5 + --> tests/ui/let_underscore_future.rs:19:5 | LL | let _ = some_async_fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = some_async_fn(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:17:5 + --> tests/ui/let_underscore_future.rs:22:5 | LL | let _ = custom(); | ^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = custom(); = help: consider awaiting the future or dropping explicitly with `std::mem::drop` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:22:5 + --> tests/ui/let_underscore_future.rs:27:5 | LL | let _ = future; | ^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.fixed b/src/tools/clippy/tests/ui/manual_abs_diff.fixed index 2766942140ce4..3b5cac5dfc986 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.fixed +++ b/src/tools/clippy/tests/ui/manual_abs_diff.fixed @@ -51,6 +51,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.rs b/src/tools/clippy/tests/ui/manual_abs_diff.rs index 2c408f2be3754..f845dd9b149a9 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.rs +++ b/src/tools/clippy/tests/ui/manual_abs_diff.rs @@ -61,6 +61,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.stderr b/src/tools/clippy/tests/ui/manual_abs_diff.stderr index bb6d312b435f8..b441ac9751f05 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.stderr +++ b/src/tools/clippy/tests/ui/manual_abs_diff.stderr @@ -80,7 +80,7 @@ LL | let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` error: manual absolute difference pattern without using `abs_diff` - --> tests/ui/manual_abs_diff.rs:119:5 + --> tests/ui/manual_abs_diff.rs:120:5 | LL | / if a < b { LL | | diff --git a/src/tools/clippy/tests/ui/manual_option_zip.fixed b/src/tools/clippy/tests/ui/manual_option_zip.fixed index 0a903fb784075..8b821f8737cbb 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.fixed +++ b/src/tools/clippy/tests/ui/manual_option_zip.fixed @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.zip(b); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.zip(get_option()); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/src/tools/clippy/tests/ui/manual_option_zip.rs b/src/tools/clippy/tests/ui/manual_option_zip.rs index 942d8aea3e835..5d059a98ee12c 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.rs +++ b/src/tools/clippy/tests/ui/manual_option_zip.rs @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.and_then(|a| b.map(|b| (a, b))); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.and_then(|a| get_option().map(|b| (a, b))); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/src/tools/clippy/tests/ui/manual_option_zip.stderr b/src/tools/clippy/tests/ui/manual_option_zip.stderr index 473f21702654c..55175660b7a26 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.stderr +++ b/src/tools/clippy/tests/ui/manual_option_zip.stderr @@ -20,34 +20,28 @@ LL | let _ = None::.and_then(|a| b.map(|b| (a, b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `None::.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:26:13 - | -LL | let _ = a.and_then(|a| get_option().map(|b| (a, b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(get_option())` - -error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:32:13 + --> tests/ui/manual_option_zip.rs:27:13 | LL | let _ = a.and_then(|a| b.map(|b| (b, a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.zip(a)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:39:13 + --> tests/ui/manual_option_zip.rs:34:13 | LL | let _ = a.and_then(|a| { b.map(|b| (a, b)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:42:13 + --> tests/ui/manual_option_zip.rs:37:13 | LL | let _ = a.and_then(|a| b.map(|b| { (a, b) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:45:13 + --> tests/ui/manual_option_zip.rs:40:13 | LL | let _ = a.and_then(|a| { b.map(|b| { (a, b) }) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed index 38ab10b539c18..aa8037af771c2 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -34,6 +34,38 @@ fn should_lint() { some_slice.fill(0); } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + s.fill(0); +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + s.fill(42); +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + s.fill(x); +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs index d80b23ce985d1..6ca200a046295 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.rs +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -47,6 +47,47 @@ fn should_lint() { } } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + for slot in s { + //~^ manual_slice_fill + *slot = 0; + } +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + for slot in s { + //~^ manual_slice_fill + *slot = 42; + } +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + for slot in s { + //~^ manual_slice_fill + *slot = x; + } +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.stderr b/src/tools/clippy/tests/ui/manual_slice_fill.stderr index 38e43d5b4e06e..cd1f36f4291f8 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_fill.stderr @@ -38,5 +38,32 @@ LL | | // foo LL | | } | |_____^ help: try: `some_slice.fill(0);` -error: aborting due to 4 previous errors +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:51:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 0; +LL | | } + | |_____^ help: try: `s.fill(0);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:58:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 42; +LL | | } + | |_____^ help: try: `s.fill(42);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:66:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = x; +LL | | } + | |_____^ help: try: `s.fill(x);` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs index bcbb14d5c2e1b..cfad8efc23979 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs @@ -161,3 +161,10 @@ fn issue15752() { x.map(|y| y.0).unwrap_or(&[]); //~^ map_unwrap_or } + +fn issue16901() { + let raw = String::from("scope:value"); + let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + //~^ map_unwrap_or + let _: &str = after_scope; +} diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr index a90da4a97e0c8..cd62c17848583 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr @@ -238,5 +238,11 @@ error: called `map().unwrap_or()` on an `Option` value LL | x.map(|y| y.0).unwrap_or(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: called `map().unwrap_or()` on an `Option` value + --> tests/ui/map_unwrap_or.rs:167:23 + | +LL | let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index f73ec563dceb1..10f0934bede74 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -1,36 +1,10 @@ -//@aux-build:option_helpers.rs - -#![allow( - clippy::disallowed_names, - clippy::default_trait_access, - clippy::let_underscore_untyped, - clippy::missing_docs_in_private_items, - clippy::missing_safety_doc, - clippy::non_ascii_literal, - clippy::new_without_default, - clippy::needless_pass_by_value, - clippy::needless_lifetimes, - clippy::elidable_lifetime_names, - clippy::print_stdout, - clippy::must_use_candidate, - clippy::use_self, - clippy::useless_format, - clippy::wrong_self_convention, - clippy::unused_async, - clippy::unused_self, - clippy::useless_vec -)] - -#[macro_use] -extern crate option_helpers; +#![warn(clippy::filter_next, clippy::new_ret_no_self)] use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; -use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; - struct Lt<'a> { foo: &'a u32, } @@ -115,43 +89,4 @@ impl Mul for T { } } -/// Checks implementation of `FILTER_NEXT` lint. -#[rustfmt::skip] -fn filter_next() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next(); -} - -#[rustfmt::skip] -fn filter_next_back() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next_back(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next_back(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next_back(); -} - fn main() {} diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 45efea4ee01cd..1136b33ea9104 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:102:5 + --> tests/ui/methods.rs:76:5 | LL | / fn new() -> i32 { LL | | @@ -10,30 +10,5 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:124:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next(); - | |___________________________^ - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods.rs:143:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next_back(); - | |________________________________^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/methods_fixable.fixed b/src/tools/clippy/tests/ui/methods_fixable.fixed deleted file mode 100644 index 287d8d881ec28..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.fixed +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().find(|&x| *x < 0); - //~^ filter_next - - let _ = v.iter().rfind(|&x| *x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().rfind(|&x| x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/src/tools/clippy/tests/ui/methods_fixable.rs b/src/tools/clippy/tests/ui/methods_fixable.rs deleted file mode 100644 index 11ce1b63560db..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().filter(|&x| *x < 0).next(); - //~^ filter_next - - let _ = v.iter().filter(|&x| *x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr deleted file mode 100644 index d26b5e9ac271e..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_fixable.rs:9:13 - | -LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().find(|&x| *x < 0)` - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:12:13 - | -LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:18:13 - | -LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/methods_unfixable.rs b/src/tools/clippy/tests/ui/methods_unfixable.rs deleted file mode 100644 index 46a5d95eb1b72..0000000000000 --- a/src/tools/clippy/tests/ui/methods_unfixable.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::filter_next)] -//@no-rustfix -fn main() {} - -pub fn issue10029() { - let iter = (0..10); - let _ = iter.filter(|_| true).next(); - //~^ filter_next -} diff --git a/src/tools/clippy/tests/ui/methods_unfixable.stderr b/src/tools/clippy/tests/ui/methods_unfixable.stderr deleted file mode 100644 index 137112ae41795..0000000000000 --- a/src/tools/clippy/tests/ui/methods_unfixable.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_unfixable.rs:7:13 - | -LL | let _ = iter.filter(|_| true).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` - | -help: you will also need to make `iter` mutable, because `find` takes `&mut self` - --> tests/ui/methods_unfixable.rs:6:9 - | -LL | let iter = (0..10); - | ^^^^ - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/min_ident_chars.rs b/src/tools/clippy/tests/ui/min_ident_chars.rs index e2f82e2a182f6..298798be7a1e6 100644 --- a/src/tools/clippy/tests/ui/min_ident_chars.rs +++ b/src/tools/clippy/tests/ui/min_ident_chars.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs -#![allow(irrefutable_let_patterns, nonstandard_style, unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::min_ident_chars)] +#![expect(irrefutable_let_patterns, nonstandard_style)] +#![allow(clippy::struct_field_names)] extern crate proc_macros; use proc_macros::{external, with_span}; diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs index ee19d3ff71421..cc97e496514bc 100644 --- a/src/tools/clippy/tests/ui/min_max.rs +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -1,4 +1,5 @@ -#![allow(clippy::manual_clamp)] +#![warn(clippy::min_max)] +#![expect(clippy::manual_clamp)] use std::cmp::{max as my_max, max, min as my_min, min}; diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr index 87510a465a08b..84b4d37545529 100644 --- a/src/tools/clippy/tests/ui/min_max.stderr +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -1,79 +1,80 @@ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:21:5 + --> tests/ui/min_max.rs:22:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::min_max)]` on by default + = note: `-D clippy::min-max` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::min_max)]` error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:24:5 + --> tests/ui/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:27:5 + --> tests/ui/min_max.rs:28:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:30:5 + --> tests/ui/min_max.rs:31:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:33:5 + --> tests/ui/min_max.rs:34:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:44:5 + --> tests/ui/min_max.rs:45:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:47:5 + --> tests/ui/min_max.rs:48:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:53:5 + --> tests/ui/min_max.rs:54:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:56:5 + --> tests/ui/min_max.rs:57:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:59:5 + --> tests/ui/min_max.rs:60:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:66:5 + --> tests/ui/min_max.rs:67:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:71:5 + --> tests/ui/min_max.rs:72:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:74:5 + --> tests/ui/min_max.rs:75:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index 4627bef28a29b..c624774a8a9cf 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -1,4 +1,3 @@ -#![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] fn main() {} diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index 809b1cfe73bf0..842bd5c238618 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,5 +1,5 @@ error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:13:19 + --> tests/ui/min_rust_version_attr.rs:12:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let log2_10 = 3.321928094887362; = note: `#[deny(clippy::approx_constant)]` on by default error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:19:19 + --> tests/ui/min_rust_version_attr.rs:18:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:30:19 + --> tests/ui/min_rust_version_attr.rs:29:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:41:19 + --> tests/ui/min_rust_version_attr.rs:40:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:52:19 + --> tests/ui/min_rust_version_attr.rs:51:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:60:27 + --> tests/ui/min_rust_version_attr.rs:59:27 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs index 11ff865b583bf..49d7031441066 100644 --- a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs +++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs @@ -1,5 +1,4 @@ #![warn(clippy::mismatching_type_param_order)] -#![allow(clippy::disallowed_names, clippy::needless_lifetimes)] fn main() { struct Foo { diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr index 214d9d734d1bb..a1b5ca7ec26c7 100644 --- a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr +++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr @@ -1,5 +1,5 @@ error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:20 + --> tests/ui/mismatching_type_param_order.rs:10:20 | LL | impl Foo {} | ^ @@ -9,7 +9,7 @@ LL | impl Foo {} = help: to override `-D warnings` add `#[allow(clippy::mismatching_type_param_order)]` error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:23 + --> tests/ui/mismatching_type_param_order.rs:10:23 | LL | impl Foo {} | ^ @@ -17,7 +17,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:16:23 + --> tests/ui/mismatching_type_param_order.rs:15:23 | LL | impl Foo {} | ^ @@ -25,7 +25,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:44 + --> tests/ui/mismatching_type_param_order.rs:27:44 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -33,7 +33,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:47 + --> tests/ui/mismatching_type_param_order.rs:27:47 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -41,7 +41,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:27 + --> tests/ui/mismatching_type_param_order.rs:45:27 | LL | impl FooEnum {} | ^ @@ -49,7 +49,7 @@ LL | impl FooEnum {} = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:30 + --> tests/ui/mismatching_type_param_order.rs:45:30 | LL | impl FooEnum {} | ^ @@ -57,7 +57,7 @@ LL | impl FooEnum {} = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:33 + --> tests/ui/mismatching_type_param_order.rs:45:33 | LL | impl FooEnum {} | ^ @@ -65,7 +65,7 @@ LL | impl FooEnum {} = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:31 + --> tests/ui/mismatching_type_param_order.rs:59:31 | LL | impl FooUnion where A: Copy {} | ^ @@ -73,7 +73,7 @@ LL | impl FooUnion where A: Copy {} = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:34 + --> tests/ui/mismatching_type_param_order.rs:59:34 | LL | impl FooUnion where A: Copy {} | ^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed index bc123d1a40ba2..479d5f91492d5 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs index 6590101157c3f..985c273c59ed7 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters.rs @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr index aaf21cecb9255..37f57d7f79e55 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:12:5 + --> tests/ui/misnamed_getters.rs:11:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:17:5 + --> tests/ui/misnamed_getters.rs:16:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -24,7 +24,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:23:5 + --> tests/ui/misnamed_getters.rs:22:5 | LL | / fn b(self) -> u8 { LL | | @@ -35,7 +35,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:29:5 + --> tests/ui/misnamed_getters.rs:28:5 | LL | / fn b_mut(&mut self) -> &mut u8 { LL | | @@ -46,7 +46,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:35:5 + --> tests/ui/misnamed_getters.rs:34:5 | LL | / fn c(&self) -> &u8 { LL | | @@ -57,7 +57,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:41:5 + --> tests/ui/misnamed_getters.rs:40:5 | LL | / fn c_mut(&mut self) -> &mut u8 { LL | | @@ -68,7 +68,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:54:5 + --> tests/ui/misnamed_getters.rs:53:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | @@ -79,7 +79,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:59:5 + --> tests/ui/misnamed_getters.rs:58:5 | LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | @@ -90,7 +90,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:65:5 + --> tests/ui/misnamed_getters.rs:64:5 | LL | / unsafe fn b(self) -> u8 { LL | | @@ -101,7 +101,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:71:5 + --> tests/ui/misnamed_getters.rs:70:5 | LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | @@ -112,7 +112,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:85:5 + --> tests/ui/misnamed_getters.rs:84:5 | LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | @@ -123,7 +123,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:90:5 + --> tests/ui/misnamed_getters.rs:89:5 | LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -134,7 +134,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:96:5 + --> tests/ui/misnamed_getters.rs:95:5 | LL | / unsafe fn b_unchecked(self) -> u8 { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:102:5 + --> tests/ui/misnamed_getters.rs:101:5 | LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -156,7 +156,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:136:5 + --> tests/ui/misnamed_getters.rs:135:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -167,7 +167,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:141:5 + --> tests/ui/misnamed_getters.rs:140:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -178,7 +178,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:147:5 + --> tests/ui/misnamed_getters.rs:146:5 | LL | / fn d(&self) -> &u8 { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:152:5 + --> tests/ui/misnamed_getters.rs:151:5 | LL | / fn d_mut(&mut self) -> &mut u8 { LL | | diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed index 7112719a9f284..a923da1962fbe 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs index 19b5d086041f4..bdb011939da64 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr index 5495e2e3733f0..9771d2d8b119f 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters_2021.rs:15:5 + --> tests/ui/misnamed_getters_2021.rs:13:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed index 882ff6bf8944a..c5628f2486b0a 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed index de3a0f1710d24..02c4383f45ab7 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs index 62d83d1619c1e..dcb284574ce43 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr index 63f3a3e28f12c..50547a91dedc1 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:6:5 + --> tests/ui/misrefactored_assign_op.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -18,7 +18,7 @@ LL + a = a + a + 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:9:5 + --> tests/ui/misrefactored_assign_op.rs:10:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + a = a + 1 + a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:12:5 + --> tests/ui/misrefactored_assign_op.rs:13:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + a = a - (a - 1); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:15:5 + --> tests/ui/misrefactored_assign_op.rs:16:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + a = a * a * 99; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:18:5 + --> tests/ui/misrefactored_assign_op.rs:19:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + a = a * 42 * a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:21:5 + --> tests/ui/misrefactored_assign_op.rs:22:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + a = a / (a / 2); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:24:5 + --> tests/ui/misrefactored_assign_op.rs:25:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + a = a % (a % 5); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:27:5 + --> tests/ui/misrefactored_assign_op.rs:28:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + a = a & a & 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:30:5 + --> tests/ui/misrefactored_assign_op.rs:31:5 | LL | a *= a * a; | ^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs index eb98969efa47f..b8cdb71bd30a9 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_asserts_for_indexing)] fn sum(v: &[u8]) -> u8 { diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 2929646494a41..fe929aa36dbad 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -1,5 +1,5 @@ error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:4:5 | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ @@ -10,7 +10,7 @@ LL | v[0] + v[1] + v[2] + v[3] + v[4] = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:9:13 | LL | let _ = v[0]; | ^^^^ @@ -21,7 +21,7 @@ LL | let _ = v[1..4]; = help: consider asserting the length before indexing: `assert!(v.len() > 3);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:16:13 | LL | let a = v[0]; | ^^^^ @@ -34,7 +34,7 @@ LL | let c = v[2]; = help: consider asserting the length before indexing: `assert!(v.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:25:13 | LL | let _ = v1[0] + v1[12]; | ^^^^^ ^^^^^^ @@ -42,7 +42,7 @@ LL | let _ = v1[0] + v1[12]; = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:27:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -50,7 +50,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:34:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -58,7 +58,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:44:13 | LL | let _ = f.v[0] + f.v[1]; | ^^^^^^ ^^^^^^ @@ -66,7 +66,7 @@ LL | let _ = f.v[0] + f.v[1]; = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:58:13 | LL | let _ = x[0] + x[1]; | ^^^^ ^^^^ @@ -74,7 +74,7 @@ LL | let _ = x[0] + x[1]; = help: consider asserting the length before indexing: `assert!(x.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:76:13 | LL | let _ = v1[1] + v1[2]; | ^^^^^ ^^^^^ @@ -82,7 +82,7 @@ LL | let _ = v1[1] + v1[2]; = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:84:13 | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ ^^^^^ ^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index 46a7f79ac16ce..bc2d5b9d1c7b4 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 78e1939a85973..26baebfc535e2 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs index b206f4d8f8ce3..86c97ce32b687 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_fields_in_debug)] use std::fmt; diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr index 75b551e1f5c7e..4ea837e47836d 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr @@ -1,5 +1,5 @@ error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:14:1 + --> tests/ui/missing_fields_in_debug.rs:13:1 | LL | / impl fmt::Debug for NamedStruct1Ignored { ... | @@ -7,7 +7,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:11:5 + --> tests/ui/missing_fields_in_debug.rs:10:5 | LL | hidden: u32, | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | hidden: u32, = help: to override `-D warnings` add `#[allow(clippy::missing_fields_in_debug)]` error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:34:1 + --> tests/ui/missing_fields_in_debug.rs:33:1 | LL | / impl fmt::Debug for NamedStructMultipleIgnored { ... | @@ -25,17 +25,17 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:28:5 + --> tests/ui/missing_fields_in_debug.rs:27:5 | LL | hidden: u32, | ^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:29:5 + --> tests/ui/missing_fields_in_debug.rs:28:5 | LL | hidden2: String, | ^^^^^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:31:5 + --> tests/ui/missing_fields_in_debug.rs:30:5 | LL | hidden4: ((((u8), u16), u32), u64), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ LL | hidden4: ((((u8), u16), u32), u64), = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:97:1 + --> tests/ui/missing_fields_in_debug.rs:96:1 | LL | / impl fmt::Debug for MultiExprDebugImpl { LL | | @@ -54,7 +54,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:93:5 + --> tests/ui/missing_fields_in_debug.rs:92:5 | LL | b: String, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs index 8e937d609512a..0adaeaa9cab8a 100644 --- a/src/tools/clippy/tests/ui/missing_inline.rs +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -2,7 +2,7 @@ #![crate_type = "dylib"] // When denying at the crate level, be sure to not get random warnings from the // injected intrinsics by the compiler. -#![allow(dead_code, non_snake_case)] +#![expect(non_snake_case)] type Typedef = String; pub type PubTypedef = String; diff --git a/src/tools/clippy/tests/ui/missing_inline.stderr b/src/tools/clippy/tests/ui/missing_inline.stderr index 99ee828e805ca..9f5987fb8e79b 100644 --- a/src/tools/clippy/tests/ui/missing_inline.stderr +++ b/src/tools/clippy/tests/ui/missing_inline.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:20:1 | LL | pub fn pub_foo() {} @@ -7,31 +7,31 @@ LL | pub fn pub_foo() {} = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]` -error: missing `#[inline]` for a default trait method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:39:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:56:5 | LL | fn PubBar_a() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:60:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:64:5 | LL | fn PubBar_c() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:76:5 | LL | pub fn PubFooImpl() {} diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.stderr b/src/tools/clippy/tests/ui/missing_inline_executable.stderr index 3108e4e490659..d083cb2b54915 100644 --- a/src/tools/clippy/tests/ui/missing_inline_executable.stderr +++ b/src/tools/clippy/tests/ui/missing_inline_executable.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline_executable.rs:3:1 | LL | pub fn foo() {} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index d016e099e303b..63e84ffaab634 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -1,6 +1,6 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_panics_doc)] -#![allow(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] +#![expect(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.fixed b/src/tools/clippy/tests/ui/missing_spin_loop.fixed index 03fbf8ccc8a37..7876a0c47d5e7 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.fixed +++ b/src/tools/clippy/tests/ui/missing_spin_loop.fixed @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.rs b/src/tools/clippy/tests/ui/missing_spin_loop.rs index bf18590b9408e..06bfe8eeaab80 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.rs +++ b/src/tools/clippy/tests/ui/missing_spin_loop.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.stderr b/src/tools/clippy/tests/ui/missing_spin_loop.stderr index 98c32d0423469..d4a93bb2d75b7 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.stderr +++ b/src/tools/clippy/tests/ui/missing_spin_loop.stderr @@ -1,5 +1,5 @@ error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:10:37 + --> tests/ui/missing_spin_loop.rs:9:37 | LL | while b.load(Ordering::Acquire) {} | ^^ help: try: `{ std::hint::spin_loop() }` @@ -8,31 +8,31 @@ LL | while b.load(Ordering::Acquire) {} = help: to override `-D warnings` add `#[allow(clippy::missing_spin_loop)]` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:13:37 + --> tests/ui/missing_spin_loop.rs:12:37 | LL | while !b.load(Ordering::SeqCst) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:16:46 + --> tests/ui/missing_spin_loop.rs:15:46 | LL | while b.load(Ordering::Acquire) == false {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:19:49 + --> tests/ui/missing_spin_loop.rs:18:49 | LL | while { true == b.load(Ordering::Acquire) } {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:22:93 + --> tests/ui/missing_spin_loop.rs:21:93 | LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:25:94 + --> tests/ui/missing_spin_loop.rs:24:94 | LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {} | ^^ help: try: `{ std::hint::spin_loop() }` diff --git a/src/tools/clippy/tests/ui/missing_trait_methods.rs b/src/tools/clippy/tests/ui/missing_trait_methods.rs index 67070a2999510..90349a0de101f 100644 --- a/src/tools/clippy/tests/ui/missing_trait_methods.rs +++ b/src/tools/clippy/tests/ui/missing_trait_methods.rs @@ -1,5 +1,5 @@ -#![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::missing_trait_methods)] +#![expect(clippy::needless_lifetimes)] trait A { fn provided() {} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed index 58faeaee09d46..2602a2fda5135 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs index c9a4c5fa83b2b..fe7b7238d33fe 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed index a0190acc5d4b3..3a7d714375a77 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs index 12a26204e7557..02e76a468d210 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr index 9c6b2fab21c31..a986bc0e69ec0 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr @@ -1,97 +1,98 @@ error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:16:18 + --> tests/ui/mistyped_literal_suffix.rs:14:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` | - = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default + = note: `-D clippy::mistyped-literal-suffixes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mistyped_literal_suffixes)]` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:18:18 + --> tests/ui/mistyped_literal_suffix.rs:16:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:20:18 + --> tests/ui/mistyped_literal_suffix.rs:18:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:23:18 + --> tests/ui/mistyped_literal_suffix.rs:21:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:28:18 + --> tests/ui/mistyped_literal_suffix.rs:26:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:31:18 + --> tests/ui/mistyped_literal_suffix.rs:29:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:36:18 + --> tests/ui/mistyped_literal_suffix.rs:34:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:38:18 + --> tests/ui/mistyped_literal_suffix.rs:36:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:40:18 + --> tests/ui/mistyped_literal_suffix.rs:38:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:42:18 + --> tests/ui/mistyped_literal_suffix.rs:40:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:47:18 + --> tests/ui/mistyped_literal_suffix.rs:45:18 | LL | let fail30 = 127_8; // should be i8 | ^^^^^ help: did you mean to write: `127_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:50:18 + --> tests/ui/mistyped_literal_suffix.rs:48:18 | LL | let fail31 = 240_8; // should be u8 | ^^^^^ help: did you mean to write: `240_u8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:54:18 + --> tests/ui/mistyped_literal_suffix.rs:52:18 | LL | let fail33 = 0x1234_16; | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:56:18 + --> tests/ui/mistyped_literal_suffix.rs:54:18 | LL | let fail34 = 0xABCD_16; | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:59:18 + --> tests/ui/mistyped_literal_suffix.rs:57:18 | LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:67:13 + --> tests/ui/mistyped_literal_suffix.rs:65:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs index 5d16858bf85ec..8fdce71d05528 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.rs +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -1,7 +1,6 @@ //@compile-flags: --test #![warn(clippy::module_name_repetitions)] -#![allow(dead_code)] pub mod foo { pub fn foo() {} diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr index 9000c44a3902e..e4c3daacdbead 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.stderr +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -1,5 +1,5 @@ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:8:12 + --> tests/ui/module_name_repetitions.rs:7:12 | LL | pub fn foo_bar() {} | ^^^^^^^ @@ -8,31 +8,31 @@ LL | pub fn foo_bar() {} = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:11:12 + --> tests/ui/module_name_repetitions.rs:10:12 | LL | pub fn bar_foo() {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:14:16 + --> tests/ui/module_name_repetitions.rs:13:16 | LL | pub struct FooCake; | ^^^^^^^ error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:17:14 + --> tests/ui/module_name_repetitions.rs:16:14 | LL | pub enum CakeFoo {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:20:16 + --> tests/ui/module_name_repetitions.rs:19:16 | LL | pub struct Foo7Bar; | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:33:20 + --> tests/ui/module_name_repetitions.rs:32:20 | LL | pub use error::FooError; | ^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs index 0e08174f686eb..46d554616f8df 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs @@ -1,7 +1,7 @@ #![feature(f128)] #![feature(f16)] #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs index d7cd910e43cb7..583637dbc2855 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint on signed integral numbers diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs index 9f5fb697df0ea..160bc86629247 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,10 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one, - clippy::identity_op -)] +#![expect(clippy::identity_op, clippy::modulo_one, clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr index 12edee8cf4255..e09673213f79a 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:11:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:6:5 | LL | -1 % 2; | ^^^^^^ @@ -10,7 +10,7 @@ LL | -1 % 2; = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]` error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:14:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:9:5 | LL | 1 % -2; | ^^^^^^ @@ -19,7 +19,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> tests/ui/modulo_arithmetic_integral_const.rs:17:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:12:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> tests/ui/modulo_arithmetic_integral_const.rs:20:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:15:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> tests/ui/modulo_arithmetic_integral_const.rs:23:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:18:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:26:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:29:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:32:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:27:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:35:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:30:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:38:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:33:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:41:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:36:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:44:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:39:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:47:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:42:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:50:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:45:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:53:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:48:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:56:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:51:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:59:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:54:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs index 63ae61059cb86..730c37d0e368e 100644 --- a/src/tools/clippy/tests/ui/modulo_one.rs +++ b/src/tools/clippy/tests/ui/modulo_one.rs @@ -1,6 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(unconditional_panic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] +#![expect(clippy::identity_op, clippy::no_effect)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr index 1c9c79d1c6214..f841ac18bbc3d 100644 --- a/src/tools/clippy/tests/ui/modulo_one.stderr +++ b/src/tools/clippy/tests/ui/modulo_one.stderr @@ -1,5 +1,5 @@ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:9:5 + --> tests/ui/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ @@ -8,31 +8,31 @@ LL | 10 % 1; = help: to override `-D warnings` add `#[allow(clippy::modulo_one)]` error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:12:5 + --> tests/ui/modulo_one.rs:11:5 | LL | 10 % -1; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:17:5 + --> tests/ui/modulo_one.rs:16:5 | LL | i32::MIN % (-1); | ^^^^^^^^^^^^^^^ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:24:5 + --> tests/ui/modulo_one.rs:23:5 | LL | 2 % ONE; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:29:5 + --> tests/ui/modulo_one.rs:28:5 | LL | 2 % NEG_ONE; | ^^^^^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:35:5 + --> tests/ui/modulo_one.rs:34:5 | LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs b/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs index dcef1a485fceb..fe83023aaeb79 100644 --- a/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs +++ b/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs @@ -1,6 +1,5 @@ //@check-pass -#![allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)] #![forbid(clippy::ptr_as_ptr)] /// MSRV checking in late passes skips checking the parent nodes if no early pass sees a diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 0ff881472cbb9..5d51c47eeed91 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,11 +1,11 @@ //@needs-asm-support //@aux-build:proc_macros.rs +#![warn(clippy::multiple_unsafe_ops_per_block)] #![expect( dropping_copy_types, - clippy::unnecessary_operation, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::unnecessary_operation )] -#![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index 17569884658b1..226fafccddced 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 8b898533d01b7..f9b6c2986504c 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr index 38c7334322503..24fda87cd16aa 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.stderr +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:16:8 + --> tests/ui/must_use_candidates.rs:10:8 | LL | pub fn pure(i: u8) -> u8 { | ^^^^ @@ -13,7 +13,7 @@ LL | pub fn pure(i: u8) -> u8 { | error: this method could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:22:12 + --> tests/ui/must_use_candidates.rs:16:12 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ pub fn inherent_pure(&self) -> u8 { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:54:8 + --> tests/ui/must_use_candidates.rs:48:8 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:67:8 + --> tests/ui/must_use_candidates.rs:61:8 | LL | pub fn rcd(_x: Rc) -> bool { | ^^^ @@ -49,7 +49,7 @@ LL | pub fn rcd(_x: Rc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:76:8 + --> tests/ui/must_use_candidates.rs:70:8 | LL | pub fn arcd(_x: Arc) -> bool { | ^^^^ @@ -61,7 +61,7 @@ LL | pub fn arcd(_x: Arc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:108:8 + --> tests/ui/must_use_candidates.rs:102:8 | LL | pub fn result_uninhabited() -> Result { | ^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | pub fn result_uninhabited() -> Result { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:113:8 + --> tests/ui/must_use_candidates.rs:107:8 | LL | pub fn controlflow_uninhabited() -> std::ops::ControlFlow { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed index b9871991b16f0..5c1993f6575a2 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.fixed +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs index a5865681c8a8e..86b4380a477aa 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.rs +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs index 1b0b351518cbb..f91e2992406b7 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.rs +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -1,11 +1,10 @@ -#![allow( - unused, +#![warn(clippy::mut_from_ref)] +#![expect( + clippy::boxed_local, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut, - clippy::redundant_allocation, - clippy::boxed_local + clippy::redundant_allocation )] -#![warn(clippy::mut_from_ref)] struct Foo; diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr index 0974268734653..92165eb9ae735 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.stderr +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:13:39 + --> tests/ui/mut_from_ref.rs:12:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:13:29 + --> tests/ui/mut_from_ref.rs:12:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,85 +13,85 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:21:25 + --> tests/ui/mut_from_ref.rs:20:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:21:16 + --> tests/ui/mut_from_ref.rs:20:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:21 + --> tests/ui/mut_from_ref.rs:30:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:12 + --> tests/ui/mut_from_ref.rs:30:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:50 + --> tests/ui/mut_from_ref.rs:36:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:25 + --> tests/ui/mut_from_ref.rs:36:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:43:67 + --> tests/ui/mut_from_ref.rs:42:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:43:27 + --> tests/ui/mut_from_ref.rs:42:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:49:46 + --> tests/ui/mut_from_ref.rs:48:46 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:49:24 + --> tests/ui/mut_from_ref.rs:48:24 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:55:37 + --> tests/ui/mut_from_ref.rs:54:37 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:55:24 + --> tests/ui/mut_from_ref.rs:54:24 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:85:35 + --> tests/ui/mut_from_ref.rs:84:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:85:26 + --> tests/ui/mut_from_ref.rs:84:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ diff --git a/src/tools/clippy/tests/ui/mut_mut.fixed b/src/tools/clippy/tests/ui/mut_mut.fixed index f9a7f5dcb5a15..c34f605e3da6e 100644 --- a/src/tools/clippy/tests/ui/mut_mut.fixed +++ b/src/tools/clippy/tests/ui/mut_mut.fixed @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut 1u32; diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs index bbec48011a17e..d17394dfe3f69 100644 --- a/src/tools/clippy/tests/ui/mut_mut.rs +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut &mut 1u32; diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr index 85e9c5649b89a..0a7a923e0f48f 100644 --- a/src/tools/clippy/tests/ui/mut_mut.stderr +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:15:11 + --> tests/ui/mut_mut.rs:8:11 | LL | fn fun(x: &mut &mut u32) { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,37 +8,37 @@ LL | fn fun(x: &mut &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:32:17 + --> tests/ui/mut_mut.rs:24:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut.rs:35:21 + --> tests/ui/mut_mut.rs:27:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:32 + --> tests/ui/mut_mut.rs:32:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:16 + --> tests/ui/mut_mut.rs:32:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:37 + --> tests/ui/mut_mut.rs:38:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:16 + --> tests/ui/mut_mut.rs:38:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` diff --git a/src/tools/clippy/tests/ui/mut_mut_unfixable.rs b/src/tools/clippy/tests/ui/mut_mut_unfixable.rs index 271cb7b968898..0c6444fd9fa35 100644 --- a/src/tools/clippy/tests/ui/mut_mut_unfixable.rs +++ b/src/tools/clippy/tests/ui/mut_mut_unfixable.rs @@ -1,7 +1,6 @@ //@no-rustfix #![warn(clippy::mut_mut)] -#![allow(unused)] #![expect(clippy::no_effect)] //! removing the extra `&mut`s will break the derefs diff --git a/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr b/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr index cf66eb2ed1eca..7e7fb801ce1e7 100644 --- a/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr +++ b/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:9:11 + --> tests/ui/mut_mut_unfixable.rs:8:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,31 +8,31 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:15:17 + --> tests/ui/mut_mut_unfixable.rs:14:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut_unfixable.rs:18:21 + --> tests/ui/mut_mut_unfixable.rs:17:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:24:17 + --> tests/ui/mut_mut_unfixable.rs:23:17 | LL | let y = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:30:17 + --> tests/ui/mut_mut_unfixable.rs:29:17 | LL | let y = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:39:17 + --> tests/ui/mut_mut_unfixable.rs:38:17 | LL | let y = &mut &mut x; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed index 5790ee8c1b022..44587b89ec836 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.rs b/src/tools/clippy/tests/ui/mut_mutex_lock.rs index 7286afac823b7..eef4f26c09595 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.rs +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr index 67543c73c6eef..587405449523c 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr @@ -1,5 +1,5 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:10:33 + --> tests/ui/mut_mutex_lock.rs:9:33 | LL | let mut value = value_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` @@ -8,7 +8,7 @@ LL | let mut value = value_mutex.lock().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::mut_mutex_lock)]` error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:16:43 + --> tests/ui/mut_mutex_lock.rs:15:43 | LL | let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs index 107a6229b86d8..4e1192a86c2c1 100644 --- a/src/tools/clippy/tests/ui/mut_range_bound.rs +++ b/src/tools/clippy/tests/ui/mut_range_bound.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![warn(clippy::mut_range_bound)] fn main() {} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.fixed b/src/tools/clippy/tests/ui/mutex_atomic.fixed index dc05d8a2c61fe..711aba8da37b6 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.fixed +++ b/src/tools/clippy/tests/ui/mutex_atomic.fixed @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 33745f8fc5e13..f0ca75458d100 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index c1391e0111c8d..85c1829f44121 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:8:13 + --> tests/ui/mutex_atomic.rs:7:13 | LL | let _ = Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + let _ = std::sync::atomic::AtomicBool::new(true); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:11:13 + --> tests/ui/mutex_atomic.rs:10:13 | LL | let _ = Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL + let _ = std::sync::atomic::AtomicUsize::new(5usize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:14:13 + --> tests/ui/mutex_atomic.rs:13:13 | LL | let _ = Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL + let _ = std::sync::atomic::AtomicIsize::new(9isize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:21:13 + --> tests/ui/mutex_atomic.rs:20:13 | LL | let _ = Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:24:13 + --> tests/ui/mutex_atomic.rs:23:13 | LL | let _ = Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL + let _ = std::sync::atomic::AtomicU32::new(0u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:27:13 + --> tests/ui/mutex_atomic.rs:26:13 | LL | let _ = Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL + let _ = std::sync::atomic::AtomicI32::new(0i32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:31:13 + --> tests/ui/mutex_atomic.rs:30:13 | LL | let _ = Mutex::new(0u8); | ^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + let _ = std::sync::atomic::AtomicU8::new(0u8); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:34:13 + --> tests/ui/mutex_atomic.rs:33:13 | LL | let _ = Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = std::sync::atomic::AtomicI16::new(0i16); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + let _x = std::sync::atomic::AtomicI8::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:41:13 + --> tests/ui/mutex_atomic.rs:40:13 | LL | let _ = Mutex::new(X); | ^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let _ = std::sync::atomic::AtomicI64::new(X); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:53:30 + --> tests/ui/mutex_atomic.rs:52:30 | LL | static MTX: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -146,7 +146,7 @@ LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32 | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:56:15 + --> tests/ui/mutex_atomic.rs:55:15 | LL | let mtx = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL + let mtx = std::sync::atomic::AtomicI32::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:60:22 + --> tests/ui/mutex_atomic.rs:59:22 | LL | let reassigned = mtx; | ^^^ @@ -168,7 +168,7 @@ LL | let reassigned = mtx; = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:65:35 + --> tests/ui/mutex_atomic.rs:64:35 | LL | let (funky_mtx): Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:76:13 + --> tests/ui/mutex_atomic.rs:75:13 | LL | let _ = Mutex::new(test_expr!(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed index adb9096c9f242..0eef094895dd8 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs index 550546ed24fd7..0ea16ce89946b 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed index eedeecd8a8cbc..a5c34939a5c9d 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs index 5c0a4ccf266c3..ca03d888f3650 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index 0af4f87bec677..c3eed4e3fd717 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index 2e8719bd041e0..4571628be20ea 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index e15260242f4dd..f8cad52c2bbf2 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:41:5 + --> tests/ui/needless_bool/fixable.rs:31:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:47:5 + --> tests/ui/needless_bool/fixable.rs:37:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:53:5 + --> tests/ui/needless_bool/fixable.rs:43:5 | LL | / if x && y { LL | | false @@ -32,7 +32,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:62:5 + --> tests/ui/needless_bool/fixable.rs:52:5 | LL | / if a == b { LL | | false @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a != b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:68:5 + --> tests/ui/needless_bool/fixable.rs:58:5 | LL | / if a != b { LL | | false @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a == b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:74:5 + --> tests/ui/needless_bool/fixable.rs:64:5 | LL | / if a < b { LL | | false @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a >= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:80:5 + --> tests/ui/needless_bool/fixable.rs:70:5 | LL | / if a <= b { LL | | false @@ -72,7 +72,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a > b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:86:5 + --> tests/ui/needless_bool/fixable.rs:76:5 | LL | / if a > b { LL | | false @@ -82,7 +82,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a <= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:92:5 + --> tests/ui/needless_bool/fixable.rs:82:5 | LL | / if a >= b { LL | | false @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:121:5 + --> tests/ui/needless_bool/fixable.rs:111:5 | LL | / if x { LL | | return true; @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:130:5 + --> tests/ui/needless_bool/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:139:5 + --> tests/ui/needless_bool/fixable.rs:129:5 | LL | / if x && y { LL | | return true; @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:148:5 + --> tests/ui/needless_bool/fixable.rs:138:5 | LL | / if x && y { LL | | return false; @@ -132,7 +132,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:157:8 + --> tests/ui/needless_bool/fixable.rs:147:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` @@ -141,25 +141,25 @@ LL | if x == true {}; = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:162:8 + --> tests/ui/needless_bool/fixable.rs:152:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:173:8 + --> tests/ui/needless_bool/fixable.rs:163:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:175:8 + --> tests/ui/needless_bool/fixable.rs:165:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:185:12 + --> tests/ui/needless_bool/fixable.rs:175:12 | LL | } else if returns_bool() { | ____________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:199:5 + --> tests/ui/needless_bool/fixable.rs:189:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -180,37 +180,37 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:205:30 + --> tests/ui/needless_bool/fixable.rs:195:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:209:9 + --> tests/ui/needless_bool/fixable.rs:199:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:221:14 + --> tests/ui/needless_bool/fixable.rs:211:14 | LL | let _x = if a && b { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:223:14 + --> tests/ui/needless_bool/fixable.rs:213:14 | LL | let _x = if a && b { true } else { false } as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:227:14 + --> tests/ui/needless_bool/fixable.rs:217:14 | LL | let _x = if a { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `a` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:239:5 + --> tests/ui/needless_bool/fixable.rs:229:5 | LL | / if test_expr!(x) { LL | | true diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs index 40ffeae6c56fa..c5d2ef51fade5 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.rs +++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs @@ -1,12 +1,5 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::needless_return, - clippy::branches_sharing_code -)] +#![expect(clippy::if_same_then_else, clippy::needless_return)] fn main() { let x = true; diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr index 077e9b5df47cf..07711b9234cdd 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:14:5 + --> tests/ui/needless_bool/simple.rs:7:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:20:5 + --> tests/ui/needless_bool/simple.rs:13:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:36:5 + --> tests/ui/needless_bool/simple.rs:29:5 | LL | / if x { LL | | return true; @@ -32,7 +32,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:45:5 + --> tests/ui/needless_bool/simple.rs:38:5 | LL | / if x { LL | | return false; diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed index 8fd5720381404..3bd592b471e4b 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed +++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs index 4721ab433b327..1279176cb20a2 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.rs +++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index 34ff782f34a35..3dfea7cd1af99 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:13:5 + --> tests/ui/needless_bool_assign.rs:12:5 | LL | / if random() && random() { LL | | a.field = true; @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::needless_bool_assign)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:19:5 + --> tests/ui/needless_bool_assign.rs:18:5 | LL | / if random() && random() { LL | | a.field = false; @@ -22,7 +22,7 @@ LL | | } | |_____^ help: you can reduce it to: `a.field = !(random() && random());` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:34:5 + --> tests/ui/needless_bool_assign.rs:33:5 | LL | / if random() { LL | | a.field = true; @@ -32,7 +32,7 @@ LL | | } | |_____^ help: you can reduce it to: `random(); a.field = true;` error: this `if` has identical blocks - --> tests/ui/needless_bool_assign.rs:34:17 + --> tests/ui/needless_bool_assign.rs:33:17 | LL | if random() { | _________________^ @@ -41,7 +41,7 @@ LL | | } else { | |_____^ | note: same as this - --> tests/ui/needless_bool_assign.rs:36:12 + --> tests/ui/needless_bool_assign.rs:35:12 | LL | } else { | ____________^ @@ -52,7 +52,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:54:12 + --> tests/ui/needless_bool_assign.rs:53:12 | LL | } else if x || y { | ____________^ @@ -63,7 +63,7 @@ LL | | } | |_____^ help: you can reduce it to: `{ z = x || y; }` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:77:5 + --> tests/ui/needless_bool_assign.rs:76:5 | LL | / if invoke!(must_keep, x, y) { LL | | dot_0!(skip) = false; diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index b686a8e9f1a06..a5c9d435b3eea 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -185,25 +185,24 @@ fn does_not_lint() { }; // using tuples would be possible, but not always preferable - let x; - let y; - if true { - x = 1; - y = 2; + + + let (x, y) = if true { + //~^ needless_late_init + (1, 2) } else { - x = 3; - y = 4; - } + (3, 4) + }; - // could match with a smarter heuristic to avoid multiple assignments - let x; - if true { + + //~^ needless_late_init + let x = if true { let mut y = 5; y = 6; - x = y; + y } else { - x = 2; - } + 2 + }; let (x, y); if true { @@ -299,3 +298,167 @@ fn issue9895() { //~^ needless_late_init let r = 5; } + +fn if_or_match_in_block_expr() { + + //~^ needless_late_init + let z = if true { + 1 + } else { + 2 + }; +} + +fn issue16330() { + // Late init in both if branches, should lint + + + let (a, b) = if true { + //~^ needless_late_init + (1, 2) + } else { + (3, 4) + }; + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + + + + let (a, b, c) = match 1 { + //~^ needless_late_init + 1 => { + (1, 2, 3) + }, + _ if false => { + (4, 5, 6) + }, + _ => { + (7, 8, 9) + }, + }; + + // Late init in all if branches, should lint + + + + let (a, b, c) = if true { + //~^ needless_late_init + (1, 2, 3) + } else if false { + (4, 5, 6) + } else { + (7, 8, 9) + }; + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + + //~^ needless_late_init + let c = if true { + a = 1; + b = a + 1; + b + 1 + } else { + a = 4; + b = a + 2; + b + 2 + }; + let a; + let b; + + + let (c, d) = if true { + //~^ needless_late_init + a = 1; + b = a + 1; + (b + 1, 1) + } else { + a = 4; + b = a + 2; + (b + 2, 2) + }; +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 23772ff702931..ee413814750c7 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -188,6 +188,7 @@ fn does_not_lint() { let x; let y; if true { + //~^ needless_late_init x = 1; y = 2; } else { @@ -195,8 +196,8 @@ fn does_not_lint() { y = 4; } - // could match with a smarter heuristic to avoid multiple assignments let x; + //~^ needless_late_init if true { let mut y = 5; y = 6; @@ -299,3 +300,183 @@ fn issue9895() { //~^ needless_late_init (r = 5); } + +fn if_or_match_in_block_expr() { + let z; + //~^ needless_late_init + if true { + z = 1; + } else { + z = 2; + } +} + +fn issue16330() { + // Late init in both if branches, should lint + let a; + let b; + if true { + //~^ needless_late_init + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + let a; + let b; + let c; + match 1 { + //~^ needless_late_init + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } + + // Late init in all if branches, should lint + let a; + let b; + let c; + if true { + //~^ needless_late_init + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + b = 5; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + let c; + //~^ needless_late_init + if true { + a = 1; + b = a + 1; + c = b + 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + } + let a; + let b; + let c; + let d; + if true { + //~^ needless_late_init + a = 1; + b = a + 1; + c = b + 1; + d = 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + d = 2; + } +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index e3e25cdc8d769..12bfc029d9488 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -276,7 +276,50 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:298:5 + --> tests/ui/needless_late_init.rs:199:5 + | +LL | let x; + | ^^^^^^ + | +help: move the declaration `x` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let x = if true { +LL | let mut y = 5; +LL | y = 6; +LL ~ y +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:190:5 + | +LL | / if true { +LL | | +LL | | x = 1; +LL | | y = 2; +... | +LL | | y = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (x, y) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:299:5 | LL | let r; | ^^^^^^ created here @@ -291,5 +334,153 @@ LL | LL ~ let r = 5; | -error: aborting due to 17 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:305:5 + | +LL | let z; + | ^^^^^^ + | +help: move the declaration `z` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let z = if true { +LL ~ 1 +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:455:5 + | +LL | let c; + | ^^^^^^ + | +help: move the declaration `c` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let c = if true { +LL | a = 1; +LL | b = a + 1; +LL ~ b + 1 +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ b + 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:318:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | b = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (a, b) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:361:5 + | +LL | / match 1 { +LL | | +LL | | 1 => { +LL | | a = 1; +... | +LL | | }, +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the `match` arms + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = match 1 { +LL | +LL | 1 => { +LL ~ (1, 2, 3) +LL | }, +LL | _ if false => { +LL ~ (4, 5, 6) +LL | }, +LL | _ => { +LL ~ (7, 8, 9) +LL | }, +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:384:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | c = 9; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = if true { +LL | +LL ~ (1, 2, 3) +LL | } else if false { +LL ~ (4, 5, 6) +LL | } else { +LL ~ (7, 8, 9) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:470:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = a + 1; +... | +LL | | d = 2; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (c, d) = if true { +LL | +LL | a = 1; +LL | b = a + 1; +LL ~ (b + 1, 1) +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ (b + 2, 2) +LL ~ }; + | + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/ref_patterns.rs b/src/tools/clippy/tests/ui/ref_patterns.rs index 2905af13ccb63..3628cc3d537c0 100644 --- a/src/tools/clippy/tests/ui/ref_patterns.rs +++ b/src/tools/clippy/tests/ui/ref_patterns.rs @@ -19,4 +19,14 @@ fn use_in_binding() { fn use_in_parameter(ref x: i32) {} //~^ ref_patterns +struct Foo {} + +// shouldn't trigger the lint +#[automatically_derived] +impl Foo { + fn foo() { + if let Some(ref x) = Some(1) {} + } +} + fn main() {} 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 c27cec558242d..3020ed5c6f39d 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn core::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn core::error::Error) {} +//~^ std_instead_of_core 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 7d53f7fc3072b..49b4218aa8983 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} +//~^ std_instead_of_core 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 a5f8fbbe37cb8..0363852cf8d49 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -97,5 +97,17 @@ error: used import from `std` instead of `core` LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` -error: aborting due to 15 previous errors +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:110:33 + | +LL | fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:117:33 + | +LL | fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: aborting due to 17 previous errors 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 index 5e71159ac6c5d..66d834b5e4279 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs @@ -14,3 +14,14 @@ fn issue15143() { //~^ std_instead_of_core //~| std_instead_of_alloc } + +#[rustfmt::skip] +fn pr16964() { + use std::{ + borrow::Cow, + //~^ std_instead_of_alloc + collections::BTreeSet, + //~^ std_instead_of_alloc + ffi::OsString, + }; +} 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 index 147b46022126b..6fa8f47a4d6f4 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr @@ -26,5 +26,21 @@ LL | use std::{error::Error, vec::Vec, fs::File}; = 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 +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:21:17 + | +LL | borrow::Cow, + | ^^^ + | + = help: consider importing the item from `alloc` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:23:22 + | +LL | collections::BTreeSet, + | ^^^^^^^^ + | + = help: consider importing the item from `alloc` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index a0bac28e87292..ae5c685d89abd 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -205,3 +205,125 @@ fn main() { } } } + +mod issue_11715 { + use std::mem::MaybeUninit; + #[cfg(target_pointer_width = "64")] + const HUGE: usize = 4_294_967_296; + #[cfg(target_pointer_width = "32")] + const HUGE: usize = 268_435_455; + + fn large_maybeuninit_vec_ice() { + let mut v: Vec<[MaybeUninit; HUGE]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn large_u8_vec_ice() { + let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn large_nested_maybeuninit_vec_ice() { + let mut v: Vec<[[MaybeUninit; HUGE]; 2]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperSafe([MaybeUninit; HUGE]); + fn large_struct_maybeuninit_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperUnsafe([u8; HUGE]); + fn large_struct_u8_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + #[allow(clippy::large_enum_variant)] + enum HeavyEnum { + A([MaybeUninit; HUGE]), + B, + } + fn large_enum_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum Uninhabited {} + fn uninhabited_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum SingleVariant { + OnlyOne, + } + fn single_variant_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum OneVariantU8 { + ThisOne([u8; HUGE]), + } + fn one_variant_u8() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum OneVariantMaybeUninit { + ThisOne([MaybeUninit; HUGE]), + } + fn one_variant_maybe_uninit() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn generic_vec_lints() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn generic_vec_maybeuninit() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + trait Assoc { + type Item; + } + fn projection_vec_lints() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + struct Concrete; + impl Assoc for Concrete { + type Item = MaybeUninit; + } + fn normalized_projection_vec_ok() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum E { + Foo(MaybeUninit), + Bar(U), + } + fn enum_uninhabited_zst() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + fn enum_uninhabited_non_zst() { + let mut v: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } +} diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 1b821ef004e6f..56a182614b64b 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -159,5 +159,82 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 15 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:222:9 + | +LL | let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:240:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:251:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:274:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:288:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:302:9 + | +LL | let mut v: Vec<::Item> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:325:9 + | +LL | let mut v: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs index a7ab7e90be06b..e550996256f44 100644 --- a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs +++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs @@ -71,6 +71,23 @@ impl HasHuge { } } +// don't lint (issue #17202): the size of `[T; N]` depends on a const generic parameter +fn const_generic_array(value: Box<[T; N]>) -> Box<[T; N]> { + value +} + +// don't lint (issue #17202): the size of `[T; 10]` depends on the generic element type +fn generic_element_array(value: Box<[T; 10]>) -> Box<[T; 10]> { + value +} + +// lint: the size of `Vec` is known regardless of the generic element type +#[expect(clippy::box_collection)] +fn generic_vec(value: Box>) -> Box> { + //~^ unnecessary_box_returns + value +} + fn main() { // don't lint: this is a closure let a = || -> Box { Box::new(5) }; diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr index ab1d90f13b922..8eb39fca5680b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr @@ -32,5 +32,13 @@ LL | fn _bxed_foo() -> Box { | = help: changing this also requires a change to the return expressions in this function -error: aborting due to 4 previous errors +error: boxed return of the sized type `std::vec::Vec` + --> tests/ui/unnecessary_box_returns.rs:86:42 + | +LL | fn generic_vec(value: Box>) -> Box> { + | ^^^^^^^^^^^ help: try: `std::vec::Vec` + | + = help: changing this also requires a change to the return expressions in this function + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index 1ecc3ecf57a00..9e07c52e82e67 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,25 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0_u64 + 0)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast +}; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index 1a6cc831aa4b8..43d3e56e365a2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,25 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast +}; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index 19d2afcb4f255..9807ae0341421 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting raw pointers to the same type and constness is unnecessary (`*const T` -> `*const T`) - --> tests/ui/unnecessary_cast.rs:19:5 + --> tests/ui/unnecessary_cast.rs:21:5 | LL | ptr as *const T | ^^^^^^^^^^^^^^^ help: try: `ptr` @@ -8,388 +8,430 @@ LL | ptr as *const T = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:55:5 + --> tests/ui/unnecessary_cast.rs:57:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:57:5 + --> tests/ui/unnecessary_cast.rs:59:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> tests/ui/unnecessary_cast.rs:59:5 + --> tests/ui/unnecessary_cast.rs:61:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:63:5 + --> tests/ui/unnecessary_cast.rs:65:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:65:5 + --> tests/ui/unnecessary_cast.rs:67:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:67:5 + --> tests/ui/unnecessary_cast.rs:69:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:69:5 + --> tests/ui/unnecessary_cast.rs:71:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:71:5 + --> tests/ui/unnecessary_cast.rs:73:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:74:22 + --> tests/ui/unnecessary_cast.rs:76:22 | LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:77:5 + --> tests/ui/unnecessary_cast.rs:79:5 | LL | [1u8, 2].as_ptr() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`) - --> tests/ui/unnecessary_cast.rs:80:5 + --> tests/ui/unnecessary_cast.rs:82:5 | LL | [1u8, 2].as_mut_ptr() as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:92:5 + --> tests/ui/unnecessary_cast.rs:94:5 | LL | owo::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:94:5 + --> tests/ui/unnecessary_cast.rs:96:5 | LL | uwu::([1u32].as_ptr()) as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:97:5 + --> tests/ui/unnecessary_cast.rs:99:5 | LL | uwu::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:133:5 + --> tests/ui/unnecessary_cast.rs:135:5 | LL | aaa() as u32; | ^^^^^^^^^^^^ help: try: `aaa()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:136:5 + --> tests/ui/unnecessary_cast.rs:138:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:138:5 + --> tests/ui/unnecessary_cast.rs:140:5 | LL | bbb() as u32; | ^^^^^^^^^^^^ help: try: `bbb()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:141:5 + --> tests/ui/unnecessary_cast.rs:143:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:174:9 + --> tests/ui/unnecessary_cast.rs:176:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:176:9 + --> tests/ui/unnecessary_cast.rs:178:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:178:9 + --> tests/ui/unnecessary_cast.rs:180:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:180:17 + --> tests/ui/unnecessary_cast.rs:182:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:182:17 + --> tests/ui/unnecessary_cast.rs:184:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:184:17 + --> tests/ui/unnecessary_cast.rs:186:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:186:9 + --> tests/ui/unnecessary_cast.rs:188:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:188:9 + --> tests/ui/unnecessary_cast.rs:190:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:201:9 + --> tests/ui/unnecessary_cast.rs:203:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:203:9 + --> tests/ui/unnecessary_cast.rs:205:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> tests/ui/unnecessary_cast.rs:205:9 + --> tests/ui/unnecessary_cast.rs:207:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> tests/ui/unnecessary_cast.rs:207:9 + --> tests/ui/unnecessary_cast.rs:209:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:209:9 + --> tests/ui/unnecessary_cast.rs:211:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:212:9 + --> tests/ui/unnecessary_cast.rs:214:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:214:9 + --> tests/ui/unnecessary_cast.rs:216:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:219:17 + --> tests/ui/unnecessary_cast.rs:221:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:221:17 + --> tests/ui/unnecessary_cast.rs:223:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> tests/ui/unnecessary_cast.rs:228:18 + --> tests/ui/unnecessary_cast.rs:230:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:235:22 + --> tests/ui/unnecessary_cast.rs:237:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> tests/ui/unnecessary_cast.rs:238:22 + --> tests/ui/unnecessary_cast.rs:240:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:246:22 + --> tests/ui/unnecessary_cast.rs:248:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:249:23 + --> tests/ui/unnecessary_cast.rs:251:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> tests/ui/unnecessary_cast.rs:259:20 + --> tests/ui/unnecessary_cast.rs:261:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:270:9 + --> tests/ui/unnecessary_cast.rs:272:9 | LL | (*x as usize).pow(2) | ^^^^^^^^^^^^^ help: try: `(*x)` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:278:31 + --> tests/ui/unnecessary_cast.rs:280:31 | LL | assert_eq!(vec.len(), x as usize); | ^^^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:281:17 + --> tests/ui/unnecessary_cast.rs:283:17 | LL | let _ = (5i32 as i64 as i64).abs(); | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:284:17 + --> tests/ui/unnecessary_cast.rs:286:17 | LL | let _ = 5i32 as i64 as i64; | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:563:24 + --> tests/ui/unnecessary_cast.rs:565:24 | LL | id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:567:26 + --> tests/ui/unnecessary_cast.rs:569:26 | LL | s.id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:571:26 + --> tests/ui/unnecessary_cast.rs:573:26 | LL | wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:575:28 + --> tests/ui/unnecessary_cast.rs:577:28 | LL | s.wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:591:31 + --> tests/ui/unnecessary_cast.rs:593:31 | LL | let _ = receiver.take(1.0_f64.pow_like_single_impl(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:601:20 + --> tests/ui/unnecessary_cast.rs:603:20 | LL | let _ = id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:604:22 + --> tests/ui/unnecessary_cast.rs:606:22 | LL | let _ = wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:607:22 + --> tests/ui/unnecessary_cast.rs:609:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:610:24 + --> tests/ui/unnecessary_cast.rs:612:24 | LL | let _ = s.wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:613:20 + --> tests/ui/unnecessary_cast.rs:615:20 | LL | let _ = id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:616:22 + --> tests/ui/unnecessary_cast.rs:618:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:629:17 + --> tests/ui/unnecessary_cast.rs:631:17 | LL | let _ = 1.0_f64.pow_like(0.5) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(0.5)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:632:17 + --> tests/ui/unnecessary_cast.rs:634:17 | LL | let _ = 1.0_f64.pow_like(2) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:635:17 + --> tests/ui/unnecessary_cast.rs:637:17 | LL | let _ = (1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:638:17 + --> tests/ui/unnecessary_cast.rs:640:17 | LL | let _ = ((Y + 2) as f64).abs(); | ^^^^^^^^^^^^^^^^ help: try: `((Y + 2))` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:641:18 + --> tests/ui/unnecessary_cast.rs:643:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + 1.0_f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:644:18 + --> tests/ui/unnecessary_cast.rs:646:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + ONE).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:647:18 + --> tests/ui/unnecessary_cast.rs:649:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + one).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting raw pointers to the same type and constness is unnecessary (`*const *const u8` -> `*const *const u8`) - --> tests/ui/unnecessary_cast.rs:655:10 + --> tests/ui/unnecessary_cast.rs:657:10 | LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)` -error: aborting due to 65 previous errors +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:665:45 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:667:45 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:670:46 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:672:48 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:675:54 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:678:56 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:680:54 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: aborting due to 72 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 409a8efbfeb95..c8d6b1832a710 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then_some::<&[u8]>({ &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert({ &[] } as &[u8]); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 54735023a935d..87e903c5bbb8c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index 75a06a419c71b..9b91b22f80d3a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -484,7 +484,31 @@ LL + or(Ok(ext_str.some_field)); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:259:14 + --> tests/ui/unnecessary_lazy_eval.rs:245:13 + | +LL | let _ = true.then(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL - let _ = true.then(|| -> &[u8] { &[] }); +LL + let _ = true.then_some::<&[u8]>({ &[] }); + | + +error: unnecessary closure used to substitute value for `Option::None` + --> tests/ui/unnecessary_lazy_eval.rs:251:13 + | +LL | let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL - let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); +LL + let _ = None.get_or_insert({ &[] } as &[u8]); + | + +error: unnecessary closure used with `bool::then` + --> tests/ui/unnecessary_lazy_eval.rs:271:14 | LL | let _x = false.then(|| i32::MAX + 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,7 +520,7 @@ LL + let _x = false.then_some(i32::MAX + 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:261:14 + --> tests/ui/unnecessary_lazy_eval.rs:273:14 | LL | let _x = false.then(|| i32::MAX * 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -508,7 +532,7 @@ LL + let _x = false.then_some(i32::MAX * 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:263:14 + --> tests/ui/unnecessary_lazy_eval.rs:275:14 | LL | let _x = false.then(|| i32::MAX - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -520,7 +544,7 @@ LL + let _x = false.then_some(i32::MAX - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:265:14 + --> tests/ui/unnecessary_lazy_eval.rs:277:14 | LL | let _x = false.then(|| i32::MIN - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +556,7 @@ LL + let _x = false.then_some(i32::MIN - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:267:14 + --> tests/ui/unnecessary_lazy_eval.rs:279:14 | LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -544,7 +568,7 @@ LL + let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:269:14 + --> tests/ui/unnecessary_lazy_eval.rs:281:14 | LL | let _x = false.then(|| 255u8 << 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -556,7 +580,7 @@ LL + let _x = false.then_some(255u8 << 7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:271:14 + --> tests/ui/unnecessary_lazy_eval.rs:283:14 | LL | let _x = false.then(|| 255u8 << 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -568,7 +592,7 @@ LL + let _x = false.then_some(255u8 << 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:273:14 + --> tests/ui/unnecessary_lazy_eval.rs:285:14 | LL | let _x = false.then(|| 255u8 >> 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +604,7 @@ LL + let _x = false.then_some(255u8 >> 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:276:14 + --> tests/ui/unnecessary_lazy_eval.rs:288:14 | LL | let _x = false.then(|| i32::MAX + -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -592,7 +616,7 @@ LL + let _x = false.then_some(i32::MAX + -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:278:14 + --> tests/ui/unnecessary_lazy_eval.rs:290:14 | LL | let _x = false.then(|| -i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -604,7 +628,7 @@ LL + let _x = false.then_some(-i32::MAX); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:280:14 + --> tests/ui/unnecessary_lazy_eval.rs:292:14 | LL | let _x = false.then(|| -i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -616,7 +640,7 @@ LL + let _x = false.then_some(-i32::MIN); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:283:14 + --> tests/ui/unnecessary_lazy_eval.rs:295:14 | LL | let _x = false.then(|| 255 >> -7); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -628,7 +652,7 @@ LL + let _x = false.then_some(255 >> -7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:285:14 + --> tests/ui/unnecessary_lazy_eval.rs:297:14 | LL | let _x = false.then(|| 255 << -1); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -640,7 +664,7 @@ LL + let _x = false.then_some(255 << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:287:14 + --> tests/ui/unnecessary_lazy_eval.rs:299:14 | LL | let _x = false.then(|| 1 / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -652,7 +676,7 @@ LL + let _x = false.then_some(1 / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:289:14 + --> tests/ui/unnecessary_lazy_eval.rs:301:14 | LL | let _x = false.then(|| x << -1); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -664,7 +688,7 @@ LL + let _x = false.then_some(x << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:291:14 + --> tests/ui/unnecessary_lazy_eval.rs:303:14 | LL | let _x = false.then(|| x << 2); | ^^^^^^^^^^^^^^^^^^^^^ @@ -676,7 +700,7 @@ LL + let _x = false.then_some(x << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:301:14 + --> tests/ui/unnecessary_lazy_eval.rs:313:14 | LL | let _x = false.then(|| x / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -688,7 +712,7 @@ LL + let _x = false.then_some(x / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:303:14 + --> tests/ui/unnecessary_lazy_eval.rs:315:14 | LL | let _x = false.then(|| x % 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -700,7 +724,7 @@ LL + let _x = false.then_some(x % 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:306:14 + --> tests/ui/unnecessary_lazy_eval.rs:318:14 | LL | let _x = false.then(|| 1 / -1); | ^^^^^^^^^^^^^^^^^^^^^ @@ -712,7 +736,7 @@ LL + let _x = false.then_some(1 / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:308:14 + --> tests/ui/unnecessary_lazy_eval.rs:320:14 | LL | let _x = false.then(|| i32::MIN / -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -724,7 +748,7 @@ LL + let _x = false.then_some(i32::MIN / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:311:14 + --> tests/ui/unnecessary_lazy_eval.rs:323:14 | LL | let _x = false.then(|| i32::MIN / 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -736,7 +760,7 @@ LL + let _x = false.then_some(i32::MIN / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:313:14 + --> tests/ui/unnecessary_lazy_eval.rs:325:14 | LL | let _x = false.then(|| 4 / 2); | ^^^^^^^^^^^^^^^^^^^^ @@ -748,7 +772,7 @@ LL + let _x = false.then_some(4 / 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:321:14 + --> tests/ui/unnecessary_lazy_eval.rs:333:14 | LL | let _x = false.then(|| f1 + f2); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -759,5 +783,5 @@ LL - let _x = false.then(|| f1 + f2); LL + let _x = false.then_some(f1 + f2); | -error: aborting due to 63 previous errors +error: aborting due to 65 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs index 6d28d544dfe0b..7ceda40597942 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -12,6 +12,8 @@ fn main() { // fix will break type inference let _ = Ok(1).unwrap_or_else(|()| 2); //~^ unnecessary_lazy_evaluations + let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + //~^ unnecessary_lazy_evaluations mod e { pub struct E; @@ -26,9 +28,3 @@ fn main() { let arr = [(Some(1),)]; Some(&0).and_then(|&i| arr[i].0); } - -fn issue11672() { - // Return type annotation helps type inference and removing it can break code - let _ = true.then(|| -> &[u8] { &[] }); - //~^ unnecessary_lazy_evaluations -} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 5174acc1e9f95..97e9ef621cee0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -13,7 +13,19 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:15:13 + | +LL | let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL - let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); +LL + let _ = Ok(1).unwrap_or({ 2 } as i32); + | + +error: unnecessary closure used to substitute value for `Result::Err` + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +37,7 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:22:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:24:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,17 +48,5 @@ LL - let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); LL + let _ = Ok(1).unwrap_or(2); | -error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:32:13 - | -LL | let _ = true.then(|| -> &[u8] { &[] }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `then_some` instead - | -LL - let _ = true.then(|| -> &[u8] { &[] }); -LL + let _ = true.then_some({ &[] }); - | - error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed index 317140eacc785..db31b339a08b0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed @@ -19,9 +19,9 @@ fn unnecessary_sort_by() { //~^ unnecessary_sort_by // Reverse examples vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow - vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); //~^ unnecessary_sort_by - vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); //~^ unnecessary_sort_by // Negative examples (shouldn't be changed) let c = &7; @@ -99,9 +99,9 @@ mod issue_6001 { args.sort_unstable_by_key(|a| a.name()); //~^ unnecessary_sort_by // Reverse - args.sort_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by - args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by } } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr index 56d4831eb70aa..868f7895fc069 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -57,7 +57,7 @@ LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); help: try | LL - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); -LL + vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); +LL + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); | error: consider using `sort_unstable_by_key` @@ -69,7 +69,7 @@ LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); help: try | LL - vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); -LL + vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); +LL + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); | error: consider using `sort_by_key` @@ -129,7 +129,7 @@ LL | args.sort_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_unstable_by_key` @@ -141,7 +141,7 @@ LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_by_key` diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed index 40e6aecd1b6d0..6fd740d87757a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed @@ -15,8 +15,8 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` - vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); //~^ unnecessary_sort_by vec } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs index 184c90d959eb5..266553e7ace6b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs @@ -15,7 +15,7 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); //~^ unnecessary_sort_by vec diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr index b4dd6a6dbdc52..26a8ad38fcd32 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr @@ -21,7 +21,7 @@ LL | vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); help: try | LL - vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); -LL + vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); +LL + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed index 499d9ee1ca235..f418fe954d400 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}"}; //~ unnecessary_trailing_comma println!{"Foo(,"}; //~ unnecessary_trailing_comma println!{"Foo[,"}; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs index 15dea27b887b6..26770ffaeee18 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}", }; //~ unnecessary_trailing_comma println!{"Foo(,", }; //~ unnecessary_trailing_comma println!{"Foo[,", }; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1,); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr index 06fd5b1861a55..8ff7195a147b0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr @@ -115,5 +115,11 @@ error: unnecessary trailing comma LL | println!{"Foo[,", }; | ^^ help: remove the trailing comma -error: aborting due to 19 previous errors +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:29:42 + | +LL | println!(concat!("Foo", "=", "{}"), 1,); + | ^ help: remove the trailing comma + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed new file mode 100644 index 0000000000000..274bb7a00bfb0 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a_unchecked(&A) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..006a541365a58 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a(&A).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a().unwrap_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr new file mode 100644 index 0000000000000..359e50a33c9b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr @@ -0,0 +1,88 @@ +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:63:33 + | +LL | let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-unwrap-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap_unchecked)]` +help: use `std::str::from_utf8_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; +LL + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:65:22 + | +LL | let a = unsafe { A::a(&A).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let a = unsafe { A::a(&A).unwrap_unchecked() }; +LL + let a = unsafe { A::a_unchecked(&A) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:69:9 + | +LL | a.a().unwrap_unchecked() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - a.a().unwrap_unchecked() +LL + a.a_unchecked() + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:72:32 + | +LL | let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::an_assoc_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; +LL + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:74:30 + | +LL | let extern_fn = unsafe { lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn = unsafe { lol().unwrap_unchecked() }; +LL + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:76:40 + | +LL | let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; +LL + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:78:29 + | +LL | let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `b::test_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; +LL + let local_fn = unsafe { b::test_fn_unchecked() }; + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 667468474b3c1..13ea2b250758c 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -60,6 +60,11 @@ labels = ["S-waiting-on-concerns"] [view-all-comments-link] threshold = 20 +# Allows `merge` and `delegate` commands for merging a PR +# Documentation at: https://forge.rust-lang.org/triagebot/merge.html +[merge] +type = "merge-queue" + [notify-zulip."lint-nominated"] zulip_stream = 577190 # #clippy/fcp topic = "FCP rust-clippy#{number}: {title}" diff --git a/src/tools/coverage-dump/Cargo.toml b/src/tools/coverage-dump/Cargo.toml index 36a66f16030ec..cb5755a22968a 100644 --- a/src/tools/coverage-dump/Cargo.toml +++ b/src/tools/coverage-dump/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.71" -itertools = "0.12" +itertools = "0.15" leb128 = "0.2.5" md5 = { package = "md-5" , version = "0.10.5" } miniz_oxide = "0.8.8" diff --git a/src/tools/enzyme b/src/tools/enzyme index 7c0141f133a35..a8668c7ca3579 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 7c0141f133a3592daa12cc6cc07f297a5222a42e +Subproject commit a8668c7ca3579c3304d628bca518bafc0fcc62d1 diff --git a/src/tools/jsondocck/Cargo.toml b/src/tools/jsondocck/Cargo.toml index 80fc26cbe6680..f2ae68f7fbd69 100644 --- a/src/tools/jsondocck/Cargo.toml +++ b/src/tools/jsondocck/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondocck" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] jsonpath-rust = "1.0.0" diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index cc8ecefd530b4..848c0b37ae94e 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsondoclint" version = "0.1.0" -edition = "2021" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 01deafe20b354..76a47c4f4fecf 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -97,7 +97,7 @@ impl<'a> Validator<'a> { ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), ItemEnum::Variant(x) => self.check_variant(x, id), - ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Function(x) => self.check_function(x, id), ItemEnum::Trait(x) => self.check_trait(x, id), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), ItemEnum::Impl(x) => self.check_impl(x, id), @@ -114,12 +114,35 @@ impl<'a> Validator<'a> { ItemEnum::Module(x) => self.check_module(x, id), // FIXME: Why don't these have their own structs? ItemEnum::ExternCrate { .. } => {} - ItemEnum::AssocConst { type_, value: _ } => self.check_type(type_), - ItemEnum::AssocType { generics, bounds, type_ } => { + ItemEnum::AssocConst { type_, value, default_unstable } => { + self.check_type(type_); + if value.is_none() + && let Some(default_unstable) = default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } + } + ItemEnum::AssocType { generics, bounds, type_, default_unstable } => { self.check_generics(generics); bounds.iter().for_each(|b| self.check_generic_bound(b)); if let Some(ty) = type_ { self.check_type(ty); + } else if let Some(default_unstable) = default_unstable { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); } } } @@ -194,9 +217,21 @@ impl<'a> Validator<'a> { } } - fn check_function(&mut self, x: &'a Function) { + fn check_function(&mut self, x: &'a Function, id: &Id) { self.check_generics(&x.generics); self.check_function_signature(&x.sig); + if !x.has_body + && let Some(default_unstable) = &x.default_unstable + { + self.fail( + id, + ErrorKind::Custom(format!( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id {} had `default_unstable` with feature `{}`", + id.0, default_unstable.feature + )), + ); + } } fn check_trait(&mut self, x: &'a Trait, id: &Id) { diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs index ff2fae157f04c..f9b54a2cc0936 100644 --- a/src/tools/jsondoclint/src/validator/tests.rs +++ b/src/tools/jsondoclint/src/validator/tests.rs @@ -1,5 +1,7 @@ use rustc_hash::FxHashMap; -use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility}; +use rustdoc_json_types::{ + Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, ProvidedDefaultUnstable, Visibility, +}; use super::*; use crate::json_find::SelectorPart; @@ -221,6 +223,7 @@ fn errors_on_missing_path() { abi: Abi::Rust, }, has_body: true, + default_unstable: None, }), }, ), @@ -246,6 +249,155 @@ fn errors_on_missing_path() { ); } +fn krate_with_trait_item(inner: ItemEnum) -> Crate { + let item_id = Id(2); + Crate { + root: Id(0), + crate_version: None, + includes_private: false, + index: FxHashMap::from_iter([ + ( + Id(0), + Item { + id: Id(0), + crate_id: 0, + name: Some("root".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Module(Module { + is_crate: true, + items: vec![Id(1)], + is_stripped: false, + }), + }, + ), + ( + Id(1), + Item { + id: Id(1), + crate_id: 0, + name: Some("Trait".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner: ItemEnum::Trait(Trait { + is_auto: false, + is_unsafe: false, + is_dyn_compatible: true, + items: vec![item_id], + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + implementations: vec![], + }), + }, + ), + ( + item_id, + Item { + id: item_id, + crate_id: 0, + name: Some("TraitItem".to_owned()), + span: None, + visibility: Visibility::Public, + docs: None, + links: FxHashMap::default(), + attrs: Vec::new(), + deprecation: None, + stability: None, + const_stability: None, + inner, + }, + ), + ]), + paths: FxHashMap::default(), + external_crates: FxHashMap::default(), + target: rustdoc_json_types::Target { triple: "".to_string(), target_features: vec![] }, + format_version: FORMAT_VERSION, + } +} + +#[test] +fn errors_on_default_unstable_without_function_body() { + let krate = krate_with_trait_item(ItemEnum::Function(Function { + sig: FunctionSignature { inputs: vec![], output: None, is_c_variadic: false }, + generics: Generics { params: vec![], where_predicates: vec![] }, + header: FunctionHeader { + is_const: false, + is_unsafe: false, + is_async: false, + abi: Abi::Rust, + }, + has_body: false, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + })); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `has_body == false`, but \ + function item id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_const_value() { + let krate = krate_with_trait_item(ItemEnum::AssocConst { + type_: Type::Primitive("usize".to_owned()), + value: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `value` is `None`, but \ + assoc const id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + +#[test] +fn errors_on_default_unstable_without_assoc_type_default() { + let krate = krate_with_trait_item(ItemEnum::AssocType { + generics: Generics { params: vec![], where_predicates: vec![] }, + bounds: vec![], + type_: None, + default_unstable: Some(Box::new(ProvidedDefaultUnstable { feature: "feature".to_owned() })), + }); + + check( + &krate, + &[Error { + id: Id(2), + kind: ErrorKind::Custom( + "`default_unstable` must be `None` when `type_` is `None`, but \ + assoc type id 2 had `default_unstable` with feature `feature`" + .to_owned(), + ), + }], + ); +} + #[test] #[should_panic = "LOCAL_CRATE_ID is wrong"] fn checks_local_crate_id_is_correct() { diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index e7455fc701cfb..0d3870f456a07 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -92,6 +92,22 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ "core\\io\\slice::sort_by_key", "#method.sort_by_cached_key" ]), + ("alloc/io/struct.IoSlice.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), + ("alloc/io/struct.IoSliceMut.html", &[ + "#method.to_ascii_uppercase", + "#method.to_ascii_lowercase", + "alloc/io/slice::sort_by_key", + "alloc\\io\\slice::sort_by_key", + "#method.sort_by_key", + "#method.sort_by_cached_key" + ]), ]; #[rustfmt::skip] diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 6b8d8ed1f8018..fdc746ead51f5 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -171,6 +171,38 @@ available (which affects `cfg(target_feature)`), and it tells Miri to consider t that the interpreted program runs on as having the feature available (meaning the code is allowed to invoke the corresponding intrinsics). +### Nextest integration + +Miri can be combined with [`cargo-nextest`](https://nexte.st): + +``` +cargo install --locked cargo-nextest +cargo miri nextest run +``` + +Nextest spawns a separate instance of Miri for each test, which has several advantages: +- Tests can run in parallel. Miri itself only uses a single thread per interpreter so this can + give a massive speedup (but see the caveat below). +- Tests do not stop when a single problem is found. Miri aborts execution when it encounters + Undefined Behavior or an unsupported operation (there is often not really any way to continue), + so once a single test fails, the remaining tests cannot be executed. Nextest's process-per-test + model means that you end up with a full list of which tests worked in Miri and which tests had a + problem. + +However, there is also a big caveat: Miri will [re-compile the test crate every time it is +invoked](https://github.com/rust-lang/miri/issues/5013), which means a crate with N tests will be +compiled N+1 times. If the test crate takes a long time to build, this can outweigh the benefits of +parallelization. + +For more information about nextest, see the [`cargo-nextest` Miri +documentation](https://nexte.st/book/miri.html). + +Note: Nextest's one-test-per-process model means that `cargo miri test` is able to detect data +races where two tests race on a shared resource, but `cargo miri nextest run` will not detect +such races. + +Note: `cargo-nextest` [does not support doctests](https://github.com/nextest-rs/nextest/issues/16). + ### Testing multiple different executions Certain parts of the execution are picked randomly by Miri, such as the exact base address @@ -184,6 +216,7 @@ MIRIFLAGS="-Zmiri-many-seeds" cargo miri test # tries the seeds in 0..64 MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test ``` +Miri will test the given range of seeds with parallel interpreter instances. The default of 64 different seeds can be quite slow, so you often want to specify a smaller range. ### Running Miri on CI @@ -243,26 +276,6 @@ However, even for targets that we do support, the degree of support for accessin (such as the file system) differs between targets: generally, Linux targets have the best support, and macOS targets are usually on par. Windows is supported less well. -### Running tests in parallel - -Though it implements Rust threading, Miri itself is a single-threaded interpreter -(it works like a multi-threaded OS on a single-core CPU). -This means that when running `cargo miri test`, you will probably see a dramatic -increase in the amount of time it takes to run your whole test suite due to the -inherent interpreter slowdown and a loss of parallelism. - -You can get your test suite's parallelism back by running `cargo miri nextest run -jN` -(note that you will need [`cargo-nextest`](https://nexte.st) installed). -This works because `cargo-nextest` collects a list of all tests then launches a -separate `cargo miri run` for each test. For more information about nextest, see the -[`cargo-nextest` Miri documentation](https://nexte.st/book/miri.html). - -Note: This one-test-per-process model means that `cargo miri test` is able to detect data -races where two tests race on a shared resource, but `cargo miri nextest run` will not detect -such races. - -Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16 - ### Directly invoking the `miri` driver The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri` diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index f58cec827cf5d..caf6987291970 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -36,6 +36,9 @@ Examples: This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. +For documentation on `-Zmiri-...` flags, see Miri's README.md, available at: +- $(rustc --print sysroot)/share/doc/miri/README.md +- https://github.com/rust-lang/miri/blob/master/README.md "; fn show_help() { diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index f7c0b739c1dd4..42107adfe566a 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -168,6 +168,7 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello ;; armv7-unknown-linux-gnueabihf) # Host diff --git a/src/tools/miri/priroda/src/main.rs b/src/tools/miri/priroda/src/main.rs index 2739b041b0b48..fe67b21d2c134 100644 --- a/src/tools/miri/priroda/src/main.rs +++ b/src/tools/miri/priroda/src/main.rs @@ -6,6 +6,7 @@ extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; extern crate rustc_hir_analysis; +extern crate rustc_index; extern crate rustc_interface; extern crate rustc_log; extern crate rustc_middle; @@ -19,13 +20,15 @@ use std::path::PathBuf; use miri::*; use rustc_driver::Compilation; use rustc_hir::attrs::CrateType; +use rustc_index::IndexVec; use rustc_interface::interface; use rustc_middle::mir; +use rustc_middle::mir::{Local, VarDebugInfoContents}; use rustc_middle::ty::TyCtxt; use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; -use rustc_span::Span; use rustc_span::source_map::SourceMap; +use rustc_span::{Span, Symbol}; fn find_sysroot() -> String { std::env::var("MIRI_SYSROOT") @@ -129,6 +132,11 @@ struct PrirodaContext<'tcx> { last_location: Option, } +struct LocalDesc { + name: Option, + local: Local, + ty: String, +} /// Controls when execution returns to the frontend. enum ResumeMode { /// Stop at the next visible MIR instruction. @@ -336,15 +344,49 @@ impl<'tcx> PrirodaContext<'tcx> { } } - /// Returns the names of all user-visible locals in the innermost stack frame. + /// Returns structured descriptions for locals in the innermost stack frame. /// - /// Uses `var_debug_info` from the MIR body, which is the same source that - /// DWARF debug info is built from, so the names match what the user wrote. - fn list_locals(&self) -> Vec { + /// Starts from all MIR locals, then enriches them with source names from + /// `var_debug_info` when a debug entry maps directly to a whole local. + fn list_locals(&self) -> Vec { let Some(frame) = self.ecx.active_thread_stack().last() else { return Vec::new(); }; - frame.body().var_debug_info.iter().map(|info| info.name.to_string()).collect() + + self.local_desc_map(frame).into_iter().collect() + } + + fn local_desc_map( + &self, + frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>, + ) -> IndexVec { + // Initialize one description per MIR local so the table can be indexed by Local. + let mut locals: IndexVec = frame + .body() + .local_decls + .iter_enumerated() + .map(|(id, local_decl)| { + LocalDesc { name: None, local: id, ty: local_decl.ty.to_string() } + }) + .collect(); + + // FIXME: Some debug-info entries do not have a backing MIR local, for example + // because the source variable was optimized out or is represented as a + // projection. This local-indexed table cannot represent those entries yet; + // the final locals list should become a `Vec` with `id : Option`, `id` + // could be renamed to `local`. + + // Attach source names from debug info when the debug entry maps directly to a whole MIR local. + for var_debug_info in &frame.body().var_debug_info { + if let VarDebugInfoContents::Place(place) = var_debug_info.value + && let Some(local) = place.as_local() + && locals[local].name.is_none() + { + locals[local].name = Some(var_debug_info.name); + } + } + + locals } } @@ -366,7 +408,7 @@ enum BreakpointSetResult { enum CommandResult { ExecutionStopped(StepResult), BreakpointResult(BreakpointSetResult), - Locals(Vec), + Locals(Vec), // FIXME: distinguish terminating the debugger session from disconnecting a // frontend and terminating the interpreted program once multiple frontends exist. TerminateSession, @@ -403,12 +445,21 @@ impl Cli { BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"), }, - CommandResult::Locals(names) => - if names.is_empty() { + CommandResult::Locals(locals_desc) => + if locals_desc.is_empty() { println!("no locals"); } else { - for name in &names { - println!("{name}"); + for local_desc in &locals_desc { + let mut name_str = "None".to_string(); + if let Some(name) = local_desc.name { + name_str = name.to_string(); + } + println!( + "Name: {}, Id: _{}, Ty: {}", + name_str, + local_desc.local.index(), + local_desc.ty, + ); } }, CommandResult::TerminateSession => { diff --git a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout index ed5889f5836e7..4f2786d3e17b7 100644 --- a/src/tools/miri/priroda/tests/ui/locals_in_function.stdout +++ b/src/tools/miri/priroda/tests/ui/locals_in_function.stdout @@ -1,6 +1,10 @@ (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 (priroda) Hit breakpoint {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5 -(priroda) x -y +(priroda) Name: None, Id: _0, Ty: () +Name: x, Id: _1, Ty: i32 +Name: y, Id: _2, Ty: bool +Name: None, Id: _3, Ty: (i32, bool) +Name: None, Id: _4, Ty: i32 +Name: None, Id: _5, Ty: bool (priroda) quitting diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4a2bfdb2cd558..d7711535dbf29 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -01f54e80e888b66d6486a3a95d481b87353016df +16761606d606b6ec4d0c88fc9251670742ad9fd2 diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 296de803d5828..b58a7cfeba92e 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -574,7 +574,9 @@ impl<'tcx> Tree { // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. && !perm.permission.is_cell() - // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. + // Only trigger UB if the accessed bit is set, i.e. if the protector + // is actually protecting this offset. See #4579. Note that this + // takes into account the access we just did above! && perm.accessed { Err(TbError { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 730c7d9fac611..f5761f4712470 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -129,7 +129,13 @@ pub fn iter_exported_symbols<'tcx>( || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) }; - if exported { + // FIXME: `#[no_mangle]` makes no sense on a generic item, but still causes it to be + // considered "extern". Remove this once `no_mangle_generic_items` is a hard error. + let exported_mono = exported && { + let generics = tcx.generics_of(def_id); + !generics.requires_monomorphization(tcx) + }; + if exported_mono { f(LOCAL_CRATE, def_id.into())?; } } @@ -406,6 +412,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let sig = this.tcx.mk_fn_sig( args.iter().map(|a| a.layout.ty), dest.layout.ty, + // FIXME(splat): Do we need to set splatted here? + // (Currently this also ignores c_variadic) FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe), ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index deab856b3e24f..3d8946fb36720 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -146,6 +146,45 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } + // Signed saturating doubling multiply returning the high half. + // + // Used by the `vqdmulh*` functions. + // + // This LLVM intrinsic multiplies the values of corresponding elements of the two source + // vector registers (which are signed integers), doubles the results, places the most significant half of the + // final results (using a saturating cast to fit the element type) into a vector, and writes the vector to the destination register. + // + // https://developer.arm.com/architectures/instruction-sets/intrinsics#f:@navigationhierarchiessimdisa=[Neon]&q=vqdmulh + name if name.starts_with("neon.sqdmulh.") => { + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + let (left, left_len) = this.project_to_simd(left)?; + let (right, right_len) = this.project_to_simd(right)?; + let (dest, dest_len) = this.project_to_simd(dest)?; + assert_eq!(left_len, right_len); + assert_eq!(left_len, dest_len); + + let elem_size = dest.layout.field(this, 0).size; + let bits = elem_size.bits(); + let min = elem_size.signed_int_min(); + let max = elem_size.signed_int_max(); + + for i in 0..dest_len { + let a = this.read_scalar(&this.project_index(&left, i)?)?.to_int(elem_size)?; + let b = this.read_scalar(&this.project_index(&right, i)?)?.to_int(elem_size)?; + + // Uses i128 arithmetic, which cannot overflow because the intrinsic takes at most i32. + let doubled = a.strict_mul(b).strict_mul(2); + let res = (doubled >> bits).clamp(min, max); + + this.write_scalar( + Scalar::from_int(res, elem_size), + &this.project_index(&dest, i)?, + )?; + } + } + // Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0. // Used to implement vtbl1_u8 function. // LLVM does not have a portable shuffle that takes non-const indices diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index e4daab7a2005d..5fd86769e96b5 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -789,7 +789,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // LLVM intrinsics - "llvm.prefetch" => { + "llvm.prefetch.p0" => { let [p, rw, loc, ty] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index 68b13a6ed58a0..99673319240fe 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -274,7 +274,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { inputs_and_output.push(shim_sig.ret); let fn_sig_binder = Binder::dummy(FnSig { inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), - // Safety does not matter for the ABI. + // Safety and splatted do not matter for the ABI. fn_sig_kind: FnSigKind::default() .set_abi(shim_sig.abi) .set_safety(rustc_hir::Safety::Safe), diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index c2ae65091ee2c..14eb7af4b52df 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -840,18 +840,46 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "mmap" => { - let [addr, length, prot, flags, fd, offset] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length, prot, flags, fd, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32, i32, i32, libc::off_t) -> *mut _), + link_name, + abi, + args, + )?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = - this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [addr, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> i32), + link_name, + abi, + args, + )?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } + "mprotect" => { + let [addr, length, prot] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.mprotect(addr, length, prot)?; + this.write_scalar(result, dest)?; + } + "madvise" => { + let [addr, length, advice] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32), + link_name, + abi, + args, + )?; + let result = this.madvise(addr, length, advice)?; + this.write_scalar(result, dest)?; + } "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. @@ -1410,7 +1438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } - "sigaction" | "mprotect" if this.frame_in_std() => { + "sigaction" if this.frame_in_std() => { let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index c2ad7c0e9d0ab..50be3c7ae15e6 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -53,9 +53,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(Scalar::from_maybe_pointer(Pointer::without_provenance(addr), this)); } - let prot_read = this.eval_libc_i32("PROT_READ"); - let prot_write = this.eval_libc_i32("PROT_WRITE"); - // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { this.set_last_error(LibcError("EINVAL"))?; @@ -80,13 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - // Miri doesn't support protections other than PROT_READ|PROT_WRITE. - if prot != prot_read | prot_write { - throw_unsup_format!( - "Miri does not support calls to mmap with protections other than \ - PROT_READ|PROT_WRITE", - ); - } + verify_prot(this, prot)?; // Miri does not support shared mappings, or any of the other extensions that for example // Linux has added to the flags arguments. @@ -103,14 +94,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let align = this.machine.page_align(); - let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(map_length) = round_up_to_page_size(this, length) else { this.set_last_error(LibcError("EINVAL"))?; return interp_ok(this.eval_libc("MAP_FAILED")); }; - if map_length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let ptr = this.allocate_ptr( Size::from_bytes(map_length), @@ -135,13 +122,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); } - let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { + let Some(length) = round_up_to_page_size(this, length) else { return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); }; - if length > this.target_usize_max() { - this.set_last_error(LibcError("EINVAL"))?; - return interp_ok(this.eval_libc("MAP_FAILED")); - } let length = Size::from_bytes(length); this.deallocate_ptr( @@ -152,4 +135,106 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(Scalar::from_i32(0)) } + + fn mprotect( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + prot: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let prot = this.read_scalar(prot)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + verify_prot(this, prot)?; + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`mprotect` called on out-of-bounds memory"))?; + + // If the memory comes from memory the Rust program has allocated with mmap, we only support + // `PROT_READ|PROT_WRITE`, so this `mprotect` is a no-op. If the memory was mmaped by the + // runtime (e.g. if it's the stack, executable memory, or static memory), POSIX also allows + // us to remap it. In those cases, such a call to `PROT_READ|PROT_WRITE` might actually change the permissions, + // but treating them as the new permissions is still UB. Therefore, we just pretend that we + // did the permission change by returning success, and will still reject if you try to use + // it with the "new" permissions. + interp_ok(Scalar::from_i32(0)) + } + + fn madvise( + &mut self, + addr: &OpTy<'tcx>, + length: &OpTy<'tcx>, + advice: &OpTy<'tcx>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + + let addr = this.read_pointer(addr)?; + let length = this.read_target_usize(length)?; + let advise = this.read_scalar(advice)?.to_i32()?; + + // addr must be a multiple of the page size. + if !addr.addr().bytes().is_multiple_of(this.machine.page_size) { + return this.set_errno_and_return_neg1_i32(LibcError("EINVAL")); + } + + // advise must be supported. + let madv_normal = this.eval_libc_i32("MADV_NORMAL"); + let madv_random = this.eval_libc_i32("MADV_RANDOM"); + let madv_sequential = this.eval_libc_i32("MADV_SEQUENTIAL"); + let madv_willneed = this.eval_libc_i32("MADV_WILLNEED"); + if advise != madv_normal + && advise != madv_random + && advise != madv_sequential + && advise != madv_willneed + { + throw_unsup_format!( + "Miri does not support calls to madvise with advice other than MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, or MADV_WILLNEED", + ); + } + + // The pages from `[addr, addr + length)` must be mapped, so length definitely must not overflow. + let Some(length) = round_up_to_page_size(this, length) else { + return this.set_errno_and_return_neg1_i32(LibcError("ENOMEM")); + }; + // Ensure this is actually allocated memory we can access. + this.check_ptr_access(addr, Size::from_bytes(length), CheckInAllocMsg::MemoryAccess) + .map_err_kind(|_| err_ub_format!("`madvise` called on out-of-bounds memory"))?; + + // All advises we support are no-ops. + interp_ok(Scalar::from_i32(0)) + } +} + +fn round_up_to_page_size(this: &MiriInterpCx<'_>, length: u64) -> Option { + length + .checked_next_multiple_of(this.machine.page_size) + .filter(|length| *length <= this.target_isize_max().try_into().unwrap()) +} + +fn verify_prot<'tcx>(this: &mut MiriInterpCx<'tcx>, prot: i32) -> InterpResult<'tcx> { + let prot_read = this.eval_libc_i32("PROT_READ"); + let prot_write = this.eval_libc_i32("PROT_WRITE"); + + // Miri doesn't support protections other than PROT_READ|PROT_WRITE. + if prot != prot_read | prot_write { + throw_unsup_format!( + "Miri does not support calls to mmap/mprotect with protections other than \ + PROT_READ|PROT_WRITE", + ); + } + + interp_ok(()) } diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index ae882f8ff3a40..72b614f58d0a4 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1487,9 +1487,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Only shutting down the write end doesn't cause an EPOLLHUP event // and thus we won't set the `write_closed` readiness for it here. readiness.write_closed |= is_read_write_shutdown; - // The Linux kernel also sets EPOLLIN when both ends of a socket are closed: + // The Linux kernel also sets EPOLLIN when the read end of a socket is closed: // - readiness.readable |= is_read_write_shutdown; + readiness.readable |= is_read_shutdown || is_read_write_shutdown; drop(readiness); @@ -1697,12 +1697,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_send must only be called when the socket is connected") }; // This is a *non-blocking* write. let result = this.write_to_host(stream, length, buffer_ptr)?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1715,8 +1719,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_written) if bytes_written < length => { - // We had a short write. On Unix hosts using the `epoll` and `kqueue` backends, a + Ok(bytes_written) + if bytes_written < length && !socket.io_readiness.borrow().write_closed => + { + // We had a short write. (Note that we don't want to clear the writable readiness for + // sockets whose write end has already been closed as those never block a write, i.e., + // they are always write-ready.) + // On Unix hosts using the `epoll` and `kqueue` backends, a // short write means that the write buffer is full. We update the readiness // accordingly, which means that next time we see "writable" we will report an epoll // edge. Some applications (e.g. tokio) rely on this behavior; see @@ -1820,7 +1829,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Result> { let this = self.eval_context_mut(); - let SocketState::Connected(stream) = &mut *socket.state.borrow_mut() else { + let mut state = socket.state.borrow_mut(); + let SocketState::Connected(stream) = &mut *state else { panic!("try_non_block_recv must only be called when the socket is connected") }; @@ -1832,6 +1842,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { length, buffer_ptr, )?; + + drop(state); + match result { Err(IoError::HostError(e)) if matches!(e.kind(), io::ErrorKind::NotConnected | io::ErrorKind::WouldBlock) => @@ -1844,9 +1857,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => { + Ok(bytes_read) + if !should_peek + && bytes_read < length + && bytes_read > 0 + && !socket.io_readiness.borrow().read_closed => + { // We had a short read (and were not peeking). (Note that reading 0 bytes is guaranteed - // to indicate EOF, and can never happen spuriously, so we have to exclude that case.) + // to indicate EOF, and can never happen spuriously, so we have to exclude that case. + // We also don't want to clear the readable readiness for sockets whose read end has + // already been closed as those never block a read, i.e., they are always read-ready.) // On Unix hosts using the `epoll` and `kqueue` backends, a short read means that the // read buffer is empty. We update the readiness accordingly, which means that next time // we see "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 4e351c1571218..a34314f3fbea6 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -35,7 +35,13 @@ const PTHREAD_INIT: u8 = 1; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { interp_ok(match &ecx.tcx.sess.target.os { - Os::Linux | Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => 0, + Os::Linux + | Os::Illumos + | Os::Solaris + | Os::MacOs + | Os::FreeBsd + | Os::Android + | Os::NetBsd => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) } @@ -135,8 +141,8 @@ impl SyncObj for PthreadMutex { fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> { let offset = match &ecx.tcx.sess.target.os { Os::Linux | Os::Illumos | Os::Solaris | Os::FreeBsd | Os::Android => 0, - // macOS stores a signature in the first bytes, so we move to offset 4. - Os::MacOs => 4, + // macOS and NetBSD store a signature in the first bytes, so we move to offset 4. + Os::MacOs | Os::NetBsd => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), }; let offset = Size::from_bytes(offset); @@ -163,7 +169,7 @@ fn mutex_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP"); check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP"); } - Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android => { + Os::Illumos | Os::Solaris | Os::MacOs | Os::FreeBsd | Os::Android | Os::NetBsd => { // No non-standard initializers. } os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 64b5b786c6c08..b3a8291b9361b 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -879,33 +879,39 @@ fn mpsadbw<'tcx>( assert_eq!(left.layout.size, dest.layout.size); let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?; + assert!(num_chunks <= 2); + let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?; let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?; assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2)); let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?; - // Bit 2 of `imm` specifies the offset for indices of `left`. - // The offset is 0 when the bit is 0 or 4 when the bit is 1. - let left_offset = u64::try_from((imm >> 2) & 1).unwrap().strict_mul(4); - // Bits 0..=1 of `imm` specify the offset for indices of - // `right` in blocks of 4 elements. - let right_offset = u64::try_from(imm & 0b11).unwrap().strict_mul(4); for i in 0..num_chunks { let left = ecx.project_index(&left, i)?; let right = ecx.project_index(&right, i)?; let dest = ecx.project_index(&dest, i)?; + // The first 128-bit chunk uses the low 3 bits of IMM, the second chunk uses bits 3..6. + let lane_imm = imm.strict_shr(i.strict_mul(3).try_into().unwrap()); + + // Bit 2 of `lane_imm` specifies the offset for indices of `left`. + // The offset is 0 when the bit is 0 or 4 when the bit is 1. + let left_base = u64::try_from((lane_imm >> 2) & 1).unwrap().strict_mul(4); + // Bits 0..=1 of `lane_imm` specify the offset for indices of + // `right` in blocks of 4 elements. + let right_base = u64::try_from(lane_imm & 0b11).unwrap().strict_mul(4); + for j in 0..dest_items_per_chunk { - let left_offset = left_offset.strict_add(j); + let left_offset = left_base.strict_add(j); let mut res: u16 = 0; for k in 0..4 { let left = ecx .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)? .to_u8()?; let right = ecx - .read_scalar(&ecx.project_index(&right, right_offset.strict_add(k))?)? + .read_scalar(&ecx.project_index(&right, right_base.strict_add(k))?)? .to_u8()?; res = res.strict_add(left.abs_diff(right).into()); } diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 60c7e192f4805..164b61f597065 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -170,9 +170,10 @@ def test_cargo_miri_test(): "test.empty.ref", env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) - test("`cargo miri test` (proc-macro crate)", - cargo_miri("test") + ["-p", "proc_macro_crate"], - "test.proc-macro.stdout.ref", "test.proc-macro.stderr.ref", + test("`cargo miri test` (entire workspace, no isolation)", + cargo_miri("test") + ["--workspace"], + "test.workspace.stdout.ref", "test.workspace.stderr.ref", + env={'MIRIFLAGS': "-Zmiri-disable-isolation"}, ) test("`cargo miri test` (custom target dir)", cargo_miri("test") + ["--target-dir=custom-test"], diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref b/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref deleted file mode 100644 index 7326c0a25a069..0000000000000 --- a/src/tools/miri/test-cargo-miri/test.proc-macro.stdout.ref +++ /dev/null @@ -1,5 +0,0 @@ - -running 0 tests - -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME - diff --git a/src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref b/src/tools/miri/test-cargo-miri/test.workspace.stderr.ref similarity index 100% rename from src/tools/miri/test-cargo-miri/test.proc-macro.stderr.ref rename to src/tools/miri/test-cargo-miri/test.workspace.stderr.ref diff --git a/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref new file mode 100644 index 0000000000000..a4e224a329797 --- /dev/null +++ b/src/tools/miri/test-cargo-miri/test.workspace.stdout.ref @@ -0,0 +1,103 @@ + +running 2 tests +.. +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +imported main + +running 6 tests +...i.. +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +subcrate testing + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + +all doctests ran in $TIME; merged doctests compilation took $TIME + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + + +running 1 test +. +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock index cbd2aa22b5236..4691588eefb40 100644 --- a/src/tools/miri/tests/deps/Cargo.lock +++ b/src/tools/miri/tests/deps/Cargo.lock @@ -290,7 +290,6 @@ dependencies = [ name = "miri-test-deps" version = "0.1.0" dependencies = [ - "cfg-if", "futures", "getrandom 0.1.16", "getrandom 0.2.17", diff --git a/src/tools/miri/tests/deps/Cargo.toml b/src/tools/miri/tests/deps/Cargo.toml index a4d06b628081c..bbbcc316f31c2 100644 --- a/src/tools/miri/tests/deps/Cargo.toml +++ b/src/tools/miri/tests/deps/Cargo.toml @@ -10,7 +10,6 @@ edition = "2021" # all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`. libc = "0.2" num_cpus = "1.10.1" -cfg-if = "1" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs index 98cc80b6b4ea2..d4d14d449aa68 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd read, get unblocked, and then // get blocked again. @@ -18,7 +22,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs index 1a1d76eda2003..39d00a7522761 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs @@ -4,6 +4,10 @@ use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // Test the behaviour of a thread being blocked on an eventfd `write`, get unblocked, and then // get blocked again. @@ -17,7 +21,7 @@ fn main() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index eecf6abb9379f..6a6d363eea6bb 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -16,8 +16,7 @@ use libc_utils::*; fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create two socketpair instances. let mut fds_a = [-1, -1]; diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index 3eb79121a2f8d..22fe014d5831c 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -7,6 +7,7 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; use libc_utils::epoll::*; +use libc_utils::*; // Test if only one thread is unblocked if multiple threads blocked on same epfd. // Expected execution: @@ -16,14 +17,13 @@ use libc_utils::epoll::*; // 4. Thread 1 deadlocks. fn main() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instance. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd1 = unsafe { libc::eventfd(0, flags) }; + let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Make a duplicate so that we have two file descriptors for the same file description. - let fd2 = unsafe { libc::dup(fd1) }; + let fd2 = errno_result(unsafe { libc::dup(fd1) }).unwrap(); // Register both with epoll. epoll_ctl_add(epfd, fd1, EPOLLIN | EPOLLOUT | EPOLLET).unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs index 59cf0fc2ba026..d611feb30a703 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_unsupported_fd.rs @@ -1,13 +1,15 @@ //@only-target: linux android illumos +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::*; + // This is a test for registering unsupported fd with epoll. // Register epoll fd with epoll is allowed in real system, but we do not support this. fn main() { // Create two epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register epoll with epoll. let mut ev = diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs new file mode 100644 index 0000000000000..29d804fd0d99a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); //~ ERROR: `madvise` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr new file mode 100644 index 0000000000000..dbf38c7e4dd0f --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/madvise_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `madvise` called on out-of-bounds memory + --> tests/fail-dep/libc/madvise_out_of_bounds.rs:LL:CC + | +LL | libc::madvise(ptr, page_size + 1, libc::MADV_NORMAL); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs new file mode 100644 index 0000000000000..1e6868b714a81 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.rs @@ -0,0 +1,20 @@ +//@compile-flags: -Zmiri-disable-isolation +//@ignore-target: windows # No mmap on Windows +//@normalize-stderr-test: "only .*? bytes" -> "only SIZE bytes" + +fn main() { + unsafe { + let page_size = page_size::get(); + let ptr = libc::mmap( + std::ptr::null_mut(), + page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert!(!ptr.is_null()); + + libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); //~ ERROR: `mprotect` called on out-of-bounds memory + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr new file mode 100644 index 0000000000000..ac48880150486 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/mprotect_out_of_bounds.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: `mprotect` called on out-of-bounds memory + --> tests/fail-dep/libc/mprotect_out_of_bounds.rs:LL:CC + | +LL | libc::mprotect(ptr, page_size + 1, libc::PROT_READ | libc::PROT_WRITE); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs index 37aa4590647b4..edf3f405056f4 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs index 2f8b5be0c0c57..d062aeaf34873 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # No libc socketpair on Windows -// test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock //@require-annotations-for-level: error diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs similarity index 50% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs index a34df7c7fe3ae..211d74caadd8a 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.rs +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.rs @@ -1,4 +1,8 @@ -//@error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +//@[stack]error-in-other-file: /deallocating while item \[Unique for .*\] is strongly protected/ +//@[tree]error-in-other-file: /deallocation through .* is forbidden/ fn inner(x: &mut i32, f: fn(&mut i32)) { // `f` may mutate, but it may not deallocate! diff --git a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr similarity index 76% rename from src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr rename to src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr index 9f0df14ee4ddd..b8a763a5fc3f4 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/deallocate_against_protector1.stderr +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.stack.stderr @@ -14,13 +14,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout); 2: std::mem::drop::> at RUSTLIB/core/src/mem/mod.rs:LL:CC 3: main::{closure#0} - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC - 4: <{closure@tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim at RUSTLIB/core/src/ops/function.rs:LL:CC 5: inner - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC 6: main - at tests/fail/stacked_borrows/deallocate_against_protector1.rs:LL:CC + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr new file mode 100644 index 0000000000000..3e97a15333ac6 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector1.tree.stderr @@ -0,0 +1,40 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> RUSTLIB/alloc/src/boxed.rs:LL:CC + | +LL | self.1.deallocate(From::from(ptr.cast()), layout); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the accessed tag was created here + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | drop(unsafe { Box::from_raw(raw) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + | +LL | inner(Box::leak(Box::new(0)), |x| { + | ^ + = note: stack backtrace: + 0: as std::ops::Drop>::drop + at RUSTLIB/alloc/src/boxed.rs:LL:CC + 1: std::ptr::drop_glue::> - shim(Some(std::boxed::Box)) + at RUSTLIB/core/src/ptr/mod.rs:LL:CC + 2: std::mem::drop::> + at RUSTLIB/core/src/mem/mod.rs:LL:CC + 3: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 4: <{closure@tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 5: inner + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + 6: main + at tests/fail/both_borrows/deallocate_against_protector1.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs new file mode 100644 index 0000000000000..4cac0cdd03f8b --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.rs @@ -0,0 +1,23 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +// Ensure that even a ZST prevents the reference from being used for deallocation. +// The `nofree` attributes we add in LLVM IR rely on this. + +use std::alloc::Layout; + +fn inner(x: &mut (), f: fn(&mut ())) { + // `f` may mutate, but it may not deallocate! + f(x) +} + +fn main() { + let ptr = Box::leak(Box::new(0i32)) as *mut i32; + inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + let raw = x as *mut _ as *mut i32; + // Avoid ever creating a `Box`, we don't want any implicit accesses. + std::alloc::dealloc(raw.cast(), Layout::new::()); + //~[tree]^ERROR: /deallocation through .* is forbidden/ + //~[stack]|ERROR: tag does not exist in the borrow stack for this location + }); +} diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr new file mode 100644 index 0000000000000..20993ef5072b1 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.stack.stderr @@ -0,0 +1,27 @@ +error: Undefined Behavior: attempting deallocation using at ALLOC, but that tag does not exist in the borrow stack for this location + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information +help: would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | let raw = x as *mut _ as *mut i32; + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr new file mode 100644 index 0000000000000..7fae0b6a07773 --- /dev/null +++ b/src/tools/miri/tests/fail/both_borrows/deallocate_against_protector2.tree.stderr @@ -0,0 +1,29 @@ +error: Undefined Behavior: deallocation through at ALLOC[0x0] is forbidden + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | std::alloc::dealloc(raw.cast(), Layout::new::()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: the allocation of the accessed tag also contains the strongly protected tag + = help: the strongly protected tag disallows deallocations +help: the strongly protected tag was created here, in the initial state Reserved + --> tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + | +LL | inner(unsafe { &mut *(ptr as *mut ()) }, |x| unsafe { + | ^ + = note: stack backtrace: + 0: main::{closure#0} + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 1: <{closure@tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC} as std::ops::FnOnce<(&mut (),)>>::call_once - shim + at RUSTLIB/core/src/ops/function.rs:LL:CC + 2: inner + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + 3: main + at tests/fail/both_borrows/deallocate_against_protector2.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 75eb06bc12cfb..90f70064932c1 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -146,7 +146,7 @@ fn test_epoll_ctl_del() { events: (EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO) as u32, u64: u64::try_from(fds[1]).unwrap(), }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; + let res = unsafe { libc::epoll_ctl(epfd, EPOLL_CTL_ADD, fds[1], &mut ev) }; assert_eq!(res, 0); // Test EPOLL_CTL_DEL. @@ -158,10 +158,8 @@ fn test_epoll_ctl_del() { // This test is for one fd registered under two different epoll instance. fn test_two_epoll_instance() { // Create two epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); - let epfd2 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd2, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + let epfd2 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -570,8 +568,7 @@ fn test_epoll_ctl_epfd_equal_fd() { // epfd that shouldn't receive a notification in edge-triggered mode. fn test_epoll_ctl_notification() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; @@ -584,8 +581,7 @@ fn test_epoll_ctl_notification() { check_epoll_wait_noblock(epfd0, &[Ev { events: EPOLLOUT, data: fds[0] }]); // Create another epoll instance. - let epfd1 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd1, -1); + let epfd1 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register the same file description for epfd1. epoll_ctl_add(epfd1, fds[0], EPOLLIN | EPOLLOUT | EPOLLET_OR_ZERO).unwrap(); @@ -692,8 +688,7 @@ fn test_issue_3858() { /// Ensure that if a socket becomes un-writable, we don't see it any more. fn test_issue_4374() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; @@ -721,8 +716,7 @@ fn test_issue_4374() { /// Same as above, but for becoming un-readable. fn test_issue_4374_reads() { // Create an epoll instance. - let epfd0 = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd0, -1); + let epfd0 = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance, make it non-blocking. let mut fds = [-1, -1]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index e86c70b590b36..e753983344bd4 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -1,6 +1,7 @@ //@only-target: linux android illumos // test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -9,6 +10,7 @@ use std::{io, thread}; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::*; fn main() { test_read_write(); @@ -35,8 +37,8 @@ fn write_bytes(fd: i32, data: [u8; N]) -> io::Result { } fn test_read_write() { - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); // Write 1 to the counter. let res = write_bytes(fd, sized_8_data).unwrap(); @@ -97,9 +99,15 @@ fn test_read_write() { fn test_race() { static mut VAL: u8 = 0; - let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + + let fd = + errno_result(unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { + if !cfg!(miri) { + // Make sure the write goes first. + thread::sleep(std::time::Duration::from_millis(10)); + } + let mut buf: [u8; 8] = [0; 8]; let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. @@ -130,8 +138,7 @@ fn test_syscall() { // This test will block on eventfd read then get unblocked by `write`. fn test_blocking_read() { // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will block. @@ -154,8 +161,7 @@ fn test_blocking_read() { fn test_blocking_write() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { @@ -192,8 +198,7 @@ fn test_blocking_write() { fn test_two_threads_blocked_on_eventfd() { // eventfd write will block when EFD_NONBLOCK flag is clear // and the addition caused counter to exceed u64::MAX - 1. - let flags = libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) }).unwrap(); // Write u64 - 1, so the all subsequent write will block. let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes(); let res: i64 = unsafe { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a131112ee258d..d9d00d7ba239b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -272,7 +272,7 @@ fn test_dup() { let name = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); unsafe { - let fd = libc::open(name.as_ptr(), libc::O_RDONLY); + let fd = errno_result(libc::open(name.as_ptr(), libc::O_RDONLY)).unwrap(); let new_fd = libc::dup(fd); let new_fd2 = libc::dup2(fd, 8); @@ -856,8 +856,8 @@ fn test_readdir() { assert!(!dirp.is_null()); let mut entries = Vec::new(); loop { - cfg_if::cfg_if! { - if #[cfg(target_os = "macos")] { + cfg_select! { + target_os = "macos" => { // On macos we only support readdir_r as that's what std uses there. use std::mem::MaybeUninit; use libc::dirent; @@ -866,7 +866,8 @@ fn test_readdir() { let ret = libc::readdir_r(dirp, entry.as_mut_ptr(), &mut result); assert_eq!(ret, 0); let entry_ptr = result; - } else { + } + _ => { let entry_ptr = libc::readdir(dirp); } } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index 98d7340fa9db3..12b5ae5666cfd 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc pipe on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native use std::thread; #[path = "../../utils/libc.rs"] @@ -39,7 +40,7 @@ fn test_pipe() { let data = b"123"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (part1, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (part1, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(part1[..], data[..part1.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"34").unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 9ed0b9c735979..b7918581bc649 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -28,6 +28,8 @@ fn main() { test_readiness_after_short_read(); test_readiness_after_short_peek(); test_readiness_after_short_write(); + test_readable_after_read_shutdown_and_short_read(); + test_writable_after_write_shutdown_with_full_buffer(); } /// Test that connecting to a server socket works when the client @@ -359,7 +361,7 @@ fn test_shutdown_read_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -387,7 +389,7 @@ fn test_shutdown_read() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); @@ -411,7 +413,7 @@ fn test_shutdown_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - let epfd = unsafe { libc::epoll_create1(0) }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Spawn the server thread. let server_thread = thread::spawn(move || { @@ -562,24 +564,14 @@ fn test_readiness_after_short_write() { unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); - // Spawn the server thread. - let server_thread = thread::spawn(move || { - let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); - // Return the peer socket file descriptor such that we can use - // it after joining the server thread. - peerfd - }); - net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); unsafe { // Change client socket to be non-blocking. errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); } - // The peer socket is a blocking socket. - let peerfd = server_thread.join().unwrap(); - // Add client socket with writable interest to epoll. epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLOUT).unwrap(); @@ -636,3 +628,153 @@ fn test_readiness_after_short_write() { // We should again be able to write into the socket. libc_utils::write_all(client_sockfd, &buffer).unwrap(); } + +/// Test that Miri correctly keeps the readable readiness when the read end of the client +/// socket has been closed -- even after a short read. +fn test_readable_after_read_shutdown_and_short_read() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + // Spawn the server thread. + let server_thread = thread::spawn(move || { + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + // Write `TEST_BYTES` into the stream. + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + // Close the write end, so that the reader will get an EOF. + // (We could alternatively test this by closing the read end of the client socket, + // but Windows has some special behavior when closing a read end while there's still + // data coming in, so we avoid that.) + unsafe { errno_check(libc::shutdown(peerfd, libc::SHUT_WR)) }; + }); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + + // Change client socket to be non-blocking. + unsafe { errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)) }; + + server_thread.join().unwrap(); + + // Add client socket with "read closed" and "readable" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLET | EPOLLIN | EPOLLRDHUP).unwrap(); + + // Ensure that the socket is readable and that its read end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLRDHUP, data: client_sockfd }], -1); + + let mut buffer = [0u8; 1024]; + + // We want to read in chunks of 16 bytes. To ensure we get a short read, `TEST_BYTES.len()` + // must not be dividable by 16. + assert!(TEST_BYTES.len() % 16 != 0); + + let mut total_bytes_read = 0; + // Read everything from the socket until we get a short read. + // We don't want to provide `TEST_BYTES.len()` as `count` because then we won't trigger + // a short read. + loop { + let bytes_read = unsafe { + errno_result(libc::read( + client_sockfd, + buffer.as_mut_ptr().byte_add(total_bytes_read).cast(), + // Read a chunk of 16 bytes. + 16, + )) + .unwrap() + }; + + total_bytes_read += bytes_read as usize; + if bytes_read < 16 { + // We had a short read; we thus assume the read buffer is empty. + break; + } + } + assert_eq!(total_bytes_read, TEST_BYTES.len()); + + // We had a short read because `buffer` is bigger than `TEST_BYTES`. + // Because the read end of the socket is closed, we should still be able to + // read to detect EOFs. + + // Ensure that the "readable" and "read closed" readiness flags are still set. + assert_eq!( + current_epoll_readiness::<8>(client_sockfd, EPOLLIN | EPOLLET | EPOLLRDHUP), + EPOLLIN | EPOLLRDHUP + ); + + // A read should not block and return 0, indicating EOF. + let mut buffer = [1u8; 16]; + let bytes_read = unsafe { + errno_result(libc::read(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len())).unwrap() + }; + assert_eq!(bytes_read, 0); +} + +/// Test that the writable readiness gets set when the write end of a socket +/// is closed -- even when the socket write buffer is full. +fn test_writable_after_write_shutdown_with_full_buffer() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // Add client socket with level-triggered "writable" and "write closed" interest to epoll. + epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLHUP).unwrap(); + + // Wait until the socket becomes writable. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // We now want to fill the write buffer of the socket by repeatedly writing + // `buffer` into it. The last write should then be a short write. + // We assume/hope that the write buffer length is not divisible by 1039. + let buffer = [123u8; 1039]; + + loop { + let result = unsafe { + errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) + }; + + match result { + Ok(bytes_written) => { + if (bytes_written as usize) < buffer.len() { + // We had a short write; we thus assume the write buffer is full. + break; + } + } + Err(err) if err.kind() == ErrorKind::WouldBlock => { + // Windows and Apple hosts behave weirdly when attempting to fill up the write buffer. + // Instead of doing a short write to completely fill the buffer, they can return an + // EWOULDBLOCK when the next write wouldn't fit into the buffer. + // When we get such an error, we also assume the write buffer is full. + break; + } + Err(err) => panic!("unexpected error whilst filling up buffer: {err}"), + } + } + + // The write buffer is full; because this is a level-triggered interest, + // a readiness of 0 means that the socket would now block on writes. + check_epoll_wait(epfd, &[], 0); + + // Close the socket write end. + unsafe { + errno_check(libc::shutdown(client_sockfd, libc::SHUT_WR)); + } + + // The socket should no longer block on writes after its write end is closed. + check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: client_sockfd }], -1); + + // A write should not block and return an error. + let result = + unsafe { errno_result(libc::write(client_sockfd, buffer.as_ptr().cast(), buffer.len())) }; + assert_eq!(result.unwrap_err().kind(), ErrorKind::BrokenPipe); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index da521600d84a6..f994dc28a9349 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -1,6 +1,7 @@ //@ignore-target: windows # No libc socketpair on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency +//@run-native // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -34,7 +35,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[0], data).unwrap(); let mut buf2: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[1], &mut buf2).unwrap(); + let (read, rest) = read_partial(fds[1], &mut buf2).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[0], b"12").unwrap(); @@ -52,7 +53,7 @@ fn test_socketpair() { let data = b"abc"; write_all(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let (read, rest) = read_split_slice(fds[0], &mut buf4).unwrap(); + let (read, rest) = read_partial(fds[0], &mut buf4).unwrap(); assert_eq!(read[..], data[..read.len()]); // Write 2 more bytes so we can exactly fill the `rest`. write_all(fds[1], b"12").unwrap(); @@ -64,9 +65,9 @@ fn test_socketpair() { errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, data); - let (read, _tail) = read_split_slice(fds[1], &mut buf).unwrap(); + let (read, _tail) = read_partial(fds[1], &mut buf).unwrap(); assert_eq!(read, &[]); // Writing the other end should emit EPIPE. let err = write_all(fds[1], &mut buf).unwrap_err(); @@ -132,6 +133,11 @@ fn test_blocking_read() { // Test the behaviour of a socketpair getting blocked on write and subsequently unblocked. fn test_blocking_write() { + // The test uses Miri's exact buffer size. + if !cfg!(miri) { + return; + } + let mut fds = [-1, -1]; errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let arr1: [u8; 0x34000] = [1; 0x34000]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index f315d2ab117f9..3bfcfab099d7b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,5 +1,6 @@ //@ignore-target: windows # no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation +//@run-native #[path = "../../utils/libc.rs"] mod libc_utils; @@ -9,6 +10,17 @@ use std::{env, mem, ptr}; use libc_utils::errno_check; +fn set_tz(name: &str) { + extern "C" { + fn tzset(); + } + + env::set_var("TZ", name); + if !cfg!(miri) { + unsafe { tzset() }; // re-read TZ env var (natively, it may be cached) + } +} + fn main() { test_clocks(); test_posix_gettimeofday(); @@ -66,11 +78,13 @@ fn test_posix_gettimeofday() { assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - // Test that non-null tz returns an error (because we don't support it). - let mut tz = mem::MaybeUninit::::uninit(); - let tz_ptr = tz.as_mut_ptr(); - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; - assert_eq!(is_error, -1); + if cfg!(miri) { + // Test that non-null tz returns an error (because we don't support it). + let mut tz = mem::MaybeUninit::::uninit(); + let tz_ptr = tz.as_mut_ptr(); + let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; + assert_eq!(is_error, -1); + } } /// Helper function to create an empty tm struct. @@ -104,9 +118,8 @@ fn create_empty_tm() -> libc::tm { /// Original GMT test fn test_localtime_r_gmt() { - // Set timezone to GMT. - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -120,7 +133,9 @@ fn test_localtime_r_gmt() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", target_os = "macos", @@ -130,21 +145,21 @@ fn test_localtime_r_gmt() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } // The returned value is the pointer passed in. assert!(ptr::eq(res, &mut tm)); - - // Remove timezone setting. - env::remove_var(key); } /// PST timezone test (testing different timezone handling). fn test_localtime_r_pst() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; // 2024-04-07 07:43:56 GMT let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -159,7 +174,9 @@ fn test_localtime_r_pst() { assert_eq!(tm.tm_year, 124); assert_eq!(tm.tm_wday, 0); assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); // DST information unavailable + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); // DST information unavailable + } #[cfg(any( target_os = "linux", @@ -170,18 +187,20 @@ fn test_localtime_r_pst() { { assert_eq!(tm.tm_gmtoff, -7 * 3600); // -7 hours in seconds unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "-07"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "-07" } else { "PDT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Unix epoch test (edge case testing). fn test_localtime_r_epoch() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); + const TIME_SINCE_EPOCH: libc::time_t = 0; // 1970-01-01 00:00:00 let custom_time_ptr = &TIME_SINCE_EPOCH; let mut tm = create_empty_tm(); @@ -196,7 +215,9 @@ fn test_localtime_r_epoch() { assert_eq!(tm.tm_year, 70); assert_eq!(tm.tm_wday, 4); // Thursday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -207,19 +228,20 @@ fn test_localtime_r_epoch() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values). #[cfg(target_pointer_width = "64")] fn test_localtime_r_future_64b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2050-01-01 00:00:00 for 64-bit systems // value that's safe for 64-bit time_t @@ -237,7 +259,9 @@ fn test_localtime_r_future_64b() { assert_eq!(tm.tm_year, 150); // 2050 - 1900 assert_eq!(tm.tm_wday, 6); // Saturday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -248,19 +272,20 @@ fn test_localtime_r_future_64b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Future date test (testing large values for 32b target). #[cfg(target_pointer_width = "32")] fn test_localtime_r_future_32b() { - let key = "TZ"; - env::set_var(key, "GMT"); + set_tz("GMT"); // Using 2030-01-01 00:00:00 for 32-bit systems // Safe value within i32 range @@ -279,7 +304,9 @@ fn test_localtime_r_future_32b() { assert_eq!(tm.tm_year, 130); // 2030 - 1900 assert_eq!(tm.tm_wday, 2); // Tuesday assert_eq!(tm.tm_yday, 0); - assert_eq!(tm.tm_isdst, -1); + if cfg!(miri) { + assert_eq!(tm.tm_isdst, -1); + } #[cfg(any( target_os = "linux", @@ -290,19 +317,20 @@ fn test_localtime_r_future_32b() { { assert_eq!(tm.tm_gmtoff, 0); unsafe { - assert_eq!(std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00"); + assert_eq!( + std::ffi::CStr::from_ptr(tm.tm_zone).to_str().unwrap(), + if cfg!(miri) { "+00" } else { "GMT" } + ); } } assert!(ptr::eq(res, &mut tm)); - env::remove_var(key); } /// Tests the behavior of `localtime_r` with multiple calls to ensure deduplication of `tm_zone` pointers. #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))] fn test_localtime_r_multiple_calls_deduplication() { - let key = "TZ"; - env::set_var(key, "PST8PDT"); + set_tz("PST8PDT"); const TIME_SINCE_EPOCH_BASE: libc::time_t = 1712475836; // Base timestamp: 2024-04-07 07:43:56 GMT const NUM_CALLS: usize = 50; @@ -321,9 +349,11 @@ fn test_localtime_r_multiple_calls_deduplication() { let unique_count = unique_pointers.len(); + // Miri non-determinisitcally de-duplicates. Native always deduplicates. + let min = if cfg!(miri) { 2 } else { 1 }; assert!( - unique_count >= 2 && unique_count <= (NUM_CALLS - 1), - "Unexpected number of unique tm_zone pointers: {} (expected between 2 and {})", + unique_count >= min && unique_count <= (NUM_CALLS - 1), + "Unexpected number of unique tm_zone pointers: {} (expected between {min} and {})", unique_count, NUM_CALLS - 1 ); diff --git a/src/tools/miri/tests/pass-dep/libc/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs index bfd840d2fb89d..692b59d443ae8 100644 --- a/src/tools/miri/tests/pass-dep/libc/mmap.rs +++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs @@ -94,6 +94,78 @@ fn test_mmap( assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); } +fn test_mprotect() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + // Protect part of it redundantly. + let res = unsafe { + libc::mprotect(ptr.byte_add(2 * page_size), 42, libc::PROT_READ | libc::PROT_WRITE) + }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::mprotect(ptr, 4 * page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, 0i32); + + // We report an error when the address is not a multiple of the page size. + let res = + unsafe { libc::mprotect(ptr.byte_add(11), page_size, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::mprotect(ptr, usize::MAX - 1, libc::PROT_READ | libc::PROT_WRITE) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + +fn test_madvise() { + let page_size = page_size::get(); + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + 4 * page_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + Default::default(), + ) + }; + assert!(!ptr.is_null()); + + for advice in [libc::MADV_NORMAL, libc::MADV_RANDOM, libc::MADV_SEQUENTIAL, libc::MADV_WILLNEED] + { + // Advise part of it redundantly. + let res = unsafe { libc::madvise(ptr.byte_add(2 * page_size), 42, advice) }; + assert_eq!(res, 0i32); + + // Protect everything redundantly. + let res = unsafe { libc::madvise(ptr, 4 * page_size, advice) }; + assert_eq!(res, 0i32); + } + + // We report an error when the address is not a multiple of the page size. + let res = unsafe { libc::madvise(ptr.byte_add(11), page_size, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL); + + // We report an error if the length cannot be rounded up to a multiple of the page size. + let res = unsafe { libc::madvise(ptr, usize::MAX - 1, libc::MADV_NORMAL) }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().raw_os_error().unwrap(), libc::ENOMEM); +} + #[cfg(target_os = "linux")] fn test_mremap() { let page_size = page_size::get(); @@ -145,6 +217,8 @@ fn main() { test_mmap(libc::mmap); #[cfg(target_os = "linux")] test_mmap(libc::mmap64); + test_mprotect(); + test_madvise(); #[cfg(target_os = "linux")] test_mremap(); } diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index cf7ea4c6abba1..6cba672c6569c 100644 --- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -4,17 +4,21 @@ use std::ffi::{CStr, CString}; use std::thread; const MAX_THREAD_NAME_LEN: usize = { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux"))] { + cfg_select! { + target_os = "linux" => { 16 - } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + } + any(target_os = "illumos", target_os = "solaris") => { 32 - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { libc::MAXTHREADNAMESIZE // 64, at the time of writing - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { usize::MAX // as far as I can tell - } else { - panic!() + } + _ => { + compile_error!("unsupported OS"); } } }; @@ -28,35 +32,38 @@ fn main() { .collect::(); fn set_thread_name(name: &CStr) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris" - ))] { + ) => { unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) } - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { unsafe { libc::pthread_setname_np(name.as_ptr().cast()) } - } else { + } + _ => { compile_error!("set_thread_name not supported for this OS") } } } fn get_thread_name(name: &mut [u8]) -> i32 { - cfg_if::cfg_if! { - if #[cfg(any( + cfg_select! { + any( target_os = "linux", target_os = "freebsd", target_os = "illumos", target_os = "solaris", target_os = "macos" - ))] { + ) => { unsafe { libc::pthread_getname_np(libc::pthread_self(), name.as_mut_ptr().cast(), name.len()) } - } else { + } + _ => { compile_error!("get_thread_name not supported for this OS") } } @@ -95,13 +102,14 @@ fn main() { // Test what happens when the buffer is shorter than 16, but still long enough. let res = get_thread_name(&mut buf[..15]); - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { // For glibc used by linux-gnu there should be a failue, // if a shorter than 16 bytes buffer is provided, even if that would be // large enough for the thread name. assert_eq!(res, libc::ERANGE); - } else { + } + _ => { // Everywhere else, this should work. assert_eq!(res, 0); // POSIX seems to promise at least 15 chars excluding a null terminator. @@ -112,15 +120,16 @@ fn main() { // Test what happens when the buffer is too short even for the short name. let res = get_thread_name(&mut buf[..4]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes_with_nul().len(), 4); assert!(short_name.as_bytes().starts_with(cstr.to_bytes())); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -128,12 +137,13 @@ fn main() { // Test zero-sized buffer. let res = get_thread_name(&mut []); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- even with size 0. assert_eq!(res, 0); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -149,16 +159,18 @@ fn main() { // Set full thread name. let cstr = CString::new(long_name.clone()).unwrap(); let res = set_thread_name(&cstr); - cfg_if::cfg_if! { - if #[cfg(target_os = "freebsd")] { + cfg_select! { + target_os = "freebsd" => { // Names of all size are supported. assert!(cstr.to_bytes_with_nul().len() <= MAX_THREAD_NAME_LEN); assert_eq!(res, 0); - } else if #[cfg(target_os = "macos")] { + } + target_os = "macos" => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ENAMETOOLONG); - } else { + } + _ => { // Name is too long. assert!(cstr.to_bytes_with_nul().len() > MAX_THREAD_NAME_LEN); assert_eq!(res, libc::ERANGE); @@ -179,14 +191,15 @@ fn main() { // Test what happens when our buffer is just one byte too small. let res = get_thread_name(&mut buf[..truncated_name.len()]); - cfg_if::cfg_if! { - if #[cfg(any(target_os = "freebsd", target_os = "macos"))] { + cfg_select! { + any(target_os = "freebsd", target_os = "macos") => { // On macOS and FreeBSD it's not an error for the buffer to be // too short for the thread name -- they truncate instead. assert_eq!(res, 0); let cstr = CStr::from_bytes_until_nul(&buf).unwrap(); assert_eq!(cstr.to_bytes(), &truncated_name.as_bytes()[..(truncated_name.len() - 1)]); - } else { + } + _ => { // The rest should give an error. assert_eq!(res, libc::ERANGE); } @@ -199,10 +212,11 @@ fn main() { // Now set the name for a non-existing thread and verify error codes. let invalid_thread = 0xdeadbeef; let error = { - cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + cfg_select! { + target_os = "linux" => { libc::ENOENT - } else { + } + _ => { libc::ESRCH } } diff --git a/src/tools/miri/tests/pass-dep/shims/gettid.rs b/src/tools/miri/tests/pass-dep/shims/gettid.rs index 0dce7bdc0c52e..9b186699ce275 100644 --- a/src/tools/miri/tests/pass-dep/shims/gettid.rs +++ b/src/tools/miri/tests/pass-dep/shims/gettid.rs @@ -5,29 +5,37 @@ #![feature(linkage)] fn gettid() -> u64 { - cfg_if::cfg_if! { - if #[cfg(any(target_os = "android", target_os = "linux"))] { + cfg_select! { + any(target_os = "android", target_os = "linux") => { gettid_linux_like() - } else if #[cfg(target_os = "nto")] { + } + target_os = "nto" => { unsafe { libc::gettid() as u64 } - } else if #[cfg(target_os = "openbsd")] { + } + target_os = "openbsd" => { unsafe { libc::getthrid() as u64 } - } else if #[cfg(target_os = "freebsd")] { + } + target_os = "freebsd" => { unsafe { libc::pthread_getthreadid_np() as u64 } - } else if #[cfg(target_os = "netbsd")] { + } + target_os = "netbsd" => { unsafe { libc::_lwp_self() as u64 } - } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + } + any(target_os = "solaris", target_os = "illumos") => { // On Solaris and Illumos, the `pthread_t` is the OS TID. unsafe { libc::pthread_self() as u64 } - } else if #[cfg(target_vendor = "apple")] { + } + target_vendor = "apple" => { let mut id = 0u64; let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; assert_eq!(status, 0); id - } else if #[cfg(windows)] { + } + windows => { use windows_sys::Win32::System::Threading::GetCurrentThreadId; unsafe { GetCurrentThreadId() as u64 } - } else { + } + _ => { compile_error!("platform has no gettid") } } diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs new file mode 100644 index 0000000000000..0989cdd86a362 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.rs @@ -0,0 +1,9 @@ +fn main() { + foo(1234); +} + +#[allow(no_mangle_generic_items)] +#[unsafe(no_mangle)] +fn foo(value: T) { + println!("{value:?}"); +} diff --git a/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout new file mode 100644 index 0000000000000..81c545efebe5f --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-154385-no-mangle-generic.stdout @@ -0,0 +1 @@ +1234 diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs index 1345924beecf0..c640dde5afaf5 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-aes.rs @@ -1,6 +1,7 @@ // We're testing aarch64 AES target specific features. //@only-target: aarch64 //@compile-flags: -C target-feature=+neon,+aes +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs index 849f99ee36cce..8d50d152f2ebe 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-crc32.rs @@ -1,6 +1,7 @@ // We're testing aarch64 CRC32 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+crc +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; diff --git a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs index 884f8eff41bdb..fdd0ff6ca6dfb 100644 --- a/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs +++ b/src/tools/miri/tests/pass/shims/aarch64/intrinsics-aarch64-neon.rs @@ -1,6 +1,7 @@ // We're testing aarch64 target specific features //@only-target: aarch64 //@compile-flags: -C target-feature=+neon +//@run-native use std::arch::aarch64::*; use std::arch::is_aarch64_feature_detected; @@ -14,6 +15,7 @@ fn main() { test_tbl1_v16i8_basic(); test_vpadd(); test_vpaddl(); + test_vqdmulh(); } } @@ -157,3 +159,51 @@ unsafe fn test_vpaddl() { vst1q_u64(r.as_mut_ptr(), vpaddlq_u32(a)); assert_eq!(r, e); } + +#[target_feature(enable = "neon")] +unsafe fn test_vqdmulh() { + let a = vld1_s32([i32::MIN, i32::MAX].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_n_s32(a, i32::MIN)); + assert_eq!(r, [i32::MAX, -i32::MAX]); + + // This is the actual calculation that happens. + let product = i32::MIN as i128 * i32::MIN as i128 * 2; + assert_eq!(i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let product = i32::MAX as i128 * i32::MIN as i128 * 2; + assert_eq!(-i32::MAX, (product >> 32).clamp(i32::MIN as i128, i32::MAX as i128) as i32); + + let b = vld1_s32([123, i32::MIN].as_ptr()); + let r: [i32; 2] = transmute(vqdmulh_s32(a, b)); + assert_eq!(r, [-123, -i32::MAX]); + + // Wider 32-bit versions. + let a = vld1q_s32([0x4000_0000, -0x4000_0000, i32::MIN, i32::MAX].as_ptr()); + + let b = vld1q_s32([123, 456, 0x4000_0000, 789].as_ptr()); + let r: [i32; 4] = transmute(vqdmulhq_s32(a, b)); + assert_eq!(r, [61, -228, -1073741824, 788]); + + let r: [i32; 4] = transmute(vqdmulhq_n_s32(a, 0x4000_0000)); + assert_eq!(r, [536870912, -536870912, -1073741824, 1073741823]); + + // 16-bit versions. + + let a = vld1_s16([i16::MIN, i16::MAX, 0, 16384].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_n_s16(a, i16::MIN)); + assert_eq!(r, [i16::MAX, -i16::MAX, 0, -16384]); + + let b = vld1_s16([123, i16::MIN, 456, 789].as_ptr()); + let r: [i16; 4] = transmute(vqdmulh_s16(a, b)); + assert_eq!(r, [-123, -i16::MAX, 0, 394]); + + // Wider 16-bit versions. + + let a = vld1q_s16([i16::MIN, i16::MAX, 0, 16384, -16384, 8192, 1, -1].as_ptr()); + let b = vld1q_s16([123, 456, 789, i16::MIN, 1, 2, 3, 4].as_ptr()); + let r: [i16; 8] = transmute(vqdmulhq_s16(a, b)); + assert_eq!(r, [-123, 455, 0, -16384, -1, 0, 0, -1]); + + let r: [i16; 8] = transmute(vqdmulhq_n_s16(a, i16::MIN)); + assert_eq!(r, [32767, -32767, 0, -16384, 16384, -8192, -1, 1]); +} diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs index ae5731bc8a6a3..1e36867c4a0b6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sha,+sse2,+ssse3,+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs index baa984e68d83b..87b2f5d3d80b3 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-adx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+adx +//@run-native #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] mod x86 { diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs index 8936ae8e91268..ba51dcea35cc6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+aes,+vaes,+avx512f +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] @@ -11,7 +12,6 @@ use std::arch::x86_64::*; fn main() { assert!(is_x86_feature_detected!("aes")); assert!(is_x86_feature_detected!("vaes")); - assert!(is_x86_feature_detected!("avx512f")); unsafe { test_aes(); @@ -86,7 +86,7 @@ unsafe fn test_aes() { // be interpreted as integers; signedness does not make sense for them, but // __m128i happens to be defined in terms of signed integers. #[allow(overflowing_literals)] -#[target_feature(enable = "vaes,avx512f")] +#[target_feature(enable = "vaes")] unsafe fn test_vaes() { #[target_feature(enable = "avx")] unsafe fn get_a256() -> __m256i { @@ -177,6 +177,12 @@ unsafe fn test_vaes() { } test_mm256_aesenclast_epi128(); + // The tests below require avx512. + if !is_x86_feature_detected!("avx512f") { + println!("warning: skipping avx512 tests"); + return; + } + #[target_feature(enable = "avx512f")] unsafe fn get_a512() -> __m512i { // Constants are random diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs index 9f7c12c4393b5..8709a17e8aeb4 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs index de1abc818420c..7fe75254c2dd8 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx2.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -1068,23 +1069,31 @@ unsafe fn test_avx2() { 18, 20, 22, 24, 26, 28, 30, ); - let r = _mm256_mpsadbw_epu8::<0b000>(a, a); + let r = _mm256_mpsadbw_epu8::<0b00000>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b001>(a, a); + let r = _mm256_mpsadbw_epu8::<0b001001>(a, a); let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 32, 24, 16, 8, 0, 8, 16, 24); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b100>(a, a); + let r = _mm256_mpsadbw_epu8::<0b000001>(a, a); + let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 0, 8, 16, 24, 32, 40, 48, 56); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b001000>(a, a); + let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 32, 24, 16, 8, 0, 8, 16, 24); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b100100>(a, a); let e = _mm256_setr_epi16(16, 20, 24, 28, 32, 36, 40, 44, 32, 40, 48, 56, 64, 72, 80, 88); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b101>(a, a); + let r = _mm256_mpsadbw_epu8::<0b101101>(a, a); let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); assert_eq_m256i(r, e); - let r = _mm256_mpsadbw_epu8::<0b111>(a, a); + let r = _mm256_mpsadbw_epu8::<0b111111>(a, a); let e = _mm256_setr_epi16(32, 28, 24, 20, 16, 12, 8, 4, 64, 56, 48, 40, 32, 24, 16, 8); assert_eq_m256i(r, e); } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 31f47b57fd285..0541e9a6cc6e0 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512bitalg,+avx512vpopcntdq,+avx512vnni,+avx512vbmi +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -9,6 +10,13 @@ use std::arch::x86_64::*; use std::mem::transmute; fn main() { + if !is_x86_feature_detected!("avx512f") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping AVX512 tests"); + return; + } + assert!(is_x86_feature_detected!("avx512f")); assert!(is_x86_feature_detected!("avx512vl")); assert!(is_x86_feature_detected!("avx512bw")); diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs index 030258f21fa51..d6c05b601f4eb 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-bmi.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+bmi1,+bmi2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs index 48958ef581096..ef0fd62da5f3f 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-gfni.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+gfni,+avx512f +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but @@ -20,8 +21,14 @@ const CONSTANT_BYTE: i32 = 0x63; fn main() { // Mostly copied from library/stdarch/crates/core_arch/src/x86/gfni.rs - assert!(is_x86_feature_detected!("avx512f")); - assert!(is_x86_feature_detected!("gfni")); + assert!(is_x86_feature_detected!("avx")); + + if !is_x86_feature_detected!("gfni") { + // GH runners don't have this, but we still want to run this natively if + // the machine happens to have gfni. So we bail out dynamically. + println!("warning: skipping gfni tests"); + return; + } unsafe { let byte_mul_test_data = generate_byte_mul_test_data(); @@ -29,15 +36,20 @@ fn main() { let affine_mul_test_data_constant = generate_affine_mul_test_data(CONSTANT_BYTE as u8); let inv_tests_data = generate_inv_tests_data(); - test_mm512_gf2p8mul_epi8(&byte_mul_test_data); test_mm256_gf2p8mul_epi8(&byte_mul_test_data); test_mm_gf2p8mul_epi8(&byte_mul_test_data); - test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm256_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); test_mm_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); - test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm256_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); test_mm_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + + if is_x86_feature_detected!("avx512f") { + test_mm512_gf2p8mul_epi8(&byte_mul_test_data); + test_mm512_gf2p8affine_epi64_epi8(&byte_mul_test_data, &affine_mul_test_data_identity); + test_mm512_gf2p8affineinv_epi64_epi8(&inv_tests_data, &affine_mul_test_data_constant); + } else { + println!("warning: skipping avx512 tests"); + } } } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs index 6051987f8d4c1..50ce9928ab7ed 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pclmulqdq.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+pclmulqdq +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs index 9136b5eda3870..f42c6db676839 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs index 570da30f0b622..8e4b284b1d150 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse2.rs @@ -1,5 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 +//@run-native #![allow(unnecessary_transmutes)] #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs index 10842160abdc6..e226f3512ac6e 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs @@ -2,6 +2,7 @@ //@only-target: x86_64 i686 // SSSE3 implicitly enables SSE3 //@compile-flags: -C target-feature=+ssse3 +//@run-native use core::mem::transmute; #[cfg(target_arch = "x86")] diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs index 7331c6ed0db33..48510b46ff327 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse41.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.1 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs index 30908baa6c15e..c998fe800df45 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-sse42.rs @@ -1,6 +1,7 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 //@compile-flags: -C target-feature=+sse4.2 +//@run-native #[cfg(target_arch = "x86")] use std::arch::x86::*; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs index e2a045bf81ff1..5ceaf405f4040 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs @@ -3,6 +3,7 @@ //@only-target: x86_64 i686 //@[avx512]compile-flags: -C target-feature=+vpclmulqdq,+avx512f //@[avx]compile-flags: -C target-feature=+vpclmulqdq,+avx2 +//@run-native // The constants in the tests below are just bit patterns. They should not // be interpreted as integers; signedness does not make sense for them, but diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs index a18b6d01524e8..9745a437eccb6 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86.rs @@ -1,4 +1,6 @@ -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +//@only-target: x86_64 i686 +//@run-native + mod x86 { #[cfg(target_arch = "x86")] use core::arch::x86 as arch; @@ -84,7 +86,6 @@ mod x86_64 { } fn main() { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] x86::main(); #[cfg(target_arch = "x86_64")] x86_64::main(); diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index f46e6aad01a5b..0f762ed916de5 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -86,7 +86,7 @@ pub fn read_exact_array(fd: libc::c_int) -> io::Result<[u8; N]> /// Do a single read from `fd` and return the part of the buffer that was written into, /// and the rest. #[track_caller] -pub fn read_split_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { +pub fn read_partial(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?; Ok(buf.split_at_mut(res as usize)) } diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 918f5ef0d5069..c1bff4f94272c 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -13,7 +13,7 @@ edition = "2024" bstr = "1.12" gimli = "0.32" libc = "0.2" -object = "0.37" +object = { version = "0.37", features = ["wasm"] } regex = "1.11" serde_json = "1.0" similar = "2.7" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs index 057ba7fa868fe..a661731e60f46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/callee.rs @@ -173,6 +173,8 @@ impl<'db> InferenceContext<'_, 'db> { // impl forces the closure kind to `FnOnce` i.e. `u8`. let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into()); let interner = autoderef.ctx().interner(); + + // Ignore splatting, it is unsupported on closures. let call_sig = interner.mk_fn_sig( [coroutine_closure_sig.tupled_inputs_ty], coroutine_closure_sig.to_coroutine( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index cc48ba06dbfc5..1fddfc09c666f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -464,6 +464,7 @@ pub fn callable_sig_from_fn_trait<'db>( args.tuple_fields(), ret, false, + // FIXME(splat): handle splatted arguments Safety::Safe, ExternAbi::Rust, )); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index fae63ddc2deae..d63ac7f7a0ed4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -637,6 +637,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { fn_.abi, if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, fn_.is_varargs, + // FIXME(splat): handle splatted arguments ), inputs_and_output: Tys::new_from_slice(&args), }), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index ef626fc0c8869..3c2976eccd980 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -2238,6 +2238,7 @@ impl<'db> DbInterner<'db> { self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } + // FIXME: add splat support when the experiment is complete pub fn mk_fn_sig( self, inputs: I, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index fe31d44207dff..d57d824e325fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1514,6 +1514,7 @@ impl<'db> DbInterner<'db> { TyKind::Tuple(params) => params, _ => panic!(), }; + // Ignore splatting, it is unsupported on closures. self.mk_fn_sig(params, s.output(), s.c_variadic(), safety, ExternAbi::Rust) }) } diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index b63f4d46139c7..db6008da59b34 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2442,7 +2442,6 @@ ui/span/issue-42234-unknown-receiver-type.rs ui/span/issue-43927-non-ADT-derive.rs ui/span/issue-71363.rs ui/span/issue-81800.rs -ui/span/issue28498-reject-ex1.rs ui/span/issue28498-reject-lifetime-param.rs ui/span/issue28498-reject-passed-to-fn.rs ui/span/issue28498-reject-trait-bound.rs diff --git a/tests/assembly-llvm/s390x-vector-abi.rs b/tests/assembly-llvm/s390x-vector-abi.rs index 90139df17ca1a..c0f770c3f0c35 100644 --- a/tests/assembly-llvm/s390x-vector-abi.rs +++ b/tests/assembly-llvm/s390x-vector-abi.rs @@ -62,16 +62,11 @@ unsafe extern "C" fn vector_ret(x: &i8x16) -> i8x16 { *x } // CHECK-LABEL: vector_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 0(%r3), 4 -// z13-NEXT: vl %v1, 16(%r3), 4 -// z13-NEXT: vst %v1, 16(%r2), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_ret_large(x: &i8x32) -> i8x32 { @@ -95,16 +90,11 @@ unsafe extern "C" fn vector_wrapper_ret(x: &Wrapper) -> Wrapper { *x } // CHECK-LABEL: vector_wrapper_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 16(%r3), 4 -// z13-NEXT: vst %v0, 16(%r2), 4 -// z13-NEXT: vl %v0, 0(%r3), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_ret_large(x: &Wrapper) -> Wrapper { @@ -141,16 +131,11 @@ unsafe extern "C" fn vector_wrapper_with_zst_ret( *x } // CHECK-LABEL: vector_wrapper_with_zst_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 16(%r3), 4 -// z13-NEXT: vst %v0, 16(%r2), 4 -// z13-NEXT: vl %v0, 0(%r3), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_wrapper_with_zst_ret_large( @@ -180,16 +165,11 @@ unsafe extern "C" fn vector_transparent_wrapper_ret( *x } // CHECK-LABEL: vector_transparent_wrapper_ret_large: -// z10: vl %v0, 16(%r3), 4 -// z10-NEXT: vl %v1, 0(%r3), 4 -// z10-NEXT: vst %v0, 16(%r2), 4 -// z10-NEXT: vst %v1, 0(%r2), 4 -// z10-NEXT: br %r14 -// z13: vl %v0, 0(%r3), 4 -// z13-NEXT: vl %v1, 16(%r3), 4 -// z13-NEXT: vst %v1, 16(%r2), 4 -// z13-NEXT: vst %v0, 0(%r2), 4 -// z13-NEXT: br %r14 +// CHECK-DAG: vl [[REG1:%v[0-9]+]], 16(%r3), 4 +// CHECK-DAG: vl [[REG2:%v[0-9]+]], 0(%r3), 4 +// CHECK-DAG: vst [[REG1]], 16(%r2), 4 +// CHECK-DAG: vst [[REG2]], 0(%r2), 4 +// CHECK: br %r14 #[cfg_attr(no_vector, target_feature(enable = "vector"))] #[no_mangle] unsafe extern "C" fn vector_transparent_wrapper_ret_large( diff --git a/tests/codegen-llvm/i128-x86-align.rs b/tests/codegen-llvm/i128-x86-align.rs index 75802b0c5056a..998821d17e80c 100644 --- a/tests/codegen-llvm/i128-x86-align.rs +++ b/tests/codegen-llvm/i128-x86-align.rs @@ -5,8 +5,6 @@ // while rustc wants it to have 16 byte alignment. This test checks that we handle this // correctly. -// CHECK: %ScalarPair = type { i32, [3 x i32], i128 } - #![feature(core_intrinsics)] #[repr(C)] @@ -62,8 +60,8 @@ pub fn load_volatile(x: &ScalarPair) -> ScalarPair { // CHECK-SAME: dereferenceable(32) %_0, // CHECK-SAME: align 16 // CHECK-SAME: dereferenceable(32) %x - // CHECK: [[LOAD:%.*]] = load volatile %ScalarPair, ptr %x, align 16 - // CHECK-NEXT: store %ScalarPair [[LOAD]], ptr %_0, align 16 + // CHECK: [[LOAD:%.*]] = load volatile i256, ptr %x, align 16 + // CHECK-NEXT: store i256 [[LOAD]], ptr %_0, align 16 // CHECK-NEXT: ret void unsafe { std::intrinsics::volatile_load(x) } } diff --git a/tests/codegen-llvm/intrinsics/volatile.rs b/tests/codegen-llvm/intrinsics/volatile.rs index 2dea5ecb2ca92..4ba4c5edd2df6 100644 --- a/tests/codegen-llvm/intrinsics/volatile.rs +++ b/tests/codegen-llvm/intrinsics/volatile.rs @@ -5,6 +5,11 @@ use std::intrinsics; +#[repr(align(32))] +pub struct CustomZst; + +type UninitFatPointer = std::mem::MaybeUninit<&'static dyn std::fmt::Debug>; + // CHECK-LABEL: @volatile_copy_memory #[no_mangle] pub unsafe fn volatile_copy_memory(a: *mut u8, b: *const u8) { @@ -28,8 +33,62 @@ pub unsafe fn volatile_set_memory(a: *mut u8, b: u8) { // CHECK-LABEL: @volatile_load #[no_mangle] -pub unsafe fn volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_bool +#[no_mangle] +pub unsafe fn volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_zst +#[no_mangle] +pub unsafe fn volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0 +#[no_mangle] +pub unsafe fn volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 2{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::volatile_load(a) +} + +// CHECK-LABEL: @volatile_load_fat +#[no_mangle] +pub unsafe fn volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE:4|8|16]] x i8 + // CHECK-SAME: align [[ALIGN:2|4|8]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT:i32|i64|i128]], ptr %a, + // CHECK-SAME: align [[ALIGN]]{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::volatile_load(a) } @@ -42,8 +101,62 @@ pub unsafe fn volatile_store(a: *mut u8, b: u8) { // CHECK-LABEL: @unaligned_volatile_load #[no_mangle] -pub unsafe fn unaligned_volatile_load(a: *const u8) -> u8 { - // CHECK: load volatile +pub unsafe fn unaligned_volatile_load(a: *const u16) -> u16 { + // CHECK: [[TEMP:%.+]] = load volatile i16, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK-NEXT: ret i16 [[TEMP]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_bool +#[no_mangle] +pub unsafe fn unaligned_volatile_load_bool(a: *const bool) -> bool { + // CHECK: [[TEMP:%.+]] = load volatile i8, ptr %a + // CHECK-SAME: align 1{{,|$}} + // CHECK: [[TRUNC:%.+]] = trunc nuw i8 [[TEMP]] to i1 + // CHECK: ret i1 [[TRUNC]] + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_zst +#[no_mangle] +pub unsafe fn unaligned_volatile_load_zst(a: *const CustomZst) -> CustomZst { + // CHECK: start: + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_array +// CHECK-SAME: ptr{{.+}}sret([16 x i8]){{.+}}%_0, +#[no_mangle] +pub unsafe fn unaligned_volatile_load_array(a: *const [u16; 8]) -> [u16; 8] { + // CHECK-NOT: alloca + // CHECK: [[TEMP:%.+]] = load volatile i128, ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store i128 [[TEMP]], ptr %_0, + // CHECK-SAME: align 2{{,|$}} + // CHECK-NEXT: ret void + intrinsics::unaligned_volatile_load(a) +} + +// CHECK-LABEL: @unaligned_volatile_load_fat +#[no_mangle] +pub unsafe fn unaligned_volatile_load_fat(a: *const UninitFatPointer) -> UninitFatPointer { + // CHECK: [[ALLOCA:%.+]] = alloca + // CHECK-SAME: [[SIZE]] x i8 + // CHECK-SAME: align [[ALIGN]] + + // CHECK: [[TEMP:%.+]] = load volatile [[INT]], ptr %a, + // CHECK-SAME: align 1{{,|$}} + // CHECK: store [[INT]] [[TEMP]], ptr [[ALLOCA]], + // CHECK-SAME: align [[ALIGN]]{{,|$}} + + // CHECK: [[T0:%.+]] = load ptr, ptr [[ALLOCA]], align [[ALIGN]] + // CHECK: [[T1:%.+]] = getelementptr inbounds i8, ptr [[ALLOCA]] + // CHECK: [[T2:%.+]] = load ptr, ptr [[T1]], align [[ALIGN]] + // CHECK: [[P1:%.+]] = insertvalue { ptr, ptr } poison, ptr [[T0]], 0 + // CHECK: [[P2:%.+]] = insertvalue { ptr, ptr } [[P1]], ptr [[T2]], 1 + // CHECK: ret { ptr, ptr } [[P2]] intrinsics::unaligned_volatile_load(a) } diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index c594e187a0eb5..5834255f3d313 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -1,6 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ filecheck-flags: --implicit-check-not 'br {{.*}}' --implicit-check-not 'select' -//@ min-llvm-version: 22 // Test for #107681. // Make sure we don't create `br` or `select` instructions. @@ -13,8 +11,10 @@ use std::slice::Iter; #[no_mangle] pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-LABEL: @foo( - // CHECK: [[INNER:%.*]] = load ptr, ptr %x - // CHECK: [[RET:%.*]] = load i32, ptr [[INNER]] - // CHECK: ret i32 [[RET]] + // CHECK-NOT: br {{.*}} + // CHECK-NOT: select + // CHECK: [[RET:%.*]] = load i32, ptr + // CHECK-NEXT: assume + // CHECK-NEXT: ret i32 [[RET]] x.next().unwrap_unchecked() } diff --git a/tests/codegen-llvm/no_builtins-at-crate.rs b/tests/codegen-llvm/no_builtins-at-crate.rs index ba1d31f60c390..de80a925aebc5 100644 --- a/tests/codegen-llvm/no_builtins-at-crate.rs +++ b/tests/codegen-llvm/no_builtins-at-crate.rs @@ -3,11 +3,13 @@ #![no_builtins] #![crate_type = "lib"] +use std::ffi::c_void; + // CHECK: define // CHECK-SAME: @__aeabi_memcpy // CHECK-SAME: #0 #[no_mangle] -pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usize) { +pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut c_void, src: *const c_void, size: usize) { // CHECK: call // CHECK-SAME: @memcpy( memcpy(dest, src, size); @@ -17,7 +19,7 @@ pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, size: usi // CHECK-SAME: @memcpy // CHECK-SAME: #0 extern "C" { - pub fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; } // CHECK: attributes #0 diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs deleted file mode 100644 index d59dd39983c78..0000000000000 --- a/tests/crashes/130310.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#130310 - -use std::marker::PhantomData; - -#[repr(C)] -struct A { - a: *const A>, - p: PhantomData, -} - -extern "C" { - fn f(a: *const A<()>); -} - -fn main() {} diff --git a/tests/crashes/147719.rs b/tests/crashes/147719.rs new file mode 100644 index 0000000000000..d9595b8ead565 --- /dev/null +++ b/tests/crashes/147719.rs @@ -0,0 +1,16 @@ +//@ known-bug: #147719 +//@ edition: 2024 +trait NodeImpl {} +struct Wrap(F, P); +impl Wrap { + fn new(_: F) -> Self { + loop {} + } +} +trait Arg {} +impl NodeImpl for Wrap where A: Arg {} +impl NodeImpl for Wrap where F: Fn(&(), A) -> Fut {} +fn trigger_ice() { + let _: &dyn NodeImpl = &Wrap::<_, (i128,)>::new(async |_: &(), i128| 0); +} +fn main() {} diff --git a/tests/crashes/148511.rs b/tests/crashes/148511.rs new file mode 100644 index 0000000000000..69203ebeb2d21 --- /dev/null +++ b/tests/crashes/148511.rs @@ -0,0 +1,42 @@ +//@ known-bug: #148511 +//@ edition: 2021 +use std::any::Any; + +fn main() { + use_service(make_service()); + let _future = async {}; +} + +fn make_service() -> impl FooService, Response: Body> {} + +fn use_service(_service: S) +where + S: FooService, + ::Data: Any, +{ +} + +trait Service { + type Response: Body; +} + +impl Service for T { + type Response = (); +} + +trait FooService: Service {} + +impl FooService for T +where + T: Service, + Resp: Body, +{ +} + +trait Body { + type Data; +} + +impl Body for T { + type Data = (); +} diff --git a/tests/crashes/148629.rs b/tests/crashes/148629.rs new file mode 100644 index 0000000000000..926b4cc75c952 --- /dev/null +++ b/tests/crashes/148629.rs @@ -0,0 +1,13 @@ +//@ known-bug: #148629 +#![feature(with_negative_coherence)] +trait Foo { + type AssociatedType; +} + +impl Foo for [(); N] {} + +pub struct Happy; + +impl Foo for Happy {} + +impl Foo for Happy where <[(); N] as Foo>::AssociatedType: Clone {} diff --git a/tests/crashes/148630.rs b/tests/crashes/148630.rs new file mode 100644 index 0000000000000..7b857bbb408a1 --- /dev/null +++ b/tests/crashes/148630.rs @@ -0,0 +1,13 @@ +//@ known-bug: #148630 +#![feature(unboxed_closures)] + +trait Tr {} +trait Foo { + fn foo() -> impl Sized + where + for<'a> <() as FnOnce<&'a i32>>::Output: Tr, + { + } +} + +fn main() {} diff --git a/tests/crashes/148632.rs b/tests/crashes/148632.rs new file mode 100644 index 0000000000000..c77ff5e8d7954 --- /dev/null +++ b/tests/crashes/148632.rs @@ -0,0 +1,10 @@ +//@ known-bug: #148632 +trait D {} + +trait Project { + const SELF: dyn D; +} + +fn main() { + let _: &dyn Project; +} diff --git a/tests/crashes/148890.rs b/tests/crashes/148890.rs new file mode 100644 index 0000000000000..6e78ca6717f15 --- /dev/null +++ b/tests/crashes/148890.rs @@ -0,0 +1,6 @@ +//@ known-bug: #148890 +impl std::ops::Neg for u128 {} + +fn foo(-128..=127: u128) {} + +fn main() {} diff --git a/tests/crashes/148891.rs b/tests/crashes/148891.rs new file mode 100644 index 0000000000000..75534440d4902 --- /dev/null +++ b/tests/crashes/148891.rs @@ -0,0 +1,14 @@ +//@ known-bug: #148891 +macro_rules! values { + ($inner:ty) => { + #[derive(Debug)] + pub enum TokenKind { + #[cfg(test)] + STRING ([u8; $inner]), + } + }; +} + +values!(String); + +pub fn main() {} diff --git a/tests/crashes/149162.rs b/tests/crashes/149162.rs new file mode 100644 index 0000000000000..33532fe365ba4 --- /dev/null +++ b/tests/crashes/149162.rs @@ -0,0 +1,30 @@ +//@ known-bug: #149162 +use std::marker::PhantomData; +pub trait ViewArgument { + type Params<'a>; +} + +impl ViewArgument for () { + type Params<'a> = (); +} + +pub trait View {} + +pub fn buttons() -> Option { + Some(()).map(|()| text_button(|()| {})) +} +pub fn text_button( + _: impl Fn(::Params<'_>), +) -> Button { + Button { + callback: || (), + phantom: PhantomData, + } +} +pub struct Button { + pub callback: F, + pub phantom: PhantomData, +} + +impl View for Button<(), F> {} +fn main() {} diff --git a/tests/crashes/149703.rs b/tests/crashes/149703.rs new file mode 100644 index 0000000000000..40d78484307a6 --- /dev/null +++ b/tests/crashes/149703.rs @@ -0,0 +1,12 @@ +//@ known-bug: #149703 +#![feature(const_trait_impl)] +trait Z { + type Assoc; +} +struct A; +impl Z for T { + type Assoc = (); +} +impl From<::Assoc> for T {} + +fn main() {} diff --git a/tests/crashes/149748.rs b/tests/crashes/149748.rs new file mode 100644 index 0000000000000..99e4a6ae15b80 --- /dev/null +++ b/tests/crashes/149748.rs @@ -0,0 +1,13 @@ +//@ known-bug: #149748 +//@ edition: 2024 +//@ compile-flags: -Zmir-enable-passes=+Inline -Zmir-enable-passes=+ReferencePropagation -Zlint-mir + +#![feature(gen_blocks)] +gen fn foo(z: i32) -> i32 { + yield z; + z; +} +pub fn main() { + let mut iter = foo(3); + assert_eq!(iter.next(), Some(3)) +} diff --git a/tests/crashes/150040.rs b/tests/crashes/150040.rs new file mode 100644 index 0000000000000..bf5b7bbf9b536 --- /dev/null +++ b/tests/crashes/150040.rs @@ -0,0 +1,7 @@ +//@ known-bug: #150040 + +fn main() { + let [(ref a, b), x]; + a = ""; + b = 5; +} diff --git a/tests/crashes/150049.rs b/tests/crashes/150049.rs new file mode 100644 index 0000000000000..fa39785779c0f --- /dev/null +++ b/tests/crashes/150049.rs @@ -0,0 +1,12 @@ +//@ known-bug: #150049 +#![feature(min_generic_const_args)] +#![feature(inherent_associated_types)] +struct Foo<'a> { + x: &'a (), +} + +impl<'a> Foo<'a> { + fn foo(_: [u8; Foo::X]) { std::mem::transmute([4]) } +} + +fn main() {} diff --git a/tests/crashes/150128.rs b/tests/crashes/150128.rs new file mode 100644 index 0000000000000..28372ee7935f7 --- /dev/null +++ b/tests/crashes/150128.rs @@ -0,0 +1,8 @@ +//@ known-bug: #150128 +fn main() { + match 0 { + _ => || (), + _ => || (), + _ => (|| ()) as unsafe fn, + }; +} diff --git a/tests/crashes/150296.rs b/tests/crashes/150296.rs new file mode 100644 index 0000000000000..f7d5a54ca9b65 --- /dev/null +++ b/tests/crashes/150296.rs @@ -0,0 +1,14 @@ +//@ known-bug: #150296 +#[derive(PartialEq)] +pub struct Thing; + +impl Thing { + const A: Self = Thing; +} + +fn broken(x: Thing) { + match x { + >::A => {} + _ => {} + } +} diff --git a/tests/crashes/150387.rs b/tests/crashes/150387.rs new file mode 100644 index 0000000000000..b29ce143fb4ea --- /dev/null +++ b/tests/crashes/150387.rs @@ -0,0 +1,13 @@ +//@ known-bug: #150387 +#![feature(min_specialization)] +#![allow(dead_code)] + +struct Thing(T) where [T]: Sized; + +impl Drop for Thing where [T]: Sized { + default fn drop(&mut self) {} +} +impl Drop for Thing where [T]: Sized { + fn drop(&mut self) {} +} +fn main() {} diff --git a/tests/crashes/150403.rs b/tests/crashes/150403.rs new file mode 100644 index 0000000000000..bc00fc32135fc --- /dev/null +++ b/tests/crashes/150403.rs @@ -0,0 +1,26 @@ +//@ known-bug: #150403 +#![feature(non_lifetime_binders)] + +trait A { + type GAT: A; + fn foo(self, t: T) -> Self::GAT + where + Self: Sized; +} + +trait B: A where + for Self::GAT: B, +{ + fn bar(self) -> Self::GAT + where + Self: Sized; + + fn baz(self, t: T) -> Self::GAT + where + Self: Sized, + { + self.foo(t).bar() + } +} + +fn main() {} diff --git a/tests/crashes/150517.rs b/tests/crashes/150517.rs new file mode 100644 index 0000000000000..de14628547d28 --- /dev/null +++ b/tests/crashes/150517.rs @@ -0,0 +1,15 @@ +//@ known-bug: #150517 +trait Stream { + type Item; + fn next(self) -> (); +} +impl Stream for &'a () {} +impl<'a, A> Stream for <&A as Stream>::Item {} +trait StreamExt { + fn f(self) -> () + where + for<'b> &'b A: Stream, + { + self.next() + } +} diff --git a/tests/crashes/150545.rs b/tests/crashes/150545.rs new file mode 100644 index 0000000000000..04e084919d4f2 --- /dev/null +++ b/tests/crashes/150545.rs @@ -0,0 +1,8 @@ +//@ known-bug: #150545 +#![feature(non_lifetime_binders)] +trait Foo: for Bar { + type Item; + fn next(self) -> Self::Item; +} +trait Bar {} +fn main() {} diff --git a/tests/crashes/150749.rs b/tests/crashes/150749.rs new file mode 100644 index 0000000000000..572dc67c27511 --- /dev/null +++ b/tests/crashes/150749.rs @@ -0,0 +1,12 @@ +//@ known-bug: #150749 +#![feature(min_generic_const_args)] + +trait CollectArray { + fn inner_array(); +} +impl CollectArray for () { + fn inner_array() { + let temp_ptr: [(); Self]; + } +} +fn main() {} diff --git a/tests/crashes/150969.rs b/tests/crashes/150969.rs new file mode 100644 index 0000000000000..daab977bce193 --- /dev/null +++ b/tests/crashes/150969.rs @@ -0,0 +1,7 @@ +//@ known-bug: #150969 +#![feature(generic_const_exprs)] +#![feature(min_generic_const_args)] + +fn pass_enum { + pass_enum::<{None}> +} diff --git a/tests/crashes/151069.rs b/tests/crashes/151069.rs new file mode 100644 index 0000000000000..ae6f4da5c6c70 --- /dev/null +++ b/tests/crashes/151069.rs @@ -0,0 +1,16 @@ +//@ known-bug: #151069 +trait Trait { + type Assoc2; +} +struct Bar; +impl Trait for Bar +where + ::Assoc2: Trait, +{ + type Assoc2 = (); +} +struct Foo { + field: ::Assoc2, +} +static FOO2: &Foo = 0; +fn main() {} diff --git a/tests/crashes/152205.rs b/tests/crashes/152205.rs new file mode 100644 index 0000000000000..d6e5a92df5d7a --- /dev/null +++ b/tests/crashes/152205.rs @@ -0,0 +1,20 @@ +//@ known-bug: #152205 +#![deny(rust_2021_incompatible_closure_captures)] +struct Foo; +struct S; +impl Drop for S { + fn drop(&mut self) {} +} +struct U(::Assoc); +fn test_precise_analysis_long_path(u: U) { + let _ = || { + let _x = u.0.0; + }; +} +trait NewTrait { + type Assoc; +} +impl NewTrait for Foo { + type Assoc = (S,); +} +fn main(){} diff --git a/tests/crashes/152295.rs b/tests/crashes/152295.rs new file mode 100644 index 0000000000000..85e23f0f41037 --- /dev/null +++ b/tests/crashes/152295.rs @@ -0,0 +1,11 @@ +//@ known-bug: #152295 +#![feature(type_alias_impl_trait)] +type Tait = impl Sized; +trait Foo: Bar {} +trait Bar { + fn bar(&self); +} +fn test_correct2(x: &dyn Foo) { + x.bar(); +} +fn main() {} diff --git a/tests/crashes/154556.rs b/tests/crashes/154556.rs new file mode 100644 index 0000000000000..d858588c11ec2 --- /dev/null +++ b/tests/crashes/154556.rs @@ -0,0 +1,25 @@ +//@ known-bug: #154556 +#![feature(generic_const_exprs)] + +pub trait Foo { + // Has to take self or a reference to self to ICE. + fn eq(self); +} + +pub trait Bar { + const NUMBER: usize; +} + +impl Foo for T +where + [(); T::NUMBER]: Sized, // Bound required for ICE +{ + fn eq(self) {} +} + +// As long as the method called below has the same name as the one defined in Foo, the ICE happens. +fn baz() { + ().eq(&()); +} + +fn main() {} diff --git a/tests/crashes/154964.rs b/tests/crashes/154964.rs new file mode 100644 index 0000000000000..d44700f5a92b3 --- /dev/null +++ b/tests/crashes/154964.rs @@ -0,0 +1,9 @@ +//@ known-bug: #154964 +//@ edition: 2021 +//@ compile-flags: -Zprint-type-sizes +fn main() { + |foo: Foo<_>| async { foo.bar }; +} +struct Foo { + bar: (), +} diff --git a/tests/crashes/157568.rs b/tests/crashes/157568.rs new file mode 100644 index 0000000000000..627bfbadf6bc4 --- /dev/null +++ b/tests/crashes/157568.rs @@ -0,0 +1,10 @@ +//@ known-bug: #157568 +//@ compile-flags: -Zpolonius +#![warn(rust_2024_compatibility)] +pub struct F; +impl std::fmt::Debug for F { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("F").finish_non_exhaustive() + } +} +fn main() {} diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 8989903939d42..4a028248d6633 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [Unevaluated(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 8989903939d42..4a028248d6633 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -2,7 +2,7 @@ | User Type Annotations | 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [Unevaluated(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [AliasConst(No, Alias { kind: Anon { def_id: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}) }, args: [], .. })], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs new file mode 100644 index 0000000000000..0294308d199f2 --- /dev/null +++ b/tests/run-make/wasm-compiler-builtins-object-arch/rmake.rs @@ -0,0 +1,54 @@ +//! Regression test for . +//! +//! The prebuilt `libcompiler_builtins` rlib bundled in the wasm sysroot must +//! contain wasm object files — never host ELF/Mach-O/COFF. Bootstrap could +//! previously pick the host C toolchain for compiler-rt fallbacks on wasm +//! targets and silently embed host objects into the wasm sysroot +//! (fixed in rust-lang/rust#137457). + +//@ only-wasm32 + +use run_make_support::object::read::Object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::{self, Architecture}; +use run_make_support::{has_extension, has_prefix, rfs, rustc, shallow_find_files}; + +fn main() { + let libdir = rustc().print("target-libdir").run().stdout_utf8(); + let libdir = libdir.trim(); + + let rlibs = shallow_find_files(libdir, |path| { + has_prefix(path, "libcompiler_builtins") && has_extension(path, "rlib") + }); + assert!(!rlibs.is_empty(), "no libcompiler_builtins rlib found in {libdir}"); + + let data = rfs::read(&rlibs[0]); + let archive = ArchiveFile::parse(&*data).unwrap(); + + let mut checked = 0usize; + for member in archive.members() { + let member = member.unwrap(); + let name = std::str::from_utf8(member.name()).unwrap_or(""); + if name.ends_with(".rmeta") || name.ends_with(".rmeta-link") { + continue; + } + let obj_data = member.data(&*data).unwrap(); + let obj = object::File::parse(obj_data).unwrap_or_else(|e| { + panic!("failed to parse member `{name}` in compiler_builtins rlib: {e}") + }); + let arch = obj.architecture(); + assert!( + matches!(arch, Architecture::Wasm32 | Architecture::Wasm64), + "object `{name}` in compiler_builtins rlib has architecture {arch:?}, \ + expected wasm — see rust-lang/rust#132802", + ); + checked += 1; + } + + assert!( + checked > 0, + "no object members found in compiler_builtins rlib at {} — \ + archive should always contain object files", + rlibs[0].display(), + ); +} diff --git a/tests/run-make/wasm-panic-small/rmake.rs b/tests/run-make/wasm-panic-small/rmake.rs index ea0b6faf037c5..f6504830a5263 100644 --- a/tests/run-make/wasm-panic-small/rmake.rs +++ b/tests/run-make/wasm-panic-small/rmake.rs @@ -24,5 +24,5 @@ fn test(cfg: &str) { let bytes = rfs::read("foo.wasm"); println!("{}", bytes.len()); - assert!(bytes.len() < 40_000, "bytes len was: {}", bytes.len()); + assert!(bytes.len() < 45_000, "bytes len was: {}", bytes.len()); } diff --git a/tests/rustdoc-html/cfg-bool.rs b/tests/rustdoc-html/doc-cfg/cfg-bool.rs similarity index 100% rename from tests/rustdoc-html/cfg-bool.rs rename to tests/rustdoc-html/doc-cfg/cfg-bool.rs diff --git a/tests/rustdoc-html/doc-cfg/decl-macro.rs b/tests/rustdoc-html/doc-cfg/decl-macro.rs index e97da8647a6ab..ce4cd36d1367e 100644 --- a/tests/rustdoc-html/doc-cfg/decl-macro.rs +++ b/tests/rustdoc-html/doc-cfg/decl-macro.rs @@ -45,7 +45,7 @@ pub mod auto_cfg_disabled { } #[cfg(feature = "routing")] -#[doc(auto_cfg(hide(feature = "routing")))] +#[doc(auto_cfg(hide(feature, values("routing"))))] pub mod auto_cfg_hidden { //@ count 'foo/macro.hidden_cfg_macro.html' '//*[@class="stab portability"]' 0 #[macro_export] @@ -55,9 +55,9 @@ pub mod auto_cfg_hidden { } #[cfg(feature = "routing")] -#[doc(auto_cfg(hide(feature = "routing")))] +#[doc(auto_cfg(hide(feature, values("routing"))))] pub mod auto_cfg_shown { - #[doc(auto_cfg(show(feature = "routing")))] + #[doc(auto_cfg(show(feature, values("routing"))))] pub mod inner { //@ has 'foo/macro.shown_cfg_macro.html' '//*[@class="stab portability"]' 'Available on crate feature routing only.' #[macro_export] diff --git a/tests/rustdoc-html/doc_auto_cfg.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs similarity index 90% rename from tests/rustdoc-html/doc_auto_cfg.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs index a1903e1a0ca3d..a33c5830157fb 100644 --- a/tests/rustdoc-html/doc_auto_cfg.rs +++ b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-2.rs @@ -2,7 +2,7 @@ #![crate_name = "foo"] #![feature(doc_cfg)] -#![doc(auto_cfg(hide(feature = "hidden")))] +#![doc(auto_cfg(hide(feature, values("hidden"))))] //@ has 'foo/index.html' //@ !has - '//*[@class="stab portability"]' 'Non-moustache' @@ -17,18 +17,18 @@ pub mod m { pub struct A; //@ has 'foo/m/inner/index.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' - #[doc(auto_cfg(show(feature = "hidden")))] + #[doc(auto_cfg(show(feature, values("hidden"))))] pub mod inner { //@ has 'foo/m/inner/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' pub struct B; //@ count 'foo/m/inner/struct.A.html' '//*[@class="stab portability"]' 0 - #[doc(auto_cfg(hide(feature = "hidden")))] + #[doc(auto_cfg(hide(feature, values("hidden"))))] pub struct A; } //@ has 'foo/m/struct.B.html' '//*[@class="stab portability"]' 'Available on non-crate feature hidden only.' - #[doc(auto_cfg(show(feature = "hidden")))] + #[doc(auto_cfg(show(feature, values("hidden"))))] pub struct B; } @@ -61,7 +61,7 @@ pub mod n { // Should re-enable `auto_cfg` and make `moustache` listed. //@ has 'foo/n/struct.Z.html' '//*[@class="stab portability"]' \ // 'Available on non-crate feature moustache only.' - #[doc(auto_cfg(hide(feature = "hidden")))] + #[doc(auto_cfg(hide(feature, values("hidden"))))] pub struct Z; } diff --git a/tests/rustdoc-html/doc-auto-cfg-public-in-private.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg-public-in-private.rs similarity index 100% rename from tests/rustdoc-html/doc-auto-cfg-public-in-private.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg-public-in-private.rs diff --git a/tests/rustdoc-html/doc-auto-cfg.rs b/tests/rustdoc-html/doc-cfg/doc-auto-cfg.rs similarity index 100% rename from tests/rustdoc-html/doc-auto-cfg.rs rename to tests/rustdoc-html/doc-cfg/doc-auto-cfg.rs diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs b/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs index e919206d3a478..72c33d209c7a1 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs +++ b/tests/rustdoc-html/doc-cfg/doc-cfg-hide.rs @@ -1,7 +1,7 @@ #![crate_name = "oud"] #![feature(doc_cfg)] -#![doc(auto_cfg(hide(feature = "solecism")))] +#![doc(auto_cfg(hide(feature, values("solecism"))))] //@ has 'oud/struct.Solecism.html' //@ count - '//*[@class="stab portability"]' 0 diff --git a/tests/rustdoc-html/doc_auto_cfg_reexports.rs b/tests/rustdoc-html/doc-cfg/doc_auto_cfg_reexports.rs similarity index 100% rename from tests/rustdoc-html/doc_auto_cfg_reexports.rs rename to tests/rustdoc-html/doc-cfg/doc_auto_cfg_reexports.rs diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs new file mode 100644 index 0000000000000..d3842b4b9591c --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance-key-only.rs @@ -0,0 +1,26 @@ +// This test ensures that `auto_cfg(hide(key))` does not hide `key = value` and that +// `auto_cfg(hide(key, values(none())))` does the same. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +#![doc(auto_cfg(hide(meow)))] +#![doc(auto_cfg(hide(another_meow, values(none()))))] + +//@ has foo/fn.foo.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-another_meow' +#[cfg(not(meow))] +#[cfg(not(another_meow))] +#[cfg(not(blob))] +pub fn foo() {} + +//@ has foo/fn.bar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-another_meow=lol' +#[cfg(not(meow = "lol"))] +#[cfg(not(another_meow = "lol"))] +#[cfg(not(blob = "lol"))] +pub fn bar() {} diff --git a/tests/rustdoc-html/doc-cfg/hide-inheritance.rs b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs new file mode 100644 index 0000000000000..3f0e2ffca2136 --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/hide-inheritance.rs @@ -0,0 +1,179 @@ +// This test ensures that using `auto_cfg(show(key))` works correctly. + +#![feature(doc_cfg)] +#![crate_name = "foo"] + +#![doc(auto_cfg(hide(meow)))] +#![doc(auto_cfg(hide(meow, values("lol"))))] + +//@ has foo/fn.foo.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' +#[cfg(not(meow))] +#[cfg(not(blob))] +pub fn foo() {} + +//@ has foo/fn.bar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' +//@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' +#[cfg(not(meow = "lol"))] +#[cfg(not(blob = "lol"))] +pub fn bar() {} + +//@ has foo/fn.babar.html +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' +//@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' +#[cfg(not(meow = "lola"))] +#[cfg(not(blob = "lola"))] +pub fn babar() {} + +pub mod sub { + // We show again `meow`, however `meow="lol"` should still be hidden. + #![doc(auto_cfg(show(meow)))] + + //@ has foo/sub/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +pub mod sub2 { + // We show again `meow = "lol`, however `meow` should still be hidden. + #![doc(auto_cfg(show(meow, values("lol"))))] + + //@ has foo/sub2/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub2/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub2/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +pub mod sub3 { + // We show again `meow = "lol`, but by using `any()` this time. + #![doc(auto_cfg(show(meow, values(any()))))] + + //@ has foo/sub3/fn.foo.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow' + #[cfg(not(meow))] + #[cfg(not(blob))] + pub fn foo() {} + + //@ has foo/sub3/fn.bar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lol' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lol' + #[cfg(not(meow = "lol"))] + #[cfg(not(blob = "lol"))] + pub fn bar() {} + + //@ has foo/sub3/fn.babar.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-blob=lola' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-meow=lola' + #[cfg(not(meow = "lola"))] + #[cfg(not(blob = "lola"))] + pub fn babar() {} +} + +// This test the mix of values and `none()`. +#[doc(auto_cfg( + hide(bla, values(none(), "tic")), + hide(alb, values(none())), +))] +pub mod sub4 { + //@ has foo/sub4/fn.foo.html + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=tic' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + #[cfg(not(bla))] + #[cfg(not(bla = "tic"))] + #[cfg(not(alb))] + pub fn foo() {} + + //@ has foo/sub4/fn.foo2.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=tic' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + #[doc(auto_cfg( + show(bla, values(none(), "tic")), + show(alb, values(none())), + ))] + #[cfg(not(bla))] + #[cfg(not(bla = "tic"))] + #[cfg(not(alb))] + pub fn foo2() {} +} + +// This test the mix of `any()` and values. +#[doc(auto_cfg( + hide(alb, values(any())), + hide(bla, values(any())), + show(bla, values("top")), +))] +pub mod sub5 { + //@ has foo/sub5/fn.foo.html + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo() {} + + //@ has foo/sub5/fn.foo2.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[doc(auto_cfg( + show(alb, values(none())), + hide(bla, values("top")), + ))] + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo2() {} + + //@ has foo/sub5/fn.foo3.html + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-alb' + //@ !has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=top' + //@ has - '//*[@class="item-info"]/*[@class="stab portability"]' 'non-bla=a' + #[doc(auto_cfg( + show(alb, values(any())), + show(bla, values(any())), + hide(bla, values(none(), "top")), + ))] + #[cfg(not(alb))] + #[cfg(not(bla = "top"))] + #[cfg(not(bla = "a"))] + pub fn foo3() {} +} diff --git a/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs b/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs index fbd96cc46d800..4329d8e06dfc5 100644 --- a/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs +++ b/tests/rustdoc-html/doc-cfg/trait-impls-manual.rs @@ -3,7 +3,7 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide( - target_pointer_width = "64", + target_pointer_width, values("64"), )))] #![crate_name = "foo"] @@ -36,7 +36,7 @@ pub struct X; //@count - '//*[@id="impl-Trait-for-X"]' 1 //@count - '//*[@id="impl-Trait-for-X"]/*[@class="item-info"]' 0 #[doc(cfg(any(target_pointer_width = "64", target_arch = "wasm32")))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp { impl super::Trait for super::X { fn f(&self) {} } } @@ -64,7 +64,7 @@ pub struct Y; //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]' 1 //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]/*[@class="item-info"]' 0 #[doc(cfg(any(target_pointer_width = "64", target_arch = "wasm32")))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp4 { impl super::Y { pub fn plain_auto() {} } } diff --git a/tests/rustdoc-html/doc-cfg/trait-impls.rs b/tests/rustdoc-html/doc-cfg/trait-impls.rs index 581d171123d00..9ea6490ae8e21 100644 --- a/tests/rustdoc-html/doc-cfg/trait-impls.rs +++ b/tests/rustdoc-html/doc-cfg/trait-impls.rs @@ -3,7 +3,7 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide( - target_pointer_width = "64", + target_pointer_width, values("64"), )))] #![crate_name = "foo"] @@ -36,7 +36,7 @@ pub struct X; //@count - '//*[@id="impl-Trait-for-X"]' 1 //@count - '//*[@id="impl-Trait-for-X"]/*[@class="item-info"]' 0 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp { impl super::Trait for super::X { fn f(&self) {} } } @@ -64,7 +64,7 @@ pub struct Y; //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]' 1 //@count - '//*[@id="implementations-list"]/*[@class="impl-items"]/*[@class="item-info"]' 0 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))] -#[doc(auto_cfg(hide(target_arch = "wasm32")))] +#[doc(auto_cfg(hide(target_arch, values("wasm32"))))] mod imp4 { impl super::Y { pub fn plain_auto() {} } } diff --git a/tests/rustdoc-html/impl/impl-fundamental-nesting.rs b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs new file mode 100644 index 0000000000000..25aed33e53514 --- /dev/null +++ b/tests/rustdoc-html/impl/impl-fundamental-nesting.rs @@ -0,0 +1,58 @@ +// Followup to https://github.com/rust-lang/rust/issues/92940 and impl-box.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +#![crate_name = "foo"] + +use std::pin::Pin; + +pub struct Local; + +//@ has 'foo/struct.Local.html' + +// Nested fundamental + foreign Self. +//@ has '-' '//*[@id="impl-From%3CBox%3CLocal%3E%3E-for-String"]' 'impl From> for String' +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} + +// Also test with Pin. +//@ has '-' '//*[@id="impl-From%3CPin%3CLocal%3E%3E-for-u8"]' 'impl From> for u8' +impl From> for u8 { + fn from(_: Pin) -> u8 { + 0 + } +} + +// Reference to a fundamental wrapper. +//@ has '-' '//*[@id="impl-From%3C%26Box%3CLocal%3E%3E-for-u16"]' "impl<'a> From<&'a Box> for u16" +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} + +// Nested two levels deep in Self. +//@ has '-' '//*[@id="impl-From%3Cu32%3E-for-Box%3CBox%3CLocal%3E%3E"]' 'impl From for Box>' +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} + +// Mixed fundamental wrappers in Self. +//@ has '-' '//*[@id="impl-From%3Cu64%3E-for-Pin%3CBox%3CLocal%3E%3E"]' 'impl From for Pin>' +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} + +// A non-fundamental wrapper must not show up on Local's page, but it should still be listed on the +// trait's own page. +pub trait Marker {} +//@ has 'foo/trait.Marker.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +//@ !has 'foo/struct.Local.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec' +impl Marker for Vec {} diff --git a/tests/rustdoc-json/attrs/stability/default_body.rs b/tests/rustdoc-json/attrs/stability/default_body.rs new file mode 100644 index 0000000000000..5a70e47d6c505 --- /dev/null +++ b/tests/rustdoc-json/attrs/stability/default_body.rs @@ -0,0 +1,76 @@ +#![feature(staged_api, rustc_attrs, associated_type_defaults)] + +#[stable(feature = "default_body_trait_feature", since = "1.0.0")] +pub trait TraitWithDefaults { + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with unstable default body')].inner.function.default_unstable.feature" '"method_default_body_feature"' + //@ is "$.index[?(@.docs=='method with unstable default body')].attrs" [] + /// method with unstable default body + #[stable(feature = "default_body_method_feature", since = "1.1.0")] + #[rustc_default_body_unstable(feature = "method_default_body_feature", issue = "none")] + fn method_with_unstable_default() {} + + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.has_body" false + //@ is "$.index[?(@.docs=='required method without default body')].inner.function.default_unstable" null + /// required method without default body + #[stable(feature = "required_method_feature", since = "1.2.0")] + fn required_method(); + + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.has_body" true + //@ is "$.index[?(@.docs=='method with stable default body')].inner.function.default_unstable" null + /// method with stable default body + #[stable(feature = "stable_default_method_feature", since = "1.3.0")] + fn method_with_stable_default() {} + + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.value" '"0"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].inner.assoc_const.default_unstable.feature" '"assoc_const_default_value_feature"' + //@ is "$.index[?(@.docs=='associated constant with unstable default value')].attrs" [] + /// associated constant with unstable default value + #[stable(feature = "assoc_const_with_unstable_default_feature", since = "1.4.0")] + #[rustc_default_body_unstable(feature = "assoc_const_default_value_feature", issue = "none")] + const UNSTABLE_DEFAULT_CONST: usize = 0; + + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.value" null + //@ is "$.index[?(@.docs=='required associated constant')].inner.assoc_const.default_unstable" null + /// required associated constant + #[stable(feature = "required_assoc_const_feature", since = "1.5.0")] + const REQUIRED_CONST: usize; + + //@ is "$.index[?(@.docs=='associated type with unstable default type')].inner.assoc_type.default_unstable.feature" '"assoc_type_default_type_feature"' + //@ is "$.index[?(@.docs=='associated type with unstable default type')].attrs" [] + /// associated type with unstable default type + #[stable(feature = "assoc_type_with_unstable_default_feature", since = "1.6.0")] + #[rustc_default_body_unstable(feature = "assoc_type_default_type_feature", issue = "none")] + type UnstableDefaultType = usize; + + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.type" null + //@ is "$.index[?(@.docs=='required associated type')].inner.assoc_type.default_unstable" null + /// required associated type + #[stable(feature = "required_assoc_type_feature", since = "1.7.0")] + type RequiredType; +} + +#[stable(feature = "default_body_impl_target_feature", since = "2.0.0")] +pub struct ImplTarget; + +// Impl items provide their own definitions, so they do not use the trait's unstable defaults. +#[stable(feature = "default_body_impl_feature", since = "2.1.0")] +impl TraitWithDefaults for ImplTarget { + //@ is "$.index[?(@.docs=='impl override for unstable default body')].inner.function.default_unstable" null + /// impl override for unstable default body + fn method_with_unstable_default() {} + + fn required_method() {} + + //@ is "$.index[?(@.docs=='impl override for unstable default value')].inner.assoc_const.default_unstable" null + /// impl override for unstable default value + const UNSTABLE_DEFAULT_CONST: usize = 1; + + const REQUIRED_CONST: usize = 2; + + //@ is "$.index[?(@.docs=='impl override for unstable default type')].inner.assoc_type.default_unstable" null + /// impl override for unstable default type + type UnstableDefaultType = u8; + + type RequiredType = (); +} diff --git a/tests/rustdoc-json/doc_cfg.rs b/tests/rustdoc-json/doc_cfg.rs new file mode 100644 index 0000000000000..0033968305bd0 --- /dev/null +++ b/tests/rustdoc-json/doc_cfg.rs @@ -0,0 +1,5 @@ +#![feature(doc_cfg)] + +//@ is "$.index[?(@.name=='f')].attrs" '[{"other": "#[doc(auto_cfg(hide(bar, values(none())), hide(blob, values(\"a\", \"14\"))))]"}]' +#[doc(auto_cfg(hide(bar, values(none())), hide(blob, values("a", "14")),))] +pub fn f() {} diff --git a/tests/rustdoc-json/impls/fundamental_nesting.rs b/tests/rustdoc-json/impls/fundamental_nesting.rs new file mode 100644 index 0000000000000..a6aefb94f961f --- /dev/null +++ b/tests/rustdoc-json/impls/fundamental_nesting.rs @@ -0,0 +1,57 @@ +// Companion to tests/rustdoc-html/impl/impl-fundamental-nesting.rs. +// +// Show traits implemented on fundamental types that wrap local ones: nested edition. + +use std::pin::Pin; + +pub struct Local; + +// Nested fundamental + foreign Self. +/// from box local +impl From> for String { + fn from(_: Box) -> String { + String::new() + } +} +//@ set from_box_local = "$.index[?(@.docs=='from box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_box_local + +// Reference to a fundamental wrapper. +/// from ref box local +impl<'a> From<&'a Box> for u16 { + fn from(_: &'a Box) -> u16 { + 0 + } +} +//@ set from_ref_box_local = "$.index[?(@.docs=='from ref box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_ref_box_local + +// Nested two levels deep in Self. +/// u32 for box box local +impl From for Box> { + fn from(_: u32) -> Box> { + Box::new(Box::new(Local)) + } +} +//@ set u32_for_box_box_local = "$.index[?(@.docs=='u32 for box box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u32_for_box_box_local + +// Mixed fundamental wrappers in Self. +/// u64 for pin box local +impl From for Pin> { + fn from(_: u64) -> Pin> { + Pin::new(Box::new(Local)) + } +} +//@ set u64_for_pin_box_local = "$.index[?(@.docs=='u64 for pin box local')].id" +//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u64_for_pin_box_local + +// A non-fundamental wrapper must not associate the impl with Local, but the impl must still be +// listed on the trait itself. +pub trait Marker {} + +/// marker for vec local +impl Marker for Vec {} +//@ set marker_for_vec_local = "$.index[?(@.docs=='marker for vec local')].id" +//@ !has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $marker_for_vec_local +//@ has "$.index[?(@.name=='Marker')].inner.trait.implementations[*]" $marker_for_vec_local diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.rs b/tests/rustdoc-ui/cfg-hide-show-conflict.rs deleted file mode 100644 index 8e98b95c85bb9..0000000000000 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![feature(doc_cfg)] -#![doc(auto_cfg(hide(target_os = "linux")))] -#![doc(auto_cfg(show(windows, target_os = "linux")))] //~ ERROR diff --git a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr index 22231e82cd7bf..b43b8104cdc12 100644 --- a/tests/rustdoc-ui/cfg-hide-show-conflict.stderr +++ b/tests/rustdoc-ui/cfg-hide-show-conflict.stderr @@ -1,14 +1,14 @@ error: same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item - --> $DIR/cfg-hide-show-conflict.rs:3:31 + --> $DIR/cfg-hide-show-conflict.rs:3:55 | -LL | #![doc(auto_cfg(show(windows, target_os = "linux")))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(show(windows), show(target_os, values("linux"))))] + | ^^^^^^^ | note: first change was here - --> $DIR/cfg-hide-show-conflict.rs:2:22 + --> $DIR/cfg-hide-show-conflict.rs:2:40 | -LL | #![doc(auto_cfg(hide(target_os = "linux")))] - | ^^^^^^^^^^^^^^^^^^^ +LL | #![doc(auto_cfg(hide(target_os, values("linux"))))] + | ^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index f615e96bbc6b5..ba9e22e755203 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -13,7 +13,5 @@ // Shouldn't lint #[doc(auto_cfg(hide(windows)))] #[doc(auto_cfg(hide(feature = "windows")))] -//~^ WARN unexpected `cfg` condition name: `feature` -#[doc(auto_cfg(hide(foo)))] -//~^ WARN unexpected `cfg` condition name: `foo` +//~^ ERROR `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 164e755de8ad7..aeb842c338c6b 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -30,19 +30,19 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] @@ -60,23 +60,11 @@ error: expected boolean for `#[doc(auto_cfg = ...)]` LL | #[doc(auto_cfg = "a")] | ^^^ -warning: unexpected `cfg` condition name: `feature` +error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or `values(...)` --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] | ^^^^^^^^^^^^^^^^^^^ - | - = help: to expect this configuration use `--check-cfg=cfg(feature, values("windows"))` - = note: see for more information about checking conditional configuration - -warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:17:21 - | -LL | #[doc(auto_cfg(hide(foo)))] - | ^^^ - | - = help: to expect this configuration use `--check-cfg=cfg(foo)` - = note: see for more information about checking conditional configuration -error: aborting due to 6 previous errors; 4 warnings emitted +error: aborting due to 7 previous errors; 2 warnings emitted diff --git a/tests/rustdoc-ui/doc-cfg-3.rs b/tests/rustdoc-ui/doc-cfg-3.rs new file mode 100644 index 0000000000000..39ae86086c882 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-3.rs @@ -0,0 +1,7 @@ +// Checks that you cannot have `any()` and values at the same time. + +#![deny(invalid_doc_attributes)] +#![feature(doc_cfg)] + +#![doc(auto_cfg(hide(target_os, values(any(), "linux"))))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values("linux", any()))))] //~ ERROR diff --git a/tests/rustdoc-ui/doc-cfg-3.stderr b/tests/rustdoc-ui/doc-cfg-3.stderr new file mode 100644 index 0000000000000..b9cd74388ecd9 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-3.stderr @@ -0,0 +1,22 @@ +error: `any()` was used when other values were provided + --> $DIR/doc-cfg-3.rs:6:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(any(), "linux"))))] + | ^^^^^ ------- value declared here + | +note: the lint level is defined here + --> $DIR/doc-cfg-3.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `any()` was used when other values were provided + --> $DIR/doc-cfg-3.rs:7:49 + | +LL | #![doc(auto_cfg(hide(target_os, values("linux", any()))))] + | ------- ^^^^^ + | | + | value declared here + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/doc-cfg-4.rs b/tests/rustdoc-ui/doc-cfg-4.rs new file mode 100644 index 0000000000000..75d0e30f76af9 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-4.rs @@ -0,0 +1,7 @@ +// Checks that you cannot have any item inside `any()` and `none()`. + +#![deny(invalid_doc_attributes)] +#![feature(doc_cfg)] + +#![doc(auto_cfg(hide(target_os, values(any("linux")))))] //~ ERROR +#![doc(auto_cfg(hide(target_os, values(none("linux")))))] //~ ERROR diff --git a/tests/rustdoc-ui/doc-cfg-4.stderr b/tests/rustdoc-ui/doc-cfg-4.stderr new file mode 100644 index 0000000000000..5de5e3b806594 --- /dev/null +++ b/tests/rustdoc-ui/doc-cfg-4.stderr @@ -0,0 +1,20 @@ +error: `#![doc(auto_cfg(any(...)))]` only accepts identifiers or `values(...)` + --> $DIR/doc-cfg-4.rs:6:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(any("linux")))))] + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/doc-cfg-4.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `#![doc(auto_cfg(none(...)))]` only accepts identifiers or `values(...)` + --> $DIR/doc-cfg-4.rs:7:40 + | +LL | #![doc(auto_cfg(hide(target_os, values(none("linux")))))] + | ^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index abaea97192808..5f8b5a29a4402 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -6,5 +6,5 @@ //~| ERROR #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR -#[doc(auto_cfg(hide(foo::bar)))] +#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index f3af95528b2d2..35d14f913b725 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -62,6 +62,15 @@ LL - #[doc(cfg(foo, bar))] LL + #[doc(cfg(any(foo, bar)))] | -error: aborting due to 4 previous errors +error[E0565]: malformed `doc` attribute input + --> $DIR/doc-cfg.rs:9:1 + | +LL | #[doc(auto_cfg(hide(foo::bar)))] + | ^^^^^^^^^^^^^^^^^^^^--------^^^^ + | | + | expected a valid identifier here + +error: aborting due to 5 previous errors -For more information about this error, try `rustc --explain E0805`. +Some errors have detailed explanations: E0565, E0805. +For more information about an error, try `rustc --explain E0565`. diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs b/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs new file mode 100644 index 0000000000000..f1ee59e64c93c --- /dev/null +++ b/tests/rustdoc-ui/lints/doc_cfg_hide-2.rs @@ -0,0 +1,4 @@ +#![feature(doc_cfg)] + +#![deny(invalid_doc_attributes)] +#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr new file mode 100644 index 0000000000000..68503ebf132fa --- /dev/null +++ b/tests/rustdoc-ui/lints/doc_cfg_hide-2.stderr @@ -0,0 +1,11 @@ +error[E0565]: malformed `doc` attribute input + --> $DIR/doc_cfg_hide-2.rs:4:1 + | +LL | #![doc(auto_cfg(hide(not(windows))))] + | ^^^^^^^^^^^^^^^^^^^^^---^^^^^^^^^^^^^ + | | + | expected a valid identifier here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 6c190f9befac8..28c0c4a89cebd 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -2,4 +2,3 @@ #![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR -#![doc(auto_cfg(hide(not(windows))))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index a5ec8fdf5d34e..4b984feb3d5a1 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -16,11 +16,5 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items LL | #![doc(auto_cfg(hide))] | ^^^^ -error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc_cfg_hide.rs:5:22 - | -LL | #![doc(auto_cfg(hide(not(windows))))] - | ^^^^^^^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/README.md b/tests/ui/README.md index 6ffef1a1cd699..d342f393d0550 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1351,6 +1351,10 @@ Generic collection of tests for suggestions, when no more specific directories a **FIXME**: Some overlap with `tests/ui/did_you_mean/`, that directory should probably be moved under here. +## `tests/ui/supertrait-shadowing/` + +Tests for supertrait item shadowing (RFC 3624). + ## `tests/ui/svh/`: Strict Version Hash Tests on the *Strict Version Hash* (SVH, also known as the "crate hash"). diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs index 7c881134ccb4c..10e34fa19648a 100644 --- a/tests/ui/abi/bad-custom.rs +++ b/tests/ui/abi/bad-custom.rs @@ -100,7 +100,7 @@ async unsafe extern "custom" fn no_async_fn() { } fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() { - //~^ ERROR expected a `Fn()` closure, found `unsafe extern "custom" fn()` + //~^ ERROR expected an `Fn()` closure, found `unsafe extern "custom" fn()` f } diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr index d7c9cae35c7b3..46da91d80c993 100644 --- a/tests/ui/abi/bad-custom.stderr +++ b/tests/ui/abi/bad-custom.stderr @@ -184,7 +184,7 @@ LL + #[unsafe(naked)] LL | async unsafe extern "custom" fn no_async_fn() { | -error[E0277]: expected a `Fn()` closure, found `unsafe extern "custom" fn()` +error[E0277]: expected an `Fn()` closure, found `unsafe extern "custom" fn()` --> $DIR/bad-custom.rs:102:64 | LL | fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn() { diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 9bb7b28b4424e..0048fa5075db2 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -57,6 +57,12 @@ error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` | LL | asm!("{}", in(reg) foo => bar); | ^^ expected one of 7 possible tokens + | +help: you might have meant to write a "greater than or equal to" comparison + | +LL - asm!("{}", in(reg) foo => bar); +LL + asm!("{}", in(reg) foo >= bar); + | error: expected a path for argument to `sym` --> $DIR/parse-error.rs:32:24 diff --git a/tests/ui/issues/issue-28550.rs b/tests/ui/associated-types/nested-fnonce-output-projection.rs similarity index 81% rename from tests/ui/issues/issue-28550.rs rename to tests/ui/associated-types/nested-fnonce-output-projection.rs index 31c7057d06f8c..aa03ca10430ac 100644 --- a/tests/ui/issues/issue-28550.rs +++ b/tests/ui/associated-types/nested-fnonce-output-projection.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ run-pass + struct AT,T>(F::Output); struct BT,T>(A); diff --git a/tests/ui/associated-types/normalization-ice-issue-149746.rs b/tests/ui/associated-types/normalization-ice-issue-149746.rs index 8932d321b7e5b..29661092b5751 100644 --- a/tests/ui/associated-types/normalization-ice-issue-149746.rs +++ b/tests/ui/associated-types/normalization-ice-issue-149746.rs @@ -4,14 +4,14 @@ trait Owner { type Ty; } impl Owner for () { type Ty = T; } pub struct Warns { _significant_drop: <() as Owner>::Ty, - //~^ ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` field: String, } pub fn test(w: Warns) { - //~^ ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` _ = || w.field - //~^ ERROR expected a `FnMut()` closure, found `T` - //~| ERROR expected a `FnMut()` closure, found `T` + //~^ ERROR expected an `FnMut()` closure, found `T` + //~| ERROR expected an `FnMut()` closure, found `T` //~| WARN: changes to closure capture in Rust 2021 will affect drop order } fn main() {} diff --git a/tests/ui/associated-types/normalization-ice-issue-149746.stderr b/tests/ui/associated-types/normalization-ice-issue-149746.stderr index 7f983d6e14ae2..ae6f6bbbb52c9 100644 --- a/tests/ui/associated-types/normalization-ice-issue-149746.stderr +++ b/tests/ui/associated-types/normalization-ice-issue-149746.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:6:24 | LL | _significant_drop: <() as Owner>::Ty, @@ -35,7 +35,7 @@ help: add a dummy let to cause `w` to be fully captured LL | _ = || { let _ = &w; w.field } | +++++++++++++ + -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:12:9 | LL | _ = || w.field @@ -48,7 +48,7 @@ note: required by a bound in `Owner::Ty` LL | trait Owner { type Ty; } | ^^^^^^^ required by this bound in `Owner::Ty` -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:10:16 | LL | pub fn test(w: Warns) { @@ -61,7 +61,7 @@ note: required by a bound in `Owner::Ty` LL | trait Owner { type Ty; } | ^^^^^^^ required by this bound in `Owner::Ty` -error[E0277]: expected a `FnMut()` closure, found `T` +error[E0277]: expected an `FnMut()` closure, found `T` --> $DIR/normalization-ice-issue-149746.rs:12:9 | LL | _ = || w.field diff --git a/tests/ui/issues/issue-28828.rs b/tests/ui/associated-types/projection-as-type-alias.rs similarity index 60% rename from tests/ui/issues/issue-28828.rs rename to tests/ui/associated-types/projection-as-type-alias.rs index b5d7385cf28f0..b3e01be818e1f 100644 --- a/tests/ui/issues/issue-28828.rs +++ b/tests/ui/associated-types/projection-as-type-alias.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! This failed to compile as associated types aliases were not normalized. //@ run-pass + pub trait Foo { type Out; } diff --git a/tests/ui/issues/issue-28344.rs b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs similarity index 74% rename from tests/ui/issues/issue-28344.rs rename to tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs index 883a8397e5a9e..2c892fae7b930 100644 --- a/tests/ui/issues/issue-28344.rs +++ b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! Test we don't ICE on item resolution when associated type is omitted. //@ edition:2015 + use std::ops::BitXor; fn main() { diff --git a/tests/ui/issues/issue-28344.stderr b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr similarity index 88% rename from tests/ui/issues/issue-28344.stderr rename to tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr index 0dcb585033d07..b942ad8d26556 100644 --- a/tests/ui/issues/issue-28344.stderr +++ b/tests/ui/associated-types/resolve-method-with-missing-assoc-type.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-28344.rs:5:17 + --> $DIR/resolve-method-with-missing-assoc-type.rs:8:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ @@ -13,7 +13,7 @@ LL | let x: u8 = ::bitor(0 as u8, 0 as u8); | ++++ + error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified - --> $DIR/issue-28344.rs:5:17 + --> $DIR/resolve-method-with-missing-assoc-type.rs:8:17 | LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | ^^^^^^ @@ -24,7 +24,7 @@ LL | let x: u8 = BitXor::::bitor(0 as u8, 0 as u8); | +++++++++++++++++++++++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-28344.rs:10:13 + --> $DIR/resolve-method-with-missing-assoc-type.rs:13:13 | LL | let g = BitXor::bitor; | ^^^^^^ @@ -37,7 +37,7 @@ LL | let g = ::bitor; | ++++ + error[E0191]: the value of the associated type `Output` in `BitXor<_>` must be specified - --> $DIR/issue-28344.rs:10:13 + --> $DIR/resolve-method-with-missing-assoc-type.rs:13:13 | LL | let g = BitXor::bitor; | ^^^^^^ diff --git a/tests/ui/async-await/async-fn/impl-header.rs b/tests/ui/async-await/async-fn/impl-header.rs index 9af5f1f42a957..349a875d4a6c4 100644 --- a/tests/ui/async-await/async-fn/impl-header.rs +++ b/tests/ui/async-await/async-fn/impl-header.rs @@ -6,7 +6,7 @@ impl async Fn<()> for F {} //~^ ERROR `async` trait implementations are unsupported //~| ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `Fn` are experimental -//~| ERROR expected a `FnMut()` closure, found `F` +//~| ERROR expected an `FnMut()` closure, found `F` //~| ERROR not all trait items implemented, missing: `call` fn main() {} diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr index d0cf0d822f254..d1e3f884d02b3 100644 --- a/tests/ui/async-await/async-fn/impl-header.stderr +++ b/tests/ui/async-await/async-fn/impl-header.stderr @@ -30,7 +30,7 @@ LL | impl async Fn<()> for F {} | = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut()` closure, found `F` +error[E0277]: expected an `FnMut()` closure, found `F` --> $DIR/impl-header.rs:5:23 | LL | impl async Fn<()> for F {} diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index a997fbee1f2a0..ad8802bf1370f 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -12,7 +12,7 @@ note: an implementation of `PartialEq` might be missing for `T1` LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` @@ -34,7 +34,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate @@ -58,7 +58,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/core/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` diff --git a/tests/ui/issues/issue-27949.rs b/tests/ui/binop/binop-subtyping-lifetimes.rs similarity index 67% rename from tests/ui/issues/issue-27949.rs rename to tests/ui/binop/binop-subtyping-lifetimes.rs index e10c60085072f..6456768fed939 100644 --- a/tests/ui/issues/issue-27949.rs +++ b/tests/ui/binop/binop-subtyping-lifetimes.rs @@ -1,10 +1,11 @@ +//! Regression test for . +//! +//! At one time, the `==` operator (and other binary operators) did not +//! support subtyping during type checking, and would therefore require +//! LHS and RHS to be exactly identical--i.e. to have the same lifetimes. +//! +//! This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. //@ run-pass -// -// At one time, the `==` operator (and other binary operators) did not -// support subtyping during type checking, and would therefore require -// LHS and RHS to be exactly identical--i.e. to have the same lifetimes. -// -// This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. #[derive(Copy, Clone)] struct Input<'a> { diff --git a/tests/ui/issues/issue-27889.rs b/tests/ui/borrowck/enum-variants-share-field-name.rs similarity index 62% rename from tests/ui/issues/issue-27889.rs rename to tests/ui/borrowck/enum-variants-share-field-name.rs index 8737f03db1a59..d357d805ea22a 100644 --- a/tests/ui/issues/issue-27889.rs +++ b/tests/ui/borrowck/enum-variants-share-field-name.rs @@ -1,8 +1,9 @@ +//! Regression test for . +//! Test that a field can have the same name in different variants +//! of an enum, and borrowck won't treat them as the same value. //@ check-pass #![allow(unused_assignments)] #![allow(unused_variables)] -// Test that a field can have the same name in different variants -// of an enum pub enum Foo { X { foo: u32 }, diff --git a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs index 6572140786d2e..d966781f90d9f 100644 --- a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs +++ b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.rs @@ -4,6 +4,6 @@ fn main() { let ptr: *mut () = core::ptr::null_mut(); let _: &mut dyn Fn() = unsafe { &mut *(ptr as *mut dyn Fn()) - //~^ ERROR expected a `Fn()` closure, found `()` + //~^ ERROR expected an `Fn()` closure, found `()` }; } diff --git a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr index d0f0bcb201425..a830ddc4468ff 100644 --- a/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr +++ b/tests/ui/cast/raw-ptr-to-dyn-fn-raw-ptr.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `()` +error[E0277]: expected an `Fn()` closure, found `()` --> $DIR/raw-ptr-to-dyn-fn-raw-ptr.rs:6:16 | LL | &mut *(ptr as *mut dyn Fn()) diff --git a/tests/ui/closures/closure-expected.rs b/tests/ui/closures/closure-expected.rs index d730bcd1faf51..4fc1b3a1fd006 100644 --- a/tests/ui/closures/closure-expected.rs +++ b/tests/ui/closures/closure-expected.rs @@ -1,5 +1,5 @@ fn main() { let x = Some(1); let y = x.or_else(4); - //~^ ERROR expected a `FnOnce()` closure, found `{integer}` + //~^ ERROR expected an `FnOnce()` closure, found `{integer}` } diff --git a/tests/ui/closures/closure-expected.stderr b/tests/ui/closures/closure-expected.stderr index 53a93e1e84dd0..490988f247cdd 100644 --- a/tests/ui/closures/closure-expected.stderr +++ b/tests/ui/closures/closure-expected.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/closure-expected.rs:3:23 | LL | let y = x.or_else(4); diff --git a/tests/ui/closures/coerce-unsafe-to-closure.stderr b/tests/ui/closures/coerce-unsafe-to-closure.stderr index 013b9009da406..2bf2739be52d6 100644 --- a/tests/ui/closures/coerce-unsafe-to-closure.stderr +++ b/tests/ui/closures/coerce-unsafe-to-closure.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&str)` closure, found `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` +error[E0277]: expected an `FnOnce(&str)` closure, found `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` --> $DIR/coerce-unsafe-to-closure.rs:2:44 | LL | let x: Option<&[u8]> = Some("foo").map(std::mem::transmute); diff --git a/tests/ui/codegen/huge-stacks.rs b/tests/ui/codegen/huge-stacks.rs index e0f2cd2cb05d6..9aee3937c7534 100644 --- a/tests/ui/codegen/huge-stacks.rs +++ b/tests/ui/codegen/huge-stacks.rs @@ -5,6 +5,7 @@ //@ min-llvm-version: 22 // Regression test for https://github.com/rust-lang/rust/issues/83060 +// Verifies a program is not miscompiled if it includes a 4GB array on the stack fn func() { const CAP: usize = std::u32::MAX as usize; @@ -17,7 +18,7 @@ fn main() { std::thread::Builder::new() .stack_size(5 * 1024 * 1024 * 1024) .spawn(func) - .unwrap() + .expect("huge-stacks.rs requires 5GB RAM to run") .join() .unwrap(); } diff --git a/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs new file mode 100644 index 0000000000000..c509c52951197 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.rs @@ -0,0 +1,13 @@ +// Regression test for issue #155834 + +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +trait Trait {} + +impl<'t> Trait for [(); N] {} +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for implementations + +fn N(arg: impl Trait) {} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr new file mode 100644 index 0000000000000..a5caa077c7620 --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-impl-trait-with-apit.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/bad-impl-trait-with-apit.rs:8:25 + | +LL | impl<'t> Trait for [(); N] {} + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs index a7e46ad877e72..b3dfef1cb26ca 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.rs @@ -5,6 +5,6 @@ #![expect(incomplete_features)] type const A: u8 = A; -//~^ ERROR overflow normalizing the unevaluated constant `A` +//~^ ERROR overflow normalizing the const alias `A` fn main() {} diff --git a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr index 47653dd1896f7..07b4ca43eb77b 100644 --- a/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr +++ b/tests/ui/const-generics/mgca/cyclic-type-const-151251.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the unevaluated constant `A` +error[E0275]: overflow normalizing the const alias `A` --> $DIR/cyclic-type-const-151251.rs:7:1 | LL | type const A: u8 = A; diff --git a/tests/ui/const-generics/mgca/double-inline-const.rs b/tests/ui/const-generics/mgca/double-inline-const.rs new file mode 100644 index 0000000000000..9bfbd1819c695 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] + +struct S; + +impl S { + const Q: usize = 2; + fn foo(_: S<{ const { const { Self::Q } } }>) {} + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-inline-const.stderr b/tests/ui/const-generics/mgca/double-inline-const.stderr new file mode 100644 index 0000000000000..bc73e3d93575e --- /dev/null +++ b/tests/ui/const-generics/mgca/double-inline-const.stderr @@ -0,0 +1,15 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/double-inline-const.rs:7:35 + | +LL | fn foo(_: S<{ const { const { Self::Q } } }>) {} + | ^^^^ + | +note: not a concrete type + --> $DIR/double-inline-const.rs:5:22 + | +LL | impl S { + | ^^^^ + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs index 11d970534f513..53cf964b8f5a5 100644 --- a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.rs @@ -4,6 +4,7 @@ trait A {} trait Trait {} impl A<[usize; fn_item]> for () {} +//~^ ERROR: the placeholder `_` is not allowed within types on item signatures for implementations fn fn_item(_: impl Trait) {} //~^ ERROR: type provided when a constant was expected diff --git a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr index 0cb59587ee20b..a68c0a1cb409d 100644 --- a/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr +++ b/tests/ui/const-generics/mgca/synth-gen-arg-ice-158152.stderr @@ -1,5 +1,11 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/synth-gen-arg-ice-158152.rs:6:16 + | +LL | impl A<[usize; fn_item]> for () {} + | ^^^^^^^ not allowed in type signatures + error[E0747]: type provided when a constant was expected - --> $DIR/synth-gen-arg-ice-158152.rs:8:26 + --> $DIR/synth-gen-arg-ice-158152.rs:9:26 | LL | fn fn_item(_: impl Trait) {} | ^^^^^ @@ -9,6 +15,7 @@ help: if this generic argument was intended as a const parameter, surround it wi LL | fn fn_item(_: impl Trait<{ usize }>) {} | + + -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0747`. +Some errors have detailed explanations: E0121, E0747. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/type_const-recursive.rs b/tests/ui/const-generics/mgca/type_const-recursive.rs index ebaab51bbc3b6..0791325d603be 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.rs +++ b/tests/ui/const-generics/mgca/type_const-recursive.rs @@ -3,6 +3,6 @@ type const A: u8 = A; -//~^ ERROR: overflow normalizing the unevaluated constant `A` [E0275] +//~^ ERROR: overflow normalizing the const alias `A` [E0275] fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-recursive.stderr b/tests/ui/const-generics/mgca/type_const-recursive.stderr index d21ccb22bc90b..f9e0965dc5412 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.stderr +++ b/tests/ui/const-generics/mgca/type_const-recursive.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow normalizing the unevaluated constant `A` +error[E0275]: overflow normalizing the const alias `A` --> $DIR/type_const-recursive.rs:5:1 | LL | type const A: u8 = A; diff --git a/tests/ui/contracts/empty-ensures.rs b/tests/ui/contracts/empty-ensures.rs index 0a52391117861..79e57df6eb984 100644 --- a/tests/ui/contracts/empty-ensures.rs +++ b/tests/ui/contracts/empty-ensures.rs @@ -6,7 +6,7 @@ extern crate core; use core::contracts::ensures; #[ensures()] -//~^ ERROR expected a `Fn(&_)` closure, found `()` [E0277] +//~^ ERROR expected an `Fn(&_)` closure, found `()` [E0277] fn foo(x: u32) -> u32 { x * 2 } diff --git a/tests/ui/contracts/empty-ensures.stderr b/tests/ui/contracts/empty-ensures.stderr index 6a19d234b52c2..b87f709eeb7a4 100644 --- a/tests/ui/contracts/empty-ensures.stderr +++ b/tests/ui/contracts/empty-ensures.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&_)` closure, found `()` +error[E0277]: expected an `Fn(&_)` closure, found `()` --> $DIR/empty-ensures.rs:8:1 | LL | #[ensures()] diff --git a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs new file mode 100644 index 0000000000000..6053b603cc8fa --- /dev/null +++ b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.rs @@ -0,0 +1,11 @@ +//! Regression test for . +//! Test anon fields in tuple-syntax structs which don't implement trait +//! get nice error message mentioning the type of field and its span. + +struct Foo; +#[derive(Copy, Clone)] +struct Bar(Foo); +//~^ ERROR: the trait `Copy` cannot be implemented for this type +//~| ERROR: `Foo: Clone` is not satisfied + +fn main() {} diff --git a/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr new file mode 100644 index 0000000000000..a997fc0aa0d98 --- /dev/null +++ b/tests/ui/derives/clone-copy/derive-copy-clone-non-copy-field-diagnostic.stderr @@ -0,0 +1,28 @@ +error[E0204]: the trait `Copy` cannot be implemented for this type + --> $DIR/derive-copy-clone-non-copy-field-diagnostic.rs:7:8 + | +LL | #[derive(Copy, Clone)] + | ---- in this derive macro expansion +LL | struct Bar(Foo); + | ^^^ --- this field does not implement `Copy` + +error[E0277]: the trait bound `Foo: Clone` is not satisfied + --> $DIR/derive-copy-clone-non-copy-field-diagnostic.rs:7:12 + | +LL | #[derive(Copy, Clone)] + | ----- in this derive macro expansion +LL | struct Bar(Foo); + | ^^^ the trait `Clone` is not implemented for `Foo` + | +note: required by a bound in `std::clone::AssertParamIsClone` + --> $SRC_DIR/core/src/clone.rs:LL:COL +help: consider annotating `Foo` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct Foo; + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0204, E0277. +For more information about an error, try `rustc --explain E0204`. diff --git a/tests/ui/issues/issue-4252.rs b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs similarity index 90% rename from tests/ui/issues/issue-4252.rs rename to tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs index 1939ad15f143a..7032a6bc5e759 100644 --- a/tests/ui/issues/issue-4252.rs +++ b/tests/ui/drop/drop-with-trait-bound-calls-method-on-self.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/4252 //@ run-pass trait X { fn call(&self, x: &T); diff --git a/tests/ui/issues/issue-27401-dropflag-reinit.rs b/tests/ui/drop/dropflag-reinit-in-loop.rs similarity index 63% rename from tests/ui/issues/issue-27401-dropflag-reinit.rs rename to tests/ui/drop/dropflag-reinit-in-loop.rs index b89497241e096..04fb8eaee965e 100644 --- a/tests/ui/issues/issue-27401-dropflag-reinit.rs +++ b/tests/ui/drop/dropflag-reinit-in-loop.rs @@ -1,9 +1,9 @@ +//! Regression test for . +//! Check that when a `let`-binding occurs in a loop, its associated +//! drop-flag is reinitialized (to indicate "needs-drop" at the end of +//! the owning variable's scope). //@ run-pass -// Check that when a `let`-binding occurs in a loop, its associated -// drop-flag is reinitialized (to indicate "needs-drop" at the end of -// the owning variable's scope). - struct A<'a>(&'a mut i32); impl<'a> Drop for A<'a> { diff --git a/tests/ui/issues/issue-41974.rs b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs similarity index 70% rename from tests/ui/issues/issue-41974.rs rename to tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs index 10c363479a374..9e046dee42cf7 100644 --- a/tests/ui/issues/issue-41974.rs +++ b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/41974 #[derive(Copy, Clone)] struct Flags; diff --git a/tests/ui/issues/issue-41974.stderr b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr similarity index 86% rename from tests/ui/issues/issue-41974.stderr rename to tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr index 2ae073dd1ba82..cc3488aaca0cf 100644 --- a/tests/ui/issues/issue-41974.stderr +++ b/tests/ui/dropck/drop-impl-for-type-param-with-trait-bound.stderr @@ -1,5 +1,5 @@ error[E0210]: type parameter `T` must be used as an argument to some local type (e.g., `MyStruct`) - --> $DIR/issue-41974.rs:7:6 + --> $DIR/drop-impl-for-type-param-with-trait-bound.rs:8:6 | LL | impl Drop for T where T: A { | ^ uncovered type parameter @@ -8,7 +8,7 @@ LL | impl Drop for T where T: A { = note: only traits defined in the current crate can be implemented for a type parameter error[E0120]: the `Drop` trait may only be implemented for local structs, enums, and unions - --> $DIR/issue-41974.rs:7:18 + --> $DIR/drop-impl-for-type-param-with-trait-bound.rs:8:18 | LL | impl Drop for T where T: A { | ^ must be a struct, enum, or union in the current crate diff --git a/tests/ui/issues/issue-48132.rs b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs similarity index 88% rename from tests/ui/issues/issue-48132.rs rename to tests/ui/dropck/dropck-resolves-associated-type-in-field.rs index d8d7167a4ce82..16a44cd0d6de4 100644 --- a/tests/ui/issues/issue-48132.rs +++ b/tests/ui/dropck/dropck-resolves-associated-type-in-field.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/48132 // Regression test for #48132. This was failing due to problems around // the projection caching and dropck type enumeration. diff --git a/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs new file mode 100644 index 0000000000000..f78f3907de9a2 --- /dev/null +++ b/tests/ui/dropck/self-referential-struct-with-boxed-closure.rs @@ -0,0 +1,7 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/26641 +//@ run-pass +struct Parser<'a>(#[allow(dead_code)] Box); + +fn main() { + let _x = Parser(Box::new(|_|{})); +} diff --git a/tests/ui/issues/issue-50781.rs b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs similarity index 81% rename from tests/ui/issues/issue-50781.rs rename to tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs index d837b848591f2..39eee9129e1dd 100644 --- a/tests/ui/issues/issue-50781.rs +++ b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/50781 trait Trait {} trait X { diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr similarity index 85% rename from tests/ui/issues/issue-50781.stderr rename to tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr index 4ba3166b6c581..81f031a2123fd 100644 --- a/tests/ui/issues/issue-50781.stderr +++ b/tests/ui/dyn-compatibility/method-with-self-trait-bound-not-dyn-safe.stderr @@ -1,12 +1,12 @@ error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:11:16 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:12:16 | LL | impl Trait for dyn X {} | ^^^^^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-50781.rs:4:8 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:5:8 | LL | trait X { | - this trait is not dyn compatible... @@ -16,14 +16,14 @@ LL | fn foo(&self) where Self: Trait; = help: only type `()` implements `X`; consider using it directly instead. error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-50781.rs:16:10 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:17:10 | LL | ::foo(&()); | ^ `X` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-50781.rs:4:8 + --> $DIR/method-with-self-trait-bound-not-dyn-safe.rs:5:8 | LL | trait X { | - this trait is not dyn compatible... diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs index 5e64fd5d2af4a..94c6c85d28d27 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.rs @@ -1,6 +1,6 @@ fn main() { let number = 2; - Some(true).filter({ //~ ERROR expected a `FnOnce(&bool)` closure, found `bool` + Some(true).filter({ //~ ERROR expected an `FnOnce(&bool)` closure, found `bool` if number % 2 == 0 { number == 0 } else { diff --git a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr index c4c1c830afa6e..b8b4a0a17bcf6 100644 --- a/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr +++ b/tests/ui/expr/malformed_closure/block_instead_of_closure_in_arg.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&bool)` closure, found `bool` +error[E0277]: expected an `FnOnce(&bool)` closure, found `bool` --> $DIR/block_instead_of_closure_in_arg.rs:3:23 | LL | Some(true).filter({ diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs index bb2e9c0a63c75..faad88764bf98 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.rs @@ -1,6 +1,6 @@ const x: usize =42; fn main() { - let p = Some(45).and_then({|x| //~ ERROR expected a `FnOnce({integer})` closure, found `Option` + let p = Some(45).and_then({|x| //~ ERROR expected an `FnOnce({integer})` closure, found `Option` 1 + 1; Some(x * 2) }); diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr index 54d3c6727f77f..ea41e77952ff5 100644 --- a/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_successful_parse.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce({integer})` closure, found `Option` +error[E0277]: expected an `FnOnce({integer})` closure, found `Option` --> $DIR/ruby_style_closure_successful_parse.rs:3:31 | LL | let p = Some(45).and_then({|x| diff --git a/tests/ui/issues/issue-28600.rs b/tests/ui/extern/extern-c-method-with-str-param.rs similarity index 57% rename from tests/ui/issues/issue-28600.rs rename to tests/ui/extern/extern-c-method-with-str-param.rs index a5427b94a57c5..d0fe55bd1d0f1 100644 --- a/tests/ui/issues/issue-28600.rs +++ b/tests/ui/extern/extern-c-method-with-str-param.rs @@ -1,5 +1,6 @@ +//! Regression test for . +//! pub extern fn with parameter type &str inside struct impl caused ICE. //@ build-pass -// #28600 ICE: pub extern fn with parameter type &str inside struct impl struct Test; diff --git a/tests/ui/extern/extern-wrong-value-type.rs b/tests/ui/extern/extern-wrong-value-type.rs index 56c6cf1dfaba7..ae07a721e83ee 100644 --- a/tests/ui/extern/extern-wrong-value-type.rs +++ b/tests/ui/extern/extern-wrong-value-type.rs @@ -7,5 +7,5 @@ fn main() { // extern functions are extern "C" fn let _x: extern "C" fn() = f; // OK is_fn(f); - //~^ ERROR expected a `Fn()` closure, found `extern "C" fn() {f}` + //~^ ERROR expected an `Fn()` closure, found `extern "C" fn() {f}` } diff --git a/tests/ui/extern/extern-wrong-value-type.stderr b/tests/ui/extern/extern-wrong-value-type.stderr index 692a660117112..e1686f8a3ab9b 100644 --- a/tests/ui/extern/extern-wrong-value-type.stderr +++ b/tests/ui/extern/extern-wrong-value-type.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() {f}` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() {f}` --> $DIR/extern-wrong-value-type.rs:9:11 | LL | is_fn(f); diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs index 738c8daa1687f..a6cbbcc9347ce 100644 --- a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs +++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.rs @@ -1,9 +1,15 @@ //@ check-pass +#![deny(implicit_provenance_casts)] +//~^ WARNING unknown lint: `implicit_provenance_casts` + +// feature-gating also applies when the old names are helpfully replaced with the new one: #![deny(fuzzy_provenance_casts)] -//~^ WARNING unknown lint: `fuzzy_provenance_casts` +//~^ WARNING lint `fuzzy_provenance_casts` has been renamed to `implicit_provenance_casts` +//~| WARNING unknown lint: `implicit_provenance_casts` #![deny(lossy_provenance_casts)] -//~^ WARNING unknown lint: `lossy_provenance_casts` +//~^ WARNING lint `lossy_provenance_casts` has been renamed to `implicit_provenance_casts` +//~| WARNING unknown lint: `implicit_provenance_casts` fn main() { // no warnings emitted since the lints are not activated diff --git a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr index 3f3b49bc60671..4e7c835b57912 100644 --- a/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr +++ b/tests/ui/feature-gates/feature-gate-strict_provenance_lints.stderr @@ -1,25 +1,50 @@ -warning: unknown lint: `fuzzy_provenance_casts` +warning: unknown lint: `implicit_provenance_casts` --> $DIR/feature-gate-strict_provenance_lints.rs:3:9 | +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `implicit_provenance_casts` lint is unstable + = note: see issue #130351 for more information + = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: `#[warn(unknown_lints)]` on by default + +warning: lint `fuzzy_provenance_casts` has been renamed to `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:7:9 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `implicit_provenance_casts` + | + = note: `#[warn(renamed_and_removed_lints)]` on by default + +warning: unknown lint: `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:7:9 + | LL | #![deny(fuzzy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the `fuzzy_provenance_casts` lint is unstable + = note: the `implicit_provenance_casts` lint is unstable = note: see issue #130351 for more information = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: `#[warn(unknown_lints)]` on by default -warning: unknown lint: `lossy_provenance_casts` - --> $DIR/feature-gate-strict_provenance_lints.rs:5:9 +warning: lint `lossy_provenance_casts` has been renamed to `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:10:9 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `implicit_provenance_casts` + +warning: unknown lint: `implicit_provenance_casts` + --> $DIR/feature-gate-strict_provenance_lints.rs:10:9 | LL | #![deny(lossy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ | - = note: the `lossy_provenance_casts` lint is unstable + = note: the `implicit_provenance_casts` lint is unstable = note: see issue #130351 for more information = help: add `#![feature(strict_provenance_lints)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: 2 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index 7b40c8760e368..d33625f06872e 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -9,7 +9,7 @@ struct Foo; impl Fn<()> for Foo { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `Fn` are experimental - //~| ERROR expected a `FnMut()` closure, found `Foo` + //~| ERROR expected an `FnMut()` closure, found `Foo` extern "rust-call" fn call(self, args: ()) -> () {} //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR `call` has an incompatible type for trait @@ -26,7 +26,7 @@ struct Bar; impl FnMut<()> for Bar { //~^ ERROR the precise format of `Fn`-family traits' type parameters is subject to change //~| ERROR manual implementations of `FnMut` are experimental - //~| ERROR expected a `FnOnce()` closure, found `Bar` + //~| ERROR expected an `FnOnce()` closure, found `Bar` extern "rust-call" fn call_mut(&self, args: ()) -> () {} //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR incompatible type for trait diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index b2a2eba9ad1da..56cfd6d0c52b9 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -87,7 +87,7 @@ LL | impl FnMut<()> for Bar { | = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable -error[E0277]: expected a `FnMut()` closure, found `Foo` +error[E0277]: expected an `FnMut()` closure, found `Foo` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:9:17 | LL | impl Fn<()> for Foo { @@ -161,7 +161,7 @@ help: change the self-receiver type to match the trait LL | extern "rust-call" fn call_mut(&mut self, args: ()) -> () {} | +++ -error[E0277]: expected a `FnOnce()` closure, found `Bar` +error[E0277]: expected an `FnOnce()` closure, found `Bar` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:26:20 | LL | impl FnMut<()> for Bar { diff --git a/tests/ui/fn/fn-trait-formatting.rs b/tests/ui/fn/fn-trait-formatting.rs index be74d1ca9be3c..a7b00491ade01 100644 --- a/tests/ui/fn/fn-trait-formatting.rs +++ b/tests/ui/fn/fn-trait-formatting.rs @@ -17,5 +17,5 @@ fn main() { //~| NOTE found struct `Box isize>` needs_fn(1); - //~^ ERROR expected a `Fn(isize)` closure, found `{integer}` + //~^ ERROR expected an `Fn(isize)` closure, found `{integer}` } diff --git a/tests/ui/fn/fn-trait-formatting.stderr b/tests/ui/fn/fn-trait-formatting.stderr index bfd86106bf923..0b86634a52e56 100644 --- a/tests/ui/fn/fn-trait-formatting.stderr +++ b/tests/ui/fn/fn-trait-formatting.stderr @@ -39,7 +39,7 @@ LL | let _: () = Box::new(|| -> isize { unimplemented!() }) as Box isize>` -error[E0277]: expected a `Fn(isize)` closure, found `{integer}` +error[E0277]: expected an `Fn(isize)` closure, found `{integer}` --> $DIR/fn-trait-formatting.rs:19:14 | LL | needs_fn(1); diff --git a/tests/ui/fn/issue-39259.rs b/tests/ui/fn/issue-39259.rs index d4569a9094b2f..d2b545e000b97 100644 --- a/tests/ui/fn/issue-39259.rs +++ b/tests/ui/fn/issue-39259.rs @@ -5,7 +5,7 @@ struct S; impl Fn(u32) -> u32 for S { //~^ ERROR associated item constraints are not allowed here [E0229] - //~| ERROR expected a `FnMut(u32)` closure, found `S` + //~| ERROR expected an `FnMut(u32)` closure, found `S` fn call(&self) -> u32 { //~^ ERROR method `call` has 1 parameter but the declaration in trait `call` has 2 5 diff --git a/tests/ui/fn/issue-39259.stderr b/tests/ui/fn/issue-39259.stderr index b932731b5275e..7f2f4ecdd8d9b 100644 --- a/tests/ui/fn/issue-39259.stderr +++ b/tests/ui/fn/issue-39259.stderr @@ -22,7 +22,7 @@ help: add the missing parameter from the trait LL | fn call(&self, args: Args) -> u32 { | ++++++++++++ -error[E0277]: expected a `FnMut(u32)` closure, found `S` +error[E0277]: expected an `FnMut(u32)` closure, found `S` --> $DIR/issue-39259.rs:6:25 | LL | impl Fn(u32) -> u32 for S { diff --git a/tests/ui/foreign/foreign-int-types.rs b/tests/ui/foreign/foreign-int-types.rs index d20a4c96ea0e2..bac9d8c603918 100644 --- a/tests/ui/foreign/foreign-int-types.rs +++ b/tests/ui/foreign/foreign-int-types.rs @@ -4,7 +4,7 @@ mod xx { extern "C" { - pub fn strlen(str: *const u8) -> usize; + pub fn strlen2(str: *const u8) -> usize; pub fn foo(x: isize, y: usize); } } diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs index c444ee9e1cdb4..edd9ad11ae778 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr index d98071efe8311..7821560d96090 100644 --- a/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr +++ b/tests/ui/generic-associated-types/issue-68642-broken-llvm-ir.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68642-broken-llvm-ir.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.rs b/tests/ui/generic-associated-types/issue-68643-broken-mir.rs index 39db51c0e2a21..23586f6af98bd 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.rs +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } pub fn main() { diff --git a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr index cd4c06a8660a8..99b56dd061933 100644 --- a/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr +++ b/tests/ui/generic-associated-types/issue-68643-broken-mir.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68643-broken-mir.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs b/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs index e379bce07d313..85b932a063c14 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr index 12f9949a0d304..4537091a60915 100644 --- a/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr +++ b/tests/ui/generic-associated-types/issue-68644-codegen-selection.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68644-codegen-selection.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs index e69a08b0a3991..b8133148b1029 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.rs @@ -10,7 +10,7 @@ trait Fun { impl Fun for T { type F<'a> = Self; - //~^ ERROR expected a `Fn()` closure, found `T` + //~^ ERROR expected an `Fn()` closure, found `T` } fn main() { diff --git a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr index 8b23f60953061..cc324f91d75eb 100644 --- a/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr +++ b/tests/ui/generic-associated-types/issue-68645-codegen-fulfillment.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `T` +error[E0277]: expected an `Fn()` closure, found `T` --> $DIR/issue-68645-codegen-fulfillment.rs:12:18 | LL | type F<'a> = Self; diff --git a/tests/ui/issues/issue-28279.rs b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs similarity index 64% rename from tests/ui/issues/issue-28279.rs rename to tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs index 23814b284e905..754e3ce2167a7 100644 --- a/tests/ui/issues/issue-28279.rs +++ b/tests/ui/higher-ranked/region-leak-rc-and-mut-ptr.rs @@ -1,4 +1,8 @@ +//! Regression test for . +//! Region variables escaped comparison for common supertype, which led +//! `Rc` and `*mut Fn(&T)` to break. //@ check-pass + #![allow(dead_code)] use std::rc::Rc; diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr index 36264b0d997b2..96cef0a6a5c9e 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-62529-3.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(<_ as ATC<'a>>::Type)` closure, found `F` +error[E0277]: expected an `Fn(<_ as ATC<'a>>::Type)` closure, found `F` --> $DIR/issue-62529-3.rs:25:14 | LL | call(f, ()); diff --git a/tests/ui/implied-bounds/issue-100690.rs b/tests/ui/implied-bounds/issue-100690.rs index b0dbf749c4670..4f8a2df9834f5 100644 --- a/tests/ui/implied-bounds/issue-100690.rs +++ b/tests/ui/implied-bounds/issue-100690.rs @@ -32,7 +32,7 @@ impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandl F: FnOnce(&mut UIView<'a, T>) -> Result<(), io::Error> + Send + 'static, { real_dispatch(f) - //~^ ERROR expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` + //~^ ERROR expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` } } diff --git a/tests/ui/implied-bounds/issue-100690.stderr b/tests/ui/implied-bounds/issue-100690.stderr index d00895d8fc9cc..b5465e2f3ce08 100644 --- a/tests/ui/implied-bounds/issue-100690.stderr +++ b/tests/ui/implied-bounds/issue-100690.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&mut UIView<'_, T>)` closure, found `F` +error[E0277]: expected an `FnOnce(&mut UIView<'_, T>)` closure, found `F` --> $DIR/issue-100690.rs:34:23 | LL | real_dispatch(f) diff --git a/tests/ui/issues/issue-28936.rs b/tests/ui/inference/closure-arg-lifetime-by-ref.rs similarity index 90% rename from tests/ui/issues/issue-28936.rs rename to tests/ui/inference/closure-arg-lifetime-by-ref.rs index 96503f0711d27..f00a38ea061c8 100644 --- a/tests/ui/issues/issue-28936.rs +++ b/tests/ui/inference/closure-arg-lifetime-by-ref.rs @@ -1,4 +1,6 @@ +//! Regression test for . //@ check-pass + pub type Session = i32; pub struct StreamParser<'a, T> { _tokens: T, diff --git a/tests/ui/intrinsics/const-eval-select-bad.rs b/tests/ui/intrinsics/const-eval-select-bad.rs index f407125129921..23a7a4be7ff10 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.rs +++ b/tests/ui/intrinsics/const-eval-select-bad.rs @@ -7,8 +7,8 @@ const fn not_fn_items() { const_eval_select((), || {}, || {}); //~^ ERROR const FnOnce()` is not satisfied const_eval_select((), 42, 0xDEADBEEF); - //~^ ERROR expected a `FnOnce()` closure - //~| ERROR expected a `FnOnce()` closure + //~^ ERROR expected an `FnOnce()` closure + //~| ERROR expected an `FnOnce()` closure } const fn foo(n: i32) -> i32 { diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index d701f5ea90973..7880dfa36a8f4 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -9,7 +9,7 @@ LL | const_eval_select((), || {}, || {}); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:9:27 | LL | const_eval_select((), 42, 0xDEADBEEF); @@ -22,7 +22,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); note: required by a bound in `const_eval_select` --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL -error[E0277]: expected a `FnOnce()` closure, found `{integer}` +error[E0277]: expected an `FnOnce()` closure, found `{integer}` --> $DIR/const-eval-select-bad.rs:9:31 | LL | const_eval_select((), 42, 0xDEADBEEF); diff --git a/tests/ui/issues/issue-28776.rs b/tests/ui/issues/issue-28776.rs deleted file mode 100644 index e564ebcd110cb..0000000000000 --- a/tests/ui/issues/issue-28776.rs +++ /dev/null @@ -1,6 +0,0 @@ -use std::ptr; - -fn main() { - (&ptr::write)(1 as *mut _, 42); - //~^ ERROR E0133 -} diff --git a/tests/ui/iterators/fold-iterator-error-23966.stderr b/tests/ui/iterators/fold-iterator-error-23966.stderr index 15249a935972a..2e27108e6a483 100644 --- a/tests/ui/iterators/fold-iterator-error-23966.stderr +++ b/tests/ui/iterators/fold-iterator-error-23966.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(_, char)` closure, found `()` +error[E0277]: expected an `FnMut(_, char)` closure, found `()` --> $DIR/fold-iterator-error-23966.rs:3:32 | LL | "".chars().fold(|_, _| (), ()); diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.rs b/tests/ui/lang-items/start_lang_item_with_target_feature.rs index 19036819d3d80..61f34d0321202 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.rs +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.rs @@ -18,7 +18,7 @@ pub trait Sized: MetaSized {} #[lang = "start"] #[target_feature(enable = "avx2")] -//~^ ERROR `start` lang item function is not allowed to have `#[target_feature]` +//~^ ERROR #[target_feature]` cannot be applied to a lang item function fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { 0 } diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr index ce0b1d7557445..8611aa7c7628d 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.stderr +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.stderr @@ -1,11 +1,13 @@ -error: `start` lang item function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a lang item function --> $DIR/start_lang_item_with_target_feature.rs:20:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { - | ------------------------------------------------------------------------------------------- `start` lang item function is not allowed to have `#[target_feature]` +LL | / fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { +LL | | 0 +LL | | } + | |_- lang item function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-28999.rs b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs similarity index 62% rename from tests/ui/issues/issue-28999.rs rename to tests/ui/lifetimes/field-borrow-lifetime-inference.rs index 572a0beff61f6..a8582dbd29b62 100644 --- a/tests/ui/issues/issue-28999.rs +++ b/tests/ui/lifetimes/field-borrow-lifetime-inference.rs @@ -1,4 +1,7 @@ +//! Regression test for . +//! `this.v` was not constrained and inferred `'a`. //@ check-pass + pub struct Xyz<'a, V> { pub v: (V, &'a u32), } diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs index 85eeb5d4c901e..ab3bf619f83b2 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs @@ -4,14 +4,13 @@ use std::future::Future; async fn wrapper(f: F) -//~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` -//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` -//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` +//~^ ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` +//~| ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` +//~| ERROR: expected an `FnOnce(&'a mut i32)` closure, found `i32` where -F:, -for<'a> >::Output: Future + 'a, + F:, + for<'a> >::Output: Future + 'a, { - //~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32` let mut i = 41; &mut i; } diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr index 6da9f7380d597..7a825b93461c0 100644 --- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr +++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr @@ -1,15 +1,15 @@ -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:1 | LL | / async fn wrapper(f: F) ... | -LL | | F:, -LL | | for<'a> >::Output: Future + 'a, - | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` +LL | | F:, +LL | | for<'a> >::Output: Future + 'a, + | |______________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32` | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:26 | LL | async fn wrapper(f: F) @@ -17,7 +17,7 @@ LL | async fn wrapper(f: F) | = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&'a mut i32)` closure, found `i32` --> $DIR/issue-76168-hr-outlives-3.rs:6:26 | LL | async fn wrapper(f: F) @@ -26,18 +26,6 @@ LL | async fn wrapper(f: F) = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32` - --> $DIR/issue-76168-hr-outlives-3.rs:13:1 - | -LL | / { -LL | | -LL | | let mut i = 41; -LL | | &mut i; -LL | | } - | |_^ expected an `FnOnce(&'a mut i32)` closure, found `i32` - | - = help: the trait `for<'a> FnOnce(&'a mut i32)` is not implemented for `i32` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lifetimes/issue-95023.rs b/tests/ui/lifetimes/issue-95023.rs index 1faae50d9f158..d26317d2b9058 100644 --- a/tests/ui/lifetimes/issue-95023.rs +++ b/tests/ui/lifetimes/issue-95023.rs @@ -4,7 +4,7 @@ impl Fn(&isize) for Error { //~^ ERROR manual implementations of `Fn` are experimental [E0183] //~| ERROR associated item constraints are not allowed here [E0229] //~| ERROR not all trait items implemented - //~| ERROR expected a `FnMut(&isize)` closure, found `Error` + //~| ERROR expected an `FnMut(&isize)` closure, found `Error` fn foo(&self) -> Self::B<{ N }>; //~^ ERROR associated function in `impl` without body //~| ERROR method `foo` is not a member of trait `Fn` [E0407] diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index 013bc51ff78a4..afb627de1c1bb 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -40,7 +40,7 @@ LL | impl Fn(&isize) for Error { | = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut(&isize)` closure, found `Error` +error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-95023.rs:3:21 | LL | impl Fn(&isize) for Error { diff --git a/tests/ui/issues/issue-27942.rs b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs similarity index 71% rename from tests/ui/issues/issue-27942.rs rename to tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs index 1c2e6456937bb..91a18bfbf4390 100644 --- a/tests/ui/issues/issue-27942.rs +++ b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.rs @@ -1,3 +1,5 @@ +//! Regression test for . +//! Test internal compiler structs do not leak into error message. //@ dont-require-annotations: NOTE pub trait Resources<'a> {} diff --git a/tests/ui/issues/issue-27942.stderr b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr similarity index 76% rename from tests/ui/issues/issue-27942.stderr rename to tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr index 671875aa7e2ac..bab0f4c180958 100644 --- a/tests/ui/issues/issue-27942.stderr +++ b/tests/ui/lifetimes/lifetime-errors/trait-method-return-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:7:25 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -7,18 +7,18 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the lifetime `'a` as defined here... - --> $DIR/issue-27942.rs:5:18 + --> $DIR/trait-method-return-lifetime-mismatch.rs:7:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ note: ...does not necessarily outlive the anonymous lifetime defined here - --> $DIR/issue-27942.rs:7:15 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ error[E0308]: mismatched types - --> $DIR/issue-27942.rs:7:25 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:25 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch @@ -26,12 +26,12 @@ LL | fn select(&self) -> BufferViewHandle; = note: expected trait `Resources<'_>` found trait `Resources<'a>` note: the anonymous lifetime defined here... - --> $DIR/issue-27942.rs:7:15 + --> $DIR/trait-method-return-lifetime-mismatch.rs:9:15 | LL | fn select(&self) -> BufferViewHandle; | ^^^^^ note: ...does not necessarily outlive the lifetime `'a` as defined here - --> $DIR/issue-27942.rs:5:18 + --> $DIR/trait-method-return-lifetime-mismatch.rs:7:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ diff --git a/tests/ui/lint/auxiliary/lint_stability.rs b/tests/ui/lint/auxiliary/lint_stability.rs index 99c29dcdda677..bc2d47aebbc29 100644 --- a/tests/ui/lint/auxiliary/lint_stability.rs +++ b/tests/ui/lint/auxiliary/lint_stability.rs @@ -1,5 +1,6 @@ #![crate_name="lint_stability"] #![crate_type = "lib"] +#![feature(extern_types)] #![feature(staged_api)] #![feature(associated_type_defaults)] #![stable(feature = "lint_stability", since = "1.0.0")] @@ -186,3 +187,8 @@ macro_rules! macro_test_arg { macro_rules! macro_test_arg_nested { ($func:ident) => (macro_test_arg!($func())); } + +extern "C" { + #[unstable(feature = "unstable_test_feature", issue = "none")] + pub type UnstableForeignType; +} diff --git a/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs b/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs new file mode 100644 index 0000000000000..22975b45a3b4d --- /dev/null +++ b/tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs @@ -0,0 +1,21 @@ +//@ check-pass + +//! this test checks that irregular recursive types do not cause stack overflow in ImproperCTypes +//! Issue: https://github.com/rust-lang/rust/issues/94223 + +#![deny(improper_ctypes, improper_ctypes_definitions)] + +use std::marker::PhantomData; + +#[repr(C)] +struct A { + a: *const A>, // without a recursion limit, checking this ends up creating checks for + // infinitely deep types the likes of `A>>>>>` + p: PhantomData, +} + +extern "C" { + fn f(a: *const A<()>); +} + +fn main() {} diff --git a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs index 61e95dc5a464c..9aa9be052d761 100644 --- a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs @@ -1,8 +1,10 @@ -//@ check-pass +//! This test checks that the depth limit of the ImproperCTypes lints counts the depth +//! of a type properly. +//! Issue: https://github.com/rust-lang/rust/issues/130757 #![recursion_limit = "5"] #![allow(unused)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes_definitions)] #[repr(C)] struct F1(*const ()); @@ -15,7 +17,7 @@ struct F4(*const ()); #[repr(C)] struct F5(*const ()); #[repr(C)] -struct F6(*const ()); +struct F6([char;8]); //oops! #[repr(C)] struct B { @@ -24,9 +26,13 @@ struct B { f3: F3, f4: F4, f5: F5, + // If the depth counter misbehaves, it will believe `f6` is "too deep" without good reason. + // And in response, it will assume `B` is safe. + // so, the error linked to F6 means the test succeeds. f6: F6, } extern "C" fn foo(_: B) {} +//~^ ERROR: uses type `char` fn main() {} diff --git a/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr new file mode 100644 index 0000000000000..7a47da8a788f6 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr @@ -0,0 +1,16 @@ +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-non-recursion-limit.rs:35:22 + | +LL | extern "C" fn foo(_: B) {} + | ^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent +note: the lint level is defined here + --> $DIR/lint-non-recursion-limit.rs:7:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/lint-stability.rs b/tests/ui/lint/lint-stability.rs index f080b5e4bbeb1..c8712397189c7 100644 --- a/tests/ui/lint/lint-stability.rs +++ b/tests/ui/lint/lint-stability.rs @@ -6,7 +6,7 @@ #![allow(deprecated)] #![allow(dead_code)] #![feature(staged_api)] - +#![feature(extern_types)] #![stable(feature = "rust1", since = "1.0.0")] #[macro_use] @@ -18,6 +18,9 @@ mod cross_crate { use lint_stability::*; + fn test_foreign_type(_: &mut UnstableForeignType) { //~ ERROR use of unstable library feature + } + fn test() { type Foo = MethodTester; let foo = MethodTester; diff --git a/tests/ui/lint/lint-stability.stderr b/tests/ui/lint/lint-stability.stderr index 53d31b16811ab..6316f470349ad 100644 --- a/tests/ui/lint/lint-stability.stderr +++ b/tests/ui/lint/lint-stability.stderr @@ -8,7 +8,16 @@ LL | extern crate stability_cfg2; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:45:9 + --> $DIR/lint-stability.rs:21:34 + | +LL | fn test_foreign_type(_: &mut UnstableForeignType) { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `unstable_test_feature` + --> $DIR/lint-stability.rs:48:9 | LL | deprecated_unstable(); | ^^^^^^^^^^^^^^^^^^^ @@ -17,7 +26,7 @@ LL | deprecated_unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:47:9 + --> $DIR/lint-stability.rs:50:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +35,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:49:9 + --> $DIR/lint-stability.rs:52:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +44,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:52:9 + --> $DIR/lint-stability.rs:55:9 | LL | deprecated_unstable_text(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +53,7 @@ LL | deprecated_unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:54:9 + --> $DIR/lint-stability.rs:57:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +62,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:56:9 + --> $DIR/lint-stability.rs:59:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +71,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:59:9 + --> $DIR/lint-stability.rs:62:9 | LL | unstable(); | ^^^^^^^^ @@ -71,7 +80,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:60:9 + --> $DIR/lint-stability.rs:63:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +89,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:61:9 + --> $DIR/lint-stability.rs:64:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +98,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:63:9 + --> $DIR/lint-stability.rs:66:9 | LL | unstable_text(); | ^^^^^^^^^^^^^ @@ -98,7 +107,7 @@ LL | unstable_text(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:65:9 + --> $DIR/lint-stability.rs:68:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -107,7 +116,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:67:9 + --> $DIR/lint-stability.rs:70:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -116,7 +125,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:99:17 + --> $DIR/lint-stability.rs:102:17 | LL | let _ = DeprecatedUnstableStruct { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -125,7 +134,7 @@ LL | let _ = DeprecatedUnstableStruct { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:103:17 + --> $DIR/lint-stability.rs:106:17 | LL | let _ = UnstableStruct { i: 0 }; | ^^^^^^^^^^^^^^ @@ -134,7 +143,7 @@ LL | let _ = UnstableStruct { i: 0 }; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:107:17 + --> $DIR/lint-stability.rs:110:17 | LL | let _ = DeprecatedUnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,7 +152,7 @@ LL | let _ = DeprecatedUnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:109:17 + --> $DIR/lint-stability.rs:112:17 | LL | let _ = UnstableUnitStruct; | ^^^^^^^^^^^^^^^^^^ @@ -152,7 +161,7 @@ LL | let _ = UnstableUnitStruct; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:113:17 + --> $DIR/lint-stability.rs:116:17 | LL | let _ = Enum::DeprecatedUnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,7 +170,7 @@ LL | let _ = Enum::DeprecatedUnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:115:17 + --> $DIR/lint-stability.rs:118:17 | LL | let _ = Enum::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^ @@ -170,7 +179,7 @@ LL | let _ = Enum::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:119:17 + --> $DIR/lint-stability.rs:122:17 | LL | let _ = DeprecatedUnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,7 +188,7 @@ LL | let _ = DeprecatedUnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:121:17 + --> $DIR/lint-stability.rs:124:17 | LL | let _ = UnstableTupleStruct (1); | ^^^^^^^^^^^^^^^^^^^ @@ -188,7 +197,7 @@ LL | let _ = UnstableTupleStruct (1); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:130:25 + --> $DIR/lint-stability.rs:133:25 | LL | macro_test_arg!(deprecated_unstable_text()); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +206,7 @@ LL | macro_test_arg!(deprecated_unstable_text()); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:144:9 + --> $DIR/lint-stability.rs:147:9 | LL | Trait::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +215,7 @@ LL | Trait::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:146:9 + --> $DIR/lint-stability.rs:149:9 | LL | ::trait_deprecated_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -215,7 +224,7 @@ LL | ::trait_deprecated_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:148:9 + --> $DIR/lint-stability.rs:151:9 | LL | Trait::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -224,7 +233,7 @@ LL | Trait::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:150:9 + --> $DIR/lint-stability.rs:153:9 | LL | ::trait_deprecated_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +242,7 @@ LL | ::trait_deprecated_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:152:9 + --> $DIR/lint-stability.rs:155:9 | LL | Trait::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^ @@ -242,7 +251,7 @@ LL | Trait::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:153:9 + --> $DIR/lint-stability.rs:156:9 | LL | ::trait_unstable(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -251,7 +260,7 @@ LL | ::trait_unstable(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:154:9 + --> $DIR/lint-stability.rs:157:9 | LL | Trait::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -260,7 +269,7 @@ LL | Trait::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature`: text - --> $DIR/lint-stability.rs:156:9 + --> $DIR/lint-stability.rs:159:9 | LL | ::trait_unstable_text(&foo); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -269,7 +278,7 @@ LL | ::trait_unstable_text(&foo); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:172:10 + --> $DIR/lint-stability.rs:175:10 | LL | impl UnstableTrait for S { } | ^^^^^^^^^^^^^ @@ -278,7 +287,7 @@ LL | impl UnstableTrait for S { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:174:24 + --> $DIR/lint-stability.rs:177:24 | LL | trait LocalTrait : UnstableTrait { } | ^^^^^^^^^^^^^ @@ -287,7 +296,7 @@ LL | trait LocalTrait : UnstableTrait { } = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:179:9 + --> $DIR/lint-stability.rs:182:9 | LL | fn trait_unstable(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -296,7 +305,7 @@ LL | fn trait_unstable(&self) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:184:5 + --> $DIR/lint-stability.rs:187:5 | LL | extern crate inherited_stability; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -305,7 +314,7 @@ LL | extern crate inherited_stability; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:185:9 + --> $DIR/lint-stability.rs:188:9 | LL | use self::inherited_stability::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -314,7 +323,7 @@ LL | use self::inherited_stability::*; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:188:9 + --> $DIR/lint-stability.rs:191:9 | LL | unstable(); | ^^^^^^^^ @@ -323,7 +332,7 @@ LL | unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:191:9 + --> $DIR/lint-stability.rs:194:9 | LL | stable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^ @@ -332,7 +341,7 @@ LL | stable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:194:9 + --> $DIR/lint-stability.rs:197:9 | LL | unstable_mod::deprecated(); | ^^^^^^^^^^^^ @@ -341,7 +350,7 @@ LL | unstable_mod::deprecated(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:195:9 + --> $DIR/lint-stability.rs:198:9 | LL | unstable_mod::unstable(); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -350,7 +359,7 @@ LL | unstable_mod::unstable(); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:197:17 + --> $DIR/lint-stability.rs:200:17 | LL | let _ = Unstable::UnstableVariant; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -359,7 +368,7 @@ LL | let _ = Unstable::UnstableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:198:17 + --> $DIR/lint-stability.rs:201:17 | LL | let _ = Unstable::StableVariant; | ^^^^^^^^ @@ -368,7 +377,7 @@ LL | let _ = Unstable::StableVariant; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:88:48 + --> $DIR/lint-stability.rs:91:48 | LL | struct S1(T::TypeUnstable); | ^^^^^^^^^^^^^^^ @@ -377,7 +386,7 @@ LL | struct S1(T::TypeUnstable); = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `unstable_test_feature` - --> $DIR/lint-stability.rs:92:13 + --> $DIR/lint-stability.rs:95:13 | LL | TypeUnstable = u8, | ^^^^^^^^^^^^^^^^^ @@ -385,6 +394,6 @@ LL | TypeUnstable = u8, = help: add `#![feature(unstable_test_feature)]` 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 43 previous errors +error: aborting due to 44 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs index 187209d4e207e..c9f494200b30a 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.rs @@ -1,7 +1,7 @@ #![feature(strict_provenance_lints)] -#![deny(fuzzy_provenance_casts)] +#![deny(implicit_provenance_casts)] fn main() { let dangling = 16_usize as *const u8; - //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + //~^ ERROR cast from `usize` to `*const u8` implicitly relies on exposed provenance } diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr index f5eec6fc65667..b88e3d4a9c738 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -1,16 +1,17 @@ -error: strict provenance disallows casting integer `usize` to pointer `*const u8` +error: cast from `usize` to `*const u8` implicitly relies on exposed provenance --> $DIR/lint-strict-provenance-fuzzy-casts.rs:5:20 | LL | let dangling = 16_usize as *const u8; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9 | -LL | #![deny(fuzzy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ -help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use `.with_addr()` to adjust the address of a valid pointer in the same allocation | LL - let dangling = 16_usize as *const u8; LL + let dangling = (...).with_addr(16_usize); diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs index 395dc75f82555..8f27d170ddec4 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.rs @@ -1,18 +1,18 @@ #![feature(strict_provenance_lints)] -#![deny(lossy_provenance_casts)] +#![deny(implicit_provenance_casts)] fn main() { let x: u8 = 37; let addr: usize = &x as *const u8 as usize; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` implicitly exposes pointer provenance let addr_32bit = &x as *const u8 as u32; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + //~^ ERROR cast from `*const u8` to `u32` implicitly exposes pointer provenance // don't add unnecessary parens in the suggestion let ptr = &x as *const u8; let ptr_addr = ptr as usize; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` implicitly exposes pointer provenance let ptr_addr_32bit = ptr as u32; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + //~^ ERROR cast from `*const u8` to `u32` implicitly exposes pointer provenance } diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr index bcef0ae424e68..22c45d7f6e52a 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -1,34 +1,36 @@ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:6:23 | LL | let addr: usize = &x as *const u8 as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-lossy-casts.rs:2:9 | -LL | #![deny(lossy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer | LL - let addr: usize = &x as *const u8 as usize; LL + let addr: usize = (&x as *const u8).addr(); | -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +error: cast from `*const u8` to `u32` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:9:22 | LL | let addr_32bit = &x as *const u8 as u32; | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit help: use `.addr()` to obtain the address of a pointer | LL | let addr_32bit = (&x as *const u8).addr() as u32; | + ++++++++ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:14:20 | LL | let ptr_addr = ptr as usize; @@ -36,9 +38,10 @@ LL | let ptr_addr = ptr as usize; | | | help: use `.addr()` to obtain the address of a pointer: `.addr()` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +error: cast from `*const u8` to `u32` implicitly exposes pointer provenance --> $DIR/lint-strict-provenance-lossy-casts.rs:16:26 | LL | let ptr_addr_32bit = ptr as u32; @@ -46,7 +49,8 @@ LL | let ptr_addr_32bit = ptr as u32; | | | help: use `.addr()` to obtain the address of a pointer: `.addr() as u32` | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit error: aborting due to 4 previous errors diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.rs b/tests/ui/lint/lint-strict-provenance-macro-casts.rs index 6d1c02c91fdbd..59a844b0fa134 100644 --- a/tests/ui/lint/lint-strict-provenance-macro-casts.rs +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.rs @@ -1,23 +1,22 @@ #![feature(strict_provenance_lints)] -#![deny(lossy_provenance_casts)] -#![deny(fuzzy_provenance_casts)] +#![deny(implicit_provenance_casts)] macro_rules! cast { ($e:expr, $t:ty) => { $e as $t - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - //~| ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + //~^ ERROR cast from `*const u8` to `usize` + //~| ERROR cast from `usize` to `*const u8` }; } macro_rules! p2i { ($e:expr) => { $e as usize }; - //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~^ ERROR cast from `*const u8` to `usize` } macro_rules! i2p { ($e:expr) => { $e as *const () }; - //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const ()` + //~^ ERROR cast from `usize` to `*const ()` } fn main() { diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr index a7ec3d56965f3..08a1707490623 100644 --- a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr @@ -1,5 +1,5 @@ -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:6:9 | LL | $e as $t | ^^^^^^^^ @@ -7,16 +7,17 @@ LL | $e as $t LL | let _addr = cast!(ptr, usize); | ----------------- in this macro invocation | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit note: the lint level is defined here --> $DIR/lint-strict-provenance-macro-casts.rs:2:9 | -LL | #![deny(lossy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(implicit_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) -error: strict provenance disallows casting integer `usize` to pointer `*const u8` - --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 +error: cast from `usize` to `*const u8` implicitly relies on exposed provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:6:9 | LL | $e as $t | ^^^^^^^^ @@ -24,16 +25,12 @@ LL | $e as $t LL | let _ptr = cast!(0usize, *const u8); | ------------------------ in this macro invocation | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead -note: the lint level is defined here - --> $DIR/lint-strict-provenance-macro-casts.rs:3:9 - | -LL | #![deny(fuzzy_provenance_casts)] - | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) -error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` - --> $DIR/lint-strict-provenance-macro-casts.rs:14:20 +error: cast from `*const u8` to `usize` implicitly exposes pointer provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:13:20 | LL | ($e:expr) => { $e as usize }; | ^^^^^^^^^^^ @@ -41,11 +38,12 @@ LL | ($e:expr) => { $e as usize }; LL | p2i!(&raw const x); | ------------------ in this macro invocation | - = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = help: if conforming to strict provenance is not possible, use `.expose_provenance()` + = note: for more information, visit = note: this error originates in the macro `p2i` (in Nightly builds, run with -Z macro-backtrace for more info) -error: strict provenance disallows casting integer `usize` to pointer `*const ()` - --> $DIR/lint-strict-provenance-macro-casts.rs:19:20 +error: cast from `usize` to `*const ()` implicitly relies on exposed provenance + --> $DIR/lint-strict-provenance-macro-casts.rs:18:20 | LL | ($e:expr) => { $e as *const () }; | ^^^^^^^^^^^^^^^ @@ -53,7 +51,8 @@ LL | ($e:expr) => { $e as *const () }; LL | i2p!(0x42); | ---------- in this macro invocation | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = help: if conforming to strict provenance is not possible, use `std::ptr::with_exposed_provenance()` + = note: for more information, visit = note: this error originates in the macro `i2p` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/lint/runtime-symbols.rs b/tests/ui/lint/runtime-symbols.rs new file mode 100644 index 0000000000000..9b03f43c8cfaf --- /dev/null +++ b/tests/ui/lint/runtime-symbols.rs @@ -0,0 +1,95 @@ +// This test checks the runtime symbols lint. + +//@ edition: 2021 +//@ normalize-stderr: "\*const [iu]8" -> "*const U8" + +#![feature(c_variadic)] +#![allow(clashing_extern_declarations)] // we are voluntarily testing different definitions + +use core::ffi::{c_char, c_int, c_void}; + +fn invalid() { + #[no_mangle] + pub fn memmove() {} + //~^ ERROR invalid definition of the runtime `memmove` symbol + + extern "C" { + pub fn memset(); + //~^ ERROR invalid definition of the runtime `memset` symbol + + pub fn memcmp(); + //~^ ERROR invalid definition of the runtime `memcmp` symbol + } + + #[no_mangle] + pub static strlen: () = (); + //~^ ERROR invalid definition of the runtime `strlen` symbol + + // ABI mismatch: Rust ABI instead of C ABI + #[no_mangle] + pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void { + dest + } + //~^^^ ERROR invalid definition of the runtime `memcpy` symbol + + // C-Variadic mismatch + #[no_mangle] + pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize, _: ...) -> c_int { + 0 + } + //~^^^ ERROR invalid definition of the runtime `bcmp` symbol + + // Return type is missing + #[export_name = "bcmp"] + pub extern "C" fn bcmp_(s1: *const c_void, s2: *const c_void, n: usize) {} + //~^ ERROR invalid definition of the runtime `bcmp` symbol +} + +fn suspicious() { + #[no_mangle] + pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ WARN suspicious definition of the runtime `memcpy` symbol + + #[no_mangle] + pub extern "C" fn memmove(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + std::ptr::null_mut() + } + //~^^^ WARN suspicious definition of the runtime `memmove` symbol + + extern "C" { + fn memset(s: *mut c_void, c: c_int, n: usize) -> f64; + //~^ WARN suspicious definition of the runtime `memset` symbol + } + + #[export_name = "bcmp"] + pub extern "C" fn bcmp_(s1: *const u8, s2: *const u8, n: usize) -> c_int { + 0 + } + //~^^^ WARN suspicious definition of the runtime `bcmp` symbol + + #[no_mangle] + pub extern "C" fn strlen(s: *const u64) -> usize { + 0 + } + //~^^^ WARN suspicious definition of the runtime `strlen` symbol +} + +fn valid() { + extern "C" { + fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memmove(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void; + + fn memset(s: *mut c_void, c: c_int, n: usize) -> *mut c_void; + + fn memcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + fn bcmp(s1: *const c_void, s2: *const c_void, n: usize) -> c_int; + + static strlen: Option usize>; + } +} + +fn main() {} diff --git a/tests/ui/lint/runtime-symbols.stderr b/tests/ui/lint/runtime-symbols.stderr new file mode 100644 index 0000000000000..712f6532c1a59 --- /dev/null +++ b/tests/ui/lint/runtime-symbols.stderr @@ -0,0 +1,129 @@ +error: invalid definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:13:5 + | +LL | pub fn memmove() {} + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default + +error: invalid definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:17:9 + | +LL | pub fn memset(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + +error: invalid definition of the runtime `memcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:20:9 + | +LL | pub fn memcmp(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcmp")]`, or `#[link_name = "memcmp"]` + +error: invalid definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:25:5 + | +LL | pub static strlen: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `static strlen: ()` + = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "strlen")]` + +error: invalid definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:30:5 + | +LL | pub fn memcpy(dest: *mut c_void, src: *const c_void, n: usize) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `fn(*mut c_void, *const c_void, usize) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:37:5 + | +LL | pub unsafe extern "C" fn bcmp(s1: *const c_void, s2: *const c_void, n: usize, _: ...) -> c_int { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `unsafe extern "C" fn(*const c_void, *const c_void, usize, ...) -> i32` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +error: invalid definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:44:5 + | +LL | pub extern "C" fn bcmp_(s1: *const c_void, s2: *const c_void, n: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `extern "C" fn(*const c_void, *const c_void, usize)` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + +warning: suspicious definition of the runtime `memcpy` symbol used by the standard library + --> $DIR/runtime-symbols.rs:50:5 + | +LL | pub extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memcpy")]`, or `#[link_name = "memcpy"]` + = help: allow this lint if the signature is compatible + = note: `#[warn(suspicious_runtime_symbol_definitions)]` on by default + +warning: suspicious definition of the runtime `memmove` symbol used by the standard library + --> $DIR/runtime-symbols.rs:56:5 + | +LL | pub extern "C" fn memmove(dest: *mut c_void, src: *const c_void, n: i64) -> *mut c_void { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> *mut c_void` + found `extern "C" fn(*mut c_void, *const c_void, i64) -> *mut c_void` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memmove")]`, or `#[link_name = "memmove"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `memset` symbol used by the standard library + --> $DIR/runtime-symbols.rs:62:9 + | +LL | fn memset(s: *mut c_void, c: c_int, n: usize) -> f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, i32, usize) -> *mut c_void` + found `unsafe extern "C" fn(*mut c_void, i32, usize) -> f64` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "memset")]`, or `#[link_name = "memset"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `bcmp` symbol used by the standard library + --> $DIR/runtime-symbols.rs:67:5 + | +LL | pub extern "C" fn bcmp_(s1: *const U8, s2: *const U8, n: usize) -> c_int { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const c_void, *const c_void, usize) -> i32` + found `extern "C" fn(*const U8, *const U8, usize) -> i32` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "bcmp")]`, or `#[link_name = "bcmp"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `strlen` symbol used by the standard library + --> $DIR/runtime-symbols.rs:73:5 + | +LL | pub extern "C" fn strlen(s: *const u64) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8) -> usize` + found `extern "C" fn(*const u64) -> usize` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "strlen")]`, or `#[link_name = "strlen"]` + = help: allow this lint if the signature is compatible + +error: aborting due to 7 previous errors; 5 warnings emitted + diff --git a/tests/ui/liveness/auxiliary/aux_issue_147648.rs b/tests/ui/liveness/auxiliary/aux_issue_147648.rs new file mode 100644 index 0000000000000..db188b50e285d --- /dev/null +++ b/tests/ui/liveness/auxiliary/aux_issue_147648.rs @@ -0,0 +1,23 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_derive(UnusedAssign)] +pub fn generate(ts: TokenStream) -> TokenStream { + let mut ts = ts.into_iter(); + let _pub = ts.next(); + let _struct = ts.next(); + let name = ts.next().unwrap(); + let TokenTree::Group(fields) = ts.next().unwrap() else { panic!() }; + let mut fields = fields.stream().into_iter(); + let field = fields.next().unwrap(); + + quote! { + impl Drop for $name { + fn drop(&mut self) { + let Self { $field } = self; + } + } + } +} diff --git a/tests/ui/liveness/unused-assignments-from-macro-147648.rs b/tests/ui/liveness/unused-assignments-from-macro-147648.rs new file mode 100644 index 0000000000000..92165c76d1d82 --- /dev/null +++ b/tests/ui/liveness/unused-assignments-from-macro-147648.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ proc-macro: aux_issue_147648.rs + +#![deny(unused_assignments)] +#![allow(dead_code)] +#![allow(unused_variables)] + +extern crate aux_issue_147648; +use aux_issue_147648::UnusedAssign; + +#[derive(UnusedAssign)] +pub struct MyError { + source_code: (), +} + +fn main() { + let _error = MyError { source_code: () }; +} diff --git a/tests/ui/issues/issue-2761.rs b/tests/ui/macros/assert-with-custom-message.rs similarity index 62% rename from tests/ui/issues/issue-2761.rs rename to tests/ui/macros/assert-with-custom-message.rs index 6df7a5118b820..78a4a2d61cb9b 100644 --- a/tests/ui/issues/issue-2761.rs +++ b/tests/ui/macros/assert-with-custom-message.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-fail //@ error-pattern:custom message //@ needs-subprocess diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs index 68346a00ae1a7..2b1bacf7e0c31 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -4,7 +4,7 @@ struct T; impl T { - #[core::contracts::ensures] //~ ERROR expected a `Fn(&_)` closure, found `()` + #[core::contracts::ensures] //~ ERROR expected an `Fn(&_)` closure, found `()` fn b() {(loop)} //~^ ERROR expected `{`, found `)` //~| ERROR expected `{`, found `)` diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr index f1ffda2a9bee6..56dbdae14189b 100644 --- a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -16,7 +16,7 @@ LL | fn b() {(loop)} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: expected a `Fn(&_)` closure, found `()` +error[E0277]: expected an `Fn(&_)` closure, found `()` --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 | LL | #[core::contracts::ensures] diff --git a/tests/ui/methods/filter-relevant-fn-bounds.rs b/tests/ui/methods/filter-relevant-fn-bounds.rs index 7945eaa0daa27..a23f4600d6d1b 100644 --- a/tests/ui/methods/filter-relevant-fn-bounds.rs +++ b/tests/ui/methods/filter-relevant-fn-bounds.rs @@ -16,5 +16,5 @@ impl Wrapper { fn main() { let mut wrapper = Wrapper; wrapper.do_something_wrapper(|value| ()); - //~^ ERROR expected a `FnOnce + //~^ ERROR expected an `FnOnce } diff --git a/tests/ui/methods/filter-relevant-fn-bounds.stderr b/tests/ui/methods/filter-relevant-fn-bounds.stderr index 4efe40ae0903b..9ad0319440e9a 100644 --- a/tests/ui/methods/filter-relevant-fn-bounds.stderr +++ b/tests/ui/methods/filter-relevant-fn-bounds.stderr @@ -12,7 +12,7 @@ help: consider further restricting type parameter `F` with trait `Output` LL | F: for<'a> FnOnce(>::Type) + for<'a> Output<'a>, | ++++++++++++++++++++ -error[E0277]: expected a `FnOnce(<{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41} as Output<'a>>::Type)` closure, found `{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41}` +error[E0277]: expected an `FnOnce(<{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41} as Output<'a>>::Type)` closure, found `{closure@$DIR/filter-relevant-fn-bounds.rs:18:34: 18:41}` --> $DIR/filter-relevant-fn-bounds.rs:18:34 | LL | wrapper.do_something_wrapper(|value| ()); diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs index 0a98cdef5c923..f204e55f5a578 100644 --- a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.rs @@ -16,7 +16,7 @@ struct Bar; fn main() { let foo: Box usize> = Box::new(Bar); - //~^ ERROR expected a `Fn(bool)` closure, found `Bar` + //~^ ERROR expected an `Fn(bool)` closure, found `Bar` foo.borrow(); //~^ ERROR no method named `borrow` found foo.take() diff --git a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr index aa46a41fa3378..73f5f441b3fef 100644 --- a/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr +++ b/tests/ui/methods/method-suggestion-trait-with-extra-generics-no-ice.stderr @@ -22,7 +22,7 @@ help: there is a method `borrow_mut` with a similar name LL | foo.borrow_mut(); | ++++ -error[E0277]: expected a `Fn(bool)` closure, found `Bar` +error[E0277]: expected an `Fn(bool)` closure, found `Bar` --> $DIR/method-suggestion-trait-with-extra-generics-no-ice.rs:18:43 | LL | let foo: Box usize> = Box::new(Bar); diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const.rs b/tests/ui/methods/supertrait-shadowing/assoc-const.rs deleted file mode 100644 index 01d045b9fb74f..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/assoc-const.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![allow(dead_code)] - -trait A { - const CONST: i32; -} -impl A for T { - const CONST: i32 = 1; -} - -trait B: A { - const CONST: i32; -} -impl B for T { - const CONST: i32 = 2; -} - -fn main() { - assert_eq!(i32::CONST, 2) -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs deleted file mode 100644 index ce56c25df19ea..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs +++ /dev/null @@ -1,33 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `C` shadows identically named item - "C" - } -} -impl C for T {} - -fn main() { - assert_eq!(().hello(), "C"); - //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs deleted file mode 100644 index b29f3c8d014ec..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs +++ /dev/null @@ -1,43 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `C` shadows identically named item - "C" - } -} -impl C for T {} - -// `D` extends `C` which extends `B` and `A` - -trait D: C { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `D` shadows identically named item - "D" - } -} -impl D for T {} - -fn main() { - assert_eq!(().hello(), "D"); - //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr deleted file mode 100644 index 28fe7f72f94c3..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr +++ /dev/null @@ -1,68 +0,0 @@ -warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:23:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:5:9 - | -LL | #![warn(shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:33:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `C`, `B`, and `A` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: trait item `hello` from `D` shadows identically named item from supertrait - --> $DIR/common-ancestor-3.rs:41:19 - | -LL | assert_eq!(().hello(), "D"); - | ^^^^^ - | -note: item from `D` shadows a supertrait item - --> $DIR/common-ancestor-3.rs:33:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: items from several supertraits are shadowed: `A`, `B`, and `C` - --> $DIR/common-ancestor-3.rs:9:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/common-ancestor-3.rs:4:9 - | -LL | #![warn(resolving_to_items_shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: 3 warnings emitted - diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor.rs deleted file mode 100644 index b288d6e22b8c3..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ run-pass - -#![feature(supertrait_item_shadowing)] -#![warn(resolving_to_items_shadowing_supertrait_items)] -#![warn(shadowing_supertrait_items)] -#![allow(dead_code)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B: A { - fn hello(&self) -> &'static str { - //~^ WARN trait item `hello` from `B` shadows identically named item - "B" - } -} -impl B for T {} - -fn main() { - assert_eq!(().hello(), "B"); - //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait -} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.rs b/tests/ui/methods/supertrait-shadowing/definition-site.rs deleted file mode 100644 index 248df032736f6..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/definition-site.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![feature(supertrait_item_shadowing)] -#![deny(shadowing_supertrait_items)] - -trait SuperSuper { - fn method(); -} - -trait Super: SuperSuper { - fn method(); - //~^ ERROR trait item `method` from `Super` shadows identically named item -} - -trait Sub: Super { - fn method(); - //~^ ERROR trait item `method` from `Sub` shadows identically named item -} - -fn main() {} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.stderr b/tests/ui/methods/supertrait-shadowing/definition-site.stderr deleted file mode 100644 index 1e35a753a9ebc..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/definition-site.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: trait item `method` from `Super` shadows identically named item from supertrait - --> $DIR/definition-site.rs:9:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ - | -note: item from `SuperSuper` is shadowed by a subtrait item - --> $DIR/definition-site.rs:5:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/definition-site.rs:2:9 - | -LL | #![deny(shadowing_supertrait_items)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: trait item `method` from `Sub` shadows identically named item from supertrait - --> $DIR/definition-site.rs:14:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ - | -note: items from several supertraits are shadowed: `Super` and `SuperSuper` - --> $DIR/definition-site.rs:5:5 - | -LL | fn method(); - | ^^^^^^^^^^^^ -... -LL | fn method(); - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs deleted file mode 100644 index b49476c7a4f68..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(supertrait_item_shadowing)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -trait C: A + B { - fn hello(&self) -> &'static str { - "C" - } -} -impl C for T {} - -// Since `D` is not a subtrait of `C`, -// we have no obvious lower bound. - -trait D: B { - fn hello(&self) -> &'static str { - "D" - } -} -impl D for T {} - -fn main() { - ().hello(); - //~^ ERROR multiple applicable items in scope -} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr deleted file mode 100644 index 745f61d00d660..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr +++ /dev/null @@ -1,50 +0,0 @@ -error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor-2.rs:35:8 - | -LL | ().hello(); - | ^^^^^ multiple `hello` found - | -note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor-2.rs:4:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor-2.rs:11:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #3 is defined in an impl of the trait `C` for the type `T` - --> $DIR/no-common-ancestor-2.rs:18:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #4 is defined in an impl of the trait `D` for the type `T` - --> $DIR/no-common-ancestor-2.rs:28:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method for candidate #1 - | -LL - ().hello(); -LL + A::hello(&()); - | -help: disambiguate the method for candidate #2 - | -LL - ().hello(); -LL + B::hello(&()); - | -help: disambiguate the method for candidate #3 - | -LL - ().hello(); -LL + C::hello(&()); - | -help: disambiguate the method for candidate #4 - | -LL - ().hello(); -LL + D::hello(&()); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs deleted file mode 100644 index 03c822eb7375e..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![feature(supertrait_item_shadowing)] - -trait A { - fn hello(&self) -> &'static str { - "A" - } -} -impl A for T {} - -trait B { - fn hello(&self) -> &'static str { - "B" - } -} -impl B for T {} - -fn main() { - ().hello(); - //~^ ERROR multiple applicable items in scope -} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr deleted file mode 100644 index 29cf7ff1dbb11..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0034]: multiple applicable items in scope - --> $DIR/no-common-ancestor.rs:18:8 - | -LL | ().hello(); - | ^^^^^ multiple `hello` found - | -note: candidate #1 is defined in an impl of the trait `A` for the type `T` - --> $DIR/no-common-ancestor.rs:4:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `B` for the type `T` - --> $DIR/no-common-ancestor.rs:11:5 - | -LL | fn hello(&self) -> &'static str { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: disambiguate the method for candidate #1 - | -LL - ().hello(); -LL + A::hello(&()); - | -help: disambiguate the method for candidate #2 - | -LL - ().hello(); -LL + B::hello(&()); - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs deleted file mode 100644 index 4112634399b78..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@ run-pass - -#![allow(dead_code)] - -mod out_of_scope { - pub trait Subtrait: super::Supertrait { - fn hello(&self) -> &'static str { - "subtrait" - } - } - impl Subtrait for T {} -} - -trait Supertrait { - fn hello(&self) -> &'static str { - "supertrait" - } -} -impl Supertrait for T {} - -fn main() { - assert_eq!(().hello(), "supertrait"); -} diff --git a/tests/ui/methods/supertrait-shadowing/type-dependent.rs b/tests/ui/methods/supertrait-shadowing/type-dependent.rs deleted file mode 100644 index 4a5af1d598812..0000000000000 --- a/tests/ui/methods/supertrait-shadowing/type-dependent.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ run-pass - -// Makes sure we can shadow with type-dependent method syntax. - -#![feature(supertrait_item_shadowing)] -#![allow(dead_code)] - -trait A { - fn hello() -> &'static str { - "A" - } -} -impl A for T {} - -trait B: A { - fn hello() -> &'static str { - "B" - } -} -impl B for T {} - -fn foo() -> &'static str { - T::hello() -} - -fn main() { - assert_eq!(foo::<()>(), "B"); -} diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index c489b6e445dda..b1158e1cbffa7 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -5,10 +5,10 @@ fn main() { let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 412fd53baeb37..eab9bf1ae34a8 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` +error[E0277]: expected an `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:29 | LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); @@ -23,7 +23,7 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0277]: expected a `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:10:29: 10:40}` +error[E0277]: expected an `FnMut(&{integer})` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:10:29: 10:40}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:10:29 | LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 434b5d3d23110..09f7af7c0253e 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -5,10 +5,10 @@ fn main() { let _ = (-10..=10).find(|x: i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:6:29} as FnOnce<(&{integer},)>>::Output == bool` let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR: expected a `FnMut(&{integer})` closure, found + //[next]~^^ ERROR: expected an `FnMut(&{integer})` closure, found //[next]~| ERROR: type mismatch resolving `<{closure@closure-arg-type-mismatch-issue-45727.rs:10:29} as FnOnce<(&{integer},)>>::Output == bool` } diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs index 42adff43a58f2..445a98cd49630 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -24,9 +24,9 @@ fn main() { let _ = produces_string().and_then(takes_str_but_too_many_refs); //~^ ERROR type mismatch in function arguments let _ = produces_string().and_then(takes_str_but_wrong_abi); - //~^ ERROR expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + //~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` let _ = produces_string().and_then(takes_str_but_unsafe); - //~^ ERROR expected a `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + //~^ ERROR expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` let _ = produces_string().and_then(no_args); //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index a657a65c426d4..14c821b260299 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -18,7 +18,7 @@ help: consider wrapping the function in a closure LL | let _ = produces_string().and_then(|arg0: String| takes_str_but_too_many_refs(/* &&str */)); | ++++++++++++++ +++++++++++++ -error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` +error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` --> $DIR/suggest-option-asderef-unfixable.rs:26:40 | LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); @@ -30,7 +30,7 @@ LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL -error[E0277]: expected a `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` +error[E0277]: expected an `FnOnce(String)` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` --> $DIR/suggest-option-asderef-unfixable.rs:28:40 | LL | let _ = produces_string().and_then(takes_str_but_unsafe); diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs new file mode 100644 index 0000000000000..694c95da7b482 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.rs @@ -0,0 +1,20 @@ +// Regression test for https://github.com/rust-lang/rust/issues/148631 + +struct C; + +struct S(T); + +trait Tr {} + +impl Clone for S +//~^ ERROR the type parameter `T` is not constrained +where + S: Tr, +{ + fn clone(&self) -> Self { + *self + //~^ ERROR cannot move out of `*self` + } +} + +fn main() {} diff --git a/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr new file mode 100644 index 0000000000000..9cf17045b0296 --- /dev/null +++ b/tests/ui/moves/unconstrained-impl-param-clone-suggestion.stderr @@ -0,0 +1,19 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:9:6 + | +LL | impl Clone for S + | -^- + | || + | |unconstrained type parameter + | help: remove the unused type parameter `T` + +error[E0507]: cannot move out of `*self` which is behind a shared reference + --> $DIR/unconstrained-impl-param-clone-suggestion.rs:15:9 + | +LL | *self + | ^^^^^ move occurs because `*self` has type `S`, which does not implement the `Copy` trait + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0207, E0507. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/offset-of/inside-array-length.stderr b/tests/ui/offset-of/inside-array-length.stderr index 0305603c1f547..0c430f0424ced 100644 --- a/tests/ui/offset-of/inside-array-length.stderr +++ b/tests/ui/offset-of/inside-array-length.stderr @@ -37,7 +37,7 @@ LL | fn foo<'a, T: 'a>(_: [(); std::mem::offset_of!((T,), 0)]) {} = note: ...which requires caching mir of `foo::{constant#0}` for CTFE... = note: ...which requires elaborating drops for `foo::{constant#0}`... = note: ...which requires borrow-checking `foo::{constant#0}`... - = note: ...which requires normalizing `Binder { value: ConstEvaluatable(Unevaluated(No, Alias { kind: Anon { def_id: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}) }, args: ['^c_1, T/#1], .. })), bound_vars: [] }`... + = note: ...which requires normalizing `Binder { value: ConstEvaluatable(AliasConst(No, Alias { kind: Anon { def_id: DefId(0:7 ~ inside_array_length[07d6]::foo::{constant#0}) }, args: ['^c_1, T/#1], .. })), bound_vars: [] }`... = note: ...which again requires evaluating type-level constant, completing the cycle = note: cycle used when normalizing `inside_array_length::::foo::{constant#0}` = note: for more information, see and diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.rs b/tests/ui/panic-handler/panic-handler-with-target-feature.rs index aec00c637bedb..6e81516a91628 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.rs +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.rs @@ -8,7 +8,7 @@ use core::panic::PanicInfo; #[panic_handler] #[target_feature(enable = "avx2")] -//~^ ERROR `#[panic_handler]` function is not allowed to have `#[target_feature]` +//~^ ERROR `#[target_feature]` cannot be applied to a `#[panic_handler]` function fn panic(info: &PanicInfo) -> ! { unimplemented!(); } diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr index ddf0ae77a0a1a..93cf164618ed9 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr @@ -1,11 +1,13 @@ -error: `#[panic_handler]` function is not allowed to have `#[target_feature]` +error: `#[target_feature]` cannot be applied to a `#[panic_handler]` function --> $DIR/panic-handler-with-target-feature.rs:10:1 | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "avx2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | fn panic(info: &PanicInfo) -> ! { - | ------------------------------- `#[panic_handler]` function is not allowed to have `#[target_feature]` +LL | / fn panic(info: &PanicInfo) -> ! { +LL | | unimplemented!(); +LL | | } + | |_- `#[panic_handler]` function is not allowed to have `#[target_feature]` error: aborting due to 1 previous error diff --git a/tests/ui/parser/eq-gt-to-gt-eq.fixed b/tests/ui/parser/eq-gt-to-gt-eq.fixed index abb328399be2a..93940a5971f08 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.fixed +++ b/tests/ui/parser/eq-gt-to-gt-eq.fixed @@ -43,3 +43,9 @@ fn b() { _ => todo!(), } } + +fn closure() { + let a = 0; + let b = 1; + let _ = [a].iter().any(|x| x >= &b); //~ERROR +} diff --git a/tests/ui/parser/eq-gt-to-gt-eq.rs b/tests/ui/parser/eq-gt-to-gt-eq.rs index 1f57fa8328198..92ca43ba3155b 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.rs +++ b/tests/ui/parser/eq-gt-to-gt-eq.rs @@ -43,3 +43,9 @@ fn b() { _ => todo!(), } } + +fn closure() { + let a = 0; + let b = 1; + let _ = [a].iter().any(|x| x => &b); //~ERROR +} diff --git a/tests/ui/parser/eq-gt-to-gt-eq.stderr b/tests/ui/parser/eq-gt-to-gt-eq.stderr index aa47ddecce9ef..d5cf71db2df38 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.stderr +++ b/tests/ui/parser/eq-gt-to-gt-eq.stderr @@ -109,5 +109,17 @@ LL - match a => b { LL + match a >= b { | -error: aborting due to 7 previous errors +error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>` + --> $DIR/eq-gt-to-gt-eq.rs:50:34 + | +LL | let _ = [a].iter().any(|x| x => &b); + | ^^ expected one of 8 possible tokens + | +help: you might have meant to write a "greater than or equal to" comparison + | +LL - let _ = [a].iter().any(|x| x => &b); +LL + let _ = [a].iter().any(|x| x >= &b); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/issues/issue-28498-must-work-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs similarity index 63% rename from tests/ui/issues/issue-28498-must-work-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs index 813cd8645f923..9afd7ddbd88a9 100644 --- a/tests/ui/issues/issue-28498-must-work-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex1.rs @@ -1,8 +1,7 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-must-continue-to-work use std::cell::Cell; diff --git a/tests/ui/issues/issue-28498-must-work-ex2.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs similarity index 67% rename from tests/ui/issues/issue-28498-must-work-ex2.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs index 98514b1163675..4e8d403672641 100644 --- a/tests/ui/issues/issue-28498-must-work-ex2.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs @@ -1,8 +1,7 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-must-continue-to-work use std::cell::Cell; diff --git a/tests/ui/span/issue28498-reject-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs similarity index 76% rename from tests/ui/span/issue28498-reject-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs index 4d1b4125503b6..3209a17041f67 100644 --- a/tests/ui/span/issue28498-reject-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.rs @@ -1,9 +1,7 @@ -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #examples-of-code-that-will-start-to-be-rejected - -// Compare against test/run-pass/issue28498-must-work-ex2.rs +//! Test for . +//! Example taken from RFC 1238 text +//! . +//! Compare against tests/ui/rfcs/rfc-1238-nonparametric-dropck/must-work-ex2.rs. use std::cell::Cell; diff --git a/tests/ui/span/issue28498-reject-ex1.stderr b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr similarity index 93% rename from tests/ui/span/issue28498-reject-ex1.stderr rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr index 7b8af23a6cd7b..6e20f06d7fe4c 100644 --- a/tests/ui/span/issue28498-reject-ex1.stderr +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/reject-ex1.stderr @@ -1,5 +1,5 @@ error[E0713]: borrow may still be in use when destructor runs - --> $DIR/issue28498-reject-ex1.rs:34:29 + --> $DIR/reject-ex1.rs:32:29 | LL | foo.data[0].1.set(Some(&foo.data[1])); | ^^^^^^^^ diff --git a/tests/ui/issues/issue-28498-ugeh-ex1.rs b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs similarity index 73% rename from tests/ui/issues/issue-28498-ugeh-ex1.rs rename to tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs index f606d2489484a..722523c094329 100644 --- a/tests/ui/issues/issue-28498-ugeh-ex1.rs +++ b/tests/ui/rfcs/rfc-1238-nonparametric-dropck/ugeh-ex1.rs @@ -1,10 +1,8 @@ +//! Test for . +//! Example taken from RFC 1238 text +//! . //@ run-pass -// Example taken from RFC 1238 text - -// https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md -// #example-of-the-unguarded-escape-hatch - #![feature(dropck_eyepatch)] use std::cell::Cell; diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 82053a12b133f..b7eaabdf6ad3f 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -26,15 +26,15 @@ fn call_once_i32(f: impl FnOnce(i32)) { } fn main() { - call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}` - call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}` - call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` - call_once_i32(bar); //~ ERROR expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + call(foo); //~ ERROR expected an `Fn()` closure, found `#[target_features] fn() {foo}` + call_mut(foo); //~ ERROR expected an `FnMut()` closure, found `#[target_features] fn() {foo}` + call_once(foo); //~ ERROR expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` + call_once_i32(bar); //~ ERROR expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` call(foo_unsafe); - //~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `Fn()` closure, found `unsafe fn() {foo_unsafe}` call_mut(foo_unsafe); - //~^ ERROR expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `FnMut()` closure, found `unsafe fn() {foo_unsafe}` call_once(foo_unsafe); - //~^ ERROR expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` + //~^ ERROR expected an `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 52543caad37db..5333307a59280 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `Fn()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:29:10 | LL | call(foo); @@ -16,7 +16,7 @@ note: required by a bound in `call` LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `FnMut()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:30:14 | LL | call_mut(foo); @@ -34,7 +34,7 @@ note: required by a bound in `call_mut` LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` +error[E0277]: expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` --> $DIR/fn-traits.rs:31:15 | LL | call_once(foo); @@ -52,7 +52,7 @@ note: required by a bound in `call_once` LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` -error[E0277]: expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` +error[E0277]: expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` --> $DIR/fn-traits.rs:32:19 | LL | call_once_i32(bar); @@ -69,7 +69,7 @@ note: required by a bound in `call_once_i32` LL | fn call_once_i32(f: impl FnOnce(i32)) { | ^^^^^^^^^^^ required by this bound in `call_once_i32` -error[E0277]: expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:34:10 | LL | call(foo_unsafe); @@ -88,7 +88,7 @@ note: required by a bound in `call` LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `FnMut()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:36:14 | LL | call_mut(foo_unsafe); @@ -107,7 +107,7 @@ note: required by a bound in `call_mut` LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` +error[E0277]: expected an `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` --> $DIR/fn-traits.rs:38:15 | LL | call_once(foo_unsafe); diff --git a/tests/ui/issues/issue-28472.rs b/tests/ui/span/foreign-item-vis-span.rs similarity index 55% rename from tests/ui/issues/issue-28472.rs rename to tests/ui/span/foreign-item-vis-span.rs index 6db1f9a5bddf9..839e07ea1b961 100644 --- a/tests/ui/issues/issue-28472.rs +++ b/tests/ui/span/foreign-item-vis-span.rs @@ -1,4 +1,5 @@ -// Check that the visibility modifier is included in the span of foreign items. +//! Regression test for . +//! Check that the visibility modifier is included in the span of foreign items. extern "C" { fn foo(); diff --git a/tests/ui/issues/issue-28472.stderr b/tests/ui/span/foreign-item-vis-span.stderr similarity index 90% rename from tests/ui/issues/issue-28472.stderr rename to tests/ui/span/foreign-item-vis-span.stderr index 051ed25b6c9d9..912a338e4616a 100644 --- a/tests/ui/issues/issue-28472.stderr +++ b/tests/ui/span/foreign-item-vis-span.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `foo` is defined multiple times - --> $DIR/issue-28472.rs:6:3 + --> $DIR/foreign-item-vis-span.rs:7:3 | LL | fn foo(); | --------- previous definition of the value `foo` here @@ -11,7 +11,7 @@ LL | | fn foo(); = note: `foo` must be defined only once in the value namespace of this module error[E0428]: the name `foo` is defined multiple times - --> $DIR/issue-28472.rs:9:3 + --> $DIR/foreign-item-vis-span.rs:10:3 | LL | fn foo(); | --------- previous definition of the value `foo` here diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs new file mode 100644 index 0000000000000..31767f11d999b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -0,0 +1,199 @@ +// ignore-tidy-linelength +//! Test `#[splat]` fails over the 255th argument index (or higher). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + +#[rustfmt::skip] +fn s_255_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 +) {} + +#[rustfmt::skip] +fn s_256_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 +) {} + +#[rustfmt::skip] +fn s_255_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_256_non_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + _: A, +) {} + +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 255 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + /* missing: a, */ + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 257 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, /* unexpected: */ a, + ); + + #[rustfmt::skip] + more_than_255_splatted_args( //~ ERROR this splatted function takes 256 arguments, but 512 were provided [E0057] + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + /* unexpected: */ + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr new file mode 100644 index 0000000000000..da62da8aa605b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -0,0 +1,71 @@ +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:50:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:71:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 255 + --> $DIR/splat-255-limit-fail.rs:91:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error: `#[splat]` is not supported on argument index 256 + --> $DIR/splat-255-limit-fail.rs:113:5 + | +LL | #[splat] (_a, _b): (u32, i8), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | + = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list + +error[E0057]: this splatted function takes 256 arguments, but 255 were provided + --> $DIR/splat-255-limit-fail.rs:124:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | /* missing: a, */ +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 257 were provided + --> $DIR/splat-255-limit-fail.rs:144:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, /* unexpected: */ a, +LL | | ); + | |_____^ + +error[E0057]: this splatted function takes 256 arguments, but 512 were provided + --> $DIR/splat-255-limit-fail.rs:164:5 + | +LL | / more_than_255_splatted_args( +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +LL | | a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, +... | +LL | | a, +LL | | ); + | |_____^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-255-limit-pass.rs b/tests/ui/splat/splat-255-limit-pass.rs new file mode 100644 index 0000000000000..9713616830f9b --- /dev/null +++ b/tests/ui/splat/splat-255-limit-pass.rs @@ -0,0 +1,201 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test `#[splat]` on the 255th argument index (or lower). +//! FIXME(splat): The 255 argument limit is a temporary performance hack. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(dead_code)] + +type A = (); + +// These types and functions are deliberately formatted with 17 arguments in 15 lines, to show they +// have ~255 arguments. +#[rustfmt::skip] +type Tuple256 = ( + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, + A, +); + +#[rustfmt::skip] +fn s_253_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_terminal( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), +) {} + +#[rustfmt::skip] +fn s_254_non_terminal_272_args( + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_253_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_254_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +#[rustfmt::skip] +fn s_0_initial_255_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, +) {} + +// It's only the splatted index that's constrained to 255, not the argument count of the caller or callee. +#[rustfmt::skip] +fn s_0_initial_256_args( + #[splat] (_a, _b): (u32, i8), + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, + _: A, +) {} + +fn more_than_255_splatted_args(#[splat] _t: Tuple256) {} + +fn main() { + let a = (); + + #[rustfmt::skip] + more_than_255_splatted_args( + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, + a, + ); +} diff --git a/tests/ui/splat/splat-assoc-fn-tuple-simple.rs b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs new file mode 100644 index 0000000000000..d2681c0d2574c --- /dev/null +++ b/tests/ui/splat/splat-assoc-fn-tuple-simple.rs @@ -0,0 +1,22 @@ +//@ run-pass +//! Test using `#[splat]` on associated function tuple arguments (no receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_1(#[splat] (_a,): (u32,)) {} + + fn tuple_3(#[splat] (_a, _b, _c): (u32, i32, i8)) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1((1u32,)); + + Foo::tuple_1(1u32); + Foo::tuple_3(1u32, 2i32, 3i8); +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs new file mode 100644 index 0000000000000..a1f67dfe606d6 --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -0,0 +1,17 @@ +//@ edition:2024 +//! Test that using `#[splat]` incorrectly on async functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + async_wrong_type(1u32); + async_multi_splat(1u32, 2i8, 3u32, 4i8); + //~^ ERROR this splatted function takes 3 arguments, but 4 were provided +} diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..e643b29c88c8a --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -0,0 +1,27 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-async-fn-tuple-fail.rs:10:28 + | +LL | async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-async-fn-tuple-fail.rs:7:40 + | +LL | async fn async_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | async_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 3 arguments, but 4 were provided + --> $DIR/splat-async-fn-tuple-fail.rs:15:5 + | +LL | async_multi_splat(1u32, 2i8, 3u32, 4i8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277. +For more information about an error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-async-fn-tuple.rs b/tests/ui/splat/splat-async-fn-tuple.rs new file mode 100644 index 0000000000000..81e56193bd4ab --- /dev/null +++ b/tests/ui/splat/splat-async-fn-tuple.rs @@ -0,0 +1,18 @@ +//@ run-pass +//@ edition:2024 +//! Test using `#[splat]` on tuple arguments of async functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +async fn async_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +async fn async_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + let _ = async_tuple_args(1u32, 2i8); + let _ = async_tuple_args(1, 2); + + let _ = async_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + let _ = async_splat_non_terminal_arg(1, 2, 3.5); +} diff --git a/tests/ui/splat/splat-cannot-resolve.rs b/tests/ui/splat/splat-cannot-resolve.rs new file mode 100644 index 0000000000000..1c22a53f82916 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.rs @@ -0,0 +1,49 @@ +//! Test that using `#[splat]` on un-resolvable types is an error. + +#![allow(incomplete_features)] +#![allow(unconditional_recursion)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn tuple(#[splat] t: impl Sized) -> impl Sized { + //~^ ERROR cannot resolve opaque type + tuple(tuple((t, ()))) +} + +fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + //~^ ERROR cannot resolve opaque type + tuple_trait(tuple_trait((t, ()))) +} + +trait Trait { + type MaybeTup; + type Tup: std::marker::Tuple; +} + +fn ambig(#[splat] t: Trait::MaybeTup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +fn ambig_tup(#[splat] t: Trait::Tup) {} +//~^ ERROR ambiguous associated type +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a +//~| ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a + +fn main() { + tuple(); + tuple_trait(); + ambig(); + ambig_tup(); + + tuple(1); + tuple_trait(1); + ambig(1); + ambig_tup(1); + + tuple(1, 2.0); + tuple_trait(1, 2.0); + ambig(1, 2.0); + ambig_tup(1, 2.0); +} diff --git a/tests/ui/splat/splat-cannot-resolve.stderr b/tests/ui/splat/splat-cannot-resolve.stderr new file mode 100644 index 0000000000000..f91267d37dbc8 --- /dev/null +++ b/tests/ui/splat/splat-cannot-resolve.stderr @@ -0,0 +1,94 @@ +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig(#[splat] t: Trait::MaybeTup) {} +LL + fn ambig(#[splat] t: ::MaybeTup) {} + | + +error[E0223]: ambiguous associated type + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ + | +help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path + | +LL - fn ambig_tup(#[splat] t: Trait::Tup) {} +LL + fn ambig_tup(#[splat] t: ::Tup) {} + | + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:8:37 + | +LL | fn tuple(#[splat] t: impl Sized) -> impl Sized { + | ^^^^^^^^^^ + +error[E0720]: cannot resolve opaque type + --> $DIR/splat-cannot-resolve.rs:13:56 + | +LL | fn tuple_trait(#[splat] t: impl std::marker::Tuple) -> impl std::marker::Tuple { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(); + | ^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(); + | ^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1); + | ^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1); + | ^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:23:22 + | +LL | fn ambig(#[splat] t: Trait::MaybeTup) {} + | ^^^^^^^^^^^^^^^ +... +LL | ambig(1, 2.0); + | ^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a {type error} ({type error}) + --> $DIR/splat-cannot-resolve.rs:28:26 + | +LL | fn ambig_tup(#[splat] t: Trait::Tup) {} + | ^^^^^^^^^^ +... +LL | ambig_tup(1, 2.0); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0223, E0277, E0720. +For more information about an error, try `rustc --explain E0223`. diff --git a/tests/ui/splat/splat-const-fn-tuple-generic.rs b/tests/ui/splat/splat-const-fn-tuple-generic.rs new file mode 100644 index 0000000000000..646bf7e9b9f19 --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple-generic.rs @@ -0,0 +1,34 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions with generics. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Generic type in first position +const fn const_generic_first(#[splat] _: (T, u32)) {} + +// Generic type in second position +const fn const_generic_second(#[splat] _: (u32, T)) {} + +// Multiple generic types +const fn const_generic_both(#[splat] _: (T, U)) {} + +// Generic with extra non-splatted arg +const fn const_generic_extra(#[splat] _: (T, u32), _extra: i32) {} + +fn main() { + const_generic_first(1i8, 2u32); + const_generic_first(true, 2u32); + const_generic_first(1u64, 2u32); + + const_generic_second(1u32, 2i8); + const_generic_second(1u32, true); + const_generic_second(1u32, 2u64); + + const_generic_both(1u32, 2i8); + const_generic_both(true, 2u64); + const_generic_both(1i8, false); + + const_generic_extra(1i8, 2u32, 42i32); + const_generic_extra(true, 2u32, 42i32); +} diff --git a/tests/ui/splat/splat-const-fn-tuple.rs b/tests/ui/splat/splat-const-fn-tuple.rs new file mode 100644 index 0000000000000..f4ae2e124a72d --- /dev/null +++ b/tests/ui/splat/splat-const-fn-tuple.rs @@ -0,0 +1,17 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of const functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +const fn sum(#[splat] (a, b): (u32, u32)) -> u32 { + a + b +} + +const RESULT: u32 = sum(1, 2); + +fn main() { + assert_eq!(RESULT, 3); + assert_eq!(sum(12, 18) , 30); + assert_eq!(sum(1, 2), 3); +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.rs b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs new file mode 100644 index 0000000000000..40d702346b7a3 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.rs @@ -0,0 +1,14 @@ +//! Test that `#[splat]` on `&dyn AsRef` where `T: Tuple` is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} +//~^ ERROR cannot use splat attribute + +fn main() { + let s: String = "hello".to_owned(); + dyn_asref_splat::(&s); + //~^ ERROR `String` is not a tuple +} diff --git a/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr new file mode 100644 index 0000000000000..cba1ab28181a4 --- /dev/null +++ b/tests/ui/splat/splat-dyn-asref-tuple-fail.stderr @@ -0,0 +1,24 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a &'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3 (&'?3 dyn [Binder { value: Trait(std::convert::AsRef), bound_vars: [] }] + '?3) + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:36 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^ +... +LL | dyn_asref_splat::(&s); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `String` is not a tuple + --> $DIR/splat-dyn-asref-tuple-fail.rs:12:23 + | +LL | dyn_asref_splat::(&s); + | ^^^^^^ the nightly-only, unstable trait `std::marker::Tuple` is not implemented for `String` + | +note: required by a bound in `dyn_asref_splat` + --> $DIR/splat-dyn-asref-tuple-fail.rs:7:60 + | +LL | fn dyn_asref_splat(#[splat] _t: &dyn AsRef) where T: std::marker::Tuple {} + | ^^^^^^^^^^^^^^^^^^ required by this bound in `dyn_asref_splat` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs new file mode 100644 index 0000000000000..297dbc0457794 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -0,0 +1,53 @@ +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +//! Test using `#[splat]` on tuple arguments of simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + // Functions + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8)) = tuple_args; + fn_ptr(1, 2); //~ ERROR no splatted def for function or method callee + fn_ptr(1u32, 2i8); + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //fn_ptr((1, 2)); // ERROR this splatted function takes 2 arguments, but 1 was provided + + #[rustfmt::skip] + let fn_ptr: fn(#[splat] (u32, i8), f64) = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); + + // Function pointers + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + (*fn_ptr)(1, 2); + (*fn_ptr)(1u32, 2i8); + + #[rustfmt::skip] + let fn_ptr: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + (*fn_ptr)(1, 2, 3.5); + (*fn_ptr)(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr new file mode 100644 index 0000000000000..a69175bd5e886 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -0,0 +1,23 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee + | +LL | fn_ptr(1, 2); + | ^^^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main` +#1 [check_unsafety] unsafety-checking `main` +#2 [analysis] running analysis passes on crate `splat_fn_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.rs b/tests/ui/splat/splat-fn-tuple-generic-fail.rs new file mode 100644 index 0000000000000..8a8651e75c9ff --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.rs @@ -0,0 +1,27 @@ +//! Test failing use of `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // FIXME(splat): Make the splat generic handling code handle tuples inside tuples + // (if we want to support tupled calls) + splat_generic_tuple::<(((u32, i8)))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<((u32, i8))>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + + splat_generic_tuple::<(u32, i8)>((1, 2)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided + splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); //~ ERROR this splatted function takes 2 arguments, but 1 was provided +} diff --git a/tests/ui/splat/splat-fn-tuple-generic-fail.stderr b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr new file mode 100644 index 0000000000000..7fd4a0719b493 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic-fail.stderr @@ -0,0 +1,39 @@ +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:19:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:20:5 + | +LL | splat_generic_tuple::<(((u32, i8)))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:22:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:23:5 + | +LL | splat_generic_tuple::<((u32, i8))>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:25:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1, 2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0057]: this splatted function takes 2 arguments, but 1 was provided + --> $DIR/splat-fn-tuple-generic-fail.rs:26:5 + | +LL | splat_generic_tuple::<(u32, i8)>((1u32, 2i8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/splat-fn-tuple-generic.rs b/tests/ui/splat/splat-fn-tuple-generic.rs new file mode 100644 index 0000000000000..b7e3615f62c45 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-generic.rs @@ -0,0 +1,23 @@ +//@ run-pass +//! Test using `#[splat]` on tuple trait arguments of generic functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn splat_generic_tuple(#[splat] _t: T) {} + +fn main() { + // Calling with un-splatted arguments might look like it works, but the actual generic type is + // a tuple inside another tuple. Aren't generics great? + splat_generic_tuple((1, 2)); + splat_generic_tuple((1u32, 2i8)); + + // Generic tuple trait implementers are resolved during caller typeck. + splat_generic_tuple::<(u32, i8)>(1u32, 2i8); + splat_generic_tuple(1u32, 2i8); + splat_generic_tuple(1, 2); + + splat_generic_tuple::<()>(); + splat_generic_tuple(); +} diff --git a/tests/ui/splat/splat-fn-tuple-simple.rs b/tests/ui/splat/splat-fn-tuple-simple.rs new file mode 100644 index 0000000000000..c7234a15b9d55 --- /dev/null +++ b/tests/ui/splat/splat-fn-tuple-simple.rs @@ -0,0 +1,32 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of simple functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + tuple_args(1, 2); + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //tuple_args((1, 2)); + + tuple_args(1, 2); + tuple_args(1u32, 2i8); + + splat_non_terminal_arg(1, 2, 3.5); + splat_non_terminal_arg(1u32, 2i8, 3.5f64); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = tuple_args; + fn_ptr(1, 2); + fn_ptr(1u32, 2i8); + + #[expect(unused_variables, reason = "FIXME(splat or lint): this is obviously used")] + let fn_ptr = splat_non_terminal_arg; + fn_ptr(1, 2, 3.5); + fn_ptr(1u32, 2i8, 3.5f64); +} diff --git a/tests/ui/splat/splat-generics-complex-types.rs b/tests/ui/splat/splat-generics-complex-types.rs new file mode 100644 index 0000000000000..7d4490c18da75 --- /dev/null +++ b/tests/ui/splat/splat-generics-complex-types.rs @@ -0,0 +1,26 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with complex generic types inside the splatted tuple. + +#![allow(incomplete_features)] +#![feature(splat)] + +// Vec and Option inside splatted tuple +fn nested_generic(#[splat] _: (Vec, Option)) {} + +// Box inside splatted tuple +fn box_generic(#[splat] _: (Box, u32)) {} + +// Multiple complex generics +fn multi_generic(#[splat] _: (Vec, Option, Box)) {} + +fn main() { + nested_generic(vec![1u32, 2u32], Some(2i8)); + nested_generic(vec![1, 2, 3], None::); + nested_generic::(vec![], Some("hello")); + + box_generic(Box::new(1u32), 42u32); + box_generic(Box::new("hello"), 1u32); + + multi_generic(vec![1u32], Some(2i8), Box::new(3.0f64)); + multi_generic::(vec![], None::, Box::new("hello")); +} diff --git a/tests/ui/splat/splat-generics-everywhere.rs b/tests/ui/splat/splat-generics-everywhere.rs new file mode 100644 index 0000000000000..be4f917ce21df --- /dev/null +++ b/tests/ui/splat/splat-generics-everywhere.rs @@ -0,0 +1,64 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics in various positions. + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo(T); + +// FIXME(splat): also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +impl Foo { + fn new(t: T) -> Self { + Self(t) + } + + fn assoc(_u: U, #[splat] _s: ()) {} + + fn method(&self, _v: V, #[splat] _s: (u32, f64)) {} + + fn lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +// FIXME(splat): also add generics to the trait +// also add assoc/method with splatted generic tuple traits +// also add generics inside the splatted tuple +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: ()); + + fn trait_method(&self, x: X, #[splat] _s: (u32, f64)); + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: ()) {} + + fn trait_method(&self, _x: X, #[splat] _s: (u32, f64)) {} + + fn trait_lifetime<'a>(&self, #[splat] _s: (u32, f64, &'a str)) {} + + fn trait_const_generic(&self, #[splat] _s: (u32, f64, [u8; N])) {} +} + +fn main() { + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::::assoc(("u",)); + + Foo::::assoc("u"); + Foo::::trait_assoc("w"); + + let foo = Foo::new("t"); + foo.method("v", 1u32, 2.3); + foo.lifetime(1u32, 2.3, "asdf"); + foo.const_generic(1u32, 2.3, [1, 2, 3]); + + foo.trait_method("x", 42u32, 9.8); + foo.trait_lifetime(1u32, 2.3, "asdf"); + foo.trait_const_generic(1u32, 2.3, [1, 2, 3]); +} diff --git a/tests/ui/splat/splat-generics-inside-tuple.rs b/tests/ui/splat/splat-generics-inside-tuple.rs new file mode 100644 index 0000000000000..dce26e55fa2d9 --- /dev/null +++ b/tests/ui/splat/splat-generics-inside-tuple.rs @@ -0,0 +1,29 @@ +//@ run-pass +//! Test using `#[splat]` on tuples with generics inside the splatted tuple. +#![allow(incomplete_features)] +#![feature(splat)] + +fn generic_second(#[splat] _s: (u32, T)) {} + +fn generic_first(#[splat] _s: (T, u32)) {} + +fn generic_both(#[splat] _s: (T, U)) {} + +fn generic_triple(#[splat] _s: (T, U, V)) {} + +fn main() { + generic_second(1u32, 2i8); + generic_second(1u32, 2.0f64); + generic_second(1u32, "hello"); + + generic_first(1i8, 2u32); + generic_first(2.0f64, 2u32); + generic_first("hello", 2u32); + + generic_both(1u32, 2i8); + generic_both("hello", 2.0f64); + generic_both(true, "world"); + + generic_triple(1u32, 2.0f64, "hello"); + generic_triple(true, 42i32, 3.14f32); +} diff --git a/tests/ui/splat/splat-invalid-trait-impl.rs b/tests/ui/splat/splat-invalid-trait-impl.rs new file mode 100644 index 0000000000000..fe0d5ddc5a1c8 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.rs @@ -0,0 +1,27 @@ +//! Test that `#[splat]` trait impls with mismatched tuple element types are rejected. +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn method(#[splat] _: (u32, i8)); +} + +struct Foo; +struct Foo1; +struct Foo2; + +impl FooTrait for Foo { + fn method(#[splat] _: (u32, f32)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo1 { + fn method(#[splat] _: (f32, i8)) {} + //~^ ERROR method `method` has an incompatible type for trait +} + +impl FooTrait for Foo2 { + fn method(#[splat] _: (f32, f64)) {} + //~^ ERROR method `method` has an incompatible type for trait +} +fn main() {} diff --git a/tests/ui/splat/splat-invalid-trait-impl.stderr b/tests/ui/splat/splat-invalid-trait-impl.stderr new file mode 100644 index 0000000000000..684cc65d7a326 --- /dev/null +++ b/tests/ui/splat/splat-invalid-trait-impl.stderr @@ -0,0 +1,60 @@ +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:14:27 + | +LL | fn method(#[splat] _: (u32, f32)) {} + | ^^^^^^^^^^ expected `i8`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (_, i8))` + found signature `fn(#[splat] (_, f32))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (u32, f32)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:19:27 + | +LL | fn method(#[splat] _: (f32, i8)) {} + | ^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, _))` + found signature `fn(#[splat] (f32, _))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, i8)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error[E0053]: method `method` has an incompatible type for trait + --> $DIR/splat-invalid-trait-impl.rs:24:27 + | +LL | fn method(#[splat] _: (f32, f64)) {} + | ^^^^^^^^^^ expected `u32`, found `f32` + | +note: type in trait + --> $DIR/splat-invalid-trait-impl.rs:6:27 + | +LL | fn method(#[splat] _: (u32, i8)); + | ^^^^^^^^^ + = note: expected signature `fn(#[splat] (u32, i8))` + found signature `fn(#[splat] (f32, f64))` +help: change the parameter type to match the trait + | +LL - fn method(#[splat] _: (f32, f64)) {} +LL + fn method(#[splat] _: (u32, i8)) {} + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 314003459ef72..2c4bf57ef972a 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -30,4 +30,18 @@ extern "C" { fn bar_2(#[splat] _: (u32, i8)); } +trait FooTrait { + fn has_splat(#[splat] _: ()); + + fn no_splat(_: (u32, f64)); +} + +struct Foo; + +impl FooTrait for Foo { + fn has_splat(_: ()) {} //~ ERROR method `has_splat` has an incompatible type for trait + + fn no_splat(#[splat] _: (u32, f64)) {} //~ ERROR method `no_splat` has an incompatible type for trait +} + fn main() {} diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index d88cbf4ff0b6d..74c54c00cd285 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -73,5 +73,34 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | = help: remove `#[splat]` or remove `...` -error: aborting due to 9 previous errors +error[E0053]: method `has_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:42:5 + | +LL | fn has_splat(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-invalid.rs:34:5 + | +LL | fn has_splat(#[splat] _: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] ())` + found signature `fn(())` + +error[E0053]: method `no_splat` has an incompatible type for trait + --> $DIR/splat-invalid.rs:44:5 + | +LL | fn no_splat(#[splat] _: (u32, f64)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted + | +note: type in trait + --> $DIR/splat-invalid.rs:36:5 + | +LL | fn no_splat(_: (u32, f64)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn((_, _))` + found signature `fn(#[splat] (_, _))` + +error: aborting due to 11 previous errors +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-maybe-tuple.rs b/tests/ui/splat/splat-maybe-tuple.rs new file mode 100644 index 0000000000000..a74af66e9a874 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.rs @@ -0,0 +1,22 @@ +//! Test that using `#[splat]` on maybe-tuple generic function arguments is an error, +//! but only when the generics aren't tuples. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn unbound_generic_arg(#[splat] t: T) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +fn main() { + unbound_generic_arg(); + unbound_generic_arg::<()>(); + + unbound_generic_arg(1); + unbound_generic_arg::<(u32,)>(1); + + unbound_generic_arg(1, 2.0); + unbound_generic_arg::<(u32, f32)>(1, 2.0); + + // The error comes from this call + unbound_generic_arg::(1); +} diff --git a/tests/ui/splat/splat-maybe-tuple.stderr b/tests/ui/splat/splat-maybe-tuple.stderr new file mode 100644 index 0000000000000..9abacdd657b93 --- /dev/null +++ b/tests/ui/splat/splat-maybe-tuple.stderr @@ -0,0 +1,12 @@ +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-maybe-tuple.rs:8:39 + | +LL | fn unbound_generic_arg(#[splat] t: T) {} + | ^ +... +LL | unbound_generic_arg::(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-method-tuple-simple.rs b/tests/ui/splat/splat-method-tuple-simple.rs new file mode 100644 index 0000000000000..887c9515bcdae --- /dev/null +++ b/tests/ui/splat/splat-method-tuple-simple.rs @@ -0,0 +1,40 @@ +//@ run-pass +//! Test using `#[splat]` on method tuple arguments (with receivers). + +#![allow(incomplete_features)] +#![feature(splat)] + +struct Foo; + +impl Foo { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } + + fn tuple_4(&self, #[splat] (a, _b, _c, _d): (u32, i8, (), f32)) -> u32 { + a + } +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl TupleStruct { + fn tuple_2(&self, #[splat] (a, _b): (u32, i8)) -> u32 { + a + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.tuple_2((1, 2)); + + foo.tuple_2(1u32, 2i8); + foo.tuple_4(1u32, 2i8, (), 3f32); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct.tuple_2(1u32, 2i8); +} diff --git a/tests/ui/splat/splat-non-tuple.rs b/tests/ui/splat/splat-non-tuple.rs new file mode 100644 index 0000000000000..35841a770b226 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.rs @@ -0,0 +1,90 @@ +//! Test that using `#[splat]` on non-tuple function arguments is an error. + +#![allow(incomplete_features)] +#![feature(splat)] +#![expect(unused)] + +fn primitive_arg(#[splat] x: u32) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +enum NotATuple { + A(u32), + B(i8), +} + +fn enum_arg(#[splat] y: NotATuple) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple + +trait FooTrait { + fn tuple_1(#[splat] _: (u32,)); //~ NOTE type in trait + + // Ambiguous case, self could be a tuple or a non-tuple + fn tuple_4(#[splat] self, _: (u32, i8, (), f32)); +} + +struct Foo; + +fn struct_arg(#[splat] z: Foo) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo + +impl Foo { + fn tuple_2_self( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + (a, b): (u32, i8), + ) -> u32 { + a + } +} + +impl FooTrait for Foo { + fn tuple_1(_: (u32,)) {} + //~^ ERROR method `tuple_1` has an incompatible type for trait + //~| NOTE expected fn with arg 0 splatted, found fn with no splatted arg + //~| NOTE expected signature `fn(#[splat] (_,))` + //~| NOTE found signature `fn((_,))` + + fn tuple_4( + // FIXME(splat): ERROR cannot use splat attribute; the splatted argument type must be a... + #[splat] self, + _: (u32, i8, (), f32), + ) { + } +} + +struct TupleStruct(u32, i8); + +fn tuple_struct_arg(#[splat] z: TupleStruct) {} //~ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct + +impl TupleStruct { + fn tuple_2( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + (a, b): (f32, f64), + ) -> f32 { + a + } +} + +impl FooTrait for TupleStruct { + fn tuple_1(#[splat] _: (u32,)) {} + + fn tuple_4( + #[splat] self, // FIXME(splat): ERROR `#[splat]` attribute must be used on a tuple + _: (u32, i8, (), f32), + ) { + } +} + +fn main() { + // FIXME(splat): is it enough for just the definitions/callees to error, + // or should the callers also error? + primitive_arg(1u32); + enum_arg(NotATuple::A(1u32)); + + let foo = Foo; + struct_arg(foo); + foo.tuple_2_self((1u32, 2i8)); + + let tuple_struct = TupleStruct(1u32, 2i8); + tuple_struct_arg(tuple_struct); + tuple_struct.tuple_2((1f32, 2f64)); + TupleStruct::tuple_1(1u32); + tuple_struct.tuple_4((1u32, 2i8, (), 3f32)); +} diff --git a/tests/ui/splat/splat-non-tuple.stderr b/tests/ui/splat/splat-non-tuple.stderr new file mode 100644 index 0000000000000..64f7148d8dbd2 --- /dev/null +++ b/tests/ui/splat/splat-non-tuple.stderr @@ -0,0 +1,54 @@ +error[E0053]: method `tuple_1` has an incompatible type for trait + --> $DIR/splat-non-tuple.rs:38:5 + | +LL | fn tuple_1(_: (u32,)) {} + | ^^^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg + | +note: type in trait + --> $DIR/splat-non-tuple.rs:17:5 + | +LL | fn tuple_1(#[splat] _: (u32,)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn(#[splat] (_,))` + found signature `fn((_,))` + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-non-tuple.rs:7:30 + | +LL | fn primitive_arg(#[splat] x: u32) {} + | ^^^ +... +LL | primitive_arg(1u32); + | ^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a NotATuple (NotATuple) + --> $DIR/splat-non-tuple.rs:14:25 + | +LL | fn enum_arg(#[splat] y: NotATuple) {} + | ^^^^^^^^^ +... +LL | enum_arg(NotATuple::A(1u32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a Foo (Foo) + --> $DIR/splat-non-tuple.rs:25:27 + | +LL | fn struct_arg(#[splat] z: Foo) {} + | ^^^ +... +LL | struct_arg(foo); + | ^^^^^^^^^^^^^^^ + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a TupleStruct (TupleStruct) + --> $DIR/splat-non-tuple.rs:54:33 + | +LL | fn tuple_struct_arg(#[splat] z: TupleStruct) {} + | ^^^^^^^^^^^ +... +LL | tuple_struct_arg(tuple_struct); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0053, E0277. +For more information about an error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-overload-at-home-fail.rs b/tests/ui/splat/splat-overload-at-home-fail.rs new file mode 100644 index 0000000000000..8e6a375e0eb96 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.rs @@ -0,0 +1,41 @@ +//! Test error cases for `#[splat]` "overloading at home" example code. +//! Splatted calls that don't match any registered MethodArgs impl should fail. +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, _this: &Foo); +} + +impl MethodArgs for () { + fn call_method(self, _this: &Foo) {} +} + +impl MethodArgs for (i32,) { + fn call_method(self, _this: &Foo) {} +} + +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) {} +} + +impl Foo { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // No impl for (f32,) — wrong type + foo.method(42f32); + //~^ ERROR mismatched types + + // No impl for (i32,i32) - wrong type + foo.method(42i32, 42i32); + //~^ ERROR mismatched types +} diff --git a/tests/ui/splat/splat-overload-at-home-fail.stderr b/tests/ui/splat/splat-overload-at-home-fail.stderr new file mode 100644 index 0000000000000..fcedccf28c359 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home-fail.stderr @@ -0,0 +1,40 @@ +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:35:16 + | +LL | foo.method(42f32); + | ------ ^^^^^ expected `i32`, found `f32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:26:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ ---------------- +help: change the type of the numeric literal from `f32` to `i32` + | +LL - foo.method(42f32); +LL + foo.method(42i32); + | + +error[E0308]: mismatched types + --> $DIR/splat-overload-at-home-fail.rs:39:23 + | +LL | foo.method(42i32, 42i32); + | ------ ^^^^^ expected `String`, found `i32` + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/splat-overload-at-home-fail.rs:26:8 + | +LL | fn method(&self, #[splat] args: T) { + | ^^^^^^ +help: try using a conversion method + | +LL | foo.method(42i32, 42i32.to_string()); + | ++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/splat/splat-overload-at-home.rs b/tests/ui/splat/splat-overload-at-home.rs new file mode 100644 index 0000000000000..621f0e04f67e1 --- /dev/null +++ b/tests/ui/splat/splat-overload-at-home.rs @@ -0,0 +1,52 @@ +//@ run-pass +// ignore-tidy-linelength +//! Test using `#[splat]` on some "overloading at home" example code. +//! + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +struct Foo; + +trait MethodArgs: std::marker::Tuple { + fn call_method(self, _this: &Foo); +} +impl MethodArgs for () { + fn call_method(self, _this: &Foo) {} +} +impl MethodArgs for (i32,) { + fn call_method(self, _this: &Foo) {} +} +impl MethodArgs for (i32, String) { + fn call_method(self, _this: &Foo) {} +} + +impl Foo { + fn method(&self, #[splat] args: T) { + args.call_method(self) + } +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //foo.method(()); + //foo.method((42i32,)); + + // Generic tuple trait implementers work without explicit tuple type parameters. + foo.method::<()>(); + foo.method(); + + foo.method::<(i32,)>(42i32); + foo.method::<(i32,)>(42); + foo.method(42i32); + foo.method(42); + + foo.method::<(i32, String)>(42i32, "asdf".to_owned()); + foo.method::<(i32, String)>(42, "asdf".to_owned()); + foo.method(42i32, "asdf".to_owned()); + foo.method(42, "asdf".to_owned()); +} diff --git a/tests/ui/splat/splat-trait-tuple.rs b/tests/ui/splat/splat-trait-tuple.rs new file mode 100644 index 0000000000000..a5b74a40cffd9 --- /dev/null +++ b/tests/ui/splat/splat-trait-tuple.rs @@ -0,0 +1,45 @@ +//@ run-pass +//! Test using `#[splat]` on trait assoc function/method tuple arguments. + +#![allow(incomplete_features)] +#![feature(splat)] + +trait FooTrait { + fn tuple_1_trait(#[splat] _: (u32,)); + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)); +} + +struct Foo; + +impl FooTrait for Foo { + // Currently, splat attributes on impls must match traits. This provides better UX. + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +#[expect(dead_code)] +struct TupleStruct(u32, i8); + +impl FooTrait for TupleStruct { + fn tuple_1_trait(#[splat] _: (u32,)) {} + + fn tuple_2_trait(&self, #[splat] _: (u32, f32)) {} +} + +fn main() { + let foo = Foo; + + // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? + // Add a tupled test for each call if they are. + //Foo::tuple_1_trait((1u32,)); + //foo.tuple_2_trait((1, 3.5)); + + Foo::tuple_1_trait(1u32); + foo.tuple_2_trait(1, 3.5); + + let tuple_struct = TupleStruct(1u32, 2i8); + TupleStruct::tuple_1_trait(1u32); + tuple_struct.tuple_2_trait(1, 3.5) +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs new file mode 100644 index 0000000000000..3f29b9d292d22 --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -0,0 +1,16 @@ +//! Test that using `#[splat]` incorrectly on unsafe functions gives errors. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} +//~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 + +unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function + +fn main() { + unsafe { + unsafe_wrong_type(1u32); + } +} diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr new file mode 100644 index 0000000000000..67935e170671a --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -0,0 +1,20 @@ +error: multiple `#[splat]`s are not allowed in the same function + --> $DIR/splat-unsafe-fn-tuple-fail.rs:9:30 + | +LL | unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error[E0277]: cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 (u32) + --> $DIR/splat-unsafe-fn-tuple-fail.rs:6:42 + | +LL | unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} + | ^^^ +... +LL | unsafe_wrong_type(1u32); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple.rs b/tests/ui/splat/splat-unsafe-fn-tuple.rs new file mode 100644 index 0000000000000..0b9b3510ad50b --- /dev/null +++ b/tests/ui/splat/splat-unsafe-fn-tuple.rs @@ -0,0 +1,19 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments of unsafe functions. + +#![allow(incomplete_features)] +#![feature(splat)] + +unsafe fn unsafe_tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +unsafe fn unsafe_splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + unsafe { + unsafe_tuple_args(1u32, 2i8); + unsafe_tuple_args(1, 2); + + unsafe_splat_non_terminal_arg(1u32, 2i8, 3.5f64); + unsafe_splat_non_terminal_arg(1, 2, 3.5); + } +} diff --git a/tests/ui/splat/splat-where-clause.rs b/tests/ui/splat/splat-where-clause.rs new file mode 100644 index 0000000000000..42a23fe67b216 --- /dev/null +++ b/tests/ui/splat/splat-where-clause.rs @@ -0,0 +1,44 @@ +//@ run-pass +//! Test using `#[splat]` on tuple arguments with where clause bounds. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(tuple_trait)] + +fn where_splat(#[splat] _t: T) where T: std::marker::Tuple {} + +fn where_splat_with_extra(#[splat] _t: T, _extra: u32) where T: std::marker::Tuple {} + +fn impl_tuple_splat(#[splat] _t: impl std::marker::Tuple) {} + +fn impl_tuple_splat_with_extra(#[splat] _t: impl std::marker::Tuple, _extra: u32) {} + +fn main() { + // empty tuple + where_splat(); + + // single element + where_splat(1u32); + where_splat(1); + + // two elements + where_splat(1u32, 2i8); + where_splat(1, 2); + + // three elements + where_splat(1u32, 2i8, 3.0f64); + where_splat(1, 2, 3.0); + + // with extra non-splatted arg + where_splat_with_extra(1u32, 2i8, 42u32); + where_splat_with_extra(1, 2, 42); + + // impl Trait syntax variants + impl_tuple_splat(); + impl_tuple_splat(1u32); + impl_tuple_splat(1u32, 2i8); + impl_tuple_splat(1, 2, 3.0); + + impl_tuple_splat_with_extra(1u32, 2i8, 42u32); + impl_tuple_splat_with_extra(1, 2, 42); +} diff --git a/tests/ui/issues/issue-27815.rs b/tests/ui/structs/non-struct-in-struct-position.rs similarity index 75% rename from tests/ui/issues/issue-27815.rs rename to tests/ui/structs/non-struct-in-struct-position.rs index 9e53014f45522..782808b8e8520 100644 --- a/tests/ui/issues/issue-27815.rs +++ b/tests/ui/structs/non-struct-in-struct-position.rs @@ -1,3 +1,6 @@ +//! Regression test for . +//! Test usage of struct literal syntax with non-structs doesn't ICE. + mod A {} fn main() { diff --git a/tests/ui/issues/issue-27815.stderr b/tests/ui/structs/non-struct-in-struct-position.stderr similarity index 79% rename from tests/ui/issues/issue-27815.stderr rename to tests/ui/structs/non-struct-in-struct-position.stderr index 43f78ccf6395a..667ebe7387d07 100644 --- a/tests/ui/issues/issue-27815.stderr +++ b/tests/ui/structs/non-struct-in-struct-position.stderr @@ -1,23 +1,23 @@ error[E0574]: expected struct, variant or union type, found module `A` - --> $DIR/issue-27815.rs:4:13 + --> $DIR/non-struct-in-struct-position.rs:7:13 | LL | let u = A { x: 1 }; | ^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found builtin type `u32` - --> $DIR/issue-27815.rs:5:13 + --> $DIR/non-struct-in-struct-position.rs:8:13 | LL | let v = u32 { x: 1 }; | ^^^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found module `A` - --> $DIR/issue-27815.rs:7:9 + --> $DIR/non-struct-in-struct-position.rs:10:9 | LL | A { x: 1 } => {} | ^ not a struct, variant or union type error[E0574]: expected struct, variant or union type, found builtin type `u32` - --> $DIR/issue-27815.rs:9:9 + --> $DIR/non-struct-in-struct-position.rs:12:9 | LL | u32 { x: 1 } => {} | ^^^ not a struct, variant or union type diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs new file mode 100644 index 0000000000000..0350ef50705d9 --- /dev/null +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.rs @@ -0,0 +1,27 @@ +// Regression test for https://github.com/rust-lang/rust/issues/142803. + +trait Pair { + type Left; + type Right; + + fn split(self) -> (Self::Left, Self::Right); +} + +impl Pair for (A, B) { + type Left = A; + type Right = B; + + fn split(self) -> (Self::Left, Self::Right) { + self + } +} + +fn frob(pair: impl Pair) -> impl Pair { + //~^ ERROR type mismatch + //~| HELP consider further restricting this bound + //~| SUGGESTION , Right = B + let (left, right) = pair.split(); + (left, right) +} + +fn main() {} diff --git a/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr b/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr new file mode 100644 index 0000000000000..a284f2adb366c --- /dev/null +++ b/tests/ui/suggestions/restrict-bound-already-has-generic-args.stderr @@ -0,0 +1,24 @@ +error[E0271]: type mismatch resolving `<(A, as Pair>::Right) as Pair>::Right == B` + --> $DIR/restrict-bound-already-has-generic-args.rs:19:45 + | +LL | fn frob(pair: impl Pair) -> impl Pair { + | - expected this type parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<(A, as Pair>::Right) as Pair>::Right == B` +... +LL | (left, right) + | ------------- return type was inferred to be `(A, as Pair>::Right)` here + | +note: expected this to be `B` + --> $DIR/restrict-bound-already-has-generic-args.rs:12:18 + | +LL | type Right = B; + | ^ + = note: expected type parameter `B` + found associated type ` as Pair>::Right` +help: consider further restricting this bound + | +LL | fn frob(pair: impl Pair) -> impl Pair { + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/supertrait-shadowing/assoc-const.rs b/tests/ui/supertrait-shadowing/assoc-const.rs new file mode 100644 index 0000000000000..be0d990284b0c --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-const.rs @@ -0,0 +1,31 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +trait A { + const CONST: i32; +} +impl A for T { + const CONST: i32 = 1; +} + +trait B: A { + type const CONST: i32; +} +impl B for T { + type const CONST: i32 = 2; +} + +trait C: B {} +impl C for T {} + +fn main() { + assert_eq!(i32::CONST, 2); + generic::(); +} + +fn generic>() { + assert_eq!(T::CONST, 2); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.rs b/tests/ui/supertrait-shadowing/assoc-type-fail.rs new file mode 100644 index 0000000000000..5df4edb7e96e2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.rs @@ -0,0 +1,47 @@ +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for X { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for X { + type Assoc = i16; +} + +trait C: B {} +impl C for X {} + +fn main() { + b_unbound::(); + c_unbound::(); + + b_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` + c_assoc_is_a::(); + //~^ ERROR type mismatch resolving `::Assoc == i8` +} + +fn b_unbound() { + let _ = size_of::(); +} + +fn c_unbound() { + let _ = size_of::(); +} + +fn b_assoc_is_a>() { + let _ = size_of::(); +} + +fn c_assoc_is_a>() { + let _ = size_of::(); +} diff --git a/tests/ui/supertrait-shadowing/assoc-type-fail.stderr b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr new file mode 100644 index 0000000000000..36a9bd082aaf6 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-fail.stderr @@ -0,0 +1,37 @@ +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:27:20 + | +LL | b_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `b_assoc_is_a` + --> $DIR/assoc-type-fail.rs:41:22 + | +LL | fn b_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `b_assoc_is_a` + +error[E0271]: type mismatch resolving `::Assoc == i8` + --> $DIR/assoc-type-fail.rs:29:20 + | +LL | c_assoc_is_a::(); + | ^^^ type mismatch resolving `::Assoc == i8` + | +note: expected this to be `i8` + --> $DIR/assoc-type-fail.rs:17:18 + | +LL | type Assoc = i16; + | ^^^ +note: required by a bound in `c_assoc_is_a` + --> $DIR/assoc-type-fail.rs:45:22 + | +LL | fn c_assoc_is_a>() { + | ^^^^^^^^^^ required by this bound in `c_assoc_is_a` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.rs b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs new file mode 100644 index 0000000000000..55d2b84a50f9a --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.rs @@ -0,0 +1,45 @@ +//@ normalize-stderr: "assoc_type_predicates\[[^\]]+\]" -> "assoc_type_predicates[HASH]" + +#![feature(rustc_attrs)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +#[rustc_dump_predicates] +fn a_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE A::Assoc + +#[rustc_dump_predicates] +fn b_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +#[rustc_dump_predicates] +fn c_bound>() {} +//~^ ERROR rustc_dump_predicates +//~| NOTE TraitPredicate( +//~| NOTE TraitPredicate( +//~| NOTE B::Assoc + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr new file mode 100644 index 0000000000000..be765e737a6e1 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type-predicates.stderr @@ -0,0 +1,32 @@ +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:25:1 + | +LL | fn a_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:4 ~ assoc_type_predicates[HASH]::A::Assoc) }, args: [T/#0], .. }, Term::Ty(i8)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:32:1 + | +LL | fn b_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: rustc_dump_predicates + --> $DIR/assoc-type-predicates.rs:39:1 + | +LL | fn c_bound>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: TraitPredicate(, polarity:Positive), bound_vars: [] } + = note: Binder { value: ProjectionPredicate(Alias { kind: ProjectionTy { def_id: DefId(0:9 ~ assoc_type_predicates[HASH]::B::Assoc) }, args: [T/#0], .. }, Term::Ty(i16)), bound_vars: [] } + +error: aborting due to 3 previous errors + diff --git a/tests/ui/supertrait-shadowing/assoc-type.rs b/tests/ui/supertrait-shadowing/assoc-type.rs new file mode 100644 index 0000000000000..9e9799397c571 --- /dev/null +++ b/tests/ui/supertrait-shadowing/assoc-type.rs @@ -0,0 +1,53 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + type Assoc; +} +impl A for T { + type Assoc = i8; +} + +trait B: A { + type Assoc; +} +impl B for T { + type Assoc = i16; +} + +trait C: B {} +impl C for T {} + +fn main() { + generic::(); + generic2::(); + generic3::(); + generic4::(); + generic5::(); +} + +fn generic() { + assert_eq!(size_of::(), 2); +} + +fn generic2>() { + assert_eq!(size_of::(), 1); +} + +fn generic3>() { + assert_eq!(size_of::(), 2); +} + +fn generic4>() { + assert_eq!(size_of::(), 2); +} + +fn generic5() { + assert_eq!(size_of::<::Assoc>(), 1); + assert_eq!(size_of::<::Assoc>(), 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs b/tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/auxiliary/shadowed_stability.rs rename to tests/ui/supertrait-shadowing/auxiliary/shadowed_stability.rs diff --git a/tests/ui/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/supertrait-shadowing/common-ancestor-2.rs new file mode 100644 index 0000000000000..908c5d2bf2199 --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.rs @@ -0,0 +1,59 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `C` shadows identically named item + "C" + } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +fn main() { + assert_eq!(().hello(), "C"); + //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 4); + assert_eq!(T::CONST, 3); +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr similarity index 55% rename from tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr rename to tests/ui/supertrait-shadowing/common-ancestor-2.stderr index b0f61b46b6911..646726b835041 100644 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor-2.stderr @@ -1,11 +1,11 @@ warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: items from several supertraits are shadowed: `B` and `A` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,24 +13,54 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:5:9 + --> $DIR/common-ancestor-2.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `C` shadows identically named item from supertrait - --> $DIR/common-ancestor-2.rs:31:19 + --> $DIR/common-ancestor-2.rs:51:19 | LL | assert_eq!(().hello(), "C"); | ^^^^^ | note: item from `C` shadows a supertrait item - --> $DIR/common-ancestor-2.rs:23:5 + --> $DIR/common-ancestor-2.rs:36:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: items from several supertraits are shadowed: `A` and `B` - --> $DIR/common-ancestor-2.rs:9:5 + --> $DIR/common-ancestor-2.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -38,10 +68,10 @@ LL | fn hello(&self) -> &'static str { LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor-2.rs:4:9 + --> $DIR/common-ancestor-2.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/supertrait-shadowing/common-ancestor-3.rs new file mode 100644 index 0000000000000..ade23ca88aec2 --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.rs @@ -0,0 +1,76 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `C` shadows identically named item + "C" + } + type Assoc; + //~^ WARN trait item `Assoc` from `C` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `C` shadows identically named item +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +// `D` extends `C` which extends `B` and `A` + +trait D: C { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `D` shadows identically named item + "D" + } + type Assoc; + //~^ WARN trait item `Assoc` from `D` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `D` shadows identically named item +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; +} + +fn main() { + assert_eq!(().hello(), "D"); + //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 8); + assert_eq!(T::CONST, 4); +} diff --git a/tests/ui/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr new file mode 100644 index 0000000000000..62132832da4ba --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor-3.stderr @@ -0,0 +1,134 @@ +warning: trait item `hello` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:36:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:6:9 + | +LL | #![warn(shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `Assoc` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:40:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:42:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:53:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `Assoc` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:57:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:59:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:68:19 + | +LL | assert_eq!(().hello(), "D"); + | ^^^^^ + | +note: item from `D` shadows a supertrait item + --> $DIR/common-ancestor-3.rs:53:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: items from several supertraits are shadowed: `A`, `B`, and `C` + --> $DIR/common-ancestor-3.rs:12:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:5:9 + | +LL | #![warn(resolving_to_items_shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 7 warnings emitted + diff --git a/tests/ui/supertrait-shadowing/common-ancestor.rs b/tests/ui/supertrait-shadowing/common-ancestor.rs new file mode 100644 index 0000000000000..903f85957870b --- /dev/null +++ b/tests/ui/supertrait-shadowing/common-ancestor.rs @@ -0,0 +1,47 @@ +//@ run-pass + +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] +#![warn(resolving_to_items_shadowing_supertrait_items)] +#![warn(shadowing_supertrait_items)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B: A { + fn hello(&self) -> &'static str { + //~^ WARN trait item `hello` from `B` shadows identically named item + "B" + } + type Assoc; + //~^ WARN trait item `Assoc` from `B` shadows identically named item + type const CONST: i32; + //~^ WARN trait item `CONST` from `B` shadows identically named item +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; +} + +fn main() { + assert_eq!(().hello(), "B"); + //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 2); + assert_eq!(T::CONST, 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr b/tests/ui/supertrait-shadowing/common-ancestor.stderr similarity index 56% rename from tests/ui/methods/supertrait-shadowing/common-ancestor.stderr rename to tests/ui/supertrait-shadowing/common-ancestor.stderr index 9afedeab5e2c2..9b13537cf800c 100644 --- a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr +++ b/tests/ui/supertrait-shadowing/common-ancestor.stderr @@ -1,41 +1,65 @@ warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:5:9 + --> $DIR/common-ancestor.rs:6:9 | LL | #![warn(shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +warning: trait item `Assoc` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:28:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +warning: trait item `CONST` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:30:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:16:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + warning: trait item `hello` from `B` shadows identically named item from supertrait - --> $DIR/common-ancestor.rs:24:19 + --> $DIR/common-ancestor.rs:39:19 | LL | assert_eq!(().hello(), "B"); | ^^^^^ | note: item from `B` shadows a supertrait item - --> $DIR/common-ancestor.rs:16:5 + --> $DIR/common-ancestor.rs:24:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: item from `A` is shadowed by a subtrait item - --> $DIR/common-ancestor.rs:9:5 + --> $DIR/common-ancestor.rs:12:5 | LL | fn hello(&self) -> &'static str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/common-ancestor.rs:4:9 + --> $DIR/common-ancestor.rs:5:9 | LL | #![warn(resolving_to_items_shadowing_supertrait_items)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: 2 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/supertrait-shadowing/definition-site.rs b/tests/ui/supertrait-shadowing/definition-site.rs new file mode 100644 index 0000000000000..3309ee97e9f00 --- /dev/null +++ b/tests/ui/supertrait-shadowing/definition-site.rs @@ -0,0 +1,28 @@ +#![feature(supertrait_item_shadowing)] +#![deny(shadowing_supertrait_items)] + +trait SuperSuper { + fn method(); + const CONST: i32; + type Assoc; +} + +trait Super: SuperSuper { + fn method(); + //~^ ERROR trait item `method` from `Super` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Super` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Super` shadows identically named item +} + +trait Sub: Super { + fn method(); + //~^ ERROR trait item `method` from `Sub` shadows identically named item + const CONST: i32; + //~^ ERROR trait item `CONST` from `Sub` shadows identically named item + type Assoc; + //~^ ERROR trait item `Assoc` from `Sub` shadows identically named item +} + +fn main() {} diff --git a/tests/ui/supertrait-shadowing/definition-site.stderr b/tests/ui/supertrait-shadowing/definition-site.stderr new file mode 100644 index 0000000000000..faf01cc711c6a --- /dev/null +++ b/tests/ui/supertrait-shadowing/definition-site.stderr @@ -0,0 +1,88 @@ +error: trait item `method` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:11:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/definition-site.rs:2:9 + | +LL | #![deny(shadowing_supertrait_items)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: trait item `CONST` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:13:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:15:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + +error: trait item `method` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:20:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +... +LL | fn method(); + | ^^^^^^^^^^^^ + +error: trait item `CONST` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:22:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:6:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +... +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ + +error: trait item `Assoc` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:24:5 + | +LL | type Assoc; + | ^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:7:5 + | +LL | type Assoc; + | ^^^^^^^^^^ +... +LL | type Assoc; + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.rs diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr b/tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr rename to tests/ui/supertrait-shadowing/false-subtrait-after-inference.stderr diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs new file mode 100644 index 0000000000000..957aabf5a51f8 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.rs @@ -0,0 +1,68 @@ +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +trait C: A + B { + fn hello(&self) -> &'static str { + "C" + } + type Assoc; + type const CONST: i32; +} +impl C for T { + type Assoc = i32; + type const CONST: i32 = 3; +} + +// Since `D` is not a subtrait of `C`, +// we have no obvious lower bound. + +trait D: B { + fn hello(&self) -> &'static str { + "D" + } + type Assoc; + type const CONST: i32; +} +impl D for T { + type Assoc = i64; + type const CONST: i32 = 4; +} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr new file mode 100644 index 0000000000000..987adcfbbe959 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor-2.stderr @@ -0,0 +1,131 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:58:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor-2.rs:7:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor-2.rs:19:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in an impl of the trait `C` for the type `T` + --> $DIR/no-common-ancestor-2.rs:31:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in an impl of the trait `D` for the type `T` + --> $DIR/no-common-ancestor-2.rs:46:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | +help: disambiguate the method for candidate #3 + | +LL - ().hello(); +LL + C::hello(&()); + | +help: disambiguate the method for candidate #4 + | +LL - ().hello(); +LL + D::hello(&()); + | + +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor-2.rs:64:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `C` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `D` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:66:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor-2.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor-2.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #3 is defined in the trait `C` + --> $DIR/no-common-ancestor-2.rs:35:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +note: candidate #4 is defined in the trait `D` + --> $DIR/no-common-ancestor-2.rs:50:5 + | +LL | type const CONST: i32; + | ^^^^^^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/supertrait-shadowing/no-common-ancestor.rs new file mode 100644 index 0000000000000..73209c015f8f5 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.rs @@ -0,0 +1,41 @@ +#![feature(supertrait_item_shadowing)] +#![feature(min_generic_const_args)] + +use std::mem::size_of; + +trait A { + fn hello(&self) -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B { + fn hello(&self) -> &'static str { + "B" + } + type Assoc; + const CONST: i32; +} +impl B for T { + type Assoc = i16; + const CONST: i32 = 2; +} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope + check::<()>(); +} + +fn check() { + let _ = size_of::(); + //~^ ERROR ambiguous associated type `Assoc` in bounds of `T` + let _ = T::CONST; + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr new file mode 100644 index 0000000000000..78fd8f4670577 --- /dev/null +++ b/tests/ui/supertrait-shadowing/no-common-ancestor.stderr @@ -0,0 +1,79 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:31:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor.rs:7:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor.rs:19:5 + | +LL | fn hello(&self) -> &'static str { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | + +error[E0221]: ambiguous associated type `Assoc` in bounds of `T` + --> $DIR/no-common-ancestor.rs:37:23 + | +LL | type Assoc; + | ---------- ambiguous `Assoc` from `A` +... +LL | type Assoc; + | ---------- ambiguous `Assoc` from `B` +... +LL | let _ = size_of::(); + | ^^^^^^^^ ambiguous associated type `Assoc` + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | +help: use fully-qualified syntax to disambiguate + | +LL - let _ = size_of::(); +LL + let _ = size_of::<::Assoc>(); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:39:16 + | +LL | let _ = T::CONST; + | ^^^^^ multiple `CONST` found + | +note: candidate #1 is defined in the trait `A` + --> $DIR/no-common-ancestor.rs:11:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `B` + --> $DIR/no-common-ancestor.rs:23:5 + | +LL | const CONST: i32; + | ^^^^^^^^^^^^^^^^ +help: use fully-qualified syntax to disambiguate + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0034, E0221. +For more information about an error, try `rustc --explain E0034`. diff --git a/tests/ui/supertrait-shadowing/out-of-scope.rs b/tests/ui/supertrait-shadowing/out-of-scope.rs new file mode 100644 index 0000000000000..7b2322843f1c3 --- /dev/null +++ b/tests/ui/supertrait-shadowing/out-of-scope.rs @@ -0,0 +1,42 @@ +//@ run-pass + +#![feature(min_generic_const_args)] +#![allow(dead_code)] + +use std::mem::size_of; + +mod out_of_scope { + pub trait Subtrait: super::Supertrait { + fn hello(&self) -> &'static str { + "subtrait" + } + type Assoc; + type const CONST: i32; + } + impl Subtrait for T { + type Assoc = i16; + type const CONST: i32 = 2; + } +} + +trait Supertrait { + fn hello(&self) -> &'static str { + "supertrait" + } + type Assoc; + const CONST: i32; +} +impl Supertrait for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +fn main() { + assert_eq!(().hello(), "supertrait"); + check::<()>(); +} + +fn check() { + assert_eq!(size_of::(), 1); + assert_eq!(T::CONST, 1); +} diff --git a/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs b/tests/ui/supertrait-shadowing/trivially-false-subtrait.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs rename to tests/ui/supertrait-shadowing/trivially-false-subtrait.rs diff --git a/tests/ui/supertrait-shadowing/type-dependent.rs b/tests/ui/supertrait-shadowing/type-dependent.rs new file mode 100644 index 0000000000000..75272d101dd29 --- /dev/null +++ b/tests/ui/supertrait-shadowing/type-dependent.rs @@ -0,0 +1,51 @@ +//@ run-pass + +// Makes sure we can shadow with type-dependent associated item syntax. + +#![feature(min_generic_const_args)] +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +use std::mem::size_of; + +trait A { + fn hello() -> &'static str { + "A" + } + type Assoc; + const CONST: i32; +} +impl A for T { + type Assoc = i8; + const CONST: i32 = 1; +} + +trait B: A { + fn hello() -> &'static str { + "B" + } + type Assoc; + type const CONST: i32; +} +impl B for T { + type Assoc = i16; + type const CONST: i32 = 2; +} + +fn foo() -> &'static str { + T::hello() +} + +fn assoc() -> usize { + size_of::() +} + +fn konst() -> i32 { + T::CONST +} + +fn main() { + assert_eq!(foo::<()>(), "B"); + assert_eq!(assoc::<()>(), 2); + assert_eq!(konst::<()>(), 2); +} diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr b/tests/ui/supertrait-shadowing/unstable.off_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.off_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr b/tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.off_shadowing.stderr rename to tests/ui/supertrait-shadowing/unstable.off_shadowing.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr b/tests/ui/supertrait-shadowing/unstable.on_normal.stderr similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.on_normal.stderr rename to tests/ui/supertrait-shadowing/unstable.on_normal.stderr diff --git a/tests/ui/methods/supertrait-shadowing/unstable.rs b/tests/ui/supertrait-shadowing/unstable.rs similarity index 100% rename from tests/ui/methods/supertrait-shadowing/unstable.rs rename to tests/ui/supertrait-shadowing/unstable.rs diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index f88fdf7509c76..59085aeb3279b 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h27f62b2d0b2beac6E) +error: symbol-name(_ZN5basic4main17h33f35fba43592008E) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h27f62b2d0b2beac6) +error: demangling(basic::main::h33f35fba43592008) --> $DIR/basic.rs:8:1 | LL | #[rustc_dump_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index c1fc9f4b1cea1..367c2d14011ac 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h1eb769490ff06e77E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hc741419c44ba4d79E) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h1eb769490ff06e77) +error: demangling(issue_60925::foo::Foo::foo::hc741419c44ba4d79) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_dump_symbol_name] diff --git a/tests/ui/trait-bounds/mismatch-fn-trait.stderr b/tests/ui/trait-bounds/mismatch-fn-trait.stderr index 051e660c2d106..a7805e25f651e 100644 --- a/tests/ui/trait-bounds/mismatch-fn-trait.stderr +++ b/tests/ui/trait-bounds/mismatch-fn-trait.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut(u32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut(u32)` --> $DIR/mismatch-fn-trait.rs:4:10 | LL | take(f) @@ -14,7 +14,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut(i32, i32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut(i32, i32)` --> $DIR/mismatch-fn-trait.rs:9:10 | LL | take(f) @@ -29,7 +29,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnMut()` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnMut()` --> $DIR/mismatch-fn-trait.rs:14:10 | LL | take(f) @@ -44,7 +44,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnOnce(i32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnOnce(i32)` --> $DIR/mismatch-fn-trait.rs:19:10 | LL | take(f) @@ -59,7 +59,7 @@ note: required by a bound in `take` LL | fn take(_f: impl FnMut(i32)) {} | ^^^^^^^^^^ required by this bound in `take` -error[E0277]: expected a `FnMut(i32)` closure, found `impl FnOnce(u32)` +error[E0277]: expected an `FnMut(i32)` closure, found `impl FnOnce(u32)` --> $DIR/mismatch-fn-trait.rs:24:10 | LL | take(f) diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs index 891cc32e7b758..f19c9da7c5eb5 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.rs @@ -11,5 +11,5 @@ fn f X<'r> + ?Sized>() { fn main() { f:: X<'x, F = i32>>(); - //~^ ERROR expected a `FnOnce(&i32)` closure, found `i32` + //~^ ERROR expected an `FnOnce(&i32)` closure, found `i32` } diff --git a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr index ebd45b9dd9635..c4bc50746ebba 100644 --- a/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr +++ b/tests/ui/traits/associated_type_bound/check-trait-object-bounds-2.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce(&i32)` closure, found `i32` +error[E0277]: expected an `FnOnce(&i32)` closure, found `i32` --> $DIR/check-trait-object-bounds-2.rs:13:9 | LL | f:: X<'x, F = i32>>(); diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index ca908a3062d33..b8b9ea57612b4 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -32,7 +32,7 @@ LL | impl Fn(&isize) for Error { | = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` -error[E0277]: expected a `FnMut(&isize)` closure, found `Error` +error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-87558.rs:3:21 | LL | impl Fn(&isize) for Error { diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs new file mode 100644 index 0000000000000..d1e2a25b29521 --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +//@ check-fail + +fn main() { + let f = |f: dyn Fn()| f; + //~^ ERROR the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + //~| ERROR return type cannot be a trait object without pointer indirection + f(); + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} diff --git a/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr new file mode 100644 index 0000000000000..a20e5f331d31a --- /dev/null +++ b/tests/ui/traits/next-solver/deferred-closure-call-recovery-issue-157951.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:17 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | let f = |f: &dyn Fn()| f; + | + + +error[E0746]: return type cannot be a trait object without pointer indirection + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:27 + | +LL | let f = |f: dyn Fn()| f; + | ^ doesn't have a size known at compile-time + +error[E0057]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:8:5 + | +LL | f(); + | ^-- argument #1 of type `(dyn Fn() + 'static)` is missing + | +note: closure defined here + --> $DIR/deferred-closure-call-recovery-issue-157951.rs:5:13 + | +LL | let f = |f: dyn Fn()| f; + | ^^^^^^^^^^^^^ +help: provide the argument + | +LL | f(/* (dyn Fn() + 'static) */); + | ++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0057, E0277, E0746. +For more information about an error, try `rustc --explain E0057`. diff --git a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs index 8463cb3f6ea1b..a0d1db3ff02b3 100644 --- a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs +++ b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.rs @@ -7,7 +7,7 @@ const fn with_positive [const] Fn(&'a ())>() {} const _: () = { with_positive::<()>(); - //~^ ERROR expected a `Fn(&'a ())` closure, found `()` + //~^ ERROR expected an `Fn(&'a ())` closure, found `()` //~| ERROR type mismatch resolving `<() as FnOnce<(&(),)>>::Output == ()` }; diff --git a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr index 8ec772a3d7fa8..7b98ba618a727 100644 --- a/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr +++ b/tests/ui/traits/next-solver/diagnostics/const-host-effect-hrtb-no-ice.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&'a ())` closure, found `()` +error[E0277]: expected an `Fn(&'a ())` closure, found `()` --> $DIR/const-host-effect-hrtb-no-ice.rs:9:21 | LL | with_positive::<()>(); diff --git a/tests/ui/traits/next-solver/fn-trait.rs b/tests/ui/traits/next-solver/fn-trait.rs index 7994dd3052c40..e31d4d2b580a5 100644 --- a/tests/ui/traits/next-solver/fn-trait.rs +++ b/tests/ui/traits/next-solver/fn-trait.rs @@ -18,11 +18,11 @@ fn main() { require_fn(f); require_fn(f as fn() -> i32); require_fn(f as unsafe fn() -> i32); - //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32` + //~^ ERROR: expected an `Fn()` closure, found `unsafe fn() -> i32` require_fn(g); - //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` + //~^ ERROR: expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` require_fn(g as extern "C" fn() -> i32); - //~^ ERROR: expected a `Fn()` closure, found `extern "C" fn() -> i32` + //~^ ERROR: expected an `Fn()` closure, found `extern "C" fn() -> i32` require_fn(h); - //~^ ERROR: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` + //~^ ERROR: expected an `Fn()` closure, found `unsafe fn() -> i32 {h}` } diff --git a/tests/ui/traits/next-solver/fn-trait.stderr b/tests/ui/traits/next-solver/fn-trait.stderr index 77c834f377852..6218928b309f7 100644 --- a/tests/ui/traits/next-solver/fn-trait.stderr +++ b/tests/ui/traits/next-solver/fn-trait.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() -> i32` --> $DIR/fn-trait.rs:20:16 | LL | require_fn(f as unsafe fn() -> i32); @@ -15,7 +15,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32 {g}` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() -> i32 {g}` --> $DIR/fn-trait.rs:22:16 | LL | require_fn(g); @@ -31,7 +31,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `extern "C" fn() -> i32` +error[E0277]: expected an `Fn()` closure, found `extern "C" fn() -> i32` --> $DIR/fn-trait.rs:24:16 | LL | require_fn(g as extern "C" fn() -> i32); @@ -47,7 +47,7 @@ note: required by a bound in `require_fn` LL | fn require_fn(_: impl Fn() -> i32) {} | ^^^^^^^^^^^ required by this bound in `require_fn` -error[E0277]: expected a `Fn()` closure, found `unsafe fn() -> i32 {h}` +error[E0277]: expected an `Fn()` closure, found `unsafe fn() -> i32 {h}` --> $DIR/fn-trait.rs:26:16 | LL | require_fn(h); diff --git a/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs new file mode 100644 index 0000000000000..ca97b25fa64fa --- /dev/null +++ b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.rs @@ -0,0 +1,22 @@ +// Regression test for #154568 +//@ compile-flags: -Znext-solver=globally + +trait Role { + type Inner; +} + +struct HandshakeCallback(C); +struct Handshake(R::Inner); + +fn main() { + let callback = HandshakeCallback(()); + let handshake = Handshake(callback.0.clone()); + //~^ ERROR type annotations needed + match &handshake { + hs if (|| { + let borrowed_inner = &hs.0; + borrowed_inner == &callback.0 + })() => println!(), + _ => {} + } +} diff --git a/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr new file mode 100644 index 0000000000000..3c5a00744a636 --- /dev/null +++ b/tests/ui/traits/next-solver/unexpected-pointer-deref-issue-154568.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed for `Handshake<_>` + --> $DIR/unexpected-pointer-deref-issue-154568.rs:13:9 + | +LL | let handshake = Handshake(callback.0.clone()); + | ^^^^^^^^^ ----------------------------- type must be known at this point + | + = note: the type must implement `Role` +note: required by a bound in `Handshake` + --> $DIR/unexpected-pointer-deref-issue-154568.rs:9:21 + | +LL | struct Handshake(R::Inner); + | ^^^^ required by this bound in `Handshake` +help: consider giving `handshake` an explicit type, where the type for type parameter `R` is specified + | +LL | let handshake: Handshake = Handshake(callback.0.clone()); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/issues/issue-27842.rs b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs similarity index 70% rename from tests/ui/issues/issue-27842.rs rename to tests/ui/tuple/tuple-bracket-index-suggest-dot.rs index 060d3b34e09d9..a72be060529c8 100644 --- a/tests/ui/issues/issue-27842.rs +++ b/tests/ui/tuple/tuple-bracket-index-suggest-dot.rs @@ -1,3 +1,7 @@ +//! Regression test for . +//! Test we suggest the use of dot syntax when user is trying to index +//! tuple via square brackets. + fn main() { let tup = (0, 1, 2); // the case where we show a suggestion diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr similarity index 84% rename from tests/ui/issues/issue-27842.stderr rename to tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr index f388fdf85cde5..f6e6528572661 100644 --- a/tests/ui/issues/issue-27842.stderr +++ b/tests/ui/tuple/tuple-bracket-index-suggest-dot.stderr @@ -1,5 +1,5 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:4:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:8:16 | LL | let _ = tup[0]; | ^^^ help: to access tuple element `0`, use: `.0` @@ -7,7 +7,7 @@ LL | let _ = tup[0]; = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:9:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:13:16 | LL | let _ = tup[i]; | ^^^ @@ -15,7 +15,7 @@ LL | let _ = tup[i]; = help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc. error[E0608]: cannot index into a value of type `({integer},)` - --> $DIR/issue-27842.rs:14:16 + --> $DIR/tuple-bracket-index-suggest-dot.rs:18:16 | LL | let _ = tup[3]; | ^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-63279.rs b/tests/ui/type-alias-impl-trait/issue-63279.rs index 10da40d2f90d2..50d4a42e1f355 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.rs +++ b/tests/ui/type-alias-impl-trait/issue-63279.rs @@ -4,11 +4,11 @@ type Closure = impl FnOnce(); #[define_opaque(Closure)] fn c() -> Closure { - //~^ ERROR: expected a `FnOnce()` closure, found `()` + //~^ ERROR: expected an `FnOnce()` closure, found `()` || -> Closure { || () } //~^ ERROR: mismatched types //~| ERROR: mismatched types - //~| ERROR: expected a `FnOnce()` closure, found `()` + //~| ERROR: expected an `FnOnce()` closure, found `()` } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/issue-63279.stderr b/tests/ui/type-alias-impl-trait/issue-63279.stderr index 8f0e9c5fde04d..165c1c25280bd 100644 --- a/tests/ui/type-alias-impl-trait/issue-63279.stderr +++ b/tests/ui/type-alias-impl-trait/issue-63279.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/issue-63279.rs:6:11 | LL | fn c() -> Closure { @@ -7,7 +7,7 @@ LL | fn c() -> Closure { = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/issue-63279.rs:8:11 | LL | || -> Closure { || () } diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs index 075b0bd75fa53..aec311ede864f 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs @@ -8,10 +8,10 @@ type Tait = impl FnOnce() -> (); #[define_opaque(Tait)] fn reify_as_tait() -> Thunk { - //~^ ERROR: expected a `FnOnce()` closure, found `()` + //~^ ERROR: expected an `FnOnce()` closure, found `()` Thunk::new(|cont| cont) //~^ ERROR: mismatched types - //~| ERROR: expected a `FnOnce()` closure, found `()` + //~| ERROR: expected an `FnOnce()` closure, found `()` } struct Thunk(F); diff --git a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr index 0bee0dfa9c7a3..22d16e39fc404 100644 --- a/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr +++ b/tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.stderr @@ -10,7 +10,7 @@ LL | Thunk::new(|cont| cont) = note: expected struct `Thunk<_>` found unit type `()` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/lazy_subtyping_of_opaques.rs:12:23 | LL | Thunk::new(|cont| cont) @@ -19,7 +19,7 @@ LL | Thunk::new(|cont| cont) = help: the trait `FnOnce()` is not implemented for `()` = note: wrap the `()` in a closure with no arguments: `|| { /* code */ }` -error[E0277]: expected a `FnOnce()` closure, found `()` +error[E0277]: expected an `FnOnce()` closure, found `()` --> $DIR/lazy_subtyping_of_opaques.rs:10:23 | LL | fn reify_as_tait() -> Thunk { diff --git a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr index 0dd6dcfe6a3f2..d579b5bd75002 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(isize)` closure, found `S` +error[E0277]: expected an `Fn(isize)` closure, found `S` --> $DIR/unboxed-closures-fnmut-as-fn.rs:27:21 | LL | let x = call_it(&S, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr index dfdb3ea19c3f2..a637b180e31e7 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:20:21 | LL | let x = call_it(&square, 22); @@ -14,7 +14,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:25:25 | LL | let y = call_it_mut(&mut square, 22); @@ -30,7 +30,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `for<'a> unsafe fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-unsafe-extern-fn.rs:30:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr index b4521cc6890d8..25cf793ca00e8 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:20:21 | LL | let x = call_it(&square, 22); @@ -13,7 +13,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:25:25 | LL | let y = call_it_mut(&mut square, 22); @@ -28,7 +28,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `for<'a> extern "C" fn(&'a isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-abi.rs:30:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr index 5519bb33ebc27..e09eda3b99d29 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr @@ -1,4 +1,4 @@ -error[E0277]: expected a `Fn(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `Fn(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:21:21 | LL | let x = call_it(&square, 22); @@ -14,7 +14,7 @@ note: required by a bound in `call_it` LL | fn call_it isize>(_: &F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it` -error[E0277]: expected a `FnMut(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `FnMut(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:26:25 | LL | let y = call_it_mut(&mut square, 22); @@ -30,7 +30,7 @@ note: required by a bound in `call_it_mut` LL | fn call_it_mut isize>(_: &mut F, _: isize) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut` -error[E0277]: expected a `FnOnce(&isize)` closure, found `unsafe fn(isize) -> isize {square}` +error[E0277]: expected an `FnOnce(&isize)` closure, found `unsafe fn(isize) -> isize {square}` --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:31:26 | LL | let z = call_it_once(square, 22); diff --git a/tests/ui/unsafe/unsafe-fn-called-through-ref.rs b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs new file mode 100644 index 0000000000000..d5ac06fe32956 --- /dev/null +++ b/tests/ui/unsafe/unsafe-fn-called-through-ref.rs @@ -0,0 +1,9 @@ +//! Regression test for . +//! Unsafe fn could be called outside of unsafe block through autoderef. + +use std::ptr; + +fn main() { + (&ptr::write)(1 as *mut _, 42); + //~^ ERROR E0133 +} diff --git a/tests/ui/issues/issue-28776.stderr b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr similarity index 90% rename from tests/ui/issues/issue-28776.stderr rename to tests/ui/unsafe/unsafe-fn-called-through-ref.stderr index 3db94ee181017..6978839ed1c8b 100644 --- a/tests/ui/issues/issue-28776.stderr +++ b/tests/ui/unsafe/unsafe-fn-called-through-ref.stderr @@ -1,5 +1,5 @@ error[E0133]: call to unsafe function `std::ptr::write` is unsafe and requires unsafe function or block - --> $DIR/issue-28776.rs:4:5 + --> $DIR/unsafe-fn-called-through-ref.rs:7:5 | LL | (&ptr::write)(1 as *mut _, 42); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function