diff options
102 files changed, 1906 insertions, 511 deletions
diff --git a/RELEASES.md b/RELEASES.md index f35dd27ec24..3bd638ff64c 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,10 @@ +Version 1.77.2 (2024-04-09) +=========================== + +<a id="1.77.2"></a> + +- [CVE-2024-24576: fix escaping of Windows batch file arguments in `std::process::Command`](https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html) + Version 1.77.1 (2024-03-28) =========================== diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 093a985495c..01addc8127e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -346,7 +346,7 @@ impl<'a> AstValidator<'a> { in_impl: matches!(parent, TraitOrTraitImpl::TraitImpl { .. }), const_context_label: parent_constness, remove_const_sugg: ( - self.session.source_map().span_extend_while(span, |c| c == ' ').unwrap_or(span), + self.session.source_map().span_extend_while_whitespace(span), match parent_constness { Some(_) => rustc_errors::Applicability::MachineApplicable, None => rustc_errors::Applicability::MaybeIncorrect, diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 06a681c24e6..e61af863dc0 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -608,7 +608,7 @@ pub(crate) fn run_pass_manager( "LTOPostLink".as_ptr().cast(), 11, ) { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( module.module_llvm.llmod(), llvm::LLVMModFlagBehavior::Error, c"LTOPostLink".as_ptr().cast(), diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index df9f066e58a..d32baa6dc02 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -180,13 +180,13 @@ pub unsafe fn create_module<'ll>( // to ensure intrinsic calls don't use it. if !sess.needs_plt() { let avoid_plt = c"RtLibUseGOT".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); } // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { let canonical_jump_tables = c"CFI Canonical Jump Tables".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, canonical_jump_tables, @@ -197,7 +197,7 @@ pub unsafe fn create_module<'ll>( // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, enable_split_lto_unit, @@ -208,7 +208,7 @@ pub unsafe fn create_module<'ll>( // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) if sess.is_sanitizer_kcfi_enabled() { let kcfi = c"kcfi".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + llvm::LLVMRustAddModuleFlagU32(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); } // Control Flow Guard is currently only supported by the MSVC linker on Windows. @@ -217,7 +217,7 @@ pub unsafe fn create_module<'ll>( CFGuard::Disabled => {} CFGuard::NoChecks => { // Set `cfguard=1` module flag to emit metadata only. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"cfguard".as_ptr() as *const _, @@ -226,7 +226,7 @@ pub unsafe fn create_module<'ll>( } CFGuard::Checks => { // Set `cfguard=2` module flag to emit metadata and checks. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"cfguard".as_ptr() as *const _, @@ -238,26 +238,26 @@ pub unsafe fn create_module<'ll>( if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { if sess.target.arch == "aarch64" { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"branch-target-enforcement".as_ptr().cast(), bti.into(), ); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address-all".as_ptr().cast(), pac_opts.leaf.into(), ); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Min, c"sign-return-address-with-bkey".as_ptr().cast(), @@ -273,7 +273,7 @@ pub unsafe fn create_module<'ll>( // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang). if let CFProtection::Branch | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, c"cf-protection-branch".as_ptr().cast(), @@ -281,7 +281,7 @@ pub unsafe fn create_module<'ll>( ) } if let CFProtection::Return | CFProtection::Full = sess.opts.unstable_opts.cf_protection { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Override, c"cf-protection-return".as_ptr().cast(), @@ -290,7 +290,7 @@ pub unsafe fn create_module<'ll>( } if sess.opts.unstable_opts.virtual_function_elimination { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Error, c"Virtual Function Elim".as_ptr().cast(), @@ -300,7 +300,7 @@ pub unsafe fn create_module<'ll>( // Set module flag to enable Windows EHCont Guard (/guard:ehcont). if sess.opts.unstable_opts.ehcont_guard { - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( llmod, llvm::LLVMModFlagBehavior::Warning, c"ehcontguard".as_ptr() as *const _, @@ -326,6 +326,22 @@ pub unsafe fn create_module<'ll>( llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), ); + // Emit RISC-V specific target-abi metadata + // to workaround lld as the LTO plugin not + // correctly setting target-abi for the LTO object + // FIXME: https://github.com/llvm/llvm-project/issues/50591 + // If llvm_abiname is empty, emit nothing. + let llvm_abiname = &sess.target.options.llvm_abiname; + if matches!(sess.target.arch.as_ref(), "riscv32" | "riscv64") && !llvm_abiname.is_empty() { + llvm::LLVMRustAddModuleFlagString( + llmod, + llvm::LLVMModFlagBehavior::Error, + c"target-abi".as_ptr(), + llvm_abiname.as_ptr().cast(), + llvm_abiname.len(), + ); + } + // Add module flags specified via -Z llvm_module_flag for (key, value, behavior) in &sess.opts.unstable_opts.llvm_module_flag { let key = format!("{key}\0"); @@ -341,7 +357,7 @@ pub unsafe fn create_module<'ll>( // We already checked this during option parsing _ => unreachable!(), }; - llvm::LLVMRustAddModuleFlag(llmod, behavior, key.as_ptr().cast(), *value) + llvm::LLVMRustAddModuleFlagU32(llmod, behavior, key.as_ptr().cast(), *value) } llmod diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index d3a851b40c0..4fdaa59e0e5 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -110,7 +110,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { .unstable_opts .dwarf_version .unwrap_or(sess.target.default_dwarf_version); - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"Dwarf Version".as_ptr().cast(), @@ -118,7 +118,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { ); } else { // Indicate that we want CodeView debug information on MSVC - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"CodeView".as_ptr().cast(), @@ -127,7 +127,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } // Prevent bitcode readers from deleting the debug info. - llvm::LLVMRustAddModuleFlag( + llvm::LLVMRustAddModuleFlagU32( self.llmod, llvm::LLVMModFlagBehavior::Warning, c"Debug Info Version".as_ptr().cast(), diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 284bc74d5c4..5509baaa3e9 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1801,12 +1801,21 @@ extern "C" { /// /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What /// "compatible" means depends on the merge behaviors involved. - pub fn LLVMRustAddModuleFlag( + pub fn LLVMRustAddModuleFlagU32( M: &Module, merge_behavior: LLVMModFlagBehavior, name: *const c_char, value: u32, ); + + pub fn LLVMRustAddModuleFlagString( + M: &Module, + merge_behavior: LLVMModFlagBehavior, + name: *const c_char, + value: *const c_char, + value_len: size_t, + ); + pub fn LLVMRustHasModuleFlag(M: &Module, name: *const c_char, len: size_t) -> bool; pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 62391daecd0..5b39248302e 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -1,10 +1,7 @@ driver_impl_ice = the compiler unexpectedly panicked. this is a bug. driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly -driver_impl_ice_bug_report_outdated = - it seems that this compiler `{$version}` is outdated, a newer nightly should have been released in the meantime - .update = please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists - .url = if the problem still persists, we would appreciate a bug report: {$bug_report_url} +driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden driver_impl_ice_flags = compiler flags: {$flags} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b4007aeb8d7..1f44621736c 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -61,7 +61,7 @@ use std::str; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, OnceLock}; use std::time::{Instant, SystemTime}; -use time::{Date, OffsetDateTime, Time}; +use time::OffsetDateTime; #[allow(unused_macros)] macro do_not_use_print($($t:tt)*) { @@ -1385,9 +1385,6 @@ pub fn install_ice_hook( using_internal_features } -const DATE_FORMAT: &[time::format_description::FormatItem<'static>] = - &time::macros::format_description!("[year]-[month]-[day]"); - /// Prints the ICE message, including query stack, but without backtrace. /// /// The message will point the user at `bug_report_url` to report the ICE. @@ -1416,33 +1413,14 @@ fn report_ice( dcx.emit_err(session_diagnostics::Ice); } - use time::ext::NumericalDuration; - - // Try to hint user to update nightly if applicable when reporting an ICE. - // Attempt to calculate when current version was released, and add 12 hours - // as buffer. If the current version's release timestamp is older than - // the system's current time + 24 hours + 12 hours buffer if we're on - // nightly. - if let Some("nightly") = option_env!("CFG_RELEASE_CHANNEL") - && let Some(version) = option_env!("CFG_VERSION") - && let Some(ver_date_str) = option_env!("CFG_VER_DATE") - && let Ok(ver_date) = Date::parse(&ver_date_str, DATE_FORMAT) - && let ver_datetime = OffsetDateTime::new_utc(ver_date, Time::MIDNIGHT) - && let system_datetime = OffsetDateTime::from(SystemTime::now()) - && system_datetime.checked_sub(36.hours()).is_some_and(|d| d > ver_datetime) - && !using_internal_features.load(std::sync::atomic::Ordering::Relaxed) - { - dcx.emit_note(session_diagnostics::IceBugReportOutdated { - version, - bug_report_url, - note_update: (), - note_url: (), - }); + if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) { + dcx.emit_note(session_diagnostics::IceBugReportInternalFeature); } else { - if using_internal_features.load(std::sync::atomic::Ordering::Relaxed) { - dcx.emit_note(session_diagnostics::IceBugReportInternalFeature); - } else { - dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + dcx.emit_note(session_diagnostics::IceBugReport { bug_report_url }); + + // Only emit update nightly hint for users on nightly builds. + if rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { + dcx.emit_note(session_diagnostics::UpdateNightlyNote); } } diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 62d0da62d2a..1a9683e840a 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -43,19 +43,12 @@ pub(crate) struct IceBugReport<'a> { } #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_internal_feature)] -pub(crate) struct IceBugReportInternalFeature; +#[diag(driver_impl_ice_bug_report_update_note)] +pub(crate) struct UpdateNightlyNote; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_outdated)] -pub(crate) struct IceBugReportOutdated<'a> { - pub version: &'a str, - pub bug_report_url: &'a str, - #[note(driver_impl_update)] - pub note_update: (), - #[note(driver_impl_url)] - pub note_url: (), -} +#[diag(driver_impl_ice_bug_report_internal_feature)] +pub(crate) struct IceBugReportInternalFeature; #[derive(Diagnostic)] #[diag(driver_impl_ice_version)] diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index f90190797ae..6c0551848d6 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -46,6 +46,7 @@ impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T { } } +#[macro_export] macro_rules! into_diag_arg_using_display { ($( $ty:ty ),+ $(,)?) => { $( diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index e66a834ab9e..86b8b6d6b2b 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -355,6 +355,9 @@ hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pa hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures +hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` + .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` + hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4fd7c870fc7..c26f982fa47 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -8,11 +8,13 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_macros::LintDiagnostic; use rustc_middle::query::Providers; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -136,6 +138,8 @@ where infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); + let errors = infcx.resolve_regions(&outlives_env); if errors.is_empty() { return Ok(()); @@ -2010,6 +2014,137 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error res } +fn lint_redundant_lifetimes<'tcx>( + tcx: TyCtxt<'tcx>, + owner_id: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, +) { + let def_kind = tcx.def_kind(owner_id); + match def_kind { + DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Trait + | DefKind::TraitAlias + | DefKind::Fn + | DefKind::Const + | DefKind::Impl { of_trait: _ } => { + // Proceed + } + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + let parent_def_id = tcx.local_parent(owner_id); + if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) { + // Don't check for redundant lifetimes for associated items of trait + // implementations, since the signature is required to be compatible + // with the trait, even if the implementation implies some lifetimes + // are redundant. + return; + } + } + DefKind::Mod + | DefKind::Variant + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TyParam + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Closure => return, + } + + // The ordering of this lifetime map is a bit subtle. + // + // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime, + // where we can prove that `'candidate = 'victim`. + // + // `'static` must come first in this list because we can never replace `'static` with + // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to + // suggest replacing `'a` with `'static`. + let mut lifetimes = vec![tcx.lifetimes.re_static]; + lifetimes.extend( + ty::GenericArgs::identity_for_item(tcx, owner_id).iter().filter_map(|arg| arg.as_region()), + ); + // If we are in a function, add its late-bound lifetimes too. + if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { + for var in tcx.fn_sig(owner_id).instantiate_identity().bound_vars() { + let ty::BoundVariableKind::Region(kind) = var else { continue }; + lifetimes.push(ty::Region::new_late_param(tcx, owner_id.to_def_id(), kind)); + } + } + lifetimes.retain(|candidate| candidate.has_name()); + + // Keep track of lifetimes which have already been replaced with other lifetimes. + // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by + // both `'a` and `'b`. + let mut shadowed = FxHashSet::default(); + + for (idx, &candidate) in lifetimes.iter().enumerate() { + // Don't suggest removing a lifetime twice. We only need to check this + // here and not up in the `victim` loop because equality is transitive, + // so if A = C and B = C, then A must = B, so it'll be shadowed too in + // A's victim loop. + if shadowed.contains(&candidate) { + continue; + } + + for &victim in &lifetimes[(idx + 1)..] { + // We should only have late-bound lifetimes of the `BrNamed` variety, + // since we get these signatures straight from `hir_lowering`. And any + // other regions (ReError/ReStatic/etc.) shouldn't matter, since we + // can't really suggest to remove them. + let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + })) = victim.kind() + else { + continue; + }; + + // Do not rename lifetimes not local to this item since they'll overlap + // with the lint running on the parent. We still want to consider parent + // lifetimes which make child lifetimes redundant, otherwise we would + // have truncated the `identity_for_item` args above. + if tcx.parent(def_id) != owner_id.to_def_id() { + continue; + } + + // If `candidate <: victim` and `victim <: candidate`, then they're equal. + if outlives_env.free_region_map().sub_free_regions(tcx, candidate, victim) + && outlives_env.free_region_map().sub_free_regions(tcx, victim, candidate) + { + shadowed.insert(victim); + tcx.emit_node_span_lint( + rustc_lint_defs::builtin::REDUNDANT_LIFETIMES, + tcx.local_def_id_to_hir_id(def_id.expect_local()), + tcx.def_span(def_id), + RedundantLifetimeArgsLint { candidate, victim }, + ); + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_redundant_lifetime_args)] +#[note] +struct RedundantLifetimeArgsLint<'tcx> { + /// The lifetime we have found to be redundant. + victim: ty::Region<'tcx>, + // The lifetime we can replace the victim with. + candidate: ty::Region<'tcx>, +} + pub fn provide(providers: &mut Providers) { *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; } diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 0b8ac9926e4..affd678fc6c 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -20,7 +20,6 @@ use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; -use rustc_session::lint; use rustc_span::def_id::DefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -867,31 +866,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) => { self.visit_lifetime(lifetime); walk_list!(self, visit_param_bound, bounds); - - if lifetime.res != hir::LifetimeName::Static { - for bound in bounds { - let hir::GenericBound::Outlives(lt) = bound else { - continue; - }; - if lt.res != hir::LifetimeName::Static { - continue; - } - self.insert_lifetime(lt, ResolvedArg::StaticLifetime); - self.tcx.node_span_lint( - lint::builtin::UNUSED_LIFETIMES, - lifetime.hir_id, - lifetime.ident.span, - format!("unnecessary lifetime parameter `{}`", lifetime.ident), - |lint| { - let help = format!( - "you can use the `'static` lifetime directly, in place of `{}`", - lifetime.ident, - ); - lint.help(help); - }, - ); - } - } } &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { self.visit_ty(lhs_ty); 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 59f0fac5aa7..9fb8b4ac40e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2223,6 +2223,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Err(LitToConstError::TypeError) => todo!(), } } + + hir::ExprKind::Path(hir::QPath::Resolved( + _, + &hir::Path { + res: Res::Def(DefKind::ConstParam, def_id), .. + }, + )) => { + let ty = tcx + .type_of(def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"); + let item_def_id = tcx.parent(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + let name = tcx.item_name(def_id); + ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty) + } + _ => { let err = tcx .dcx() diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d3df3dd3885..7a1a2c498aa 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2023,8 +2023,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .sess .source_map() - .span_extend_while(range_start.span, |c| c.is_whitespace()) - .unwrap_or(range_start.span) + .span_extend_while_whitespace(range_start.span) .shrink_to_hi() .to(range_end.span); diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 9a05fb1c30f..7855031e705 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -15,7 +15,7 @@ use rustc_middle::traits::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{sym, Span}; use crate::errors::{ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, @@ -763,8 +763,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span); self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? } else { - last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) + self.tcx + .sess + .source_map() + .span_extend_while_whitespace(last_expr.span) + .shrink_to_hi() + .with_hi(last_stmt.span.hi()) }; + Some((span, needs_box)) } @@ -867,10 +873,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!(" {ident} ") }; let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); - ( - sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), - sugg, - ) + (sm.span_extend_while_whitespace(left_span), sugg) }; Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident }) } diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index e2010ab3830..16e3b8c11c1 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -215,10 +215,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di if let Some(deletion_span) = deletion_span { let msg = "elide the single-use lifetime"; let (use_span, replace_lt) = if elide { - let use_span = sess - .source_map() - .span_extend_while(use_span, char::is_whitespace) - .unwrap_or(use_span); + let use_span = sess.source_map().span_extend_while_whitespace(use_span); (use_span, String::new()) } else { (use_span, "'_".to_owned()) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 53b5273803c..2713690f812 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -79,6 +79,7 @@ declare_lint_pass! { PROC_MACRO_BACK_COMPAT, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, PUB_USE_OF_PRIVATE_EXTERN_CRATE, + REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE, RENAMED_AND_REMOVED_LINTS, @@ -1708,6 +1709,33 @@ declare_lint! { } declare_lint! { + /// The `redundant_lifetimes` lint detects lifetime parameters that are + /// redundant because they are equal to another named lifetime. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(redundant_lifetimes)] + /// + /// // `'a = 'static`, so all usages of `'a` can be replaced with `'static` + /// pub fn bar<'a: 'static>() {} + /// + /// // `'a = 'b`, so all usages of `'b` can be replaced with `'a` + /// pub fn bar<'a: 'b, 'b: 'a>() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused lifetime parameters may signal a mistake or unfinished code. + /// Consider removing the parameter. + pub REDUNDANT_LIFETIMES, + Allow, + "detects lifetime parameters that are redundant because they are equal to some other named lifetime" +} + +declare_lint! { /// The `tyvar_behind_raw_pointer` lint detects raw pointer to an /// inference variable. /// diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a6894a7e089..37c2da4c23a 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -817,7 +817,7 @@ extern "C" uint32_t LLVMRustVersionMinor() { return LLVM_VERSION_MINOR; } extern "C" uint32_t LLVMRustVersionMajor() { return LLVM_VERSION_MAJOR; } -extern "C" void LLVMRustAddModuleFlag( +extern "C" void LLVMRustAddModuleFlagU32( LLVMModuleRef M, Module::ModFlagBehavior MergeBehavior, const char *Name, @@ -825,6 +825,16 @@ extern "C" void LLVMRustAddModuleFlag( unwrap(M)->addModuleFlag(MergeBehavior, Name, Value); } +extern "C" void LLVMRustAddModuleFlagString( + LLVMModuleRef M, + Module::ModFlagBehavior MergeBehavior, + const char *Name, + const char *Value, + size_t ValueLen) { + unwrap(M)->addModuleFlag(MergeBehavior, Name, + MDString::get(unwrap(M)->getContext(), StringRef(Value, ValueLen))); +} + extern "C" bool LLVMRustHasModuleFlag(LLVMModuleRef M, const char *Name, size_t Len) { return unwrap(M)->getModuleFlag(StringRef(Name, Len)) != nullptr; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0daf83162db..6275c5d2a11 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -757,7 +757,7 @@ pub struct GlobalCtxt<'tcx> { impl<'tcx> GlobalCtxt<'tcx> { /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of /// `f`. - pub fn enter<'a: 'tcx, F, R>(&'a self, f: F) -> R + pub fn enter<F, R>(&'tcx self, f: F) -> R where F: FnOnce(TyCtxt<'tcx>) -> R, { diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index ee18647cdd8..cc1d6e50f6d 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -5,13 +5,13 @@ use std::fmt::Write; use std::ops::ControlFlow; use crate::ty::{ - AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, PolyTraitPredicate, - Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, + self, AliasTy, Const, ConstKind, FallibleTypeFolder, InferConst, InferTy, Opaque, + PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg}; +use rustc_errors::{into_diag_arg_using_display, Applicability, Diag, DiagArgValue, IntoDiagArg}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -19,10 +19,9 @@ use rustc_hir::{PredicateOrigin, WherePredicate}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; -impl<'tcx> IntoDiagArg for Ty<'tcx> { - fn into_diag_arg(self) -> DiagArgValue { - self.to_string().into_diag_arg() - } +into_diag_arg_using_display! { + Ty<'_>, + ty::Region<'_>, } impl<'tcx> Ty<'tcx> { diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index d6ec7d64926..b26f968bf5e 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -126,59 +126,41 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { let mut field_remapping = UnordMap::default(); - // One parent capture may correspond to several child captures if we end up - // refining the set of captures via edition-2021 precise captures. We want to - // match up any number of child captures with one parent capture, so we keep - // peeking off this `Peekable` until the child doesn't match anymore. - let mut parent_captures = - tcx.closure_captures(parent_def_id).iter().copied().enumerate().peekable(); - // Make sure we use every field at least once, b/c why are we capturing something - // if it's not used in the inner coroutine. - let mut field_used_at_least_once = false; - - for (child_field_idx, child_capture) in tcx + let mut child_captures = tcx .closure_captures(coroutine_def_id) .iter() .copied() // By construction we capture all the args first. .skip(num_args) .enumerate() + .peekable(); + + // One parent capture may correspond to several child captures if we end up + // refining the set of captures via edition-2021 precise captures. We want to + // match up any number of child captures with one parent capture, so we keep + // peeking off this `Peekable` until the child doesn't match anymore. + for (parent_field_idx, parent_capture) in + tcx.closure_captures(parent_def_id).iter().copied().enumerate() { - loop { - let Some(&(parent_field_idx, parent_capture)) = parent_captures.peek() else { - bug!("we ran out of parent captures!") - }; + // Make sure we use every field at least once, b/c why are we capturing something + // if it's not used in the inner coroutine. + let mut field_used_at_least_once = false; - let PlaceBase::Upvar(parent_base) = parent_capture.place.base else { - bug!("expected capture to be an upvar"); - }; - let PlaceBase::Upvar(child_base) = child_capture.place.base else { - bug!("expected capture to be an upvar"); - }; + // A parent matches a child if they share the same prefix of projections. + // The child may have more, if it is capturing sub-fields out of + // something that is captured by-move in the parent closure. + while child_captures.peek().map_or(false, |(_, child_capture)| { + child_prefix_matches_parent_projections(parent_capture, child_capture) + }) { + let (child_field_idx, child_capture) = child_captures.next().unwrap(); + // This analysis only makes sense if the parent capture is a + // prefix of the child capture. assert!( - child_capture.place.projections.len() >= parent_capture.place.projections.len() + child_capture.place.projections.len() >= parent_capture.place.projections.len(), + "parent capture ({parent_capture:#?}) expected to be prefix of \ + child capture ({child_capture:#?})" ); - // A parent matches a child they share the same prefix of projections. - // The child may have more, if it is capturing sub-fields out of - // something that is captured by-move in the parent closure. - if parent_base.var_path.hir_id != child_base.var_path.hir_id - || !std::iter::zip( - &child_capture.place.projections, - &parent_capture.place.projections, - ) - .all(|(child, parent)| child.kind == parent.kind) - { - // Make sure the field was used at least once. - assert!( - field_used_at_least_once, - "we captured {parent_capture:#?} but it was not used in the child coroutine?" - ); - field_used_at_least_once = false; - // Skip this field. - let _ = parent_captures.next().unwrap(); - continue; - } // Store this set of additional projections (fields and derefs). // We need to re-apply them later. @@ -221,15 +203,15 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { ); field_used_at_least_once = true; - break; } - } - // Pop the last parent capture - if field_used_at_least_once { - let _ = parent_captures.next().unwrap(); + // Make sure the field was used at least once. + assert!( + field_used_at_least_once, + "we captured {parent_capture:#?} but it was not used in the child coroutine?" + ); } - assert_eq!(parent_captures.next(), None, "leftover parent captures?"); + assert_eq!(child_captures.next(), None, "leftover child captures?"); if coroutine_kind == ty::ClosureKind::FnOnce { assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len()); @@ -251,6 +233,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { let mut by_move_body = body.clone(); MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); + // FIXME: use query feeding to generate the body right here and then only store the `DefId` of the new body. by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { coroutine_def_id: coroutine_def_id.to_def_id(), }); @@ -258,6 +241,22 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { } } +fn child_prefix_matches_parent_projections( + parent_capture: &ty::CapturedPlace<'_>, + child_capture: &ty::CapturedPlace<'_>, +) -> bool { + let PlaceBase::Upvar(parent_base) = parent_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + let PlaceBase::Upvar(child_base) = child_capture.place.base else { + bug!("expected capture to be an upvar"); + }; + + parent_base.var_path.hir_id == child_base.var_path.hir_id + && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections) + .all(|(child, parent)| child.kind == parent.kind) +} + struct MakeByMoveBody<'tcx> { tcx: TyCtxt<'tcx>, field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>, diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs index 0762016ef75..221224eed01 100644 --- a/compiler/rustc_smir/src/rustc_smir/builder.rs +++ b/compiler/rustc_smir/src/rustc_smir/builder.rs @@ -4,9 +4,10 @@ //! monomorphic body using internal representation. //! After that, we convert the internal representation into a stable one. use crate::rustc_smir::{Stable, Tables}; +use rustc_hir::def::DefKind; use rustc_middle::mir; use rustc_middle::mir::visit::MutVisitor; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; /// Builds a monomorphic body for a given instance. pub struct BodyBuilder<'tcx> { @@ -16,46 +17,43 @@ pub struct BodyBuilder<'tcx> { impl<'tcx> BodyBuilder<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>) -> Self { + let instance = match instance.def { + // To get the fallback body of an intrinsic, we need to convert it to an item. + ty::InstanceDef::Intrinsic(def_id) => ty::Instance::new(def_id, instance.args), + _ => instance, + }; BodyBuilder { tcx, instance } } /// Build a stable monomorphic body for a given instance based on the MIR body. /// - /// Note that we skip instantiation for static and constants. Trying to do so can cause ICE. - /// - /// We do monomorphize non-generic functions to eval unevaluated constants. + /// All constants are also evaluated. pub fn build(mut self, tables: &mut Tables<'tcx>) -> stable_mir::mir::Body { - let mut body = self.tcx.instance_mir(self.instance.def).clone(); - if self.tcx.def_kind(self.instance.def_id()).is_fn_like() || !self.instance.args.is_empty() + let body = tables.tcx.instance_mir(self.instance.def).clone(); + let mono_body = if !self.instance.args.is_empty() + // Without the `generic_const_exprs` feature gate, anon consts in signatures do not + // get generic parameters. Which is wrong, but also not a problem without + // generic_const_exprs + || self.tcx.def_kind(self.instance.def_id()) != DefKind::AnonConst { - self.visit_body(&mut body); - } - body.stable(tables) - } - - fn monomorphize<T>(&self, value: T) -> T - where - T: ty::TypeFoldable<TyCtxt<'tcx>>, - { - self.instance.instantiate_mir_and_normalize_erasing_regions( - self.tcx, - ty::ParamEnv::reveal_all(), - ty::EarlyBinder::bind(value), - ) + let mut mono_body = self.instance.instantiate_mir_and_normalize_erasing_regions( + tables.tcx, + ty::ParamEnv::reveal_all(), + ty::EarlyBinder::bind(body), + ); + self.visit_body(&mut mono_body); + mono_body + } else { + // Already monomorphic. + body + }; + mono_body.stable(tables) } } impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { - fn visit_ty_const(&mut self, ct: &mut ty::Const<'tcx>, _location: mir::Location) { - *ct = self.monomorphize(*ct); - } - - fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: mir::visit::TyContext) { - *ty = self.monomorphize(*ty); - } - fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) { - let const_ = self.monomorphize(constant.const_); + let const_ = constant.const_; let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) { Ok(v) => v, Err(mir::interpret::ErrorHandled::Reported(..)) => return, @@ -68,10 +66,6 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> { self.super_constant(constant, location); } - fn visit_args(&mut self, args: &mut GenericArgsRef<'tcx>, _: mir::Location) { - *args = self.monomorphize(*args); - } - fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 770624d331d..f721a04d6b9 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -654,6 +654,12 @@ impl SourceMap { }) } + /// Extends the span to include any trailing whitespace, or returns the original + /// span if a `SpanSnippetError` was encountered. + pub fn span_extend_while_whitespace(&self, span: Span) -> Span { + self.span_extend_while(span, char::is_whitespace).unwrap_or(span) + } + /// Extends the given `Span` to previous character while the previous character matches the predicate pub fn span_extend_prev_while( &self, @@ -1034,12 +1040,9 @@ impl SourceMap { /// // ^^^^^^ input /// ``` pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option<Span> { - let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?; - let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?)); - if self.span_to_snippet(span).as_deref() != Ok(";") { - return None; - } - Some(span) + let span = self.span_extend_while_whitespace(mac_call); + let span = self.next_point(span); + if self.span_to_snippet(span).as_deref() == Ok(";") { Some(span) } else { None } } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index af90372b97c..2067956d0f5 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1592,8 +1592,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .tcx .sess .source_map() - .span_extend_while(expr_span, char::is_whitespace) - .unwrap_or(expr_span) + .span_extend_while_whitespace(expr_span) .shrink_to_hi() .to(await_expr.span.shrink_to_hi()); err.span_suggestion( @@ -4851,10 +4850,7 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( let hir::IsAsync::Async(async_span) = sig.header.asyncness else { return None; }; - let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace()) - else { - return None; - }; + let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span); let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 77cd4662ae5..902b76e8c1e 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -133,10 +133,14 @@ fn layout_of_uncached<'tcx>( ty::PatternKind::Range { start, end, include_end } => { if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &mut layout.abi { if let Some(start) = start { - scalar.valid_range_mut().start = start.eval_bits(tcx, param_env); + scalar.valid_range_mut().start = start + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { - let mut end = end.eval_bits(tcx, param_env); + let mut end = end + .try_eval_bits(tcx, param_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { end = end.wrapping_sub(1); } diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index aafa89c03e0..a032a180fcf 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -41,13 +41,22 @@ impl Instance { with(|cx| cx.instance_args(self.def)) } - /// Get the body of an Instance. The body will be eagerly monomorphized. + /// Get the body of an Instance. + /// + /// The body will be eagerly monomorphized and all constants will already be evaluated. + /// + /// This method will return the intrinsic fallback body if one was defined. pub fn body(&self) -> Option<Body> { with(|context| context.instance_body(self.def)) } /// Check whether this instance has a body available. /// + /// For intrinsics with fallback body, this will return `true`. It is up to the user to decide + /// whether to specialize the intrinsic or to use its fallback body. + /// + /// For more information on fallback body, see <https://github.com/rust-lang/rust/issues/93145>. + /// /// This call is much cheaper than `instance.body().is_some()`, since it doesn't try to build /// the StableMIR body. pub fn has_body(&self) -> bool { diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index acaa7e9228e..250cd583696 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -322,6 +322,14 @@ impl<R: ?Sized + Read> Read for BufReader<R> { crate::io::default_read_exact(self, buf) } + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + if self.buf.consume_with(cursor.capacity(), |claimed| cursor.append(claimed)) { + return Ok(()); + } + + crate::io::default_read_buf_exact(self, cursor) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let total_len = bufs.iter().map(|b| b.len()).sum::<usize>(); if self.buf.pos() == self.buf.filled() && total_len >= self.capacity() { diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 4ef1f1b695e..49dde828c1f 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -357,6 +357,13 @@ where self.pos += n as u64; Ok(()) } + + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + let n = cursor.capacity(); + Read::read_buf_exact(&mut self.remaining_slice(), cursor)?; + self.pos += n as u64; + Ok(()) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index cb972abd2b8..ee7ed4bcc9a 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -50,6 +50,10 @@ impl<R: Read + ?Sized> Read for &mut R { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { (**self).read_exact(buf) } + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } } #[stable(feature = "rust1", since = "1.0.0")] impl<W: Write + ?Sized> Write for &mut W { @@ -154,6 +158,10 @@ impl<R: Read + ?Sized> Read for Box<R> { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { (**self).read_exact(buf) } + #[inline] + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + (**self).read_buf_exact(cursor) + } } #[stable(feature = "rust1", since = "1.0.0")] impl<W: Write + ?Sized> Write for Box<W> { @@ -302,6 +310,22 @@ impl Read for &[u8] { } #[inline] + fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { + if cursor.capacity() > self.len() { + return Err(io::const_io_error!( + ErrorKind::UnexpectedEof, + "failed to fill whole buffer" + )); + } + let (a, b) = self.split_at(cursor.capacity()); + + cursor.append(a); + + *self = b; + Ok(()) + } + + #[inline] fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { let len = self.len(); buf.try_reserve(len)?; diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 10bf9c51d16..980b3e7aa48 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -582,6 +582,29 @@ where Ok(()) } +pub(crate) fn default_read_buf_exact<R: Read + ?Sized>( + this: &mut R, + mut cursor: BorrowedCursor<'_>, +) -> Result<()> { + while cursor.capacity() > 0 { + let prev_written = cursor.written(); + match this.read_buf(cursor.reborrow()) { + Ok(()) => {} + Err(e) if e.is_interrupted() => continue, + Err(e) => return Err(e), + } + + if cursor.written() == prev_written { + return Err(error::const_io_error!( + ErrorKind::UnexpectedEof, + "failed to fill whole buffer" + )); + } + } + + Ok(()) +} + /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -978,24 +1001,8 @@ pub trait Read { /// /// If this function returns an error, all bytes read will be appended to `cursor`. #[unstable(feature = "read_buf", issue = "78485")] - fn read_buf_exact(&mut self, mut cursor: BorrowedCursor<'_>) -> Result<()> { - while cursor.capacity() > 0 { - let prev_written = cursor.written(); - match self.read_buf(cursor.reborrow()) { - Ok(()) => {} - Err(e) if e.is_interrupted() => continue, - Err(e) => return Err(e), - } - - if cursor.written() == prev_written { - return Err(error::const_io_error!( - ErrorKind::UnexpectedEof, - "failed to fill whole buffer" - )); - } - } - - Ok(()) + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> { + default_read_buf_exact(self, cursor) } /// Creates a "by reference" adaptor for this instance of `Read`. diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 8f60b3b1535..73fa7cbc3fe 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -451,6 +451,9 @@ impl Read for Stdin { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.lock().read_exact(buf) } + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf_exact(cursor) + } } #[stable(feature = "read_shared_stdin", since = "1.78.0")] @@ -477,6 +480,9 @@ impl Read for &Stdin { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.lock().read_exact(buf) } + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.lock().read_buf_exact(cursor) + } } // only used by platform-dependent io::copy specializations, i.e. unused on some platforms @@ -517,6 +523,10 @@ impl Read for StdinLock<'_> { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.inner.read_exact(buf) } + + fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf_exact(cursor) + } } impl SpecReadByte for StdinLock<'_> { diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 1be3acf5d43..15ab2250122 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -199,8 +199,60 @@ pub trait CommandExt: Sealed { /// Append literal text to the command line without any quoting or escaping. /// - /// This is useful for passing arguments to `cmd.exe /c`, which doesn't follow - /// `CommandLineToArgvW` escaping rules. + /// This is useful for passing arguments to applications which doesn't follow + /// the standard C run-time escaping rules, such as `cmd.exe /c`. + /// + /// # Bat files + /// + /// Note the `cmd /c` command line has slightly different escaping rules then bat files + /// themselves. If possible, it may be better to write complex arguments to a temporary + /// .bat file, with appropriate escaping, and simply run that using: + /// + /// ```no_run + /// # use std::process::Command; + /// # let temp_bat_file = ""; + /// # #[allow(unused)] + /// let output = Command::new("cmd").args(["/c", &format!("\"{temp_bat_file}\"")]).output(); + /// ``` + /// + /// # Example + /// + /// Run a bat script using both trusted and untrusted arguments. + /// + /// ```no_run + /// #[cfg(windows)] + /// // `my_script_path` is a path to known bat file. + /// // `user_name` is an untrusted name given by the user. + /// fn run_script( + /// my_script_path: &str, + /// user_name: &str, + /// ) -> Result<std::process::Output, std::io::Error> { + /// use std::io::{Error, ErrorKind}; + /// use std::os::windows::process::CommandExt; + /// use std::process::Command; + /// + /// // Create the command line, making sure to quote the script path. + /// // This assumes the fixed arguments have been tested to work with the script we're using. + /// let mut cmd_args = format!(r#""{my_script_path}" "--features=[a,b,c]""#); + /// + /// // Make sure the user name is safe. In particular we need to be + /// // cautious of ascii symbols that cmd may interpret specially. + /// // Here we only allow alphanumeric characters. + /// if !user_name.chars().all(|c| c.is_alphanumeric()) { + /// return Err(Error::new(ErrorKind::InvalidInput, "invalid user name")); + /// } + /// // now we've checked the user name, let's add that too. + /// cmd_args.push(' '); + /// cmd_args.push_str(&format!("--user {user_name}")); + /// + /// // call cmd.exe and return the output + /// Command::new("cmd.exe") + /// .arg("/c") + /// // surround the entire command in an extra pair of quotes, as required by cmd.exe. + /// .raw_arg(&format!("\"{cmd_args}\"")) + /// .output() + /// } + /// ```` #[stable(feature = "windows_process_extensions_raw_arg", since = "1.62.0")] fn raw_arg<S: AsRef<OsStr>>(&mut self, text_to_append_as_is: S) -> &mut process::Command; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index b84d5f11954..69cc61b30ef 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -88,6 +88,47 @@ //! assert_eq!(b"test", output.stdout.as_slice()); //! ``` //! +//! # Windows argument splitting +//! +//! On Unix systems arguments are passed to a new process as an array of strings +//! but on Windows arguments are passed as a single commandline string and it's +//! up to the child process to parse it into an array. Therefore the parent and +//! child processes must agree on how the commandline string is encoded. +//! +//! Most programs use the standard C run-time `argv`, which in practice results +//! in consistent argument handling. However some programs have their own way of +//! parsing the commandline string. In these cases using [`arg`] or [`args`] may +//! result in the child process seeing a different array of arguments then the +//! parent process intended. +//! +//! Two ways of mitigating this are: +//! +//! * Validate untrusted input so that only a safe subset is allowed. +//! * Use [`raw_arg`] to build a custom commandline. This bypasses the escaping +//! rules used by [`arg`] so should be used with due caution. +//! +//! `cmd.exe` and `.bat` use non-standard argument parsing and are especially +//! vulnerable to malicious input as they may be used to run arbitrary shell +//! commands. Untrusted arguments should be restricted as much as possible. +//! For examples on handling this see [`raw_arg`]. +//! +//! ### Bat file special handling +//! +//! On Windows, `Command` uses the Windows API function [`CreateProcessW`] to +//! spawn new processes. An undocumented feature of this function is that, +//! when given a `.bat` file as the application to run, it will automatically +//! convert that into running `cmd.exe /c` with the bat file as the next argument. +//! +//! For historical reasons Rust currently preserves this behaviour when using +//! [`Command::new`], and escapes the arguments according to `cmd.exe` rules. +//! Due to the complexity of `cmd.exe` argument handling, it might not be +//! possible to safely escape some special chars, and using them will result +//! in an error being returned at process spawn. The set of unescapeable +//! special chars might change between releases. +//! +//! Also note that running `.bat` scripts in this way may be removed in the +//! future and so should not be relied upon. +//! //! [`spawn`]: Command::spawn //! [`output`]: Command::output //! @@ -97,6 +138,12 @@ //! //! [`Write`]: io::Write //! [`Read`]: io::Read +//! +//! [`arg`]: Command::arg +//! [`args`]: Command::args +//! [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg +//! +//! [`CreateProcessW`]: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw #![stable(feature = "process", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] @@ -611,6 +658,22 @@ impl Command { /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// + /// <div class="warning"> + /// + /// On Windows use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to use with `arg`. + /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments + /// and are therefore vulnerable to malicious input. + /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + /// </div> + /// /// # Examples /// /// Basic usage: @@ -641,6 +704,22 @@ impl Command { /// escaped characters, word splitting, glob patterns, variable substitution, etc. /// have no effect. /// + /// <div class="warning"> + /// + /// On Windows use caution with untrusted inputs. Most applications use the + /// standard convention for decoding arguments passed to them. These are safe to use with `args`. + /// However some applications, such as `cmd.exe` and `.bat` files, use a non-standard way of decoding arguments + /// and are therefore vulnerable to malicious input. + /// In the case of `cmd.exe` this is especially important because a malicious argument can potentially run arbitrary shell commands. + /// + /// See [Windows argument splitting][windows-args] for more details + /// or [`raw_arg`] for manually implementing non-standard argument encoding. + /// + /// [`raw_arg`]: crate::os::windows::process::CommandExt::raw_arg + /// [windows-args]: crate::process#windows-argument-splitting + /// + /// </div> + /// /// # Examples /// /// Basic usage: diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 96f64051cda..e8430b6ae70 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -1,10 +1,13 @@ // miri has some special hacks here that make things unused. #![cfg_attr(miri, allow(unused))] +#[cfg(test)] +mod tests; + use crate::os::unix::prelude::*; use crate::ffi::{CStr, OsStr, OsString}; -use crate::fmt; +use crate::fmt::{self, Write as _}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -356,7 +359,7 @@ pub struct DirEntry { entry: dirent64, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct OpenOptions { // generic read: bool, @@ -370,7 +373,7 @@ pub struct OpenOptions { mode: mode_t, } -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq)] pub struct FilePermissions { mode: mode_t, } @@ -389,7 +392,7 @@ pub struct FileTimes { created: Option<SystemTime>, } -#[derive(Copy, Clone, Eq, Debug)] +#[derive(Copy, Clone, Eq)] pub struct FileType { mode: mode_t, } @@ -406,11 +409,13 @@ impl core::hash::Hash for FileType { } } -#[derive(Debug)] pub struct DirBuilder { mode: mode_t, } +#[derive(Copy, Clone)] +struct Mode(mode_t); + cfg_has_statx! {{ impl FileAttr { fn from_stat64(stat: stat64) -> Self { @@ -689,12 +694,26 @@ impl FileType { } } +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FileType { mode } = self; + f.debug_struct("FileType").field("mode", &Mode(*mode)).finish() + } +} + impl FromInner<u32> for FilePermissions { fn from_inner(mode: u32) -> FilePermissions { FilePermissions { mode: mode as mode_t } } } +impl fmt::Debug for FilePermissions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let FilePermissions { mode } = self; + f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish() + } +} + impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. @@ -1135,6 +1154,23 @@ impl OpenOptions { } } +impl fmt::Debug for OpenOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } = + self; + f.debug_struct("OpenOptions") + .field("read", read) + .field("write", write) + .field("append", append) + .field("truncate", truncate) + .field("create", create) + .field("create_new", create_new) + .field("custom_flags", custom_flags) + .field("mode", &Mode(*mode)) + .finish() + } +} + impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { run_path_with_cstr(path, &|path| File::open_c(path, opts)) @@ -1425,6 +1461,13 @@ impl DirBuilder { } } +impl fmt::Debug for DirBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let DirBuilder { mode } = self; + f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish() + } +} + impl AsInner<FileDesc> for File { #[inline] fn as_inner(&self) -> &FileDesc { @@ -1597,6 +1640,73 @@ impl fmt::Debug for File { } } +// Format in octal, followed by the mode format used in `ls -l`. +// +// References: +// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html +// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html +// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html +// +// Example: +// 0o100664 (-rw-rw-r--) +impl fmt::Debug for Mode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self(mode) = *self; + write!(f, "0o{mode:06o}")?; + + let entry_type = match mode & libc::S_IFMT { + libc::S_IFDIR => 'd', + libc::S_IFBLK => 'b', + libc::S_IFCHR => 'c', + libc::S_IFLNK => 'l', + libc::S_IFIFO => 'p', + libc::S_IFREG => '-', + _ => return Ok(()), + }; + + f.write_str(" (")?; + f.write_char(entry_type)?; + + // Owner permissions + f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?; + let owner_executable = mode & libc::S_IXUSR != 0; + let setuid = mode as c_int & libc::S_ISUID as c_int != 0; + f.write_char(match (owner_executable, setuid) { + (true, true) => 's', // executable and setuid + (false, true) => 'S', // setuid + (true, false) => 'x', // executable + (false, false) => '-', + })?; + + // Group permissions + f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?; + let group_executable = mode & libc::S_IXGRP != 0; + let setgid = mode as c_int & libc::S_ISGID as c_int != 0; + f.write_char(match (group_executable, setgid) { + (true, true) => 's', // executable and setgid + (false, true) => 'S', // setgid + (true, false) => 'x', // executable + (false, false) => '-', + })?; + + // Other permissions + f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?; + f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?; + let other_executable = mode & libc::S_IXOTH != 0; + let sticky = mode as c_int & libc::S_ISVTX as c_int != 0; + f.write_char(match (entry_type, other_executable, sticky) { + ('d', true, true) => 't', // searchable and restricted deletion + ('d', false, true) => 'T', // restricted deletion + (_, true, _) => 'x', // executable + (_, false, _) => '-', + })?; + + f.write_char(')') + } +} + pub fn readdir(path: &Path) -> io::Result<ReadDir> { let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?; if ptr.is_null() { @@ -1847,39 +1957,9 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { use crate::sync::atomic::{AtomicBool, Ordering}; - const COPYFILE_ACL: u32 = 1 << 0; - const COPYFILE_STAT: u32 = 1 << 1; - const COPYFILE_XATTR: u32 = 1 << 2; - const COPYFILE_DATA: u32 = 1 << 3; - - const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; - const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; - const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; - - const COPYFILE_STATE_COPIED: u32 = 8; - - #[allow(non_camel_case_types)] - type copyfile_state_t = *mut libc::c_void; - #[allow(non_camel_case_types)] - type copyfile_flags_t = u32; - - extern "C" { - fn fcopyfile( - from: libc::c_int, - to: libc::c_int, - state: copyfile_state_t, - flags: copyfile_flags_t, - ) -> libc::c_int; - fn copyfile_state_alloc() -> copyfile_state_t; - fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; - fn copyfile_state_get( - state: copyfile_state_t, - flag: u32, - dst: *mut libc::c_void, - ) -> libc::c_int; - } - - struct FreeOnDrop(copyfile_state_t); + const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA; + + struct FreeOnDrop(libc::copyfile_state_t); impl Drop for FreeOnDrop { fn drop(&mut self) { // The code below ensures that `FreeOnDrop` is never a null pointer @@ -1887,7 +1967,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // `copyfile_state_free` returns -1 if the `to` or `from` files // cannot be closed. However, this is not considered this an // error. - copyfile_state_free(self.0); + libc::copyfile_state_free(self.0); } } } @@ -1896,6 +1976,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // We store the availability in a global to avoid unnecessary syscalls static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); syscall! { + // Mirrors `libc::fclonefileat` fn fclonefileat( srcfd: libc::c_int, dst_dirfd: libc::c_int, @@ -1932,22 +2013,22 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { // We ensure that `FreeOnDrop` never contains a null pointer so it is // always safe to call `copyfile_state_free` let state = unsafe { - let state = copyfile_state_alloc(); + let state = libc::copyfile_state_alloc(); if state.is_null() { return Err(crate::io::Error::last_os_error()); } FreeOnDrop(state) }; - let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; + let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA }; - cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; let mut bytes_copied: libc::off_t = 0; cvt(unsafe { - copyfile_state_get( + libc::copyfile_state_get( state.0, - COPYFILE_STATE_COPIED, + libc::COPYFILE_STATE_COPIED as u32, core::ptr::addr_of_mut!(bytes_copied) as *mut libc::c_void, ) })?; diff --git a/library/std/src/sys/pal/unix/fs/tests.rs b/library/std/src/sys/pal/unix/fs/tests.rs new file mode 100644 index 00000000000..71be3472148 --- /dev/null +++ b/library/std/src/sys/pal/unix/fs/tests.rs @@ -0,0 +1,71 @@ +use crate::sys::pal::unix::fs::FilePermissions; + +#[test] +fn test_debug_permissions() { + for (expected, mode) in [ + // typical directory + ("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775), + // typical text file + ("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664), + // setuid executable (/usr/bin/doas) + ("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755), + // char device (/dev/zero) + ("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666), + // block device (/dev/vda) + ("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660), + // symbolic link + ("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777), + // fifo + ("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664), + // none + ("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000), + // unrecognized + ("FilePermissions { mode: 0o000001 }", 1), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode }), expected); + } + + for (expected, mode) in [ + // owner readable + ("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR), + // owner writable + ("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR), + // owner executable + ("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR), + // setuid + ("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID), + // owner executable and setuid + ("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID), + // group readable + ("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP), + // group writable + ("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP), + // group executable + ("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP), + // setgid + ("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID), + // group executable and setgid + ("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID), + // other readable + ("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH), + // other writeable + ("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH), + // other executable + ("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH), + // sticky + ("FilePermissions { mode: 0o101000 (----------) }", libc::S_ISVTX), + // other executable and sticky + ("FilePermissions { mode: 0o101001 (---------x) }", libc::S_IXOTH | libc::S_ISVTX), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFREG | mode }), expected); + } + + for (expected, mode) in [ + // restricted deletion ("sticky") flag is set, and search permission is not granted to others + ("FilePermissions { mode: 0o041000 (d--------T) }", libc::S_ISVTX), + // sticky and searchable + ("FilePermissions { mode: 0o041001 (d--------t) }", libc::S_ISVTX | libc::S_IXOTH), + ] { + assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFDIR | mode }), expected); + } +} diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs index 6a989dd3e76..2445d9073db 100644 --- a/library/std/src/sys/pal/unsupported/process.rs +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -1,7 +1,6 @@ -use crate::ffi::OsStr; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; -use crate::marker::PhantomData; use crate::num::NonZero; use crate::path::Path; use crate::sys::fs::File; @@ -16,7 +15,14 @@ pub use crate::ffi::OsString as EnvKey; //////////////////////////////////////////////////////////////////////////////// pub struct Command { + program: OsString, + args: Vec<OsString>, env: CommandEnv, + + cwd: Option<OsString>, + stdin: Option<Stdio>, + stdout: Option<Stdio>, + stderr: Option<Stdio>, } // passed back to std::process with the pipes connected to the child, if any @@ -27,39 +33,62 @@ pub struct StdioPipes { pub stderr: Option<AnonPipe>, } -// FIXME: This should be a unit struct, so we can always construct it -// The value here should be never used, since we cannot spawn processes. +#[derive(Debug)] pub enum Stdio { Inherit, Null, MakePipe, + ParentStdout, + ParentStderr, + #[allow(dead_code)] // This variant exists only for the Debug impl + InheritFile(File), } impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_owned(), + args: vec![program.to_owned()], + env: Default::default(), + cwd: None, + stdin: None, + stdout: None, + stderr: None, + } } - pub fn arg(&mut self, _arg: &OsStr) {} + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } pub fn env_mut(&mut self) -> &mut CommandEnv { &mut self.env } - pub fn cwd(&mut self, _dir: &OsStr) {} + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_owned()); + } - pub fn stdin(&mut self, _stdin: Stdio) {} + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } - pub fn stdout(&mut self, _stdout: Stdio) {} + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } - pub fn stderr(&mut self, _stderr: Stdio) {} + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } pub fn get_program(&self) -> &OsStr { - panic!("unsupported") + &self.program } pub fn get_args(&self) -> CommandArgs<'_> { - CommandArgs { _p: PhantomData } + let mut iter = self.args.iter(); + iter.next(); + CommandArgs { iter } } pub fn get_envs(&self) -> CommandEnvs<'_> { @@ -67,7 +96,7 @@ impl Command { } pub fn get_current_dir(&self) -> Option<&Path> { - None + self.cwd.as_ref().map(|cs| Path::new(cs)) } pub fn spawn( @@ -91,31 +120,83 @@ impl From<AnonPipe> for Stdio { impl From<io::Stdout> for Stdio { fn from(_: io::Stdout) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + Stdio::ParentStdout } } impl From<io::Stderr> for Stdio { fn from(_: io::Stderr) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + Stdio::ParentStderr } } impl From<File> for Stdio { - fn from(_file: File) -> Stdio { - // FIXME: This is wrong. - // Instead, the Stdio we have here should be a unit struct. - panic!("unsupported") + fn from(file: File) -> Stdio { + Stdio::InheritFile(file) } } impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) + // show all attributes + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let mut debug_command = f.debug_struct("Command"); + debug_command.field("program", &self.program).field("args", &self.args); + if !self.env.is_unchanged() { + debug_command.field("env", &self.env); + } + + if self.cwd.is_some() { + debug_command.field("cwd", &self.cwd); + } + + if self.stdin.is_some() { + debug_command.field("stdin", &self.stdin); + } + if self.stdout.is_some() { + debug_command.field("stdout", &self.stdout); + } + if self.stderr.is_some() { + debug_command.field("stderr", &self.stderr); + } + + debug_command.finish() + } else { + if let Some(ref cwd) = self.cwd { + write!(f, "cd {cwd:?} && ")?; + } + if self.env.does_clear() { + write!(f, "env -i ")?; + // Altered env vars will be printed next, that should exactly work as expected. + } else { + // Removed env vars need the command to be wrapped in `env`. + let mut any_removed = false; + for (key, value_opt) in self.get_envs() { + if value_opt.is_none() { + if !any_removed { + write!(f, "env ")?; + any_removed = true; + } + write!(f, "-u {} ", key.to_string_lossy())?; + } + } + } + // Altered env vars can just be added in front of the program. + for (key, value_opt) in self.get_envs() { + if let Some(value) = value_opt { + write!(f, "{}={value:?} ", key.to_string_lossy())?; + } + } + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } } } @@ -217,23 +298,30 @@ impl Process { } pub struct CommandArgs<'a> { - _p: PhantomData<&'a ()>, + iter: crate::slice::Iter<'a, OsString>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; fn next(&mut self) -> Option<&'a OsStr> { - None + self.iter.next().map(|os| &**os) } fn size_hint(&self) -> (usize, Option<usize>) { - (0, Some(0)) + self.iter.size_hint() } } -impl<'a> ExactSizeIterator for CommandArgs<'a> {} +impl<'a> ExactSizeIterator for CommandArgs<'a> { + fn len(&self) -> usize { + self.iter.len() + } + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} impl<'a> fmt::Debug for CommandArgs<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() + f.debug_list().entries(self.iter.clone()).finish() } } diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 90e1bff52a3..555ad581b85 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -34,6 +34,102 @@ use core::ptr::addr_of; use super::c; +/// Creates a null-terminated UTF-16 string from a str. +pub macro wide_str($str:literal) {{ + const _: () = { + if core::slice::memchr::memchr(0, $str.as_bytes()).is_some() { + panic!("null terminated strings cannot contain interior nulls"); + } + }; + crate::sys::pal::windows::api::utf16!(concat!($str, '\0')) +}} + +/// Creates a UTF-16 string from a str without null termination. +pub macro utf16($str:expr) {{ + const UTF8: &str = $str; + const UTF16_LEN: usize = crate::sys::pal::windows::api::utf16_len(UTF8); + const UTF16: [u16; UTF16_LEN] = crate::sys::pal::windows::api::to_utf16(UTF8); + &UTF16 +}} + +#[cfg(test)] +mod tests; + +/// Gets the UTF-16 length of a UTF-8 string, for use in the wide_str macro. +pub const fn utf16_len(s: &str) -> usize { + let s = s.as_bytes(); + let mut i = 0; + let mut len = 0; + while i < s.len() { + // the length of a UTF-8 encoded code-point is given by the number of + // leading ones, except in the case of ASCII. + let utf8_len = match s[i].leading_ones() { + 0 => 1, + n => n as usize, + }; + i += utf8_len; + // Note that UTF-16 surrogates (U+D800 to U+DFFF) are not encodable as UTF-8, + // so (unlike with WTF-8) we don't have to worry about how they'll get re-encoded. + len += if utf8_len < 4 { 1 } else { 2 }; + } + len +} + +/// Const convert UTF-8 to UTF-16, for use in the wide_str macro. +/// +/// Note that this is designed for use in const contexts so is not optimized. +pub const fn to_utf16<const UTF16_LEN: usize>(s: &str) -> [u16; UTF16_LEN] { + let mut output = [0_u16; UTF16_LEN]; + let mut pos = 0; + let s = s.as_bytes(); + let mut i = 0; + while i < s.len() { + match s[i].leading_ones() { + // Decode UTF-8 based on its length. + // See https://en.wikipedia.org/wiki/UTF-8 + 0 => { + // ASCII is the same in both encodings + output[pos] = s[i] as u16; + i += 1; + pos += 1; + } + 2 => { + // Bits: 110xxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b11111) << 6) | (s[i + 1] as u16 & 0b111111); + i += 2; + pos += 1; + } + 3 => { + // Bits: 1110xxxx 10xxxxxx 10xxxxxx + output[pos] = ((s[i] as u16 & 0b1111) << 12) + | ((s[i + 1] as u16 & 0b111111) << 6) + | (s[i + 2] as u16 & 0b111111); + i += 3; + pos += 1; + } + 4 => { + // Bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + let mut c = ((s[i] as u32 & 0b111) << 18) + | ((s[i + 1] as u32 & 0b111111) << 12) + | ((s[i + 2] as u32 & 0b111111) << 6) + | (s[i + 3] as u32 & 0b111111); + // re-encode as UTF-16 (see https://en.wikipedia.org/wiki/UTF-16) + // - Subtract 0x10000 from the code point + // - For the high surrogate, shift right by 10 then add 0xD800 + // - For the low surrogate, take the low 10 bits then add 0xDC00 + c -= 0x10000; + output[pos] = ((c >> 10) + 0xD800) as u16; + output[pos + 1] = ((c & 0b1111111111) + 0xDC00) as u16; + i += 4; + pos += 2; + } + // valid UTF-8 cannot have any other values + _ => unreachable!(), + } + } + output +} + /// Helper method for getting the size of `T` as a u32. /// Errors at compile time if the size would overflow. /// diff --git a/library/std/src/sys/pal/windows/api/tests.rs b/library/std/src/sys/pal/windows/api/tests.rs new file mode 100644 index 00000000000..fab022c7b93 --- /dev/null +++ b/library/std/src/sys/pal/windows/api/tests.rs @@ -0,0 +1,16 @@ +use crate::sys::pal::windows::api::{utf16, wide_str}; + +macro_rules! check_utf16 { + ($str:literal) => {{ + assert!(wide_str!($str).iter().copied().eq($str.encode_utf16().chain([0]))); + assert!(utf16!($str).iter().copied().eq($str.encode_utf16())); + }}; +} + +#[test] +fn test_utf16_macros() { + check_utf16!("hello world"); + check_utf16!("€4.50"); + check_utf16!("𨉟呐㗂越"); + check_utf16!("Pchnąć w tę łódź jeża lub ośm skrzyń fig"); +} diff --git a/library/std/src/sys/pal/windows/args.rs b/library/std/src/sys/pal/windows/args.rs index 2ecfe088d10..5098c05196e 100644 --- a/library/std/src/sys/pal/windows/args.rs +++ b/library/std/src/sys/pal/windows/args.rs @@ -7,7 +7,7 @@ mod tests; use super::os::current_exe; -use crate::ffi::OsString; +use crate::ffi::{OsStr, OsString}; use crate::fmt; use crate::io; use crate::num::NonZero; @@ -17,6 +17,7 @@ use crate::sys::path::get_long_path; use crate::sys::process::ensure_no_nuls; use crate::sys::{c, to_u16s}; use crate::sys_common::wstr::WStrUnits; +use crate::sys_common::AsInner; use crate::vec; use crate::iter; @@ -262,16 +263,92 @@ pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> i Ok(()) } +fn append_bat_arg(cmd: &mut Vec<u16>, arg: &OsStr, mut quote: bool) -> io::Result<()> { + ensure_no_nuls(arg)?; + // If an argument has 0 characters then we need to quote it to ensure + // that it actually gets passed through on the command line or otherwise + // it will be dropped entirely when parsed on the other end. + // + // We also need to quote the argument if it ends with `\` to guard against + // bat usage such as `"%~2"` (i.e. force quote arguments) otherwise a + // trailing slash will escape the closing quote. + if arg.is_empty() || arg.as_encoded_bytes().last() == Some(&b'\\') { + quote = true; + } + for cp in arg.as_inner().inner.code_points() { + if let Some(cp) = cp.to_char() { + // Rather than trying to find every ascii symbol that must be quoted, + // we assume that all ascii symbols must be quoted unless they're known to be good. + // We also quote Unicode control blocks for good measure. + // Note an unquoted `\` is fine so long as the argument isn't otherwise quoted. + static UNQUOTED: &str = r"#$*+-./:?@\_"; + let ascii_needs_quotes = + cp.is_ascii() && !(cp.is_ascii_alphanumeric() || UNQUOTED.contains(cp)); + if ascii_needs_quotes || cp.is_control() { + quote = true; + } + } + } + + if quote { + cmd.push('"' as u16); + } + // Loop through the string, escaping `\` only if followed by `"`. + // And escaping `"` by doubling them. + let mut backslashes: usize = 0; + for x in arg.encode_wide() { + if x == '\\' as u16 { + backslashes += 1; + } else { + if x == '"' as u16 { + // Add n backslashes to total 2n before internal `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + // Appending an additional double-quote acts as an escape. + cmd.push(b'"' as u16) + } else if x == '%' as u16 || x == '\r' as u16 { + // yt-dlp hack: replaces `%` with `%%cd:~,%` to stop %VAR% being expanded as an environment variable. + // + // # Explanation + // + // cmd supports extracting a substring from a variable using the following syntax: + // %variable:~start_index,end_index% + // + // In the above command `cd` is used as the variable and the start_index and end_index are left blank. + // `cd` is a built-in variable that dynamically expands to the current directory so it's always available. + // Explicitly omitting both the start and end index creates a zero-length substring. + // + // Therefore it all resolves to nothing. However, by doing this no-op we distract cmd.exe + // from potentially expanding %variables% in the argument. + cmd.extend_from_slice(&[ + '%' as u16, '%' as u16, 'c' as u16, 'd' as u16, ':' as u16, '~' as u16, + ',' as u16, + ]); + } + backslashes = 0; + } + cmd.push(x); + } + if quote { + // Add n backslashes to total 2n before ending `"`. + cmd.extend((0..backslashes).map(|_| '\\' as u16)); + cmd.push('"' as u16); + } + Ok(()) +} + pub(crate) fn make_bat_command_line( script: &[u16], args: &[Arg], force_quotes: bool, ) -> io::Result<Vec<u16>> { + const INVALID_ARGUMENT_ERROR: io::Error = + io::const_io_error!(io::ErrorKind::InvalidInput, r#"batch file arguments are invalid"#); // Set the start of the command line to `cmd.exe /c "` // It is necessary to surround the command in an extra pair of quotes, // hence the trailing quote here. It will be closed after all arguments // have been added. - let mut cmd: Vec<u16> = "cmd.exe /d /c \"".encode_utf16().collect(); + // Using /e:ON enables "command extensions" which is essential for the `%` hack to work. + let mut cmd: Vec<u16> = "cmd.exe /e:ON /v:OFF /d /c \"".encode_utf16().collect(); // Push the script name surrounded by its quote pair. cmd.push(b'"' as u16); @@ -291,18 +368,22 @@ pub(crate) fn make_bat_command_line( // reconstructed by the batch script by default. for arg in args { cmd.push(' ' as u16); - // Make sure to always quote special command prompt characters, including: - // * Characters `cmd /?` says require quotes. - // * `%` for environment variables, as in `%TMP%`. - // * `|<>` pipe/redirect characters. - const SPECIAL: &[u8] = b"\t &()[]{}^=;!'+,`~%|<>"; - let force_quotes = match arg { - Arg::Regular(arg) if !force_quotes => { - arg.as_encoded_bytes().iter().any(|c| SPECIAL.contains(c)) + match arg { + Arg::Regular(arg_os) => { + let arg_bytes = arg_os.as_encoded_bytes(); + // Disallow \r and \n as they may truncate the arguments. + const DISALLOWED: &[u8] = b"\r\n"; + if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) { + return Err(INVALID_ARGUMENT_ERROR); + } + append_bat_arg(&mut cmd, arg_os, force_quotes)?; + } + _ => { + // Raw arguments are passed on as-is. + // It's the user's responsibility to properly handle arguments in this case. + append_arg(&mut cmd, arg, force_quotes)?; } - _ => force_quotes, }; - append_arg(&mut cmd, arg, force_quotes)?; } // Close the quote we left opened earlier. diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 6a561518fad..a734c2bd4c7 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -5,6 +5,7 @@ use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; +use crate::sys::pal::windows::api::wide_str; use crate::time::Duration; pub use self::rand::hashmap_random_keys; @@ -12,6 +13,8 @@ pub use self::rand::hashmap_random_keys; #[macro_use] pub mod compat; +mod api; + pub mod alloc; pub mod args; pub mod c; @@ -41,8 +44,6 @@ cfg_if::cfg_if! { } } -mod api; - /// Map a Result<T, WinError> to io::Result<T>. trait IoResult<T> { fn io_result(self) -> crate::io::Result<T>; @@ -60,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&c"main"); + thread::Thread::set_name_wide(wide_str!("main")); } // SAFETY: must be called only once during runtime cleanup. diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index c0c63c3340f..9b1c5b34bbf 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -59,13 +59,17 @@ impl Thread { pub fn set_name(name: &CStr) { if let Ok(utf8) = name.to_str() { if let Ok(utf16) = to_u16s(utf8) { - unsafe { - c::SetThreadDescription(c::GetCurrentThread(), utf16.as_ptr()); - }; + Self::set_name_wide(&utf16) }; }; } + pub fn set_name_wide(name: &[u16]) { + unsafe { + c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()); + }; + } + pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index fc6f51e8272..4cab2d64257 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -231,7 +231,7 @@ impl<'tcx> Context<'tcx> { rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo), }; let mut page_buffer = Buffer::html(); - print_item(self, it, &mut page_buffer, &page); + print_item(self, it, &mut page_buffer); layout::render( &clone_shared.layout, &page, diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index fbb521a6188..168db5c0948 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -31,11 +31,10 @@ use crate::html::format::{ display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause, visibility_print_with_space, Buffer, Ending, PrintWithSpace, }; -use crate::html::layout::Page; +use crate::html::highlight; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; use crate::html::render::{document_full, document_item_info}; use crate::html::url_parts_builder::UrlPartsBuilder; -use crate::html::{highlight, static_files}; use askama::Template; use itertools::Itertools; @@ -157,8 +156,6 @@ struct PathComponent { #[derive(Template)] #[template(path = "print_item.html")] struct ItemVars<'a> { - static_root_path: &'a str, - clipboard_svg: &'static static_files::StaticFile, typ: &'a str, name: &'a str, item_type: &'a str, @@ -178,12 +175,7 @@ fn print_where_clause_and_check<'a, 'tcx: 'a>( len_before != buffer.len() } -pub(super) fn print_item( - cx: &mut Context<'_>, - item: &clean::Item, - buf: &mut Buffer, - page: &Page<'_>, -) { +pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buffer) { debug_assert!(!item.is_stripped()); let typ = match *item.kind { clean::ModuleItem(_) => { @@ -252,8 +244,6 @@ pub(super) fn print_item( }; let item_vars = ItemVars { - static_root_path: &page.get_static_root_path(), - clipboard_svg: &static_files::STATIC_FILES.clipboard_svg, typ, name: item.name.as_ref().unwrap().as_str(), item_type: &item.type_().to_string(), @@ -1237,22 +1227,18 @@ fn item_opaque_ty( } fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) { - wrap_item(w, |w| { - write!( - w, - "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", - attrs = render_attributes_in_pre(it, "", cx), - vis = visibility_print_with_space(it, cx), - name = it.name.unwrap(), - generics = t.generics.print(cx), - where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), - type_ = t.type_.print(cx), - ); - }); - } - - write_content(w, cx, it, t); + wrap_item(w, |w| { + write!( + w, + "{attrs}{vis}type {name}{generics}{where_clause} = {type_};", + attrs = render_attributes_in_pre(it, "", cx), + vis = visibility_print_with_space(it, cx), + name = it.name.unwrap(), + generics = t.generics.print(cx), + where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), + type_ = t.type_.print(cx), + ); + }); write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 0bb073b1cea..e9c687b42fa 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1603,6 +1603,16 @@ a.tooltip:hover::after { border-color: var(--settings-button-border-focus); } +#settings-menu > a { + line-height: 0; + font-size: 0; +} +#settings-menu > a:before { + content: url('wheel-63255fc4502dca9a.svg'); + width: 22px; + height: 22px; +} + #sidebar-button > a:before { content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" \ fill="none" stroke="black">\ @@ -1622,11 +1632,17 @@ a.tooltip:hover::after { padding-left: 2px; border: 0; width: 33px; + line-height: 0; + font-size: 0; } -#copy-path > img { + +#copy-path:before { filter: var(--copy-path-img-filter); + content: url('clipboard-24048e6d87f63d07.svg'); + width: 19px; + height: 18px; } -#copy-path:hover > img { +#copy-path:hover:before { filter: var(--copy-path-img-hover-filter); } diff --git a/src/librustdoc/html/static/images/clipboard.svg b/src/librustdoc/html/static/images/clipboard.svg index 8adbd996304..e437c83fb6b 100644 --- a/src/librustdoc/html/static/images/clipboard.svg +++ b/src/librustdoc/html/static/images/clipboard.svg @@ -1 +1 @@ -<svg width="24" height="25" viewBox="0 0 24 25" xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard"><path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/><path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/></svg> +<svg width="19" height="18" viewBox="0 0 24 25" xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard"><path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/><path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/></svg> diff --git a/src/librustdoc/html/static/images/favicon-16x16.png b/src/librustdoc/html/static/images/favicon-16x16.png deleted file mode 100644 index ea4b45cae16..00000000000 --- a/src/librustdoc/html/static/images/favicon-16x16.png +++ /dev/null Binary files differdiff --git a/src/librustdoc/html/static/images/wheel.svg b/src/librustdoc/html/static/images/wheel.svg index 83c07f63d10..ba30f13dd58 100644 --- a/src/librustdoc/html/static/images/wheel.svg +++ b/src/librustdoc/html/static/images/wheel.svg @@ -1 +1 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="27.434" height="29.5" enable-background="new 0 0 27.434 29.5" viewBox="0 0 27.434 29.5"><path d="M27.316 18.39a2.696 2.696 0 0 0-.98-1.46 1.62 1.62 0 0 1-.016-.762l.035-.176v-1.191c0-1.246-.003-1.278-.046-1.473a1.717 1.717 0 0 1 .007-.805c.477-.343.829-.859.997-1.472.257-.957.074-2.094-.508-3.117l-.594-1.032c-.746-1.304-1.965-2.117-3.18-2.117-.379 0-.75.078-1.086.235a1.958 1.958 0 0 1-.855-.391l-.102-.082-.117-.063-1.855-1.07-.094-.055-.106-.043c-.378-.156-.66-.41-.77-.554C17.919 1.172 16.349 0 14.297 0h-1.155c-2.043 0-3.61 1.152-3.75 2.723-.114.14-.391.382-.758.527l-.102.04-.094.05-1.94 1.066-.134.074-.117.094a2.019 2.019 0 0 1-.832.403 2.518 2.518 0 0 0-1.008-.211c-1.199 0-2.414.82-3.168 2.14l-.59 1.032c-.41.718-.64 1.523-.64 2.257-.004.953.36 1.758 1.012 2.258.035.152.058.445-.016.785-.04.168-.063.282-.063 1.563 0 1.148 0 1.148.016 1.261l.008.075.015.074c.075.344.047.64.012.8-.644.5-1.004 1.302-.992 2.259.008.726.238 1.52.648 2.242l.59 1.027c.758 1.332 1.965 2.16 3.149 2.16.324 0 .644-.062.937-.187.168.039.492.156.813.418l.11.086.124.07 2.047 1.156.102.059.105.043c.363.144.648.379.766.52.164 1.519 1.718 2.632 3.746 2.632h1.156c2.035 0 3.598-1.133 3.746-2.672.117-.144.402-.394.773-.55l.114-.047.101-.063 1.961-1.156.106-.063.097-.078c.309-.246.653-.37.832-.398.313.136.66.21 1.016.21 1.2 0 2.41-.82 3.164-2.14l.594-1.031c.59-1.028.777-2.164.52-3.117Zm-2.043 2.247-.59 1.031c-.437.766-1.105 1.25-1.636 1.25a.7.7 0 0 1-.371-.094 1.146 1.146 0 0 0-.567-.129c-.593 0-1.382.297-2.007.797l-1.961 1.156c-1.016.426-1.848 1.293-1.848 1.93 0 .64-.898 1.16-1.996 1.16H13.14c-1.102 0-2-.515-2-1.14 0-.63-.832-1.477-1.852-1.887l-2.047-1.16c-.637-.512-1.426-.813-2.008-.813-.199 0-.379.035-.515.114a.648.648 0 0 1-.332.085c-.52 0-1.18-.5-1.621-1.273l-.59-1.031c-.543-.953-.555-1.98-.024-2.285.532-.305.782-1.434.551-2.504V14.8c0-1.09.02-1.18.02-1.18.238-1.074-.008-2.203-.551-2.516-.54-.304-.54-1.34.008-2.293l.59-1.03c.437-.766 1.101-1.255 1.636-1.255a.73.73 0 0 1 .364.094c.152.086.343.125.566.125.594 0 1.379-.297 2.004-.793l1.945-1.066c1.02-.407 1.856-1.278 1.856-1.934 0-.656.898-1.191 2-1.191h1.156c1.098 0 1.996.543 1.996 1.21 0 .669.832 1.555 1.848 1.973L20 6.012c.617.492 1.402.777 2.012.777.242 0 .453-.047.62-.14a.79.79 0 0 1 .403-.102c.55 0 1.223.476 1.652 1.23l.59 1.032c.543.953.52 2.004-.062 2.336-.574.332-.86 1.48-.625 2.554 0 0 .008.04.008 1.102v1.011c-.215 1.051.07 2.176.636 2.5.567.325.586 1.368.04 2.325Zm0 0"/><path d="M13.61 7.61a7.084 7.084 0 0 0-7.083 7.085 7.085 7.085 0 1 0 14.168 0A7.088 7.088 0 0 0 13.61 7.61Zm0 12.41a5.33 5.33 0 0 1-5.325-5.325 5.33 5.33 0 0 1 5.324-5.32 5.327 5.327 0 0 1 5.325 5.32 5.328 5.328 0 0 1-5.325 5.325Zm0 0"/><path d="M13.684 9.906a4.722 4.722 0 0 0-4.72 4.719 4.722 4.722 0 0 0 4.72 4.719 4.724 4.724 0 0 0 4.714-4.719 4.724 4.724 0 0 0-4.714-4.719Zm0 7.676a2.954 2.954 0 1 1 0-5.91 2.953 2.953 0 0 1 2.953 2.953 2.957 2.957 0 0 1-2.953 2.957Zm0 0"/></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" enable-background="new 0 0 22 22" viewBox="0 0 27.434 29.5"><path d="M27.316 18.39a2.696 2.696 0 0 0-.98-1.46 1.62 1.62 0 0 1-.016-.762l.035-.176v-1.191c0-1.246-.003-1.278-.046-1.473a1.717 1.717 0 0 1 .007-.805c.477-.343.829-.859.997-1.472.257-.957.074-2.094-.508-3.117l-.594-1.032c-.746-1.304-1.965-2.117-3.18-2.117-.379 0-.75.078-1.086.235a1.958 1.958 0 0 1-.855-.391l-.102-.082-.117-.063-1.855-1.07-.094-.055-.106-.043c-.378-.156-.66-.41-.77-.554C17.919 1.172 16.349 0 14.297 0h-1.155c-2.043 0-3.61 1.152-3.75 2.723-.114.14-.391.382-.758.527l-.102.04-.094.05-1.94 1.066-.134.074-.117.094a2.019 2.019 0 0 1-.832.403 2.518 2.518 0 0 0-1.008-.211c-1.199 0-2.414.82-3.168 2.14l-.59 1.032c-.41.718-.64 1.523-.64 2.257-.004.953.36 1.758 1.012 2.258.035.152.058.445-.016.785-.04.168-.063.282-.063 1.563 0 1.148 0 1.148.016 1.261l.008.075.015.074c.075.344.047.64.012.8-.644.5-1.004 1.302-.992 2.259.008.726.238 1.52.648 2.242l.59 1.027c.758 1.332 1.965 2.16 3.149 2.16.324 0 .644-.062.937-.187.168.039.492.156.813.418l.11.086.124.07 2.047 1.156.102.059.105.043c.363.144.648.379.766.52.164 1.519 1.718 2.632 3.746 2.632h1.156c2.035 0 3.598-1.133 3.746-2.672.117-.144.402-.394.773-.55l.114-.047.101-.063 1.961-1.156.106-.063.097-.078c.309-.246.653-.37.832-.398.313.136.66.21 1.016.21 1.2 0 2.41-.82 3.164-2.14l.594-1.031c.59-1.028.777-2.164.52-3.117Zm-2.043 2.247-.59 1.031c-.437.766-1.105 1.25-1.636 1.25a.7.7 0 0 1-.371-.094 1.146 1.146 0 0 0-.567-.129c-.593 0-1.382.297-2.007.797l-1.961 1.156c-1.016.426-1.848 1.293-1.848 1.93 0 .64-.898 1.16-1.996 1.16H13.14c-1.102 0-2-.515-2-1.14 0-.63-.832-1.477-1.852-1.887l-2.047-1.16c-.637-.512-1.426-.813-2.008-.813-.199 0-.379.035-.515.114a.648.648 0 0 1-.332.085c-.52 0-1.18-.5-1.621-1.273l-.59-1.031c-.543-.953-.555-1.98-.024-2.285.532-.305.782-1.434.551-2.504V14.8c0-1.09.02-1.18.02-1.18.238-1.074-.008-2.203-.551-2.516-.54-.304-.54-1.34.008-2.293l.59-1.03c.437-.766 1.101-1.255 1.636-1.255a.73.73 0 0 1 .364.094c.152.086.343.125.566.125.594 0 1.379-.297 2.004-.793l1.945-1.066c1.02-.407 1.856-1.278 1.856-1.934 0-.656.898-1.191 2-1.191h1.156c1.098 0 1.996.543 1.996 1.21 0 .669.832 1.555 1.848 1.973L20 6.012c.617.492 1.402.777 2.012.777.242 0 .453-.047.62-.14a.79.79 0 0 1 .403-.102c.55 0 1.223.476 1.652 1.23l.59 1.032c.543.953.52 2.004-.062 2.336-.574.332-.86 1.48-.625 2.554 0 0 .008.04.008 1.102v1.011c-.215 1.051.07 2.176.636 2.5.567.325.586 1.368.04 2.325Zm0 0"/><path d="M13.61 7.61a7.084 7.084 0 0 0-7.083 7.085 7.085 7.085 0 1 0 14.168 0A7.088 7.088 0 0 0 13.61 7.61Zm0 12.41a5.33 5.33 0 0 1-5.325-5.325 5.33 5.33 0 0 1 5.324-5.32 5.327 5.327 0 0 1 5.325 5.32 5.328 5.328 0 0 1-5.325 5.325Zm0 0"/><path d="M13.684 9.906a4.722 4.722 0 0 0-4.72 4.719 4.722 4.722 0 0 0 4.72 4.719 4.724 4.724 0 0 0 4.714-4.719 4.724 4.724 0 0 0-4.714-4.719Zm0 7.676a2.954 2.954 0 1 1 0-5.91 2.953 2.953 0 0 1 2.953 2.953 2.957 2.957 0 0 1-2.953 2.957Zm0 0"/></svg> \ No newline at end of file diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index ca9a78f51b3..d8874c2fda0 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -106,7 +106,6 @@ static_files! { license_mit => "static/LICENSE-MIT.txt", rust_logo_svg => "static/images/rust-logo.svg", rust_favicon_svg => "static/images/favicon.svg", - rust_favicon_png_16 => "static/images/favicon-16x16.png", rust_favicon_png_32 => "static/images/favicon-32x32.png", fira_sans_regular => "static/fonts/FiraSans-Regular.woff2", fira_sans_medium => "static/fonts/FiraSans-Medium.woff2", diff --git a/src/librustdoc/html/templates/page.html b/src/librustdoc/html/templates/page.html index 0f3debae66c..0941f758de5 100644 --- a/src/librustdoc/html/templates/page.html +++ b/src/librustdoc/html/templates/page.html @@ -6,13 +6,10 @@ <meta name="generator" content="rustdoc"> {# #} <meta name="description" content="{{page.description}}"> {# #} <title>{{page.title}}</title> {# #} - <script> if (window.location.protocol !== "file:") document.write(` {# Hack to skip preloading fonts locally - see #98769 #} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_regular}}"> {# #} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_regular}}"> {# #} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_medium}}"> {# #} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_regular}}"> {# #} - <link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_semibold}}"> {# #} - `)</script> {# #} + <script>if(window.location.protocol!=="file:") {# Hack to skip preloading fonts locally - see #98769 #} + for(f of "{{files.source_serif_4_regular}},{{files.fira_sans_regular}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",")) {# #} + document.write(`<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}${f}">`) {# #} + </script> {# #} <link rel="stylesheet" {#+ #} href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #} <link rel="stylesheet" {#+ #} @@ -62,8 +59,6 @@ <link rel="icon" href="{{layout.favicon}}"> {# #} {% else %} <link rel="alternate icon" type="image/png" {#+ #} - href="{{static_root_path|safe}}{{files.rust_favicon_png_16}}"> {# #} - <link rel="alternate icon" type="image/png" {#+ #} href="{{static_root_path|safe}}{{files.rust_favicon_png_32}}"> {# #} <link rel="icon" type="image/svg+xml" {#+ #} href="{{static_root_path|safe}}{{files.rust_favicon_svg}}"> {# #} @@ -114,13 +109,13 @@ <div class="version">{{+ display_krate_version_extra}}</div> {# #} {% endif %} {% else %} - <div class="src-sidebar-title"> + <div class="src-sidebar-title"> {# #} <h2>Files</h2> {# #} </div> {# #} {% endif %} {{ sidebar|safe }} </nav> {# #} - <div class="sidebar-resizer"></div> + <div class="sidebar-resizer"></div> {# #} <main> {# #} {% if page.css_class != "src" %}<div class="width-limiter">{% endif %} <nav class="sub"> {# #} @@ -142,8 +137,7 @@ </div> {# #} <div id="settings-menu" tabindex="-1"> {# #} <a href="{{page.root_path|safe}}settings.html" title="settings"> {# #} - <img width="22" height="22" alt="Change settings" {#+ #} - src="{{static_root_path|safe}}{{files.wheel_svg}}"> {# #} + Settings {# #} </a> {# #} </div> {# #} </form> {# #} diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html index 1d215c26968..76e770453b6 100644 --- a/src/librustdoc/html/templates/print_item.html +++ b/src/librustdoc/html/templates/print_item.html @@ -7,9 +7,7 @@ {% endfor %} <a class="{{item_type}}" href="#">{{name}}</a> {# #} <button id="copy-path" title="Copy item path to clipboard"> {# #} - <img src="{{static_root_path|safe}}{{clipboard_svg}}" {#+ #} - width="19" height="18" {#+ #} - alt="Copy item path"> {# #} + Copy item path {# #} </button> {# #} </h1> {# #} <span class="out-of-band"> diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index d982134181c..3251b4c14c9 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -5,7 +5,7 @@ {% endif %} <div class="sidebar-elems"> {% if is_crate %} - <ul class="block"> + <ul class="block"> {# #} <li><a id="all-types" href="all.html">All Items</a></li> {# #} </ul> {% endif %} diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index a60a40a2a47..2bb63ec2b04 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -294,8 +294,7 @@ fn elision_suggestions( let span = cx .sess() .source_map() - .span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace()) - .unwrap_or(usage.ident.span); + .span_extend_while_whitespace(usage.ident.span); (span, String::new()) }, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 626841cb1bd..ec944cb7fb4 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -819,6 +819,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-dynamic-linking", "needs-git-hash", "needs-llvm-components", + "needs-matching-clang", "needs-profiler-support", "needs-relocation-model-pic", "needs-run-enabled", diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 217da36ccc7..5a5b008a7cb 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -86,6 +86,18 @@ impl Rustc { self } + /// This flag defers LTO optimizations to the linker. + pub fn linker_plugin_lto(&mut self, option: &str) -> &mut Self { + self.cmd.arg(format!("-Clinker-plugin-lto={option}")); + self + } + + /// Specify what happens when the code panics. + pub fn panic(&mut self, option: &str) -> &mut Self { + self.cmd.arg(format!("-Cpanic={option}")); + self + } + /// Specify number of codegen units pub fn codegen_units(&mut self, units: usize) -> &mut Self { self.cmd.arg(format!("-Ccodegen-units={units}")); @@ -183,6 +195,18 @@ impl Rustc { output } + #[track_caller] + pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if output.status.code().unwrap() != code { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } + /// Inspect what the underlying [`Command`] is up to the current construction. pub fn inspect(&mut self, f: impl FnOnce(&Command)) -> &mut Self { f(&self.cmd); diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index 9607ff02f96..1fb4b589d76 100644 --- a/src/tools/run-make-support/src/rustdoc.rs +++ b/src/tools/run-make-support/src/rustdoc.rs @@ -1,4 +1,5 @@ use std::env; +use std::ffi::OsStr; use std::path::Path; use std::process::{Command, Output}; @@ -58,9 +59,8 @@ impl Rustdoc { self } - /// Fallback argument provider. Consider adding meaningfully named methods instead of using - /// this method. - pub fn arg(&mut self, arg: &str) -> &mut Self { + /// Generic command argument provider. Use `.arg("-Zname")` over `.arg("-Z").arg("arg")`. + pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self { self.cmd.arg(arg); self } @@ -77,4 +77,16 @@ impl Rustdoc { } output } + + #[track_caller] + pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if output.status.code().unwrap() != code { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } } diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index dfd30d79abc..3914feb3499 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -59,7 +59,6 @@ run-make/emit/Makefile run-make/env-dep-info/Makefile run-make/error-found-staticlib-instead-crate/Makefile run-make/error-writing-dependencies/Makefile -run-make/exit-code/Makefile run-make/export-executable-symbols/Makefile run-make/extern-diff-internal-name/Makefile run-make/extern-flag-disambiguates/Makefile diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 4fae7572ffb..78de8c0537d 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -18,7 +18,7 @@ const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1722; -const ROOT_ENTRY_LIMIT: usize = 861; +const ROOT_ENTRY_LIMIT: usize = 859; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files @@ -49,6 +49,9 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ "tests/ui/shell-argfiles/shell-argfiles-badquotes.args", // passing args via a file "tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args", // passing args via a file "tests/ui/shell-argfiles/shell-argfiles-via-argfile.args", // passing args via a file + "tests/ui/std/windows-bat-args1.bat", // tests escaping arguments through batch files + "tests/ui/std/windows-bat-args2.bat", // tests escaping arguments through batch files + "tests/ui/std/windows-bat-args3.bat", // tests escaping arguments through batch files ]; fn check_entries(tests_path: &Path, bad: &mut bool) { diff --git a/tests/codegen/riscv-target-abi.rs b/tests/codegen/riscv-target-abi.rs new file mode 100644 index 00000000000..5d545af9c76 --- /dev/null +++ b/tests/codegen/riscv-target-abi.rs @@ -0,0 +1,20 @@ +//@ revisions:riscv64gc riscv32gc riscv32imac + +//@[riscv64gc] compile-flags: --target=riscv64gc-unknown-linux-gnu +//@[riscv64gc] needs-llvm-components: riscv +// riscv64gc: !{i32 1, !"target-abi", !"lp64d"} + +//@[riscv32gc] compile-flags: --target=riscv32gc-unknown-linux-musl +//@[riscv32gc] needs-llvm-components: riscv +// riscv32gc: !{i32 1, !"target-abi", !"ilp32d"} + +//@[riscv32imac] compile-flags: --target=riscv32imac-unknown-none-elf +//@[riscv32imac] needs-llvm-components: riscv +// riscv32imac-NOT: !"target-abi" + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +#[lang = "sized"] +trait Sized {} diff --git a/tests/run-make/cross-lang-lto-riscv-abi/cstart.c b/tests/run-make/cross-lang-lto-riscv-abi/cstart.c new file mode 100644 index 00000000000..660469b75a8 --- /dev/null +++ b/tests/run-make/cross-lang-lto-riscv-abi/cstart.c @@ -0,0 +1,5 @@ +extern void hello(); + +void _start() { + hello(); +} diff --git a/tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs b/tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs new file mode 100644 index 00000000000..c31cf27f9ae --- /dev/null +++ b/tests/run-make/cross-lang-lto-riscv-abi/riscv-xlto.rs @@ -0,0 +1,9 @@ +#![allow(internal_features)] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[no_mangle] +pub fn hello() {} diff --git a/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs new file mode 100644 index 00000000000..2f13cf17169 --- /dev/null +++ b/tests/run-make/cross-lang-lto-riscv-abi/rmake.rs @@ -0,0 +1,74 @@ +//! Make sure that cross-language LTO works on riscv targets, +//! which requires extra abi metadata to be emitted. +//@ needs-matching-clang +//@ needs-llvm-components riscv +extern crate run_make_support; + +use run_make_support::{bin_name, rustc, tmp_dir}; +use std::{ + env, + path::PathBuf, + process::{Command, Output}, + str, +}; + +fn handle_failed_output(output: Output) { + eprintln!("output status: `{}`", output.status); + eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap()); + eprintln!("=== STDERR ===\n{}\n\n", String::from_utf8(output.stderr).unwrap()); + std::process::exit(1) +} + +fn check_target(target: &str, clang_target: &str, carch: &str, is_double_float: bool) { + eprintln!("Checking target {target}"); + // Rust part + rustc() + .input("riscv-xlto.rs") + .crate_type("rlib") + .target(target) + .panic("abort") + .linker_plugin_lto("on") + .run(); + // C part + let clang = env::var("CLANG").unwrap(); + let mut cmd = Command::new(clang); + let executable = tmp_dir().join("riscv-xlto"); + cmd.arg("-target") + .arg(clang_target) + .arg(format!("-march={carch}")) + .arg(format!("-flto=thin")) + .arg(format!("-fuse-ld=lld")) + .arg("-nostdlib") + .arg("-o") + .arg(&executable) + .arg("cstart.c") + .arg(tmp_dir().join("libriscv_xlto.rlib")); + eprintln!("{cmd:?}"); + let output = cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(output); + } + // Check that the built binary has correct float abi + let llvm_readobj = + PathBuf::from(env::var("LLVM_BIN_DIR").unwrap()).join(bin_name("llvm-readobj")); + let mut cmd = Command::new(llvm_readobj); + cmd.arg("--file-header").arg(executable); + eprintln!("{cmd:?}"); + let output = cmd.output().unwrap(); + if output.status.success() { + assert!( + !(is_double_float + ^ dbg!(str::from_utf8(&output.stdout).unwrap()) + .contains("EF_RISCV_FLOAT_ABI_DOUBLE")) + ) + } else { + handle_failed_output(output); + } +} + +fn main() { + check_target("riscv64gc-unknown-linux-gnu", "riscv64-linux-gnu", "rv64gc", true); + check_target("riscv64imac-unknown-none-elf", "riscv64-unknown-elf", "rv64imac", false); + check_target("riscv32imac-unknown-none-elf", "riscv32-unknown-elf", "rv32imac", false); + check_target("riscv32gc-unknown-linux-gnu", "riscv32-linux-gnu", "rv32gc", true); +} diff --git a/tests/run-make/exit-code/Makefile b/tests/run-make/exit-code/Makefile deleted file mode 100644 index 155e5cd1123..00000000000 --- a/tests/run-make/exit-code/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) success.rs; [ $$? -eq 0 ] - $(RUSTC) --invalid-arg-foo; [ $$? -eq 1 ] - $(RUSTC) compile-error.rs; [ $$? -eq 1 ] - RUSTC_ICE=0 $(RUSTC) -Ztreat-err-as-bug compile-error.rs; [ $$? -eq 101 ] - $(RUSTDOC) -o $(TMPDIR)/exit-code success.rs; [ $$? -eq 0 ] - $(RUSTDOC) --invalid-arg-foo; [ $$? -eq 1 ] - $(RUSTDOC) compile-error.rs; [ $$? -eq 1 ] - $(RUSTDOC) lint-failure.rs; [ $$? -eq 1 ] diff --git a/tests/run-make/exit-code/rmake.rs b/tests/run-make/exit-code/rmake.rs new file mode 100644 index 00000000000..f387626287e --- /dev/null +++ b/tests/run-make/exit-code/rmake.rs @@ -0,0 +1,43 @@ +// Test that we exit with the correct exit code for successful / unsuccessful / ICE compilations + +extern crate run_make_support; + +use run_make_support::{rustc, rustdoc, tmp_dir}; + +fn main() { + rustc() + .arg("success.rs") + .run(); + + rustc() + .arg("--invalid-arg-foo") + .run_fail_assert_exit_code(1); + + rustc() + .arg("compile-error.rs") + .run_fail_assert_exit_code(1); + + rustc() + .env("RUSTC_ICE", "0") + .arg("-Ztreat-err-as-bug") + .arg("compile-error.rs") + .run_fail_assert_exit_code(101); + + rustdoc() + .arg("success.rs") + .arg("-o") + .arg(tmp_dir().join("exit-code")) + .run(); + + rustdoc() + .arg("--invalid-arg-foo") + .run_fail_assert_exit_code(1); + + rustdoc() + .arg("compile-error.rs") + .run_fail_assert_exit_code(1); + + rustdoc() + .arg("lint-failure.rs") + .run_fail_assert_exit_code(1); +} diff --git a/tests/rustdoc-gui/search-result-go-to-first.goml b/tests/rustdoc-gui/search-result-go-to-first.goml index a0bc2bb16ba..f4cfe096386 100644 --- a/tests/rustdoc-gui/search-result-go-to-first.goml +++ b/tests/rustdoc-gui/search-result-go-to-first.goml @@ -3,17 +3,17 @@ // First, we check that the first page doesn't have the string we're looking for to ensure // that the feature is changing page as expected. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -assert-text-false: (".main-heading h1", "Struct test_docs::Foo") +assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path") // We now check that we land on the search result page if "go_to_first" isn't set. go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo" // Waiting for the search results to appear... wait-for: "#search-tabs" -assert-text-false: (".main-heading h1", "Struct test_docs::Foo") +assert-text-false: (".main-heading h1", "Struct test_docs::FooCopy item path") // Ensure that the search results are displayed, not the "normal" content. assert-css: ("#main-content", {"display": "none"}) // Now we can check that the feature is working as expected! go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo&go_to_first=true" // Waiting for the page to load... -wait-for-text: (".main-heading h1", "Struct test_docs::Foo") +wait-for-text: (".main-heading h1", "Struct test_docs::FooCopy item path") diff --git a/tests/rustdoc-gui/toggle-click-deadspace.goml b/tests/rustdoc-gui/toggle-click-deadspace.goml index f115f63ab6b..37bc3f7c372 100644 --- a/tests/rustdoc-gui/toggle-click-deadspace.goml +++ b/tests/rustdoc-gui/toggle-click-deadspace.goml @@ -12,4 +12,4 @@ assert-attribute-false: (".impl-items .toggle", {"open": ""}) // Click the "Trait" part of "impl Trait" and verify it navigates. click: "#impl-Trait-for-Foo h3 a:first-of-type" -assert-text: (".main-heading h1", "Trait lib2::Trait") +assert-text: (".main-heading h1", "Trait lib2::TraitCopy item path") diff --git a/tests/rustdoc-ui/ice-bug-report-url.stderr b/tests/rustdoc-ui/ice-bug-report-url.stderr index 06a52691310..66622a7654c 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.stderr +++ b/tests/rustdoc-ui/ice-bug-report-url.stderr @@ -12,6 +12,8 @@ error: the compiler unexpectedly panicked. this is a bug. note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-rustdoc&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: diff --git a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs new file mode 100644 index 00000000000..171850b89bb --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs @@ -0,0 +1,115 @@ +//@ run-pass +//! Test information regarding intrinsics and ensure we can retrieve the fallback body if it exists. +//! +//! This tests relies on the intrinsics implementation, and requires one intrinsic with and one +//! without a body. It doesn't matter which intrinsic is called here, and feel free to update that +//! if needed. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] + +extern crate rustc_hir; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::{Instance, InstanceKind}; +use stable_mir::mir::visit::{Location, MirVisitor}; +use stable_mir::mir::{LocalDecl, Terminator, TerminatorKind}; +use stable_mir::ty::{RigidTy, TyKind}; +use std::collections::HashSet; +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +/// This function tests that we can correctly get type information from binary operations. +fn test_intrinsics() -> ControlFlow<()> { + // Find items in the local crate. + let main_def = stable_mir::all_local_items()[0]; + let main_instance = Instance::try_from(main_def).unwrap(); + let main_body = main_instance.body().unwrap(); + let mut visitor = CallsVisitor { locals: main_body.locals(), calls: Default::default() }; + visitor.visit_body(&main_body); + + let calls = visitor.calls; + assert_eq!(calls.len(), 2, "Expected 2 calls, but found: {calls:?}"); + for intrinsic in &calls { + check_intrinsic(intrinsic) + } + + ControlFlow::Continue(()) +} + +/// This check is unfortunately tight to the implementation of intrinsics. +/// +/// We want to ensure that StableMIR can handle intrinsics with and without fallback body. +/// +/// If by any chance this test breaks because you changed how an intrinsic is implemented, please +/// update the test to invoke a different intrinsic. +fn check_intrinsic(intrinsic: &Instance) { + assert_eq!(intrinsic.kind, InstanceKind::Intrinsic); + let name = intrinsic.intrinsic_name().unwrap(); + if intrinsic.has_body() { + let Some(body) = intrinsic.body() else { unreachable!("Expected a body") }; + assert!(!body.blocks.is_empty()); + assert_eq!(&name, "likely"); + } else { + assert!(intrinsic.body().is_none()); + assert_eq!(&name, "size_of_val"); + } +} + +struct CallsVisitor<'a> { + locals: &'a [LocalDecl], + calls: HashSet<Instance>, +} + +impl<'a> MirVisitor for CallsVisitor<'a> { + fn visit_terminator(&mut self, term: &Terminator, _loc: Location) { + match &term.kind { + TerminatorKind::Call { func, .. } => { + let TyKind::RigidTy(RigidTy::FnDef(def, args)) = + func.ty(self.locals).unwrap().kind() + else { + return; + }; + self.calls.insert(Instance::resolve(def, &args).unwrap()); + } + _ => {} + } + } +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "binop_input.rs"; + generate_input(&path).unwrap(); + let args = vec!["rustc".to_string(), "--crate-type=lib".to_string(), path.to_string()]; + run!(args, test_intrinsics).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + #![feature(core_intrinsics)] + use std::intrinsics::*; + pub fn use_intrinsics(init: bool) -> bool {{ + let sz = unsafe {{ size_of_val("hi") }}; + likely(init && sz == 2) + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/async-await/async-closures/different-projection-lengths-for-different-upvars.rs b/tests/ui/async-await/async-closures/different-projection-lengths-for-different-upvars.rs new file mode 100644 index 00000000000..2313db506be --- /dev/null +++ b/tests/ui/async-await/async-closures/different-projection-lengths-for-different-upvars.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ edition: 2021 +// issue: rust-lang/rust#123697 + +#![feature(async_closure)] + +struct S { t: i32 } + +fn test(s: &S, t: &i32) { + async || { + println!("{}", s.t); + println!("{}", t); + }; +} + +fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-generic-params.rs b/tests/ui/conditional-compilation/cfg-generic-params.rs index 76ba7f9b86e..2a83be21498 100644 --- a/tests/ui/conditional-compilation/cfg-generic-params.rs +++ b/tests/ui/conditional-compilation/cfg-generic-params.rs @@ -1,36 +1,36 @@ //@ compile-flags:--cfg yes -fn f_lt<#[cfg(yes)] 'a: 'a, #[cfg(no)] T>() {} -fn f_ty<#[cfg(no)] 'a: 'a, #[cfg(yes)] T>() {} +fn f_lt<#[cfg(yes)] 'a: 'a, #[cfg(FALSE)] T>() {} +fn f_ty<#[cfg(FALSE)] 'a: 'a, #[cfg(yes)] T>() {} -type FnGood = for<#[cfg(yes)] 'a, #[cfg(no)] T> fn(); // OK -type FnBad = for<#[cfg(no)] 'a, #[cfg(yes)] T> fn(); +type FnGood = for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> fn(); // OK +type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); //~^ ERROR only lifetime parameters can be used in this context -type PolyGood = dyn for<#[cfg(yes)] 'a, #[cfg(no)] T> Copy; // OK -type PolyBad = dyn for<#[cfg(no)] 'a, #[cfg(yes)] T> Copy; +type PolyGood = dyn for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> Copy; // OK +type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; //~^ ERROR only lifetime parameters can be used in this context -struct WhereGood where for<#[cfg(yes)] 'a, #[cfg(no)] T> u8: Copy; // OK -struct WhereBad where for<#[cfg(no)] 'a, #[cfg(yes)] T> u8: Copy; +struct WhereGood where for<#[cfg(yes)] 'a, #[cfg(FALSE)] T> u8: Copy; // OK +struct WhereBad where for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> u8: Copy; //~^ ERROR only lifetime parameters can be used in this context -fn f_lt_no<#[cfg_attr(no, unknown)] 'a>() {} // OK +fn f_lt_no<#[cfg_attr(FALSE, unknown)] 'a>() {} // OK fn f_lt_yes<#[cfg_attr(yes, unknown)] 'a>() {} //~^ ERROR cannot find attribute `unknown` in this scope -fn f_ty_no<#[cfg_attr(no, unknown)] T>() {} // OK +fn f_ty_no<#[cfg_attr(FALSE, unknown)] T>() {} // OK fn f_ty_yes<#[cfg_attr(yes, unknown)] T>() {} //~^ ERROR cannot find attribute `unknown` in this scope -type FnNo = for<#[cfg_attr(no, unknown)] 'a> fn(); // OK +type FnNo = for<#[cfg_attr(FALSE, unknown)] 'a> fn(); // OK type FnYes = for<#[cfg_attr(yes, unknown)] 'a> fn(); //~^ ERROR cannot find attribute `unknown` in this scope -type PolyNo = dyn for<#[cfg_attr(no, unknown)] 'a> Copy; // OK +type PolyNo = dyn for<#[cfg_attr(FALSE, unknown)] 'a> Copy; // OK type PolyYes = dyn for<#[cfg_attr(yes, unknown)] 'a> Copy; //~^ ERROR cannot find attribute `unknown` in this scope -struct WhereNo where for<#[cfg_attr(no, unknown)] 'a> u8: Copy; // OK +struct WhereNo where for<#[cfg_attr(FALSE, unknown)] 'a> u8: Copy; // OK struct WhereYes where for<#[cfg_attr(yes, unknown)] 'a> u8: Copy; //~^ ERROR cannot find attribute `unknown` in this scope diff --git a/tests/ui/conditional-compilation/cfg-generic-params.stderr b/tests/ui/conditional-compilation/cfg-generic-params.stderr index 4143e2019ae..563616be36b 100644 --- a/tests/ui/conditional-compilation/cfg-generic-params.stderr +++ b/tests/ui/conditional-compilation/cfg-generic-params.stderr @@ -29,30 +29,30 @@ LL | struct WhereYes where for<#[cfg_attr(yes, unknown)] 'a> u8: Copy; | ^^^^^^^ error[E0658]: only lifetime parameters can be used in this context - --> $DIR/cfg-generic-params.rs:7:45 + --> $DIR/cfg-generic-params.rs:7:48 | -LL | type FnBad = for<#[cfg(no)] 'a, #[cfg(yes)] T> fn(); - | ^ +LL | type FnBad = for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> fn(); + | ^ | = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information = help: add `#![feature(non_lifetime_binders)]` 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]: only lifetime parameters can be used in this context - --> $DIR/cfg-generic-params.rs:11:51 + --> $DIR/cfg-generic-params.rs:11:54 | -LL | type PolyBad = dyn for<#[cfg(no)] 'a, #[cfg(yes)] T> Copy; - | ^ +LL | type PolyBad = dyn for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> Copy; + | ^ | = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information = help: add `#![feature(non_lifetime_binders)]` 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]: only lifetime parameters can be used in this context - --> $DIR/cfg-generic-params.rs:15:54 + --> $DIR/cfg-generic-params.rs:15:57 | -LL | struct WhereBad where for<#[cfg(no)] 'a, #[cfg(yes)] T> u8: Copy; - | ^ +LL | struct WhereBad where for<#[cfg(FALSE)] 'a, #[cfg(yes)] T> u8: Copy; + | ^ | = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information = help: add `#![feature(non_lifetime_binders)]` to the crate attributes to enable diff --git a/tests/ui/const-generics/const-arg-in-const-arg.rs b/tests/ui/const-generics/const-arg-in-const-arg.rs index 6d30943ab7e..27b74489fe8 100644 --- a/tests/ui/const-generics/const-arg-in-const-arg.rs +++ b/tests/ui/const-generics/const-arg-in-const-arg.rs @@ -2,8 +2,8 @@ // we use a single revision because this should have a `full` revision // but right now that ICEs and I(@BoxyUwU) could not get stderr normalization to work -#![cfg_attr(full, feature(generic_const_exprs))] -#![cfg_attr(full, allow(incomplete_features))] +// #![cfg_attr(full, feature(generic_const_exprs))] +// #![cfg_attr(full, allow(incomplete_features))] const fn foo<T>() -> usize { std::mem::size_of::<T>() } const fn bar<const N: usize>() -> usize { N } diff --git a/tests/ui/consts/const-eval/const-eval-query-stack.stderr b/tests/ui/consts/const-eval/const-eval-query-stack.stderr index 96fd9ed5f04..0a28c5b80bf 100644 --- a/tests/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/tests/ui/consts/const-eval/const-eval-query-stack.stderr @@ -4,6 +4,8 @@ error: internal compiler error[E0080]: evaluation of constant value failed LL | const X: i32 = 1 / 0; | ^^^^^ attempt to divide `1_i32` by zero +note: please make sure that you have updated to the latest nightly + query stack during panic: #0 [eval_to_allocation_raw] const-evaluating + checking `X` #1 [eval_to_const_value_raw] simplifying constant for the type system `X` diff --git a/tests/ui/debuginfo/auxiliary/line-tables-only-helper.rs b/tests/ui/debuginfo/auxiliary/line-tables-only-helper.rs new file mode 100644 index 00000000000..65da2c3f5c7 --- /dev/null +++ b/tests/ui/debuginfo/auxiliary/line-tables-only-helper.rs @@ -0,0 +1,22 @@ +//@ compile-flags: -Cstrip=none -Cdebuginfo=line-tables-only + +#[no_mangle] +pub fn baz<F>(mut cb: F, data: u32) where F: FnMut(u32) { + cb(data); +} + +#[no_mangle] +pub fn bar<F>(cb: F, data: u32) where F: FnMut(u32) { + baz(cb, data); +} + +#[no_mangle] +pub fn foo<F>(cb: F, data: u32) where F: FnMut(u32) { + bar(cb, data); +} + +pub fn capture_backtrace() -> std::backtrace::Backtrace { + let mut bt = None; + foo(|_| bt = Some(std::backtrace::Backtrace::capture()), 42); + bt.unwrap() +} diff --git a/tests/ui/debuginfo/backtrace-line-tables-only.rs b/tests/ui/debuginfo/backtrace-line-tables-only.rs new file mode 100644 index 00000000000..044f59e483a --- /dev/null +++ b/tests/ui/debuginfo/backtrace-line-tables-only.rs @@ -0,0 +1,49 @@ +// Test that when debug info only includes line tables that backtrace is still generated +// successfully. +// Original test: +// <https://github.com/rust-lang/backtrace-rs/tree/6fa4b85b9962c3e1be8c2e5cc605cd078134152b/crates/line-tables-only>. +// Part of <https://github.com/rust-lang/rust/issues/122899> porting some backtrace tests to rustc. +// This test diverges from the original test in that it now uses a Rust library auxiliary because +// rustc now has `-Cdebuginfo=line-tables-only`. +// ignore-tidy-linelength +//@ run-pass +//@ compile-flags: -Cstrip=none -Cdebuginfo=line-tables-only +//@ ignore-android FIXME #17520 +//@ ignore-fuchsia Backtraces not symbolized +//@ needs-unwind +//@ aux-build: line-tables-only-helper.rs + +#![feature(backtrace_frames)] + +extern crate line_tables_only_helper; + +use std::backtrace::Backtrace; + +fn assert_contains( + backtrace: &Backtrace, + expected_name: &str, + expected_file: &str, + expected_line: u32, +) { + // FIXME(jieyouxu): fix this ugly fragile test when `BacktraceFrame` has accessors like... + // `symbols()`. + let backtrace = format!("{:#?}", backtrace); + eprintln!("{}", backtrace); + assert!(backtrace.contains(expected_name), "backtrace does not contain expected name {}", expected_name); + assert!(backtrace.contains(expected_file), "backtrace does not contain expected file {}", expected_file); + assert!(backtrace.contains(&expected_line.to_string()), "backtrace does not contain expected line {}", expected_line); +} + +fn main() { + std::env::set_var("RUST_BACKTRACE", "1"); + let backtrace = line_tables_only_helper::capture_backtrace(); + + // FIXME(jieyouxu): for some forsaken reason on i686-msvc `foo` doesn't have an entry in the + // line tables? + #[cfg(not(all(target_pointer_width = "32", target_env = "msvc")))] + { + assert_contains(&backtrace, "foo", "line-tables-only-helper.rs", 5); + } + assert_contains(&backtrace, "bar", "line-tables-only-helper.rs", 10); + assert_contains(&backtrace, "baz", "line-tables-only-helper.rs", 15); +} diff --git a/tests/ui/does-nothing.rs b/tests/ui/does-nothing.rs deleted file mode 100644 index e4992e2cfd3..00000000000 --- a/tests/ui/does-nothing.rs +++ /dev/null @@ -1,2 +0,0 @@ -fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); } -//~^ ERROR cannot find value `this_does_nothing_what_the` in this scope diff --git a/tests/ui/does-nothing.stderr b/tests/ui/does-nothing.stderr deleted file mode 100644 index d5ea3626e81..00000000000 --- a/tests/ui/does-nothing.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0425]: cannot find value `this_does_nothing_what_the` in this scope - --> $DIR/does-nothing.rs:1:32 - | -LL | fn main() { println!("doing"); this_does_nothing_what_the; println!("boing"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs b/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs index 6fe51330118..33fda822baa 100644 --- a/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs +++ b/tests/ui/feature-gates/feature-gate-yeet_expr-in-cfg.rs @@ -1,7 +1,7 @@ //@ compile-flags: --edition 2021 pub fn demo() -> Option<i32> { - #[cfg(nope)] + #[cfg(FALSE)] { do yeet //~ ERROR `do yeet` expression is experimental } @@ -9,7 +9,7 @@ pub fn demo() -> Option<i32> { Some(1) } -#[cfg(nope)] +#[cfg(FALSE)] pub fn alternative() -> Result<(), String> { do yeet "hello"; //~ ERROR `do yeet` expression is experimental } diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs index a3f3b1a6d4d..e06341ddf31 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.rs @@ -1,8 +1,7 @@ -#![warn(unused_lifetimes)] +#![warn(unused_lifetimes, redundant_lifetimes)] pub trait X { - type Y<'a: 'static>; - //~^ WARNING unnecessary lifetime parameter + type Y<'a: 'static>; //~ WARN unnecessary lifetime parameter `'a` } impl X for () { diff --git a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr index 8d21b9172c8..4f41ec025fc 100644 --- a/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr +++ b/tests/ui/generic-associated-types/unsatisfied-item-lifetime-bound.stderr @@ -1,18 +1,5 @@ -warning: unnecessary lifetime parameter `'a` - --> $DIR/unsatisfied-item-lifetime-bound.rs:4:12 - | -LL | type Y<'a: 'static>; - | ^^ - | - = help: you can use the `'static` lifetime directly, in place of `'a` -note: the lint level is defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:1:9 - | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ - error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:9:18 + --> $DIR/unsatisfied-item-lifetime-bound.rs:8:18 | LL | type Y<'a: 'static>; | ------------------- definition of `Y` from trait @@ -21,7 +8,7 @@ LL | type Y<'a> = &'a (); | ^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:9:12 + --> $DIR/unsatisfied-item-lifetime-bound.rs:8:12 | LL | type Y<'a> = &'a (); | ^^ @@ -32,44 +19,57 @@ LL | type Y<'a> = &'a () where 'a: 'static; | +++++++++++++++++ error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:14:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:13:8 | LL | f: <T as X>::Y<'a>, | ^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:13:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:12:10 | LL | struct B<'a, T: for<'r> X<Y<'r> = &'r ()>> { | ^^ = note: but lifetime parameter must outlive the static lifetime error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:19:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:18:8 | LL | f: <T as X>::Y<'a>, | ^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:18:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:17:10 | LL | struct C<'a, T: X> { | ^^ = note: but lifetime parameter must outlive the static lifetime error[E0478]: lifetime bound not satisfied - --> $DIR/unsatisfied-item-lifetime-bound.rs:24:8 + --> $DIR/unsatisfied-item-lifetime-bound.rs:23:8 | LL | f: <() as X>::Y<'a>, | ^^^^^^^^^^^^^^^^ | note: lifetime parameter instantiated with the lifetime `'a` as defined here - --> $DIR/unsatisfied-item-lifetime-bound.rs:23:10 + --> $DIR/unsatisfied-item-lifetime-bound.rs:22:10 | LL | struct D<'a> { | ^^ = note: but lifetime parameter must outlive the static lifetime +warning: unnecessary lifetime parameter `'a` + --> $DIR/unsatisfied-item-lifetime-bound.rs:4:12 + | +LL | type Y<'a: 'static>; + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` +note: the lint level is defined here + --> $DIR/unsatisfied-item-lifetime-bound.rs:1:27 + | +LL | #![warn(unused_lifetimes, redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ + error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0478`. diff --git a/tests/ui/panics/default-backtrace-ice.stderr b/tests/ui/panics/default-backtrace-ice.stderr index 82b61e43f44..23b863568bc 100644 --- a/tests/ui/panics/default-backtrace-ice.stderr +++ b/tests/ui/panics/default-backtrace-ice.stderr @@ -20,6 +20,8 @@ error: the compiler unexpectedly panicked. this is a bug. + + query stack during panic: #0 [resolver_for_lowering_raw] getting the resolver for lowering end of query stack diff --git a/tests/ui/proc-macro/derive-helper-configured.rs b/tests/ui/proc-macro/derive-helper-configured.rs index 74d5d827f16..45e6e64d392 100644 --- a/tests/ui/proc-macro/derive-helper-configured.rs +++ b/tests/ui/proc-macro/derive-helper-configured.rs @@ -1,17 +1,15 @@ // Derive helpers are resolved successfully inside `cfg_attr`. //@ check-pass -// compile-flats:--cfg TRUE //@ aux-build:test-macros.rs #[macro_use] extern crate test_macros; -#[cfg_attr(TRUE, empty_helper)] #[derive(Empty)] -#[cfg_attr(TRUE, empty_helper)] +#[cfg_attr(all(), empty_helper)] struct S { - #[cfg_attr(TRUE, empty_helper)] + #[cfg_attr(all(), empty_helper)] field: u8, } diff --git a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs index f6a628e97f5..bef0d70c776 100644 --- a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs +++ b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.rs @@ -8,10 +8,10 @@ // // 'a : 'b -#![warn(unused_lifetimes)] +#![warn(redundant_lifetimes)] -fn test<'a,'b>(x: &'a i32) -> &'b i32 - where 'a: 'static //~ WARN unnecessary lifetime parameter `'a` +fn test<'a,'b>(x: &'a i32) -> &'b i32 //~ WARN unnecessary lifetime parameter `'a` + where 'a: 'static { x } diff --git a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr index 9f03a6553ba..d97cfd59f2b 100644 --- a/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr +++ b/tests/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr @@ -1,15 +1,15 @@ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:14:11 + --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:13:9 | -LL | where 'a: 'static - | ^^ +LL | fn test<'a,'b>(x: &'a i32) -> &'b i32 + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` note: the lint level is defined here --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:11:9 | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ +LL | #![warn(redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/ui/regions/regions-static-bound-rpass.rs b/tests/ui/regions/regions-static-bound-rpass.rs index 27da42882f3..f4177f835b1 100644 --- a/tests/ui/regions/regions-static-bound-rpass.rs +++ b/tests/ui/regions/regions-static-bound-rpass.rs @@ -1,18 +1,19 @@ //@ run-pass -#![warn(unused_lifetimes)] +#![warn(redundant_lifetimes)] fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () - where 'a: 'static { t } //~^ WARN unnecessary lifetime parameter `'a` + where 'a: 'static { t } fn static_id<'a>(t: &'a ()) -> &'static () - where 'a: 'static { t } //~^ WARN unnecessary lifetime parameter `'a` + where 'a: 'static { t } fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () +//~^ WARN unnecessary lifetime parameter `'a` +//~| WARN unnecessary lifetime parameter `'b` where 'a: 'b, 'b: 'static { t } -//~^ WARN unnecessary lifetime parameter `'b` fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t } diff --git a/tests/ui/regions/regions-static-bound-rpass.stderr b/tests/ui/regions/regions-static-bound-rpass.stderr index f0f3a4c5261..4199ac7bb3d 100644 --- a/tests/ui/regions/regions-static-bound-rpass.stderr +++ b/tests/ui/regions/regions-static-bound-rpass.stderr @@ -1,31 +1,39 @@ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-static-bound-rpass.rs:6:11 + --> $DIR/regions-static-bound-rpass.rs:5:17 | -LL | where 'a: 'static { t } - | ^^ +LL | fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` note: the lint level is defined here --> $DIR/regions-static-bound-rpass.rs:3:9 | -LL | #![warn(unused_lifetimes)] - | ^^^^^^^^^^^^^^^^ +LL | #![warn(redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-static-bound-rpass.rs:10:11 + --> $DIR/regions-static-bound-rpass.rs:9:14 | -LL | where 'a: 'static { t } - | ^^ +LL | fn static_id<'a>(t: &'a ()) -> &'static () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` + +warning: unnecessary lifetime parameter `'a` + --> $DIR/regions-static-bound-rpass.rs:13:23 + | +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'b` - --> $DIR/regions-static-bound-rpass.rs:14:19 + --> $DIR/regions-static-bound-rpass.rs:13:26 | -LL | where 'a: 'b, 'b: 'static { t } - | ^^ +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'b` + = note: you can use the `'static` lifetime directly, in place of `'b` -warning: 3 warnings emitted +warning: 4 warnings emitted diff --git a/tests/ui/regions/regions-static-bound.rs b/tests/ui/regions/regions-static-bound.rs index e7aa8795f01..32fa2536533 100644 --- a/tests/ui/regions/regions-static-bound.rs +++ b/tests/ui/regions/regions-static-bound.rs @@ -1,12 +1,13 @@ -#![warn(unused_lifetimes)] +#![warn(unused_lifetimes, redundant_lifetimes)] fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } -//~^ WARN lifetime parameter `'b` never used -//~| WARN unnecessary lifetime parameter `'a` +//~^ WARN unnecessary lifetime parameter `'a` +//~| WARN lifetime parameter `'b` never used fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () +//~^ WARN unnecessary lifetime parameter `'a` +//~| WARN unnecessary lifetime parameter `'b` where 'a: 'b, 'b: 'static { t } -//~^ WARN unnecessary lifetime parameter `'b` fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { t diff --git a/tests/ui/regions/regions-static-bound.stderr b/tests/ui/regions/regions-static-bound.stderr index b314e9fe85d..48aa8f32329 100644 --- a/tests/ui/regions/regions-static-bound.stderr +++ b/tests/ui/regions/regions-static-bound.stderr @@ -9,27 +9,40 @@ LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } note: the lint level is defined here --> $DIR/regions-static-bound.rs:1:9 | -LL | #![warn(unused_lifetimes)] +LL | #![warn(unused_lifetimes, redundant_lifetimes)] | ^^^^^^^^^^^^^^^^ warning: unnecessary lifetime parameter `'a` - --> $DIR/regions-static-bound.rs:3:53 + --> $DIR/regions-static-bound.rs:3:14 | LL | fn static_id<'a,'b>(t: &'a ()) -> &'static () where 'a: 'static { t } - | ^^ + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'a` + = note: you can use the `'static` lifetime directly, in place of `'a` +note: the lint level is defined here + --> $DIR/regions-static-bound.rs:1:27 + | +LL | #![warn(unused_lifetimes, redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: unnecessary lifetime parameter `'a` + --> $DIR/regions-static-bound.rs:7:23 + | +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` warning: unnecessary lifetime parameter `'b` - --> $DIR/regions-static-bound.rs:8:19 + --> $DIR/regions-static-bound.rs:7:26 | -LL | where 'a: 'b, 'b: 'static { t } - | ^^ +LL | fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + | ^^ | - = help: you can use the `'static` lifetime directly, in place of `'b` + = note: you can use the `'static` lifetime directly, in place of `'b` error: lifetime may not live long enough - --> $DIR/regions-static-bound.rs:12:5 + --> $DIR/regions-static-bound.rs:13:5 | LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { | -- lifetime `'a` defined here @@ -37,7 +50,7 @@ LL | t | ^ returning this value requires that `'a` must outlive `'static` error[E0521]: borrowed data escapes outside of function - --> $DIR/regions-static-bound.rs:17:5 + --> $DIR/regions-static-bound.rs:18:5 | LL | fn error(u: &(), v: &()) { | - - let's call the lifetime of this reference `'1` @@ -50,7 +63,7 @@ LL | static_id(&u); | argument requires that `'1` must outlive `'static` error[E0521]: borrowed data escapes outside of function - --> $DIR/regions-static-bound.rs:19:5 + --> $DIR/regions-static-bound.rs:20:5 | LL | fn error(u: &(), v: &()) { | - - let's call the lifetime of this reference `'2` @@ -63,6 +76,6 @@ LL | static_id_indirect(&v); | `v` escapes the function body here | argument requires that `'2` must outlive `'static` -error: aborting due to 3 previous errors; 3 warnings emitted +error: aborting due to 3 previous errors; 4 warnings emitted For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/regions/transitively-redundant-lifetimes.rs b/tests/ui/regions/transitively-redundant-lifetimes.rs new file mode 100644 index 00000000000..9c29f66e54c --- /dev/null +++ b/tests/ui/regions/transitively-redundant-lifetimes.rs @@ -0,0 +1,20 @@ +#![deny(redundant_lifetimes)] + +fn a<'a, 'b>(x: &'a &'b &'a ()) {} //~ ERROR unnecessary lifetime parameter `'b` + +fn b<'a: 'b, 'b: 'a>() {} //~ ERROR unnecessary lifetime parameter `'b` + +struct Foo<T: 'static>(T); +fn c<'a>(_: Foo<&'a ()>) {} //~ ERROR unnecessary lifetime parameter `'a` + +struct Bar<'a>(&'a ()); +impl<'a> Bar<'a> { + fn d<'b: 'a>(&'b self) {} //~ ERROR unnecessary lifetime parameter `'b` +} + +fn ok(x: &'static &()) {} + +trait Tr<'a> {} +impl<'a: 'static> Tr<'a> for () {} //~ ERROR unnecessary lifetime parameter `'a` + +fn main() {} diff --git a/tests/ui/regions/transitively-redundant-lifetimes.stderr b/tests/ui/regions/transitively-redundant-lifetimes.stderr new file mode 100644 index 00000000000..2d8fc433b24 --- /dev/null +++ b/tests/ui/regions/transitively-redundant-lifetimes.stderr @@ -0,0 +1,47 @@ +error: unnecessary lifetime parameter `'b` + --> $DIR/transitively-redundant-lifetimes.rs:3:10 + | +LL | fn a<'a, 'b>(x: &'a &'b &'a ()) {} + | ^^ + | + = note: you can use the `'a` lifetime directly, in place of `'b` +note: the lint level is defined here + --> $DIR/transitively-redundant-lifetimes.rs:1:9 + | +LL | #![deny(redundant_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^ + +error: unnecessary lifetime parameter `'b` + --> $DIR/transitively-redundant-lifetimes.rs:5:14 + | +LL | fn b<'a: 'b, 'b: 'a>() {} + | ^^ + | + = note: you can use the `'a` lifetime directly, in place of `'b` + +error: unnecessary lifetime parameter `'a` + --> $DIR/transitively-redundant-lifetimes.rs:8:6 + | +LL | fn c<'a>(_: Foo<&'a ()>) {} + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` + +error: unnecessary lifetime parameter `'a` + --> $DIR/transitively-redundant-lifetimes.rs:18:6 + | +LL | impl<'a: 'static> Tr<'a> for () {} + | ^^ + | + = note: you can use the `'static` lifetime directly, in place of `'a` + +error: unnecessary lifetime parameter `'b` + --> $DIR/transitively-redundant-lifetimes.rs:12:10 + | +LL | fn d<'b: 'a>(&'b self) {} + | ^^ + | + = note: you can use the `'a` lifetime directly, in place of `'b` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs index 17817a460d7..cd5fa609947 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs @@ -1,7 +1,7 @@ //@ known-bug: #110395 #![feature(const_trait_impl)] #![feature(const_mut_refs)] -#![cfg_attr(precise, feature(const_precise_live_drops))] +// #![cfg_attr(precise, feature(const_precise_live_drops))] use std::marker::{Destruct, PhantomData}; diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed index 75b3918be1d..f4506dd929e 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.fixed @@ -1,6 +1,6 @@ //@ aux-build:edition-lint-paths.rs //@ run-rustfix -//@ compile-flags:--extern edition_lint_paths --cfg blandiloquence +//@ compile-flags:--extern edition_lint_paths //@ edition:2018 #![deny(rust_2018_idioms)] diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs index eff03c6fbe6..4f1cb71dc51 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.rs @@ -1,6 +1,6 @@ //@ aux-build:edition-lint-paths.rs //@ run-rustfix -//@ compile-flags:--extern edition_lint_paths --cfg blandiloquence +//@ compile-flags:--extern edition_lint_paths //@ edition:2018 #![deny(rust_2018_idioms)] @@ -8,7 +8,7 @@ // The suggestion span should include the attribute. -#[cfg(blandiloquence)] //~ HELP remove it +#[cfg(not(FALSE))] //~ HELP remove it extern crate edition_lint_paths; //~^ ERROR unused extern crate diff --git a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr index 801d16af82d..038a9dd967b 100644 --- a/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr +++ b/tests/ui/rust-2018/issue-54400-unused-extern-crate-attr-span.stderr @@ -1,7 +1,7 @@ error: unused extern crate --> $DIR/issue-54400-unused-extern-crate-attr-span.rs:12:1 | -LL | / #[cfg(blandiloquence)] +LL | / #[cfg(not(FALSE))] LL | | extern crate edition_lint_paths; | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- | |________________________________| diff --git a/tests/ui/std/windows-bat-args.rs b/tests/ui/std/windows-bat-args.rs new file mode 100644 index 00000000000..a9b6252b78c --- /dev/null +++ b/tests/ui/std/windows-bat-args.rs @@ -0,0 +1,90 @@ +//@ only-windows +//@ run-pass +//@ run-flags:--parent-process + +use std::env; +use std::io::ErrorKind::{self, InvalidInput}; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + if env::args().nth(1).as_deref() == Some("--parent-process") { + parent(); + } else { + child(); + } +} + +fn child() { + if env::args().len() == 1 { + panic!("something went wrong :/"); + } + for arg in env::args().skip(1) { + print!("{arg}\0"); + } +} + +fn parent() { + let mut bat = PathBuf::from(file!()); + bat.set_file_name("windows-bat-args1.bat"); + let bat1 = String::from(bat.to_str().unwrap()); + bat.set_file_name("windows-bat-args2.bat"); + let bat2 = String::from(bat.to_str().unwrap()); + bat.set_file_name("windows-bat-args3.bat"); + let bat3 = String::from(bat.to_str().unwrap()); + let bat = [bat1.as_str(), bat2.as_str(), bat3.as_str()]; + + check_args(&bat, &["a", "b"]).unwrap(); + check_args(&bat, &["c is for cat", "d is for dog"]).unwrap(); + check_args(&bat, &["\"", " \""]).unwrap(); + check_args(&bat, &["\\", "\\"]).unwrap(); + check_args(&bat, &[">file.txt"]).unwrap(); + check_args(&bat, &["whoami.exe"]).unwrap(); + check_args(&bat, &["&a.exe"]).unwrap(); + check_args(&bat, &["&echo hello "]).unwrap(); + check_args(&bat, &["&echo hello", "&whoami", ">file.txt"]).unwrap(); + check_args(&bat, &["!TMP!"]).unwrap(); + check_args(&bat, &["key=value"]).unwrap(); + check_args(&bat, &["\"key=value\""]).unwrap(); + check_args(&bat, &["key = value"]).unwrap(); + check_args(&bat, &["key=[\"value\"]"]).unwrap(); + check_args(&bat, &["", "a=b"]).unwrap(); + check_args(&bat, &["key=\"foo bar\""]).unwrap(); + check_args(&bat, &["key=[\"my_value]"]).unwrap(); + check_args(&bat, &["key=[\"my_value\",\"other-value\"]"]).unwrap(); + check_args(&bat, &["key\\=value"]).unwrap(); + check_args(&bat, &["key=\"&whoami\""]).unwrap(); + check_args(&bat, &["key=\"value\"=5"]).unwrap(); + check_args(&bat, &["key=[\">file.txt\"]"]).unwrap(); + assert_eq!(check_args(&bat, &["\n"]), Err(InvalidInput)); + assert_eq!(check_args(&bat, &["\r"]), Err(InvalidInput)); + check_args(&bat, &["%hello"]).unwrap(); + check_args(&bat, &["%PATH%"]).unwrap(); + check_args(&bat, &["%%cd:~,%"]).unwrap(); + check_args(&bat, &["%PATH%PATH%"]).unwrap(); + check_args(&bat, &["\">file.txt"]).unwrap(); + check_args(&bat, &["abc\"&echo hello"]).unwrap(); + check_args(&bat, &["123\">file.txt"]).unwrap(); + check_args(&bat, &["\"&echo hello&whoami.exe"]).unwrap(); + check_args(&bat, &[r#"hello^"world"#, "hello &echo oh no >file.txt"]).unwrap(); +} + +// Check if the arguments roundtrip through a bat file and back into a Rust process. +// Our Rust process outptuts the arguments as null terminated strings. +#[track_caller] +fn check_args(bats: &[&str], args: &[&str]) -> Result<(), ErrorKind> { + for bat in bats { + let output = Command::new(&bat).args(args).output().map_err(|e| e.kind())?; + assert!(output.status.success()); + let child_args = String::from_utf8(output.stdout).unwrap(); + let mut child_args: Vec<&str> = + child_args.strip_suffix('\0').unwrap().split('\0').collect(); + // args3.bat can append spurious empty arguments, so trim them here. + child_args.truncate( + child_args.iter().rposition(|s| !s.is_empty()).unwrap_or(child_args.len() - 1) + 1, + ); + assert_eq!(&child_args, &args); + assert!(!Path::new("file.txt").exists()); + } + Ok(()) +} diff --git a/tests/ui/std/windows-bat-args1.bat b/tests/ui/std/windows-bat-args1.bat new file mode 100644 index 00000000000..edd36bd5530 --- /dev/null +++ b/tests/ui/std/windows-bat-args1.bat @@ -0,0 +1 @@ +@a.exe %* diff --git a/tests/ui/std/windows-bat-args2.bat b/tests/ui/std/windows-bat-args2.bat new file mode 100644 index 00000000000..8d5a7dd8a9e --- /dev/null +++ b/tests/ui/std/windows-bat-args2.bat @@ -0,0 +1 @@ +@a.exe %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/tests/ui/std/windows-bat-args3.bat b/tests/ui/std/windows-bat-args3.bat new file mode 100644 index 00000000000..7fe360a6d36 --- /dev/null +++ b/tests/ui/std/windows-bat-args3.bat @@ -0,0 +1 @@ +@a.exe "%~1" "%~2" "%~3" "%~4" "%~5" "%~6" "%~7" "%~8" "%~9" diff --git a/tests/ui/track-diagnostics/track.stderr b/tests/ui/track-diagnostics/track.stderr index 54b1ea2764a..436f9ecf93d 100644 --- a/tests/ui/track-diagnostics/track.stderr +++ b/tests/ui/track-diagnostics/track.stderr @@ -30,6 +30,8 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a 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 $TARGET note: compiler flags: ... -Z ui-testing ... -Z track-diagnostics diff --git a/tests/ui/type/pattern_types/const_generics.rs b/tests/ui/type/pattern_types/const_generics.rs new file mode 100644 index 00000000000..5bc6fd54e0f --- /dev/null +++ b/tests/ui/type/pattern_types/const_generics.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(pattern_types)] +#![feature(core_pattern_types)] +#![feature(core_pattern_type)] + +use std::pat::pattern_type; + +trait Foo {} + +impl<const START: u32, const END: u32> Foo for pattern_type!(u32 is START..=END) {} + +fn main() {} diff --git a/tests/ui/typeck/remove-semi-but-confused-char.rs b/tests/ui/typeck/remove-semi-but-confused-char.rs new file mode 100644 index 00000000000..ccc6f59344c --- /dev/null +++ b/tests/ui/typeck/remove-semi-but-confused-char.rs @@ -0,0 +1,11 @@ +// Ensures our "remove semicolon" suggestion isn't hardcoded with a character width, +// in case it was accidentally mixed up with a greek question mark. +// issue: rust-lang/rust#123607 + +pub fn square(num: i32) -> i32 { + //~^ ERROR mismatched types + num * num; + //~^ ERROR unknown start of token +} + +fn main() {} diff --git a/tests/ui/typeck/remove-semi-but-confused-char.stderr b/tests/ui/typeck/remove-semi-but-confused-char.stderr new file mode 100644 index 00000000000..2d0b53a60ce --- /dev/null +++ b/tests/ui/typeck/remove-semi-but-confused-char.stderr @@ -0,0 +1,25 @@ +error: unknown start of token: \u{37e} + --> $DIR/remove-semi-but-confused-char.rs:7:14 + | +LL | num * num; + | ^ + | +help: Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not + | +LL | num * num; + | ~ + +error[E0308]: mismatched types + --> $DIR/remove-semi-but-confused-char.rs:5:28 + | +LL | pub fn square(num: i32) -> i32 { + | ------ ^^^ expected `i32`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression +LL | +LL | num * num; + | - help: remove this semicolon to return this value + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. |
