diff options
| author | bors <bors@rust-lang.org> | 2024-01-13 06:29:38 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-01-13 06:29:38 +0000 |
| commit | 12c0f090af379f2a6334cc35100cb6ee3f13f0ef (patch) | |
| tree | ab6ca2c31a68888b7c34f3b091e899054f3a56aa | |
| parent | 67659fb7952bad8c5d54955f69d289f03763e0b1 (diff) | |
| parent | 9b46f3bb6499c263c9c81e7cea4815396bbbce14 (diff) | |
| download | rust-12c0f090af379f2a6334cc35100cb6ee3f13f0ef.tar.gz rust-12c0f090af379f2a6334cc35100cb6ee3f13f0ef.zip | |
Auto merge of #3265 - rust-lang:rustup-2024-01-13, r=saethlin
Automatic Rustup
401 files changed, 7533 insertions, 2393 deletions
diff --git a/Cargo.lock b/Cargo.lock index 75c7de4f405..440def72768 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,16 @@ dependencies = [ ] [[package]] +name = "annotate-snippets" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a433302f833baa830c0092100c481c7ea768c5981a3c36f549517a502f246dd" +dependencies = [ + "anstyle", + "unicode-width", +] + +[[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3771,7 +3781,7 @@ dependencies = [ name = "rustc_errors" version = "0.0.0" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.10.1", "derive_setters", "rustc_ast", "rustc_ast_pretty", @@ -3831,7 +3841,7 @@ dependencies = [ name = "rustc_fluent_macro" version = "0.0.0" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.10.1", "fluent-bundle", "fluent-syntax", "proc-macro2", @@ -4564,6 +4574,7 @@ checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f" name = "rustc_trait_selection" version = "0.0.0" dependencies = [ + "bitflags 2.4.1", "itertools", "rustc_ast", "rustc_attr", @@ -4738,7 +4749,7 @@ dependencies = [ name = "rustfmt-nightly" version = "1.7.0" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.9.1", "anyhow", "bytecount", "cargo_metadata 0.15.4", @@ -5728,7 +5739,7 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf4bf7c184b8dfc7a4d3b90df789b1eb992ee42811cd115f32a7a1eb781058d" dependencies = [ - "annotate-snippets", + "annotate-snippets 0.9.1", "anyhow", "bstr", "cargo-platform", @@ -5859,9 +5870,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/Cargo.toml b/Cargo.toml index 03915078838..2ea16c22666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ exclude = [ ] [profile.release.package.compiler_builtins] -# The compiler-builtins crate cannot reference libcore, and it's own CI will +# The compiler-builtins crate cannot reference libcore, and its own CI will # verify that this is the case. This requires, however, that the crate is built # without overflow checks and debug assertions. Forcefully disable debug # assertions and overflow checks here which should ensure that even if these diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 495b255583c..0457b4e6ddc 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2399,10 +2399,10 @@ mod error { /// and we want only the best of those errors. /// /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the - /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the - /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once - /// all move errors have been reported, any diagnostics in this map are added to the buffer - /// to be emitted. + /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of + /// the `Place` of the previous most diagnostic. This happens instead of buffering the + /// error. Once all move errors have been reported, any diagnostics in this map are added + /// to the buffer to be emitted. /// /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary /// when errors in the map are being re-added to the error buffer so that errors with the @@ -2410,7 +2410,8 @@ mod error { buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)>, buffered_mut_errors: FxIndexMap<Span, (DiagnosticBuilder<'tcx>, usize)>, - /// Diagnostics to be reported buffer. + /// Buffer of diagnostics to be reported. Uses `Diagnostic` rather than `DiagnosticBuilder` + /// because it has a mixture of error diagnostics and non-error diagnostics. buffered: Vec<Diagnostic>, /// Set to Some if we emit an error during borrowck tainted_by_errors: Option<ErrorGuaranteed>, @@ -2434,11 +2435,11 @@ mod error { "diagnostic buffered but not emitted", )) } - t.buffer(&mut self.buffered); + self.buffered.push(t.into_diagnostic()); } pub fn buffer_non_error_diag(&mut self, t: DiagnosticBuilder<'_, ()>) { - t.buffer(&mut self.buffered); + self.buffered.push(t.into_diagnostic()); } pub fn set_tainted_by_errors(&mut self, e: ErrorGuaranteed) { @@ -2486,13 +2487,13 @@ mod error { // Buffer any move errors that we collected and de-duplicated. for (_, (_, diag)) in std::mem::take(&mut self.errors.buffered_move_errors) { // We have already set tainted for this error, so just buffer it. - diag.buffer(&mut self.errors.buffered); + self.errors.buffered.push(diag.into_diagnostic()); } for (_, (mut diag, count)) in std::mem::take(&mut self.errors.buffered_mut_errors) { if count > 10 { diag.note(format!("...and {} other attempted mutable borrows", count - 10)); } - diag.buffer(&mut self.errors.buffered); + self.errors.buffered.push(diag.into_diagnostic()); } if !self.errors.buffered.is_empty() { diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 4b3eaf78557..a0fd0e3f9be 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -18,7 +18,7 @@ fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option<Symbol> { if let Some(value) = cx.sess.opts.logical_env.get(var) { return Some(Symbol::intern(value)); } - // If the environment variable was not defined with the `--env` option, we try to retrieve it + // If the environment variable was not defined with the `--env-set` option, we try to retrieve it // from rustc's environment. env::var(var).ok().as_deref().map(Symbol::intern) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index e9e8ade09b7..42bd8687042 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -60,7 +60,7 @@ fn prepare_lto( }; let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used || info.used_compiler { + if info.level.is_below_threshold(export_threshold) || info.used { Some(CString::new(name.as_str()).unwrap()) } else { None diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index a912ef9e755..27cb0366f17 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -569,6 +569,7 @@ pub(crate) unsafe fn llvm_optimize( unroll_loops, config.vectorize_slp, config.vectorize_loop, + config.no_builtins, config.emit_lifetime_markers, sanitizer_options.as_ref(), pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), @@ -677,6 +678,7 @@ pub(crate) unsafe fn codegen( unsafe fn with_codegen<'ll, F, R>( tm: &'ll llvm::TargetMachine, llmod: &'ll llvm::Module, + no_builtins: bool, f: F, ) -> R where @@ -684,7 +686,7 @@ pub(crate) unsafe fn codegen( { let cpm = llvm::LLVMCreatePassManager(); llvm::LLVMAddAnalysisPasses(tm, cpm); - llvm::LLVMRustAddLibraryInfo(cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); f(cpm) } @@ -785,7 +787,7 @@ pub(crate) unsafe fn codegen( } else { llmod }; - with_codegen(tm, llmod, |cpm| { + with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file( dcx, tm, @@ -820,7 +822,7 @@ pub(crate) unsafe fn codegen( (_, SplitDwarfKind::Split) => Some(dwo_out.as_path()), }; - with_codegen(tm, llmod, |cpm| { + with_codegen(tm, llmod, config.no_builtins, |cpm| { write_output_file( dcx, tm, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index aefca6b34f5..ee73c6b4756 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2173,8 +2173,13 @@ extern "C" { ArgsCstrBuff: *const c_char, ArgsCstrBuffLen: usize, ) -> *mut TargetMachine; + pub fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); - pub fn LLVMRustAddLibraryInfo<'a>(PM: &PassManager<'a>, M: &'a Module); + pub fn LLVMRustAddLibraryInfo<'a>( + PM: &PassManager<'a>, + M: &'a Module, + DisableSimplifyLibCalls: bool, + ); pub fn LLVMRustWriteOutputFile<'a>( T: &'a TargetMachine, PM: &PassManager<'a>, @@ -2196,6 +2201,7 @@ extern "C" { UnrollLoops: bool, SLPVectorize: bool, LoopVectorize: bool, + DisableSimplifyLibCalls: bool, EmitLifetimeMarkers: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 70fda982b01..ace356ab153 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -270,14 +270,8 @@ pub fn each_linked_rlib( for &cnum in crates { match fmts.get(cnum.as_usize() - 1) { - Some(&Linkage::NotLinked | &Linkage::Dynamic) => continue, - Some(&Linkage::IncludedFromDylib) => { - // We always link crate `compiler_builtins` statically. When enabling LTO, we include it as well. - if info.compiler_builtins != Some(cnum) { - continue; - } - } - Some(&Linkage::Static) => {} + Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue, + Some(_) => {} None => return Err(errors::LinkRlibError::MissingFormat), } let crate_name = info.crate_name[&cnum]; @@ -526,7 +520,8 @@ fn link_staticlib<'a>( &codegen_results.crate_info, Some(CrateType::Staticlib), &mut |cnum, path| { - let lto = are_upstream_rust_objects_already_included(sess); + let lto = are_upstream_rust_objects_already_included(sess) + && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter(); let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, lib)); @@ -1277,6 +1272,24 @@ fn link_sanitizer_runtime( } } +/// Returns a boolean indicating whether the specified crate should be ignored +/// during LTO. +/// +/// Crates ignored during LTO are not lumped together in the "massive object +/// file" that we create and are linked in their normal rlib states. See +/// comments below for what crates do not participate in LTO. +/// +/// It's unusual for a crate to not participate in LTO. Typically only +/// compiler-specific and unstable crates have a reason to not participate in +/// LTO. +pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { + // If our target enables builtin function lowering in LLVM then the + // crates providing these functions don't participate in LTO (e.g. + // no_builtins or compiler builtins crates). + !sess.target.no_builtins + && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) +} + /// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { fn infer_from( @@ -2742,6 +2755,10 @@ fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf { // symbols). We must continue to include the rest of the rlib, however, as // it may contain static native libraries which must be linked in. // +// (*) Crates marked with `#![no_builtins]` don't participate in LTO and +// their bytecode wasn't included. The object files in those libraries must +// still be passed to the linker. +// // Note, however, that if we're not doing LTO we can just pass the rlib // blindly to the linker (fast) because it's fine if it's not actually // included as we're at the end of the dependency chain. @@ -2767,7 +2784,9 @@ fn add_static_crate<'a>( cmd.link_rlib(&rlib_path); }; - if !are_upstream_rust_objects_already_included(sess) { + if !are_upstream_rust_objects_already_included(sess) + || ignored_for_lto(sess, &codegen_results.crate_info, cnum) + { link_upstream(cratepath); return; } @@ -2781,6 +2800,8 @@ fn add_static_crate<'a>( let canonical_name = name.replace('-', "_"); let upstream_rust_objects_already_included = are_upstream_rust_objects_already_included(sess); + let is_builtins = + sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum); let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( @@ -2797,8 +2818,9 @@ fn add_static_crate<'a>( // If we're performing LTO and this is a rust-generated object // file, then we don't need the object file as it's part of the - // LTO module. - if upstream_rust_objects_already_included && is_rust_object { + // LTO module. Note that `#![no_builtins]` is excluded from LTO, + // though, so we let that object file slide. + if upstream_rust_objects_already_included && is_rust_object && is_builtins { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 94841ab7b33..cae7c40c5ad 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -54,8 +54,8 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S // export level, however, as they're just implementation details. // Down below we'll hardwire all of the symbols to the `Rust` export // level instead. - let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE); - let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || is_compiler_builtins; + let special_runtime_crate = + tcx.is_panic_runtime(LOCAL_CRATE) || tcx.is_compiler_builtins(LOCAL_CRATE); let mut reachable_non_generics: DefIdMap<_> = tcx .reachable_set(()) @@ -105,14 +105,8 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S } }) .map(|def_id| { - let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id()); // We won't link right if this symbol is stripped during LTO. let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name; - // We have to preserve the symbols of the built-in functions during LTO. - let is_builtin_fn = is_compiler_builtins - && symbol_export_level(tcx, def_id.to_def_id()) - .is_below_threshold(SymbolExportLevel::C) - && codegen_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE); let used = name == "rust_eh_personality"; let export_level = if special_runtime_crate { @@ -120,6 +114,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S } else { symbol_export_level(tcx, def_id.to_def_id()) }; + let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id()); debug!( "EXPORTED SYMBOL (local): {} ({:?})", tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())), @@ -139,7 +134,6 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) || used, - used_compiler: is_builtin_fn, }; (def_id.to_def_id(), info) }) @@ -152,7 +146,6 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: false, - used_compiler: false, }, ); } @@ -201,7 +194,6 @@ fn exported_symbols_provider_local( level: info.level, kind: SymbolExportKind::Text, used: info.used, - used_compiler: false, }, ) }) @@ -218,7 +210,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::C, kind: SymbolExportKind::Text, used: false, - used_compiler: false, }, )); } @@ -238,7 +229,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, - used_compiler: false, }, )); } @@ -251,7 +241,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Data, used: false, - used_compiler: false, }, )) } @@ -271,7 +260,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: false, - used_compiler: false, }, ) })); @@ -297,7 +285,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: false, - used_compiler: false, }, ) })); @@ -315,7 +302,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::C, kind: SymbolExportKind::Data, used: true, - used_compiler: false, }, )); } @@ -356,7 +342,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, - used_compiler: false, }, )); } @@ -373,7 +358,6 @@ fn exported_symbols_provider_local( level: SymbolExportLevel::Rust, kind: SymbolExportKind::Text, used: false, - used_compiler: false, }, )); } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 8e835039970..af1c6594446 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -148,12 +148,23 @@ impl ModuleConfig { let emit_obj = if !should_emit_obj { EmitObj::None - } else if sess.target.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled() { + } else if sess.target.obj_is_bitcode + || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) + { // This case is selected if the target uses objects as bitcode, or // if linker plugin LTO is enabled. In the linker plugin LTO case // the assumption is that the final link-step will read the bitcode // and convert it to object code. This may be done by either the // native linker or rustc itself. + // + // Note, however, that the linker-plugin-lto requested here is + // explicitly ignored for `#![no_builtins]` crates. These crates are + // specifically ignored by rustc's LTO passes and wouldn't work if + // loaded into the linker. These crates define symbols that LLVM + // lowers intrinsics to, and these symbol dependencies aren't known + // until after codegen. As a result any crate marked + // `#![no_builtins]` is assumed to not participate in LTO and + // instead goes on to generate object code. EmitObj::Bitcode } else if need_bitcode_in_object(tcx) { EmitObj::ObjectCode(BitcodeSection::Full) @@ -1023,6 +1034,9 @@ fn start_executing_work<B: ExtraBackendMethods>( let mut each_linked_rlib_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { + if link::ignored_for_lto(sess, crate_info, cnum) { + return; + } each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 9d1729c4b54..0ad4af968db 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -859,6 +859,7 @@ impl CrateInfo { local_crate_name, compiler_builtins, profiler_runtime: None, + is_no_builtins: Default::default(), native_libraries: Default::default(), used_libraries: tcx.native_libraries(LOCAL_CRATE).iter().map(Into::into).collect(), crate_name: Default::default(), @@ -885,6 +886,9 @@ impl CrateInfo { if tcx.is_profiler_runtime(cnum) { info.profiler_runtime = Some(cnum); } + if tcx.is_no_builtins(cnum) { + info.is_no_builtins.insert(cnum); + } } // Handle circular dependencies in the standard library. @@ -892,7 +896,9 @@ impl CrateInfo { // If global LTO is enabled then almost everything (*) is glued into a single object file, // so this logic is not necessary and can cause issues on some targets (due to weak lang // item symbols being "privatized" to that object file), so we disable it. - // (*) Native libs are not glued, and we assume that they cannot define weak lang items. + // (*) Native libs, and `#[compiler_builtins]` and `#[no_builtins]` crates are not glued, + // and we assume that they cannot define weak lang items. This is not currently enforced + // by the compiler, but that's ok because all this stuff is unstable anyway. let target = &tcx.sess.target; if !are_upstream_rust_objects_already_included(tcx.sess) { let missing_weak_lang_items: FxHashSet<Symbol> = info diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 0d88df63280..99280fdba7c 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -25,7 +25,7 @@ extern crate tracing; extern crate rustc_middle; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::CrateNum; use rustc_middle::dep_graph::WorkProduct; @@ -158,6 +158,7 @@ pub struct CrateInfo { pub local_crate_name: Symbol, pub compiler_builtins: Option<CrateNum>, pub profiler_runtime: Option<CrateNum>, + pub is_no_builtins: FxHashSet<CrateNum>, pub native_libraries: FxHashMap<CrateNum, Vec<NativeLib>>, pub crate_name: FxHashMap<CrateNum, Symbol>, pub used_libraries: Vec<NativeLib>, diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 738c532964a..ae9595d7e64 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -1,6 +1,6 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. -use rustc_errors::{Diagnostic, ErrorGuaranteed}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; @@ -214,7 +214,7 @@ pub struct Checker<'mir, 'tcx> { local_has_storage_dead: Option<BitSet<Local>>, error_emitted: Option<ErrorGuaranteed>, - secondary_errors: Vec<Diagnostic>, + secondary_errors: Vec<DiagnosticBuilder<'tcx>>, } impl<'mir, 'tcx> Deref for Checker<'mir, 'tcx> { @@ -272,14 +272,17 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { } // If we got through const-checking without emitting any "primary" errors, emit any - // "secondary" errors if they occurred. + // "secondary" errors if they occurred. Otherwise, cancel the "secondary" errors. let secondary_errors = mem::take(&mut self.secondary_errors); if self.error_emitted.is_none() { for error in secondary_errors { - self.tcx.dcx().emit_diagnostic(error); + error.emit(); } } else { assert!(self.tcx.dcx().has_errors().is_some()); + for error in secondary_errors { + error.cancel(); + } } } @@ -347,7 +350,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { self.error_emitted = Some(reported); } - ops::DiagnosticImportance::Secondary => err.buffer(&mut self.secondary_errors), + ops::DiagnosticImportance::Secondary => self.secondary_errors.push(err), } } diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index fc3ff835a81..0c1fcecb571 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -annotate-snippets = "0.9" +annotate-snippets = "0.10" derive_setters = "0.1.6" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 97f2efa7874..648c9118400 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -12,8 +12,7 @@ use crate::{ CodeSuggestion, Diagnostic, DiagnosticId, DiagnosticMessage, Emitter, FluentBundle, LazyFallbackBundle, Level, MultiSpan, Style, SubDiagnostic, }; -use annotate_snippets::display_list::{DisplayList, FormatOptions}; -use annotate_snippets::snippet::*; +use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use rustc_span::source_map::SourceMap; @@ -86,7 +85,7 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String { /// Maps `Diagnostic::Level` to `snippet::AnnotationType` fn annotation_type_for_level(level: Level) -> AnnotationType { match level { - Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error => AnnotationType::Error, + Level::Bug | Level::DelayedBug(_) | Level::Fatal | Level::Error => AnnotationType::Error, Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning, Level::Note | Level::OnceNote => AnnotationType::Note, Level::Help | Level::OnceHelp => AnnotationType::Help, @@ -190,11 +189,6 @@ impl AnnotateSnippetEmitter { annotation_type: annotation_type_for_level(*level), }), footer: vec![], - opt: FormatOptions { - color: true, - anonymized_line_numbers: self.ui_testing, - margin: None, - }, slices: annotated_files .iter() .map(|(file_name, source, line_index, annotations)| { @@ -222,7 +216,8 @@ impl AnnotateSnippetEmitter { // FIXME(#59346): Figure out if we can _always_ print to stderr or not. // `emitter.rs` has the `Destination` enum that lists various possible output // destinations. - eprintln!("{}", DisplayList::from(snippet)) + let renderer = Renderer::plain().anonymized_line_numbers(self.ui_testing); + eprintln!("{}", renderer.render(snippet)) } // FIXME(#59346): Is it ok to return None if there's no source_map? } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index d8d6922a1bc..786aced5b4f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,7 +1,7 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan, - SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, + CodeSuggestion, DelayedBugKind, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, + MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; @@ -243,12 +243,15 @@ impl Diagnostic { pub fn is_error(&self) -> bool { match self.level { - Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error | Level::FailureNote => { - true - } + Level::Bug + | Level::DelayedBug(DelayedBugKind::Normal) + | Level::Fatal + | Level::Error + | Level::FailureNote => true, Level::ForceWarning(_) | Level::Warning + | Level::DelayedBug(DelayedBugKind::GoodPath) | Level::Note | Level::OnceNote | Level::Help @@ -318,7 +321,7 @@ impl Diagnostic { "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error", self.level ); - self.level = Level::DelayedBug; + self.level = Level::DelayedBug(DelayedBugKind::Normal); } /// Appends a labeled span to the diagnostic. diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index a02909f29c4..bd7c58d904e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -255,35 +255,13 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Stashes diagnostic for possible later improvement in a different, /// later stage of the compiler. The diagnostic can be accessed with /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. - /// - /// As with `buffer`, this is unless the dcx has disabled such buffering. pub fn stash(self, span: Span, key: StashKey) { - if let Some((diag, dcx)) = self.into_diagnostic() { - dcx.stash_diagnostic(span, key, diag); - } - } - - /// Converts the builder to a `Diagnostic` for later emission, - /// unless dcx has disabled such buffering. - fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> { - if self.dcx.inner.lock().flags.treat_err_as_bug.is_some() { - self.emit(); - return None; - } - - let diag = self.take_diag(); - - // Logging here is useful to help track down where in logs an error was - // actually emitted. - debug!("buffer: diag={:?}", diag); - - Some((diag, self.dcx)) + self.dcx.stash_diagnostic(span, key, self.into_diagnostic()); } - /// Buffers the diagnostic for later emission, - /// unless dcx has disabled such buffering. - pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) { - buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag)); + /// Converts the builder to a `Diagnostic` for later emission. + pub fn into_diagnostic(mut self) -> Diagnostic { + self.take_diag() } /// Delay emission of this diagnostic as a bug. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8fb539fc358..8c2752af659 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -519,6 +519,12 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> = AtomicRef::new(&(default_track_diagnostic as _)); +#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)] +pub enum DelayedBugKind { + Normal, + GoodPath, +} + #[derive(Copy, Clone, Default)] pub struct DiagCtxtFlags { /// If false, warning-level lints are suppressed. @@ -527,6 +533,9 @@ pub struct DiagCtxtFlags { /// If Some, the Nth error-level diagnostic is upgraded to bug-level. /// (rustc: see `-Z treat-err-as-bug`) pub treat_err_as_bug: Option<NonZeroUsize>, + /// Eagerly emit delayed bugs as errors, so that the compiler debugger may + /// see all of the errors being emitted at once. + pub eagerly_emit_delayed_bugs: bool, /// Show macro backtraces. /// (rustc: see `-Z macro-backtrace`) pub macro_backtrace: bool, @@ -541,8 +550,7 @@ impl Drop for DiagCtxtInner { self.emit_stashed_diagnostics(); if !self.has_errors() { - let bugs = std::mem::replace(&mut self.span_delayed_bugs, Vec::new()); - self.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued"); + self.flush_delayed(DelayedBugKind::Normal) } // FIXME(eddyb) this explains what `good_path_delayed_bugs` are! @@ -551,11 +559,7 @@ impl Drop for DiagCtxtInner { // lints can be `#[allow]`'d, potentially leading to this triggering. // Also, "good path" should be replaced with a better naming. if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { - let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new()); - self.flush_delayed( - bugs, - "no warnings or errors encountered even though `good_path_delayed_bugs` issued", - ); + self.flush_delayed(DelayedBugKind::GoodPath); } if self.check_unstable_expect_diagnostics { @@ -865,7 +869,8 @@ impl DiagCtxt { if treat_next_err_as_bug { self.bug(msg); } - DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit() + DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg) + .emit() } /// Like `delayed_bug`, but takes an additional span. @@ -882,16 +887,15 @@ impl DiagCtxt { if treat_next_err_as_bug { self.span_bug(sp, msg); } - DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit() + DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug(DelayedBugKind::Normal), msg) + .with_span(sp) + .emit() } // FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's // where the explanation of what "good path" is (also, it should be renamed). pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) { - let mut inner = self.inner.borrow_mut(); - let diagnostic = Diagnostic::new(DelayedBug, msg); - let backtrace = std::backtrace::Backtrace::capture(); - inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); + DiagnosticBuilder::<()>::new(self, DelayedBug(DelayedBugKind::GoodPath), msg).emit() } #[track_caller] @@ -981,6 +985,10 @@ impl DiagCtxt { inner.emit_stashed_diagnostics(); + if inner.treat_err_as_bug() { + return; + } + let warnings = match inner.deduplicated_warn_count { 0 => Cow::from(""), 1 => Cow::from("1 warning emitted"), @@ -991,9 +999,6 @@ impl DiagCtxt { 1 => Cow::from("aborting due to 1 previous error"), count => Cow::from(format!("aborting due to {count} previous errors")), }; - if inner.treat_err_as_bug() { - return; - } match (errors.len(), warnings.len()) { (0, 0) => return, @@ -1168,7 +1173,8 @@ impl DiagCtxt { let mut inner = self.inner.borrow_mut(); if loud && lint_level.is_error() { - inner.bump_err_count(); + inner.err_count += 1; + inner.panic_if_treat_err_as_bug(); } inner.emitter.emit_unused_externs(lint_level, unused_externs) @@ -1216,9 +1222,7 @@ impl DiagCtxt { } pub fn flush_delayed(&self) { - let mut inner = self.inner.borrow_mut(); - let bugs = std::mem::replace(&mut inner.span_delayed_bugs, Vec::new()); - inner.flush_delayed(bugs, "no errors encountered even though `span_delayed_bug` issued"); + self.inner.borrow_mut().flush_delayed(DelayedBugKind::Normal); } } @@ -1255,7 +1259,7 @@ impl DiagCtxtInner { } fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> { - if matches!(diagnostic.level, Error | Fatal) && self.treat_err_as_bug() { + if matches!(diagnostic.level, Error | Fatal) && self.treat_next_err_as_bug() { diagnostic.level = Bug; } @@ -1268,17 +1272,30 @@ impl DiagCtxtInner { return None; } - if diagnostic.level == DelayedBug { - // FIXME(eddyb) this should check for `has_errors` and stop pushing - // once *any* errors were emitted (and truncate `span_delayed_bugs` - // when an error is first emitted, also), but maybe there's a case - // in which that's not sound? otherwise this is really inefficient. - let backtrace = std::backtrace::Backtrace::capture(); - self.span_delayed_bugs - .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); + // FIXME(eddyb) this should check for `has_errors` and stop pushing + // once *any* errors were emitted (and truncate `span_delayed_bugs` + // when an error is first emitted, also), but maybe there's a case + // in which that's not sound? otherwise this is really inefficient. + match diagnostic.level { + DelayedBug(_) if self.flags.eagerly_emit_delayed_bugs => { + diagnostic.level = Error; + } + DelayedBug(DelayedBugKind::Normal) => { + let backtrace = std::backtrace::Backtrace::capture(); + self.span_delayed_bugs + .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); - #[allow(deprecated)] - return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + #[allow(deprecated)] + return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); + } + DelayedBug(DelayedBugKind::GoodPath) => { + let backtrace = std::backtrace::Backtrace::capture(); + self.good_path_delayed_bugs + .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); + + return None; + } + _ => {} } if diagnostic.has_future_breakage() { @@ -1353,10 +1370,11 @@ impl DiagCtxtInner { } if diagnostic.is_error() { if diagnostic.is_lint { - self.bump_lint_err_count(); + self.lint_err_count += 1; } else { - self.bump_err_count(); + self.err_count += 1; } + self.panic_if_treat_err_as_bug(); #[allow(deprecated)] { @@ -1393,11 +1411,18 @@ impl DiagCtxtInner { self.emit_diagnostic(Diagnostic::new(FailureNote, msg)); } - fn flush_delayed( - &mut self, - bugs: Vec<DelayedDiagnostic>, - explanation: impl Into<DiagnosticMessage> + Copy, - ) { + fn flush_delayed(&mut self, kind: DelayedBugKind) { + let (bugs, explanation) = match kind { + DelayedBugKind::Normal => ( + std::mem::take(&mut self.span_delayed_bugs), + "no errors encountered even though `span_delayed_bug` issued", + ), + DelayedBugKind::GoodPath => ( + std::mem::take(&mut self.good_path_delayed_bugs), + "no warnings or errors encountered even though `good_path_delayed_bugs` issued", + ), + }; + if bugs.is_empty() { return; } @@ -1430,7 +1455,7 @@ impl DiagCtxtInner { if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; // "Undelay" the `DelayedBug`s (into plain `Bug`s). - if bug.level != DelayedBug { + if !matches!(bug.level, DelayedBug(_)) { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel { @@ -1447,16 +1472,6 @@ impl DiagCtxtInner { panic::panic_any(DelayedBugPanic); } - fn bump_lint_err_count(&mut self) { - self.lint_err_count += 1; - self.panic_if_treat_err_as_bug(); - } - - fn bump_err_count(&mut self) { - self.err_count += 1; - self.panic_if_treat_err_as_bug(); - } - fn panic_if_treat_err_as_bug(&self) { if self.treat_err_as_bug() { match ( @@ -1528,8 +1543,9 @@ pub enum Level { /// silently dropped. I.e. "expect other errors are emitted" semantics. Useful on code paths /// that should only be reached when compiling erroneous code. /// - /// Its `EmissionGuarantee` is `ErrorGuaranteed`. - DelayedBug, + /// Its `EmissionGuarantee` is `ErrorGuaranteed` for `Normal` delayed bugs, and `()` for + /// `GoodPath` delayed bugs. + DelayedBug(DelayedBugKind), /// An error that causes an immediate abort. Used for things like configuration errors, /// internal overflows, some file operation errors. @@ -1604,7 +1620,7 @@ impl Level { fn color(self) -> ColorSpec { let mut spec = ColorSpec::new(); match self { - Bug | DelayedBug | Fatal | Error => { + Bug | DelayedBug(_) | Fatal | Error => { spec.set_fg(Some(Color::Red)).set_intense(true); } ForceWarning(_) | Warning => { @@ -1624,7 +1640,7 @@ impl Level { pub fn to_str(self) -> &'static str { match self { - Bug | DelayedBug => "error: internal compiler error", + Bug | DelayedBug(_) => "error: internal compiler error", Fatal | Error => "error", ForceWarning(_) | Warning => "warning", Note | OnceNote => "note", diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs index a0d956bf0cd..bab47dcc175 100644 --- a/compiler/rustc_errors/src/markdown/tests/term.rs +++ b/compiler/rustc_errors/src/markdown/tests/term.rs @@ -5,7 +5,8 @@ use termcolor::{BufferWriter, ColorChoice}; use super::*; const INPUT: &str = include_str!("input.md"); -const OUTPUT_PATH: &[&str] = &[env!("CARGO_MANIFEST_DIR"), "src","markdown","tests","output.stdout"]; +const OUTPUT_PATH: &[&str] = + &[env!("CARGO_MANIFEST_DIR"), "src", "markdown", "tests", "output.stdout"]; const TEST_WIDTH: usize = 80; @@ -34,7 +35,7 @@ quis dolor non venenatis. Aliquam ut. "; fn test_wrapping_write() { WIDTH.with(|w| w.set(TEST_WIDTH)); let mut buf = BufWriter::new(Vec::new()); - let txt = TXT.replace("-\n","-").replace("_\n","_").replace('\n', " ").replace(" ", ""); + let txt = TXT.replace("-\n", "-").replace("_\n", "_").replace('\n', " ").replace(" ", ""); write_wrapping(&mut buf, &txt, 0, None).unwrap(); write_wrapping(&mut buf, &txt, 4, None).unwrap(); write_wrapping( diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index aa0db9891a5..6e3996b4509 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -77,7 +77,7 @@ declare_features! ( /// Allows empty structs and enum variants with braces. (accepted, braced_empty_structs, "1.8.0", Some(29720)), /// Allows `c"foo"` literals. - (accepted, c_str_literals, "1.76.0", Some(105723)), + (accepted, c_str_literals, "CURRENT_RUSTC_VERSION", Some(105723)), /// Allows `#[cfg_attr(predicate, multiple, attributes, here)]`. (accepted, cfg_attr_multi, "1.33.0", Some(54881)), /// Allows the use of `#[cfg(doctest)]`, set when rustdoc is collecting doctests. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 59ea828440f..2f2b551e6ec 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -450,7 +450,7 @@ declare_features! ( (unstable, doc_masked, "1.21.0", Some(44027)), /// Allows `dyn* Trait` objects. (incomplete, dyn_star, "1.65.0", Some(102425)), - // Uses generic effect parameters for ~const bounds + /// Uses generic effect parameters for ~const bounds (unstable, effects, "1.72.0", Some(102090)), /// Allows `X..Y` patterns. (unstable, exclusive_range_pattern, "1.11.0", Some(37854)), diff --git a/compiler/rustc_fluent_macro/Cargo.toml b/compiler/rustc_fluent_macro/Cargo.toml index 872dd29a7a8..c5a53ae8313 100644 --- a/compiler/rustc_fluent_macro/Cargo.toml +++ b/compiler/rustc_fluent_macro/Cargo.toml @@ -8,7 +8,7 @@ proc-macro = true [dependencies] # tidy-alphabetical-start -annotate-snippets = "0.9" +annotate-snippets = "0.10" fluent-bundle = "0.15.2" fluent-syntax = "0.11" proc-macro2 = "1" diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index 3b1b63455ed..520a64aaf5e 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -1,7 +1,4 @@ -use annotate_snippets::{ - display_list::DisplayList, - snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, -}; +use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; use fluent_bundle::{FluentBundle, FluentError, FluentResource}; use fluent_syntax::{ ast::{ @@ -179,10 +176,9 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok range: (pos.start, pos.end - 1), }], }], - opt: Default::default(), }; - let dl = DisplayList::from(snippet); - eprintln!("{dl}\n"); + let renderer = Renderer::plain(); + eprintln!("{}\n", renderer.render(snippet)); } return failed(&crate_name); diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 1f88aaa6a4b..2ad96a24891 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -300,13 +300,15 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .expect("missing associated item"); if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) { - tcx.dcx() + let reported = tcx + .dcx() .struct_span_err( binding.span, format!("{} `{}` is private", assoc_item.kind, binding.item_name), ) .with_span_label(binding.span, format!("private {}", assoc_item.kind)) .emit(); + self.set_tainted_by_errors(reported); } tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None); diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index fc2ed104b3d..7b23b74f829 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -354,7 +354,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); err.span_label(name.span, format!("multiple `{name}` found")); self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span); - err.emit() + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported } // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. @@ -843,7 +845,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - err.emit(); + self.set_tainted_by_errors(err.emit()); } } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 1f47564649e..2886ec21320 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -390,6 +390,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { infer_args, ); + if let Err(err) = &arg_count.correct + && let Some(reported) = err.reported + { + self.set_tainted_by_errors(reported); + } + // Skip processing if type has no generic parameters. // Traits always have `Self` as a generic parameter, which means they will not return early // here and so associated type bindings will be handled regardless of whether there are any @@ -568,6 +574,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, modifier: constness.as_str(), }); + self.set_tainted_by_errors(e); arg_count.correct = Err(GenericArgCountMismatch { reported: Some(e), invalid_args: vec![] }); } @@ -966,7 +973,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } - err.emit() + let reported = err.emit(); + self.set_tainted_by_errors(reported); + reported } // Search for a bound on a type parameter which includes the associated item @@ -1043,6 +1052,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { span, binding, ); + self.set_tainted_by_errors(reported); return Err(reported); }; debug!(?bound); @@ -1120,6 +1130,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )); } let reported = err.emit(); + self.set_tainted_by_errors(reported); if !where_bounds.is_empty() { return Err(reported); } @@ -1374,6 +1385,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assoc_ident.name, ) }; + self.set_tainted_by_errors(reported); return Err(reported); } }; @@ -1616,12 +1628,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let kind = tcx.def_kind_descr(kind, item); let msg = format!("{kind} `{name}` is private"); let def_span = tcx.def_span(item); - tcx.dcx() + let reported = tcx + .dcx() .struct_span_err(span, msg) .with_code(rustc_errors::error_code!(E0624)) .with_span_label(span, format!("private {kind}")) .with_span_label(def_span, format!("{kind} defined here")) .emit(); + self.set_tainted_by_errors(reported); } tcx.check_stability(item, Some(block), span, None); } @@ -1862,7 +1876,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.span_label(span, format!("not allowed on {what}")); } extend(&mut err); - err.emit(); + self.set_tainted_by_errors(err.emit()); emitted = true; } @@ -2184,7 +2198,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { { err.span_note(impl_.self_ty.span, "not a concrete type"); } - Ty::new_error(tcx, err.emit()) + let reported = err.emit(); + self.set_tainted_by_errors(reported); + Ty::new_error(tcx, reported) } else { ty } @@ -2586,7 +2602,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); } - diag.emit(); + self.set_tainted_by_errors(diag.emit()); } // Find any late-bound regions declared in return type that do @@ -2686,7 +2702,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.note("consider introducing a named lifetime parameter"); } - err.emit(); + self.set_tainted_by_errors(err.emit()); } } @@ -2725,7 +2741,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // error. let r = derived_region_bounds[0]; if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - tcx.dcx().emit_err(AmbiguousLifetimeBound { span }); + self.set_tainted_by_errors(tcx.dcx().emit_err(AmbiguousLifetimeBound { span })); } Some(r) } diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index ea2f5f50b5c..f77f250cd28 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -116,7 +116,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for more information on them, visit \ <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>", ); - err.emit(); + self.set_tainted_by_errors(err.emit()); } if regular_traits.is_empty() && auto_traits.is_empty() { @@ -127,6 +127,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map(|trait_ref| tcx.def_span(trait_ref)); let reported = tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }); + self.set_tainted_by_errors(reported); return Ty::new_error(tcx, reported); } @@ -290,7 +291,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if references_self { let def_id = i.bottom().0.def_id(); - struct_span_code_err!( + let reported = struct_span_code_err!( tcx.dcx(), i.bottom().1, E0038, @@ -303,6 +304,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .error_msg(), ) .emit(); + self.set_tainted_by_errors(reported); } ty::ExistentialTraitRef { def_id: trait_ref.def_id, args } @@ -389,6 +391,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } else { err.emit() }; + self.set_tainted_by_errors(e); ty::Region::new_error(tcx, e) }) } diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 5f599487912..556560945e9 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -74,7 +74,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { // we have some type like `&<Ty as Trait>::Assoc`, since users of // autoderef expect this type to have been structurally normalized. if self.infcx.next_trait_solver() - && let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = ty.kind() + && let ty::Alias(..) = ty.kind() { let (normalized_ty, obligations) = self.structurally_normalize(ty)?; self.state.obligations.extend(obligations); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index c9f89a0c3ef..e557f36037b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -35,6 +35,7 @@ use rustc_target::spec::abi; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; use rustc_trait_selection::traits::ObligationCtxt; +use std::cell::Cell; use std::iter; use std::ops::Bound; @@ -119,6 +120,7 @@ pub fn provide(providers: &mut Providers) { pub struct ItemCtxt<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, + tainted_by_errors: Cell<Option<ErrorGuaranteed>>, } /////////////////////////////////////////////////////////////////////////// @@ -343,7 +345,7 @@ fn bad_placeholder<'tcx>( impl<'tcx> ItemCtxt<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) -> ItemCtxt<'tcx> { - ItemCtxt { tcx, item_def_id } + ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) } } pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { @@ -357,6 +359,13 @@ impl<'tcx> ItemCtxt<'tcx> { pub fn node(&self) -> hir::Node<'tcx> { self.tcx.hir_node(self.hir_id()) } + + fn check_tainted_by_errors(&self) -> Result<(), ErrorGuaranteed> { + match self.tainted_by_errors.get() { + Some(err) => Err(err), + None => Ok(()), + } + } } impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { @@ -492,8 +501,8 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { ty.ty_adt_def() } - fn set_tainted_by_errors(&self, _: ErrorGuaranteed) { - // There's no obvious place to track this, so just let it go. + fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { + self.tainted_by_errors.set(Some(err)); } fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 3ceea3dc7ae..b936b0c0805 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -513,7 +513,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty bug!("unexpected sort of node in type_of(): {:?}", x); } }; - ty::EarlyBinder::bind(output) + if let Err(e) = icx.check_tainted_by_errors() { + ty::EarlyBinder::bind(Ty::new_error(tcx, e)) + } else { + ty::EarlyBinder::bind(output) + } } pub(super) fn type_of_opaque( diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 7edb5912dd5..b6b33299315 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -802,7 +802,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .explicit_item_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, - ty::Error(_) => return None, + ty::Error(_) => return Some(ret_ty), _ => span_bug!( closure_span, "async fn coroutine return type not an inference variable: {ret_ty}" diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index c56a028321a..3430a5fb00d 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -498,14 +498,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // order when emitting them. let err = self.tcx().dcx().struct_span_err(span, format!("user args: {user_args:?}")); - err.buffer(&mut errors_buffer); + errors_buffer.push(err); } } if !errors_buffer.is_empty() { errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for diag in errors_buffer { - self.tcx().dcx().emit_diagnostic(diag); + for err in errors_buffer { + err.emit(); } } } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 6c000380e71..03335996c03 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -82,7 +82,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg { Ok(..) => {} Err(err) => err.cancel(), }, - Err(errs) => drop(errs), + Err(errs) => errs.into_iter().for_each(|err| err.cancel()), } // If the user tried to use a key="value" flag, but is missing the quotes, provide @@ -129,9 +129,12 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg { error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`") }; - let Ok(mut parser) = maybe_new_parser_from_source_str(&sess, filename, s.to_string()) - else { - expected_error(); + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) { + Ok(parser) => parser, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + expected_error(); + } }; let meta_item = match parser.parse_meta_item() { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 40e6b1b579f..b6fa2f1f221 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -532,8 +532,14 @@ lint_unknown_gated_lint = lint_unknown_lint = unknown lint: `{$name}` - .suggestion = did you mean - .help = did you mean: `{$replace}` + .suggestion = {$from_rustc -> + [true] a lint with a similar name exists in `rustc` lints + *[false] did you mean + } + .help = {$from_rustc -> + [true] a lint with a similar name exists in `rustc` lints: `{$replace}` + *[false] did you mean: `{$replace}` + } lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d0fd019a8b1..ffd8f1b3c79 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -33,7 +33,7 @@ use rustc_middle::ty::{self, print::Printer, GenericArg, RegisteredTools, Ty, Ty use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::{LintStoreMarker, Session}; -use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi; @@ -117,7 +117,7 @@ struct LintGroup { pub enum CheckLintNameResult<'a> { Ok(&'a [LintId]), /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. - NoLint(Option<Symbol>), + NoLint(Option<(Symbol, bool)>), /// The lint refers to a tool that has not been registered. NoTool, /// The lint has been renamed to a new name. @@ -377,7 +377,7 @@ impl LintStore { debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>()); let tool_prefix = format!("{tool_name}::"); return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { - self.no_lint_suggestion(&complete_name) + self.no_lint_suggestion(&complete_name, tool_name.as_str()) } else { // 2. The tool isn't currently running, so no lints will be registered. // To avoid giving a false positive, ignore all unknown lints. @@ -419,13 +419,14 @@ impl LintStore { } } - fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> { + fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> { let name_lower = lint_name.to_lowercase(); if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() { // First check if the lint name is (partly) in upper case instead of lower case... - return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower))); + return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false))); } + // ...if not, search for lints with a similar name // Note: find_best_match_for_name depends on the sort order of its input vector. // To ensure deterministic output, sort elements of the lint_groups hash map. @@ -441,7 +442,16 @@ impl LintStore { let groups = groups.iter().map(|k| Symbol::intern(k)); let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower())); let names: Vec<Symbol> = groups.chain(lints).collect(); - let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None); + let mut lookups = vec![Symbol::intern(&name_lower)]; + if let Some(stripped) = name_lower.split("::").last() { + lookups.push(Symbol::intern(stripped)); + } + let res = find_best_match_for_names(&names, &lookups, None); + let is_rustc = res.map_or_else( + || false, + |s| name_lower.contains("::") && !s.as_str().starts_with(tool_name), + ); + let suggestion = res.map(|s| (s, is_rustc)); CheckLintNameResult::NoLint(suggestion) } @@ -454,7 +464,7 @@ impl LintStore { match self.by_name.get(&complete_name) { None => match self.lint_groups.get(&*complete_name) { // Now we are sure, that this lint exists nowhere - None => self.no_lint_suggestion(lint_name), + None => self.no_lint_suggestion(lint_name, tool_name), Some(LintGroup { lint_ids, depr, .. }) => { // Reaching this would be weird, but let's cover this case anyway if let Some(LintAlias { name, silent }) = depr { diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 75756c6946a..9e6a6f70eac 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -356,6 +356,12 @@ pub(super) fn builtin( } } + // We don't want to suggest adding values to well known names + // since those are defined by rustc it-self. Users can still + // do it if they want, but should not encourage them. + let is_cfg_a_well_know_name = + sess.parse_sess.check_config.well_known_names.contains(&name); + let inst = if let Some((value, _value_span)) = value { let pre = if is_from_cargo { "\\" } else { "" }; format!("cfg({name}, values({pre}\"{value}{pre}\"))") @@ -368,12 +374,14 @@ pub(super) fn builtin( if let Some((value, _value_span)) = value { db.help(format!("consider adding `{value}` as a feature in `Cargo.toml`")); } - } else { + } else if !is_cfg_a_well_know_name { db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`")); } db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration"); } else { - db.help(format!("to expect this configuration use `--check-cfg={inst}`")); + if !is_cfg_a_well_know_name { + db.help(format!("to expect this configuration use `--check-cfg={inst}`")); + } db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration"); } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 49821437b76..3c2d0c7b205 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -582,8 +582,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } CheckLintNameResult::NoLint(suggestion) => { let name = lint_name.clone(); - let suggestion = - suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace }); + let suggestion = suggestion.map(|(replace, from_rustc)| { + UnknownLintSuggestion::WithoutSpan { replace, from_rustc } + }); let requested_level = RequestedLevel { level, lint_name }; let lint = UnknownLintFromCommandLine { name, suggestion, requested_level }; self.emit_lint(UNKNOWN_LINTS, lint); @@ -990,8 +991,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } else { name.to_string() }; - let suggestion = suggestion.map(|replace| { - UnknownLintSuggestion::WithSpan { suggestion: sp, replace } + let suggestion = suggestion.map(|(replace, from_rustc)| { + UnknownLintSuggestion::WithSpan { suggestion: sp, replace, from_rustc } }); let lint = UnknownLint { name, suggestion }; self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index bc9a9d7b745..f370c4392b3 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1050,9 +1050,10 @@ pub enum UnknownLintSuggestion { #[primary_span] suggestion: Span, replace: Symbol, + from_rustc: bool, }, #[help(lint_help)] - WithoutSpan { replace: Symbol }, + WithoutSpan { replace: Symbol, from_rustc: bool }, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 0386f2ec56c..0f4528d1d5c 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1071,17 +1071,31 @@ impl UnusedParens { self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false); } } + + fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> { + if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind + && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) + { + let mut cur = lhs; + while let ExprKind::Binary(_, _, rhs) = &cur.kind { + cur = rhs; + } + + if let ExprKind::Cast(_, ty) = &cur.kind + && let ast::TyKind::Paren(_) = &ty.kind + { + return Some(ty.id); + } + } + None + } } impl EarlyLintPass for UnusedParens { #[inline] fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ExprKind::Binary(op, lhs, _rhs) = &e.kind - && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) - && let ExprKind::Cast(_expr, ty) = &lhs.kind - && let ast::TyKind::Paren(_) = &ty.kind - { - self.parens_in_cast_in_lt.push(ty.id); + if let Some(ty_id) = self.cast_followed_by_lt(e) { + self.parens_in_cast_in_lt.push(ty_id); } match e.kind { @@ -1133,17 +1147,13 @@ impl EarlyLintPass for UnusedParens { } fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ExprKind::Binary(op, lhs, _rhs) = &e.kind - && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) - && let ExprKind::Cast(_expr, ty) = &lhs.kind - && let ast::TyKind::Paren(_) = &ty.kind - { + if let Some(ty_id) = self.cast_followed_by_lt(e) { let id = self .parens_in_cast_in_lt .pop() .expect("check_expr and check_expr_post must balance"); assert_eq!( - id, ty.id, + id, ty_id, "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor" ); } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 76eb6bfaef7..6114f7c8678 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -531,9 +531,12 @@ extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { // Unfortunately, the LLVM C API doesn't provide a way to create the // TargetLibraryInfo pass, so we use this method to do so. -extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M) { +extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, + bool DisableSimplifyLibCalls) { Triple TargetTriple(unwrap(M)->getTargetTriple()); TargetLibraryInfoImpl TLII(TargetTriple); + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); } @@ -700,7 +703,7 @@ LLVMRustOptimize( bool IsLinkerPluginLTO, bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, - bool EmitLifetimeMarkers, + bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, @@ -800,6 +803,8 @@ LLVMRustOptimize( Triple TargetTriple(TheModule->getTargetTriple()); std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple)); + if (DisableSimplifyLibCalls) + TLII->disableAllFunctions(); FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); PB.registerModuleAnalyses(MAM); diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 9ca7ce09ba6..98b53945b91 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -76,8 +76,17 @@ fn decodable_body( ty_name, variants.len() ); + let tag = if variants.len() < u8::MAX as usize { + quote! { + ::rustc_serialize::Decoder::read_u8(__decoder) as usize + } + } else { + quote! { + ::rustc_serialize::Decoder::read_usize(__decoder) + } + }; quote! { - match ::rustc_serialize::Decoder::read_usize(__decoder) { + match #tag { #match_inner n => panic!(#message, n), } @@ -206,11 +215,20 @@ fn encodable_body( variant_idx += 1; result }); - quote! { - let disc = match *self { - #encode_inner - }; - ::rustc_serialize::Encoder::emit_usize(__encoder, disc); + if variant_idx < u8::MAX as usize { + quote! { + let disc = match *self { + #encode_inner + }; + ::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8); + } + } else { + quote! { + let disc = match *self { + #encode_inner + }; + ::rustc_serialize::Encoder::emit_usize(__encoder, disc); + } } }; diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 0ab09dadf58..8a4fd01437f 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -113,6 +113,7 @@ macro_rules! arena_types { [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, [] features: rustc_feature::Features, + [decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph, ]); ) } diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 59ce0a14b2a..e30b6b203d7 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -35,12 +35,7 @@ pub enum SymbolExportKind { pub struct SymbolExportInfo { pub level: SymbolExportLevel, pub kind: SymbolExportKind, - /// Used to mark these symbols not to be internalized by LTO. These symbols - /// are also added to `symbols.o` to avoid circular dependencies when linking. pub used: bool, - /// Also used to mark these symbols not to be internalized by LTO. But will - /// not be added to `symbols.o`. Currently there are only builtin functions. - pub used_compiler: bool, } #[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0e3b9984423..1dc77220881 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1294,8 +1294,7 @@ rustc_queries! { desc { |tcx| "finding trait impls of `{}`", tcx.def_path_str(trait_id) } } - query specialization_graph_of(trait_id: DefId) -> &'tcx specialization_graph::Graph { - arena_cache + query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 31db4ba62fb..f4dfbe059eb 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -786,6 +786,15 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [rustc_ast::InlineAsm } } +impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> + for &'tcx crate::traits::specialization_graph::Graph +{ + #[inline] + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + macro_rules! impl_ref_decoder { (<$tcx:tt> $($ty:ty,)*) => { $(impl<'a, $tcx> Decodable<CacheDecoder<'a, $tcx>> for &$tcx [$ty] { diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 77d112d0afc..e94ad4aa539 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -73,6 +73,7 @@ pub struct CanonicalGoalEvaluation<'tcx> { pub enum CanonicalGoalEvaluationKind<'tcx> { Overflow, CycleInStack, + ProvisionalCacheHit, Evaluation { revisions: &'tcx [GoalEvaluationStep<'tcx>] }, } impl Debug for GoalEvaluation<'_> { diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index 4e2207ed523..54db8dbd336 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -77,6 +77,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { CanonicalGoalEvaluationKind::CycleInStack => { writeln!(self.f, "CYCLE IN STACK: {:?}", eval.result) } + CanonicalGoalEvaluationKind::ProvisionalCacheHit => { + writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result) + } CanonicalGoalEvaluationKind::Evaluation { revisions } => { for (n, step) in revisions.iter().enumerate() { writeln!(self.f, "REVISION {n}")?; diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 898258445dc..ba29d4040a1 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -30,18 +30,16 @@ pub struct Graph { /// The "root" impls are found by looking up the trait's def_id. pub children: DefIdMap<Children>, - - /// Whether an error was emitted while constructing the graph. - pub has_errored: Option<ErrorGuaranteed>, } impl Graph { pub fn new() -> Graph { - Graph { parent: Default::default(), children: Default::default(), has_errored: None } + Graph { parent: Default::default(), children: Default::default() } } /// The parent of a given impl, which is the `DefId` of the trait when the /// impl is a "specialization root". + #[track_caller] pub fn parent(&self, child: DefId) -> DefId { *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {child:?}")) } @@ -255,13 +253,9 @@ pub fn ancestors( trait_def_id: DefId, start_from_impl: DefId, ) -> Result<Ancestors<'_>, ErrorGuaranteed> { - let specialization_graph = tcx.specialization_graph_of(trait_def_id); + let specialization_graph = tcx.specialization_graph_of(trait_def_id)?; - if let Some(reported) = specialization_graph.has_errored { - Err(reported) - } else if let Err(reported) = - tcx.type_of(start_from_impl).instantiate_identity().error_reported() - { + if let Err(reported) = tcx.type_of(start_from_impl).instantiate_identity().error_reported() { Err(reported) } else { Ok(Ancestors { diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index 6ed68f90eb3..b71919adc58 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -32,6 +32,7 @@ pub enum SimplifiedType { CoroutineWitness(DefId), Function(usize), Placeholder, + Error, } /// Generic parameters are pretty much just bound variables, e.g. @@ -153,7 +154,8 @@ pub fn simplify_type<'tcx>( TreatParams::ForLookup | TreatParams::AsCandidateKey => None, }, ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), - ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, + ty::Error(_) => Some(SimplifiedType::Error), + ty::Bound(..) | ty::Infer(_) => None, } } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index bf9b244936f..227a0753d04 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,6 +1,5 @@ use crate::traits::specialization_graph; use crate::ty::fast_reject::{self, SimplifiedType, TreatParams, TreatProjections}; -use crate::ty::visit::TypeVisitableExt; use crate::ty::{Ident, Ty, TyCtxt}; use hir::def_id::LOCAL_CRATE; use rustc_hir as hir; @@ -241,9 +240,6 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait let impl_def_id = impl_def_id.to_def_id(); let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); - if impl_self_ty.references_error() { - continue; - } if let Some(simplified_self_ty) = fast_reject::simplify_type(tcx, impl_self_ty, TreatParams::AsCandidateKey) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 43c5b3873b9..ce9043ec287 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -566,17 +566,28 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { body, &[ &check_alignment::CheckAlignment, - &lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first + // Before inlining: trim down MIR with passes to reduce inlining work. + + // Has to be done before inlining, otherwise actual call will be almost always inlined. + // Also simple, so can just do first + &lower_slice_len::LowerSliceLenCalls, + // Perform inlining, which may add a lot of code. &inline::Inline, - // Substitutions during inlining may introduce switch on enums with uninhabited branches. + // Code from other crates may have storage markers, so this needs to happen after inlining. + &remove_storage_markers::RemoveStorageMarkers, + // Inlining and substitution may introduce ZST and useless drops. + &remove_zsts::RemoveZsts, + &remove_unneeded_drops::RemoveUnneededDrops, + // Type substitution may create uninhabited enums. &uninhabited_enum_branching::UninhabitedEnumBranching, &unreachable_prop::UnreachablePropagation, &o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching), - &remove_storage_markers::RemoveStorageMarkers, - &remove_zsts::RemoveZsts, - &normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering + // Inlining may have introduced a lot of redundant code and a large move pattern. + // Now, we need to shrink the generated MIR. + + // Has to run after `slice::len` lowering + &normalize_array_len::NormalizeArrayLen, &const_goto::ConstGoto, - &remove_unneeded_drops::RemoveUnneededDrops, &ref_prop::ReferencePropagation, &sroa::ScalarReplacementOfAggregates, &match_branches::MatchBranchSimplification, diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index e27875853df..94b553a07a7 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -20,6 +20,9 @@ monomorphize_recursion_limit = reached the recursion limit while instantiating `{$shrunk}` .note = `{$def_path_str}` defined here +monomorphize_start_not_found = using `fn main` requires the standard library + .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` + monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined monomorphize_type_length_limit = reached the type-length limit while instantiating `{$shrunk}` diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 44beafa0873..92f001cc321 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -194,7 +194,7 @@ use rustc_target::abi::Size; use std::path::PathBuf; use crate::errors::{ - EncounteredErrorWhileInstantiating, LargeAssignmentsLint, NoOptimizedMir, RecursionLimit, + self, EncounteredErrorWhileInstantiating, LargeAssignmentsLint, NoOptimizedMir, RecursionLimit, TypeLengthLimit, }; @@ -1272,7 +1272,9 @@ impl<'v> RootCollector<'_, 'v> { return; }; - let start_def_id = self.tcx.require_lang_item(LangItem::Start, None); + let Some(start_def_id) = self.tcx.lang_items().start_fn() else { + self.tcx.dcx().emit_fatal(errors::StartNotFound); + }; let main_ret_ty = self.tcx.fn_sig(main_def_id).no_bound_vars().unwrap().output(); // Given that `main()` has no arguments, diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 2ca14673a58..bd89874b5cc 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -95,6 +95,11 @@ pub struct EncounteredErrorWhileInstantiating { } #[derive(Diagnostic)] +#[diag(monomorphize_start_not_found)] +#[help] +pub struct StartNotFound; + +#[derive(Diagnostic)] #[diag(monomorphize_unknown_cgu_collection_mode)] pub struct UnknownCguCollectionMode<'a> { pub mode: &'a str, diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 7db9291921f..d7ecf577ed6 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -7,7 +7,7 @@ use rustc_ast::ast::{self, AttrStyle}; use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::contains_text_flow_control_chars; -use rustc_errors::{error_code, Applicability, DiagCtxt, Diagnostic, StashKey}; +use rustc_errors::{error_code, Applicability, DiagCtxt, DiagnosticBuilder, StashKey}; use rustc_lexer::unescape::{self, EscapeError, Mode}; use rustc_lexer::{Base, DocStyle, RawStrError}; use rustc_lexer::{Cursor, LiteralKind}; @@ -42,12 +42,12 @@ pub struct UnmatchedDelim { pub candidate_span: Option<Span>, } -pub(crate) fn parse_token_trees<'a>( - sess: &'a ParseSess, - mut src: &'a str, +pub(crate) fn parse_token_trees<'sess, 'src>( + sess: &'sess ParseSess, + mut src: &'src str, mut start_pos: BytePos, override_span: Option<Span>, -) -> Result<TokenStream, Vec<Diagnostic>> { +) -> Result<TokenStream, Vec<DiagnosticBuilder<'sess>>> { // Skip `#!`, if present. if let Some(shebang_len) = rustc_lexer::strip_shebang(src) { src = &src[shebang_len..]; @@ -76,13 +76,13 @@ pub(crate) fn parse_token_trees<'a>( let mut buffer = Vec::with_capacity(1); for unmatched in unmatched_delims { if let Some(err) = make_unclosed_delims_error(unmatched, sess) { - err.buffer(&mut buffer); + buffer.push(err); } } if let Err(errs) = res { // Add unclosing delimiter or diff marker errors for err in errs { - err.buffer(&mut buffer); + buffer.push(err); } } Err(buffer) @@ -90,16 +90,16 @@ pub(crate) fn parse_token_trees<'a>( } } -struct StringReader<'a> { - sess: &'a ParseSess, +struct StringReader<'sess, 'src> { + sess: &'sess ParseSess, /// Initial position, read-only. start_pos: BytePos, /// The absolute offset within the source_map of the current character. pos: BytePos, /// Source text to tokenize. - src: &'a str, + src: &'src str, /// Cursor for getting lexer tokens. - cursor: Cursor<'a>, + cursor: Cursor<'src>, override_span: Option<Span>, /// When a "unknown start of token: \u{a0}" has already been emitted earlier /// in this file, it's safe to treat further occurrences of the non-breaking @@ -107,8 +107,8 @@ struct StringReader<'a> { nbsp_is_whitespace: bool, } -impl<'a> StringReader<'a> { - pub fn dcx(&self) -> &'a DiagCtxt { +impl<'sess, 'src> StringReader<'sess, 'src> { + pub fn dcx(&self) -> &'sess DiagCtxt { &self.sess.dcx } @@ -526,7 +526,7 @@ impl<'a> StringReader<'a> { /// Slice of the source text from `start` up to but excluding `self.pos`, /// meaning the slice does not include the character `self.ch`. - fn str_from(&self, start: BytePos) -> &'a str { + fn str_from(&self, start: BytePos) -> &'src str { self.str_from_to(start, self.pos) } @@ -537,12 +537,12 @@ impl<'a> StringReader<'a> { } /// Slice of the source text spanning from `start` up to but excluding `end`. - fn str_from_to(&self, start: BytePos, end: BytePos) -> &'a str { + fn str_from_to(&self, start: BytePos, end: BytePos) -> &'src str { &self.src[self.src_index(start)..self.src_index(end)] } /// Slice of the source text spanning from `start` until the end - fn str_from_to_end(&self, start: BytePos) -> &'a str { + fn str_from_to_end(&self, start: BytePos) -> &'src str { &self.src[self.src_index(start)..] } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 64b3928689a..c9ff2d58e2c 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -8,18 +8,18 @@ use rustc_ast_pretty::pprust::token_to_string; use rustc_errors::{Applicability, PErr}; use rustc_span::symbol::kw; -pub(super) struct TokenTreesReader<'a> { - string_reader: StringReader<'a>, +pub(super) struct TokenTreesReader<'sess, 'src> { + string_reader: StringReader<'sess, 'src>, /// The "next" token, which has been obtained from the `StringReader` but /// not yet handled by the `TokenTreesReader`. token: Token, diag_info: TokenTreeDiagInfo, } -impl<'a> TokenTreesReader<'a> { +impl<'sess, 'src> TokenTreesReader<'sess, 'src> { pub(super) fn parse_all_token_trees( - string_reader: StringReader<'a>, - ) -> (TokenStream, Result<(), Vec<PErr<'a>>>, Vec<UnmatchedDelim>) { + string_reader: StringReader<'sess, 'src>, + ) -> (TokenStream, Result<(), Vec<PErr<'sess>>>, Vec<UnmatchedDelim>) { let mut tt_reader = TokenTreesReader { string_reader, token: Token::dummy(), @@ -35,7 +35,7 @@ impl<'a> TokenTreesReader<'a> { fn parse_token_trees( &mut self, is_delimited: bool, - ) -> (Spacing, TokenStream, Result<(), Vec<PErr<'a>>>) { + ) -> (Spacing, TokenStream, Result<(), Vec<PErr<'sess>>>) { // Move past the opening delimiter. let (_, open_spacing) = self.bump(false); @@ -71,7 +71,7 @@ impl<'a> TokenTreesReader<'a> { } } - fn eof_err(&mut self) -> PErr<'a> { + fn eof_err(&mut self) -> PErr<'sess> { let msg = "this file contains an unclosed delimiter"; let mut err = self.string_reader.sess.dcx.struct_span_err(self.token.span, msg); for &(_, sp) in &self.diag_info.open_braces { @@ -99,7 +99,7 @@ impl<'a> TokenTreesReader<'a> { fn parse_token_tree_open_delim( &mut self, open_delim: Delimiter, - ) -> Result<TokenTree, Vec<PErr<'a>>> { + ) -> Result<TokenTree, Vec<PErr<'sess>>> { // The span for beginning of the delimited section let pre_span = self.token.span; @@ -229,7 +229,11 @@ impl<'a> TokenTreesReader<'a> { (this_tok, this_spacing) } - fn unclosed_delim_err(&mut self, tts: TokenStream, mut errs: Vec<PErr<'a>>) -> Vec<PErr<'a>> { + fn unclosed_delim_err( + &mut self, + tts: TokenStream, + mut errs: Vec<PErr<'sess>>, + ) -> Vec<PErr<'sess>> { // If there are unclosed delims, see if there are diff markers and if so, point them // out instead of complaining about the unclosed delims. let mut parser = crate::stream_to_parser(self.string_reader.sess, tts, None); @@ -285,7 +289,7 @@ impl<'a> TokenTreesReader<'a> { return errs; } - fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'a> { + fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'sess> { // An unexpected closing delimiter (i.e., there is no // matching opening delimiter). let token_str = token_to_string(&self.token); diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index dac7569e385..a136abaa28b 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -337,7 +337,7 @@ const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[ ]; pub(super) fn check_for_substitution( - reader: &StringReader<'_>, + reader: &StringReader<'_, '_>, pos: BytePos, ch: char, count: usize, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index b93f08a21e3..c00e318f227 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -19,7 +19,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrItem, Attribute, MetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Diagnostic, FatalError, Level, PResult}; +use rustc_errors::{DiagnosticBuilder, FatalError, PResult}; use rustc_session::parse::ParseSess; use rustc_span::{FileName, SourceFile, Span}; @@ -45,14 +45,13 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } /// A variant of 'panictry!' that works on a `Vec<Diagnostic>` instead of a single /// `DiagnosticBuilder`. macro_rules! panictry_buffer { - ($handler:expr, $e:expr) => {{ - use rustc_errors::FatalError; + ($e:expr) => {{ use std::result::Result::{Err, Ok}; match $e { Ok(e) => e, Err(errs) => { for e in errs { - $handler.emit_diagnostic(e); + e.emit(); } FatalError.raise() } @@ -100,36 +99,41 @@ pub fn parse_stream_from_source_str( /// Creates a new parser from a source string. pub fn new_parser_from_source_str(sess: &ParseSess, name: FileName, source: String) -> Parser<'_> { - panictry_buffer!(&sess.dcx, maybe_new_parser_from_source_str(sess, name, source)) + panictry_buffer!(maybe_new_parser_from_source_str(sess, name, source)) } /// Creates a new parser from a source string. Returns any buffered errors from lexing the initial -/// token stream. +/// token stream; these must be consumed via `emit`, `cancel`, etc., otherwise a panic will occur +/// when they are dropped. pub fn maybe_new_parser_from_source_str( sess: &ParseSess, name: FileName, source: String, -) -> Result<Parser<'_>, Vec<Diagnostic>> { +) -> Result<Parser<'_>, Vec<DiagnosticBuilder<'_>>> { maybe_source_file_to_parser(sess, sess.source_map().new_source_file(name, source)) } -/// Creates a new parser, handling errors as appropriate if the file doesn't exist. -/// If a span is given, that is used on an error as the source of the problem. +/// Creates a new parser, aborting if the file doesn't exist. If a span is given, that is used on +/// an error as the source of the problem. pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option<Span>) -> Parser<'a> { - source_file_to_parser(sess, file_to_source_file(sess, path, sp)) -} + let source_file = sess.source_map().load_file(path).unwrap_or_else(|e| { + let msg = format!("couldn't read {}: {}", path.display(), e); + let mut err = sess.dcx.struct_fatal(msg); + if let Some(sp) = sp { + err.span(sp); + } + err.emit(); + }); -/// Given a session and a `source_file`, returns a parser. -fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> { - panictry_buffer!(&sess.dcx, maybe_source_file_to_parser(sess, source_file)) + panictry_buffer!(maybe_source_file_to_parser(sess, source_file)) } -/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing the -/// initial token stream. +/// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing +/// the initial token stream. fn maybe_source_file_to_parser( sess: &ParseSess, source_file: Lrc<SourceFile>, -) -> Result<Parser<'_>, Vec<Diagnostic>> { +) -> Result<Parser<'_>, Vec<DiagnosticBuilder<'_>>> { let end_pos = source_file.end_position(); let stream = maybe_file_to_stream(sess, source_file, None)?; let mut parser = stream_to_parser(sess, stream, None); @@ -142,52 +146,22 @@ fn maybe_source_file_to_parser( // Base abstractions -/// Given a session and a path and an optional span (for error reporting), -/// add the path to the session's source_map and return the new source_file or -/// error when a file can't be read. -fn try_file_to_source_file( - sess: &ParseSess, - path: &Path, - spanopt: Option<Span>, -) -> Result<Lrc<SourceFile>, Diagnostic> { - sess.source_map().load_file(path).map_err(|e| { - let msg = format!("couldn't read {}: {}", path.display(), e); - let mut diag = Diagnostic::new(Level::Fatal, msg); - if let Some(sp) = spanopt { - diag.span(sp); - } - diag - }) -} - -/// Given a session and a path and an optional span (for error reporting), -/// adds the path to the session's `source_map` and returns the new `source_file`. -fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>) -> Lrc<SourceFile> { - match try_file_to_source_file(sess, path, spanopt) { - Ok(source_file) => source_file, - Err(d) => { - sess.dcx.emit_diagnostic(d); - FatalError.raise(); - } - } -} - /// Given a `source_file`, produces a sequence of token trees. pub fn source_file_to_stream( sess: &ParseSess, source_file: Lrc<SourceFile>, override_span: Option<Span>, ) -> TokenStream { - panictry_buffer!(&sess.dcx, maybe_file_to_stream(sess, source_file, override_span)) + panictry_buffer!(maybe_file_to_stream(sess, source_file, override_span)) } /// Given a source file, produces a sequence of token trees. Returns any buffered errors from /// parsing the token stream. -pub fn maybe_file_to_stream( - sess: &ParseSess, +fn maybe_file_to_stream<'sess>( + sess: &'sess ParseSess, source_file: Lrc<SourceFile>, override_span: Option<Span>, -) -> Result<TokenStream, Vec<Diagnostic>> { +) -> Result<TokenStream, Vec<DiagnosticBuilder<'sess>>> { let src = source_file.src.as_ref().unwrap_or_else(|| { sess.dcx.bug(format!( "cannot lex `source_file` without source: {}", diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index be50aad1303..d41cc724408 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -575,6 +575,11 @@ passes_outside_loop = passes_outside_loop_suggestion = consider labeling this block to be able to break within it +passes_panic_unwind_without_std = + unwinding panics are not supported without std + .note = since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem + .help = using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + passes_params_not_allowed = referencing function parameters is not allowed in naked functions .help = follow the calling convention in asm block to use parameters diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d934e959a41..cf3c7cc4ace 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -813,6 +813,12 @@ pub struct UnknownExternLangItem { pub struct MissingPanicHandler; #[derive(Diagnostic)] +#[diag(passes_panic_unwind_without_std)] +#[help] +#[note] +pub struct PanicUnwindWithoutStd; + +#[derive(Diagnostic)] #[diag(passes_missing_lang_item)] #[note] #[help] diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index db3d442676e..4eb0c6c275e 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -9,7 +9,9 @@ use rustc_middle::middle::lang_items::required; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; -use crate::errors::{MissingLangItem, MissingPanicHandler, UnknownExternLangItem}; +use crate::errors::{ + MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd, UnknownExternLangItem, +}; /// Checks the crate for usage of weak lang items, returning a vector of all the /// language items required by this crate, but not defined yet. @@ -76,6 +78,8 @@ fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) { if missing.contains(&item) && required(tcx, item) && items.get(item).is_none() { if item == LangItem::PanicImpl { tcx.dcx().emit_err(MissingPanicHandler); + } else if item == LangItem::EhPersonality { + tcx.dcx().emit_err(PanicUnwindWithoutStd); } else { tcx.dcx().emit_err(MissingLangItem { name: item.name() }); } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index b52643adcc9..374914055d8 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -124,8 +124,10 @@ pub fn analyze_match<'p, 'tcx>( let pat_column = PatternColumn::new(arms); - // Lint on ranges that overlap on their endpoints, which is likely a mistake. - lint_overlapping_range_endpoints(cx, &pat_column)?; + // Lint ranges that overlap on their endpoints, which is likely a mistake. + if !report.overlapping_range_endpoints.is_empty() { + lint_overlapping_range_endpoints(cx, &report.overlapping_range_endpoints); + } // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 52c9af85006..cfe4ca3ce93 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,20 +1,14 @@ -use smallvec::SmallVec; - -use rustc_data_structures::captures::Captures; -use rustc_middle::ty; use rustc_session::lint; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_span::{ErrorGuaranteed, Span}; +use rustc_span::ErrorGuaranteed; -use crate::constructor::{IntRange, MaybeInfiniteInt}; use crate::errors::{ - NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap, - OverlappingRangeEndpoints, Uncovered, + self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered, }; use crate::pat::PatOrWild; use crate::rustc::{ - Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt, - SplitConstructorSet, WitnessPat, + self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, + RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat, }; /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that @@ -68,10 +62,6 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> { Ok(ctors_for_ty.split(pcx, column_ctors)) } - fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'_> { - self.patterns.iter().copied() - } - /// Does specialization: given a constructor, this takes the patterns from the column that match /// the constructor, and outputs their fields. /// This returns one column per field of the constructor. They usually all have the same length @@ -207,78 +197,25 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>( Ok(()) } -/// Traverse the patterns to warn the user about ranges that overlap on their endpoints. -#[instrument(level = "debug", skip(cx))] pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>( cx: MatchCtxt<'a, 'p, 'tcx>, - column: &PatternColumn<'p, 'tcx>, -) -> Result<(), ErrorGuaranteed> { - let Some(ty) = column.head_ty() else { - return Ok(()); - }; - let pcx = &PlaceCtxt::new_dummy(cx, ty); - let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx; - - let set = column.analyze_ctors(pcx)?; - - if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) { - let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| { - let overlap_as_pat = rcx.hoist_pat_range(overlap, ty); - let overlaps: Vec<_> = overlapped_spans - .iter() - .copied() - .map(|span| Overlap { range: overlap_as_pat.clone(), span }) - .collect(); - rcx.tcx.emit_spanned_lint( - lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, - rcx.match_lint_level, - this_span, - OverlappingRangeEndpoints { overlap: overlaps, range: this_span }, - ); - }; - - // If two ranges overlapped, the split set will contain their intersection as a singleton. - let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range()); - for overlap_range in split_int_ranges.clone() { - if overlap_range.is_singleton() { - let overlap: MaybeInfiniteInt = overlap_range.lo; - // Ranges that look like `lo..=overlap`. - let mut prefixes: SmallVec<[_; 1]> = Default::default(); - // Ranges that look like `overlap..=hi`. - let mut suffixes: SmallVec<[_; 1]> = Default::default(); - // Iterate on patterns that contained `overlap`. - for pat in column.iter() { - let Constructor::IntRange(this_range) = pat.ctor() else { continue }; - let this_span = pat.data().unwrap().span; - if this_range.is_singleton() { - // Don't lint when one of the ranges is a singleton. - continue; - } - if this_range.lo == overlap { - // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any - // ranges that look like `lo..=overlap`. - if !prefixes.is_empty() { - emit_lint(overlap_range, this_span, &prefixes); - } - suffixes.push(this_span) - } else if this_range.hi == overlap.plus_one() { - // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any - // ranges that look like `overlap..=hi`. - if !suffixes.is_empty() { - emit_lint(overlap_range, this_span, &suffixes); - } - prefixes.push(this_span) - } - } - } - } - } else { - // Recurse into the fields. - for ctor in set.present { - for col in column.specialize(pcx, &ctor) { - lint_overlapping_range_endpoints(cx, &col)?; - } - } + overlapping_range_endpoints: &[rustc::OverlappingRanges<'p, 'tcx>], +) { + let rcx = cx.tycx; + for overlap in overlapping_range_endpoints { + let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty()); + let overlaps: Vec<_> = overlap + .overlaps_with + .iter() + .map(|pat| pat.data().unwrap().span) + .map(|span| errors::Overlap { range: overlap_as_pat.clone(), span }) + .collect(); + let pat_span = overlap.pat.data().unwrap().span; + rcx.tcx.emit_spanned_lint( + lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, + rcx.match_lint_level, + pat_span, + errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span }, + ); } - Ok(()) } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index a8d1bece613..a17cd2c81b9 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -34,6 +34,8 @@ pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; +pub type OverlappingRanges<'p, 'tcx> = + crate::usefulness::OverlappingRanges<'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type PlaceCtxt<'a, 'p, 'tcx> = crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; pub(crate) type SplitConstructorSet<'p, 'tcx> = diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index b4935d280e6..85b6a6a3b6c 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -712,10 +712,11 @@ //! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific //! reason not to, for example if they crucially depend on a particular feature like `or_patterns`. +use rustc_index::bit_set::BitSet; use smallvec::{smallvec, SmallVec}; use std::fmt; -use crate::constructor::{Constructor, ConstructorSet}; +use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat}; use crate::{Captures, MatchArm, MatchCtxt, TypeCx}; @@ -911,6 +912,11 @@ struct MatrixRow<'p, Cx: TypeCx> { /// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful. /// This is reset to `false` when specializing. useful: bool, + /// Tracks which rows above this one have an intersection with this one, i.e. such that there is + /// a value that matches both rows. + /// Note: Because of relevancy we may miss some intersections. The intersections we do find are + /// correct. + intersects: BitSet<usize>, } impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> { @@ -938,6 +944,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> { parent_row: self.parent_row, is_under_guard: self.is_under_guard, useful: false, + intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. }) } @@ -955,6 +962,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> { parent_row, is_under_guard: self.is_under_guard, useful: false, + intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. } } } @@ -993,13 +1001,15 @@ struct Matrix<'p, Cx: TypeCx> { impl<'p, Cx: TypeCx> Matrix<'p, Cx> { /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively /// expands it. Internal method, prefer [`Matrix::new`]. - fn expand_and_push(&mut self, row: MatrixRow<'p, Cx>) { + fn expand_and_push(&mut self, mut row: MatrixRow<'p, Cx>) { if !row.is_empty() && row.head().is_or_pat() { // Expand nested or-patterns. - for new_row in row.expand_or_pat() { + for mut new_row in row.expand_or_pat() { + new_row.intersects = BitSet::new_empty(self.rows.len()); self.rows.push(new_row); } } else { + row.intersects = BitSet::new_empty(self.rows.len()); self.rows.push(row); } } @@ -1019,9 +1029,10 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> { for (row_id, arm) in arms.iter().enumerate() { let v = MatrixRow { pats: PatStack::from_pattern(arm.pat), - parent_row: row_id, // dummy, we won't read it + parent_row: row_id, // dummy, we don't read it is_under_guard: arm.has_guard, useful: false, + intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. }; matrix.expand_and_push(v); } @@ -1317,6 +1328,83 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> { } } +/// Collect ranges that overlap like `lo..=overlap`/`overlap..=hi`. Must be called during +/// exhaustiveness checking, if we find a singleton range after constructor splitting. This reuses +/// row intersection information to only detect ranges that truly overlap. +/// +/// If two ranges overlapped, the split set will contain their intersection as a singleton. +/// Specialization will then select rows that match the overlap, and exhaustiveness will compute +/// which rows have an intersection that includes the overlap. That gives us all the info we need to +/// compute overlapping ranges without false positives. +/// +/// We can however get false negatives because exhaustiveness does not explore all cases. See the +/// section on relevancy at the top of the file. +fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>( + overlap_range: IntRange, + matrix: &Matrix<'p, Cx>, + specialized_matrix: &Matrix<'p, Cx>, + overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>, +) { + let overlap = overlap_range.lo; + // Ranges that look like `lo..=overlap`. + let mut prefixes: SmallVec<[_; 1]> = Default::default(); + // Ranges that look like `overlap..=hi`. + let mut suffixes: SmallVec<[_; 1]> = Default::default(); + // Iterate on patterns that contained `overlap`. We iterate on `specialized_matrix` which + // contains only rows that matched the current `ctor` as well as accurate intersection + // information. It doesn't contain the column that contains the range; that can be found in + // `matrix`. + for (child_row_id, child_row) in specialized_matrix.rows().enumerate() { + let PatOrWild::Pat(pat) = matrix.rows[child_row.parent_row].head() else { continue }; + let Constructor::IntRange(this_range) = pat.ctor() else { continue }; + // Don't lint when one of the ranges is a singleton. + if this_range.is_singleton() { + continue; + } + if this_range.lo == overlap { + // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any + // ranges that look like `lo..=overlap`. + if !prefixes.is_empty() { + let overlaps_with: Vec<_> = prefixes + .iter() + .filter(|&&(other_child_row_id, _)| { + child_row.intersects.contains(other_child_row_id) + }) + .map(|&(_, pat)| pat) + .collect(); + if !overlaps_with.is_empty() { + overlapping_range_endpoints.push(OverlappingRanges { + pat, + overlaps_on: overlap_range, + overlaps_with, + }); + } + } + suffixes.push((child_row_id, pat)) + } else if this_range.hi == overlap.plus_one() { + // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any + // ranges that look like `overlap..=hi`. + if !suffixes.is_empty() { + let overlaps_with: Vec<_> = suffixes + .iter() + .filter(|&&(other_child_row_id, _)| { + child_row.intersects.contains(other_child_row_id) + }) + .map(|&(_, pat)| pat) + .collect(); + if !overlaps_with.is_empty() { + overlapping_range_endpoints.push(OverlappingRanges { + pat, + overlaps_on: overlap_range, + overlaps_with, + }); + } + } + prefixes.push((child_row_id, pat)) + } + } +} + /// The core of the algorithm. /// /// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks @@ -1335,6 +1423,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> { fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( mcx: MatchCtxt<'a, 'p, Cx>, matrix: &mut Matrix<'p, Cx>, + overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>, is_top_level: bool, ) -> Result<WitnessMatrix<Cx>, Cx::Error> { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); @@ -1349,21 +1438,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( let Some(ty) = matrix.head_ty() else { // The base case: there are no columns in the matrix. We are morally pattern-matching on (). // A row is useful iff it has no (unguarded) rows above it. - for row in matrix.rows_mut() { - // All rows are useful until they're not. - row.useful = true; - // When there's an unguarded row, the match is exhaustive and any subsequent row is not - // useful. - if !row.is_under_guard { - return Ok(WitnessMatrix::empty()); - } + let mut useful = true; // Whether the next row is useful. + for (i, row) in matrix.rows_mut().enumerate() { + row.useful = useful; + row.intersects.insert_range(0..i); + // The next rows stays useful if this one is under a guard. + useful &= row.is_under_guard; } - // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless - // irrelevant. - return if matrix.wildcard_row_is_relevant { + return if useful && matrix.wildcard_row_is_relevant { + // The wildcard row is useful; the match is non-exhaustive. Ok(WitnessMatrix::unit_witness()) } else { - // We choose to not report anything here; see at the top for details. + // Either the match is exhaustive, or we choose not to report anything because of + // relevancy. See at the top for details. Ok(WitnessMatrix::empty()) }; }; @@ -1416,7 +1503,12 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty(); let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant); let mut witnesses = ensure_sufficient_stack(|| { - compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false) + compute_exhaustiveness_and_usefulness( + mcx, + &mut spec_matrix, + overlapping_range_endpoints, + false, + ) })?; // Transform witnesses for `spec_matrix` into witnesses for `matrix`. @@ -1424,10 +1516,34 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>( // Accumulate the found witnesses. ret.extend(witnesses); - // A parent row is useful if any of its children is. for child_row in spec_matrix.rows() { - let parent_row = &mut matrix.rows[child_row.parent_row]; - parent_row.useful = parent_row.useful || child_row.useful; + let parent_row_id = child_row.parent_row; + let parent_row = &mut matrix.rows[parent_row_id]; + // A parent row is useful if any of its children is. + parent_row.useful |= child_row.useful; + for child_intersection in child_row.intersects.iter() { + // Convert the intersecting ids into ids for the parent matrix. + let parent_intersection = spec_matrix.rows[child_intersection].parent_row; + // Note: self-intersection can happen with or-patterns. + if parent_intersection != parent_row_id { + parent_row.intersects.insert(parent_intersection); + } + } + } + + // Detect ranges that overlap on their endpoints. + if let Constructor::IntRange(overlap_range) = ctor { + if overlap_range.is_singleton() + && spec_matrix.rows.len() >= 2 + && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty()) + { + collect_overlapping_range_endpoints( + overlap_range, + matrix, + &spec_matrix, + overlapping_range_endpoints, + ); + } } } @@ -1453,6 +1569,15 @@ pub enum Usefulness<'p, Cx: TypeCx> { Redundant, } +/// Indicates that the range `pat` overlapped with all the ranges in `overlaps_with`, where the +/// range they overlapped over is `overlaps_on`. We only detect singleton overlaps. +#[derive(Clone, Debug)] +pub struct OverlappingRanges<'p, Cx: TypeCx> { + pub pat: &'p DeconstructedPat<'p, Cx>, + pub overlaps_on: IntRange, + pub overlaps_with: Vec<&'p DeconstructedPat<'p, Cx>>, +} + /// The output of checking a match for exhaustiveness and arm usefulness. pub struct UsefulnessReport<'p, Cx: TypeCx> { /// For each arm of the input, whether that arm is useful after the arms above it. @@ -1460,6 +1585,7 @@ pub struct UsefulnessReport<'p, Cx: TypeCx> { /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>, + pub overlapping_range_endpoints: Vec<OverlappingRanges<'p, Cx>>, } /// Computes whether a match is exhaustive and which of its arms are useful. @@ -1470,9 +1596,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( scrut_ty: Cx::Ty, scrut_validity: ValidityConstraint, ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> { + let mut overlapping_range_endpoints = Vec::new(); let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity); - let non_exhaustiveness_witnesses = - compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?; + let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness( + cx, + &mut matrix, + &mut overlapping_range_endpoints, + true, + )?; let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column(); let arm_usefulness: Vec<_> = arms @@ -1489,5 +1620,10 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( (arm, usefulness) }) .collect(); - Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }) + + Ok(UsefulnessReport { + arm_usefulness, + non_exhaustiveness_witnesses, + overlapping_range_endpoints, + }) } diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 63bd3457eb9..287e317b10f 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -70,14 +70,6 @@ pub trait Encoder { } fn emit_raw_bytes(&mut self, s: &[u8]); - - fn emit_enum_variant<F>(&mut self, v_id: usize, f: F) - where - F: FnOnce(&mut Self), - { - self.emit_usize(v_id); - f(self); - } } // Note: all the methods in this trait are infallible, which may be surprising. @@ -132,10 +124,6 @@ pub trait Decoder { fn read_raw_bytes(&mut self, len: usize) -> &[u8]; - // Although there is an `emit_enum_variant` method in `Encoder`, the code - // patterns in decoding are different enough to encoding that there is no - // need for a corresponding `read_enum_variant` method here. - fn peek_byte(&self) -> u8; fn position(&self) -> usize; } @@ -372,15 +360,18 @@ impl<'a, D: Decoder> Decodable<D> for Cow<'a, str> { impl<S: Encoder, T: Encodable<S>> Encodable<S> for Option<T> { fn encode(&self, s: &mut S) { match *self { - None => s.emit_enum_variant(0, |_| {}), - Some(ref v) => s.emit_enum_variant(1, |s| v.encode(s)), + None => s.emit_u8(0), + Some(ref v) => { + s.emit_u8(1); + v.encode(s); + } } } } impl<D: Decoder, T: Decodable<D>> Decodable<D> for Option<T> { fn decode(d: &mut D) -> Option<T> { - match d.read_usize() { + match d.read_u8() { 0 => None, 1 => Some(Decodable::decode(d)), _ => panic!("Encountered invalid discriminant while decoding `Option`."), @@ -391,15 +382,21 @@ impl<D: Decoder, T: Decodable<D>> Decodable<D> for Option<T> { impl<S: Encoder, T1: Encodable<S>, T2: Encodable<S>> Encodable<S> for Result<T1, T2> { fn encode(&self, s: &mut S) { match *self { - Ok(ref v) => s.emit_enum_variant(0, |s| v.encode(s)), - Err(ref v) => s.emit_enum_variant(1, |s| v.encode(s)), + Ok(ref v) => { + s.emit_u8(0); + v.encode(s); + } + Err(ref v) => { + s.emit_u8(1); + v.encode(s); + } } } } impl<D: Decoder, T1: Decodable<D>, T2: Decodable<D>> Decodable<D> for Result<T1, T2> { fn decode(d: &mut D) -> Result<T1, T2> { - match d.read_usize() { + match d.read_u8() { 0 => Ok(T1::decode(d)), 1 => Err(T2::decode(d)), _ => panic!("Encountered invalid discriminant while decoding `Result`."), diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 61796d7a6ca..fe1166457ba 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1146,6 +1146,7 @@ impl UnstableOptions { DiagCtxtFlags { can_emit_warnings, treat_err_as_bug: self.treat_err_as_bug, + eagerly_emit_delayed_bugs: self.eagerly_emit_delayed_bugs, macro_backtrace: self.macro_backtrace, deduplicate_diagnostics: self.deduplicate_diagnostics, track_diagnostics: self.track_diagnostics, @@ -1379,6 +1380,8 @@ pub struct CheckCfg { pub exhaustive_values: bool, /// All the expected values for a config name pub expecteds: FxHashMap<Symbol, ExpectedValues<Symbol>>, + /// Well known names (only used for diagnostics purposes) + pub well_known_names: FxHashSet<Symbol>, } pub enum ExpectedValues<T> { @@ -1431,9 +1434,10 @@ impl CheckCfg { }; macro_rules! ins { - ($name:expr, $values:expr) => { + ($name:expr, $values:expr) => {{ + self.well_known_names.insert($name); self.expecteds.entry($name).or_insert_with($values) - }; + }}; } // Symbols are inserted in alphabetical order as much as possible. @@ -1823,7 +1827,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { "Remap source names in all output (compiler messages and output files)", "FROM=TO", ), - opt::multi("", "env", "Inject an environment variable", "VAR=VALUE"), + opt::multi("", "env-set", "Inject an environment variable", "VAR=VALUE"), ]); opts } @@ -2599,11 +2603,11 @@ fn parse_logical_env( ) -> FxIndexMap<String, String> { let mut vars = FxIndexMap::default(); - for arg in matches.opt_strs("env") { + for arg in matches.opt_strs("env-set") { if let Some((name, val)) = arg.split_once('=') { vars.insert(name.to_string(), val.to_string()); } else { - early_dcx.early_fatal(format!("`--env`: specify value for variable `{arg}`")); + early_dcx.early_fatal(format!("`--env-set`: specify value for variable `{arg}`")); } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c97b18ebd66..2d91a3fbd91 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1583,6 +1583,9 @@ options! { "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], "enables LTO for dylib crate type"), + eagerly_emit_delayed_bugs: bool = (false, parse_bool, [UNTRACKED], + "emit delayed bugs eagerly as errors instead of stashing them and emitting \ + them only if an error has not been emitted"), ehcont_guard: bool = (false, parse_bool, [TRACKED], "generate Windows EHCont Guard tables"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 14cb1d6d362..87a0ccbb1a5 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -170,6 +170,34 @@ pub fn find_best_match_for_name( find_best_match_for_name_impl(false, candidates, lookup, dist) } +/// Find the best match for multiple words +/// +/// This function is intended for use when the desired match would never be +/// returned due to a substring in `lookup` which is superfluous. +/// +/// For example, when looking for the closest lint name to `clippy:missing_docs`, +/// we would find `clippy::erasing_op`, despite `missing_docs` existing and being a better suggestion. +/// `missing_docs` would have a larger edit distance because it does not contain the `clippy` tool prefix. +/// In order to find `missing_docs`, this function takes multiple lookup strings, computes the best match +/// for each and returns the match which had the lowest edit distance. In our example, `clippy:missing_docs` and +/// `missing_docs` would be `lookups`, enabling `missing_docs` to be the best match, as desired. +pub fn find_best_match_for_names( + candidates: &[Symbol], + lookups: &[Symbol], + dist: Option<usize>, +) -> Option<Symbol> { + lookups + .iter() + .map(|s| (s, find_best_match_for_name_impl(false, candidates, *s, dist))) + .filter_map(|(s, r)| r.map(|r| (s, r))) + .min_by(|(s1, r1), (s2, r2)| { + let d1 = edit_distance(s1.as_str(), r1.as_str(), usize::MAX).unwrap(); + let d2 = edit_distance(s2.as_str(), r2.as_str(), usize::MAX).unwrap(); + d1.cmp(&d2) + }) + .map(|(_, r)| r) +} + #[cold] fn find_best_match_for_name_impl( use_substring_score: bool, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 4235293823c..65702f76fda 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -203,18 +203,19 @@ impl Hash for RealFileName { impl<S: Encoder> Encodable<S> for RealFileName { fn encode(&self, encoder: &mut S) { match *self { - RealFileName::LocalPath(ref local_path) => encoder.emit_enum_variant(0, |encoder| { + RealFileName::LocalPath(ref local_path) => { + encoder.emit_u8(0); local_path.encode(encoder); - }), - - RealFileName::Remapped { ref local_path, ref virtual_name } => encoder - .emit_enum_variant(1, |encoder| { - // For privacy and build reproducibility, we must not embed host-dependant path - // in artifacts if they have been remapped by --remap-path-prefix - assert!(local_path.is_none()); - local_path.encode(encoder); - virtual_name.encode(encoder); - }), + } + + RealFileName::Remapped { ref local_path, ref virtual_name } => { + encoder.emit_u8(1); + // For privacy and build reproducibility, we must not embed host-dependant path + // in artifacts if they have been remapped by --remap-path-prefix + assert!(local_path.is_none()); + local_path.encode(encoder); + virtual_name.encode(encoder); + } } } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8e26327196a..da8706ea715 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3396,19 +3396,22 @@ impl Hash for TargetTriple { impl<S: Encoder> Encodable<S> for TargetTriple { fn encode(&self, s: &mut S) { match self { - TargetTriple::TargetTriple(triple) => s.emit_enum_variant(0, |s| s.emit_str(triple)), - TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => s - .emit_enum_variant(1, |s| { - s.emit_str(triple); - s.emit_str(contents) - }), + TargetTriple::TargetTriple(triple) => { + s.emit_u8(0); + s.emit_str(triple); + } + TargetTriple::TargetJson { path_for_rustdoc: _, triple, contents } => { + s.emit_u8(1); + s.emit_str(triple); + s.emit_str(contents); + } } } } impl<D: Decoder> Decodable<D> for TargetTriple { fn decode(d: &mut D) -> Self { - match d.read_usize() { + match d.read_u8() { 0 => TargetTriple::TargetTriple(d.read_str().to_owned()), 1 => TargetTriple::TargetJson { path_for_rustdoc: PathBuf::new(), diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 1883099d345..00ce9fbe758 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +bitflags = "2.4.1" itertools = "0.11.0" rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index de2577cca49..552c28c0586 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -19,6 +19,7 @@ #![feature(control_flow_enum)] #![feature(extract_if)] #![feature(let_chains)] +#![feature(option_take_if)] #![feature(if_let_guard)] #![feature(never_type)] #![feature(type_alias_impl_trait)] diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 6db53d6ddc4..f33d0f397ce 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -171,7 +171,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { let mut candidates = vec![]; let last_eval_step = match self.evaluation.evaluation.kind { inspect::CanonicalGoalEvaluationKind::Overflow - | inspect::CanonicalGoalEvaluationKind::CycleInStack => { + | inspect::CanonicalGoalEvaluationKind::CycleInStack + | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => { warn!("unexpected root evaluation: {:?}", self.evaluation); return vec![]; } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs index d8caef5b03f..b587a93b24c 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -118,6 +118,7 @@ pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> { pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<'tcx> { Overflow, CycleInStack, + ProvisionalCacheHit, Interned { revisions: &'tcx [inspect::GoalEvaluationStep<'tcx>] }, } @@ -126,6 +127,7 @@ impl std::fmt::Debug for WipCanonicalGoalEvaluationKind<'_> { match self { Self::Overflow => write!(f, "Overflow"), Self::CycleInStack => write!(f, "CycleInStack"), + Self::ProvisionalCacheHit => write!(f, "ProvisionalCacheHit"), Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(), } } @@ -151,6 +153,9 @@ impl<'tcx> WipCanonicalGoalEvaluation<'tcx> { WipCanonicalGoalEvaluationKind::CycleInStack => { inspect::CanonicalGoalEvaluationKind::CycleInStack } + WipCanonicalGoalEvaluationKind::ProvisionalCacheHit => { + inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit + } WipCanonicalGoalEvaluationKind::Interned { revisions } => { inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 55b79e6fc39..d87cc89954a 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::InferCtxt; use rustc_infer::traits::TraitEngineExt; use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::traits::{ObligationCause, Reveal}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; @@ -52,14 +52,16 @@ struct NormalizationFolder<'me, 'tcx> { impl<'tcx> NormalizationFolder<'_, 'tcx> { fn normalize_alias_ty( &mut self, - alias: AliasTy<'tcx>, + alias_ty: Ty<'tcx>, ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> { + assert!(matches!(alias_ty.kind(), ty::Alias(..))); + let infcx = self.at.infcx; let tcx = infcx.tcx; let recursion_limit = tcx.recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { self.at.infcx.err_ctxt().report_overflow_error( - &alias.to_ty(tcx), + &alias_ty, self.at.cause.span, true, |_| {}, @@ -76,7 +78,11 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::NormalizesTo { alias, term: new_infer_ty.into() }, + ty::PredicateKind::AliasRelate( + alias_ty.into(), + new_infer_ty.into(), + ty::AliasRelationDirection::Equate, + ), ); // Do not emit an error if normalization is known to fail but instead @@ -90,9 +96,12 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { return Err(errors); } let ty = infcx.resolve_vars_if_possible(new_infer_ty); - ty.try_fold_with(self)? + + // Alias is guaranteed to be fully structurally resolved, + // so we can super fold here. + ty.try_super_fold_with(self)? } else { - alias.to_ty(tcx).try_super_fold_with(self)? + alias_ty.try_super_fold_with(self)? }; self.depth -= 1; @@ -170,24 +179,18 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { } fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { - let reveal = self.at.param_env.reveal(); let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); - if !needs_normalization(&ty, reveal) { + if !ty.has_projections() { return Ok(ty); } - // We don't normalize opaque types unless we have - // `Reveal::All`, even if we're in the defining scope. - let data = match *ty.kind() { - ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::All => alias_ty, - _ => return ty.try_super_fold_with(self), - }; + let ty::Alias(..) = *ty.kind() else { return ty.try_super_fold_with(self) }; - if data.has_escaping_bound_vars() { - let (data, mapped_regions, mapped_types, mapped_consts) = - BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); - let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?; + if ty.has_escaping_bound_vars() { + let (ty, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); + let result = ensure_sufficient_stack(|| self.normalize_alias_ty(ty))?; Ok(PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, @@ -197,7 +200,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { result, )) } else { - ensure_sufficient_stack(|| self.normalize_alias_ty(data)) + ensure_sufficient_stack(|| self.normalize_alias_ty(ty)) } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs index 2a161c2d956..bede94a2e43 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -11,37 +11,90 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, Qu use rustc_middle::ty; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; -use std::collections::hash_map::Entry; +use std::mem; rustc_index::newtype_index! { #[orderable] pub struct StackDepth {} } +bitflags::bitflags! { + /// Whether and how this goal has been used as the root of a + /// cycle. We track the kind of cycle as we're otherwise forced + /// to always rerun at least once. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct HasBeenUsed: u8 { + const INDUCTIVE_CYCLE = 1 << 0; + const COINDUCTIVE_CYCLE = 1 << 1; + } +} + #[derive(Debug)] struct StackEntry<'tcx> { input: CanonicalInput<'tcx>, + available_depth: Limit, - // The maximum depth reached by this stack entry, only up-to date - // for the top of the stack and lazily updated for the rest. + + /// The maximum depth reached by this stack entry, only up-to date + /// for the top of the stack and lazily updated for the rest. reached_depth: StackDepth, - // In case of a cycle, the depth of the root. - cycle_root_depth: StackDepth, + + /// Whether this entry is a non-root cycle participant. + /// + /// We must not move the result of non-root cycle participants to the + /// global cache. See [SearchGraph::cycle_participants] for more details. + /// We store the highest stack depth of a head of a cycle this goal is involved + /// in. This necessary to soundly cache its provisional result. + non_root_cycle_participant: Option<StackDepth>, encountered_overflow: bool, - has_been_used: bool, + + has_been_used: HasBeenUsed, /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. provisional_result: Option<QueryResult<'tcx>>, +} - /// We put only the root goal of a coinductive cycle into the global cache. +/// The provisional result for a goal which is not on the stack. +struct DetachedEntry<'tcx> { + /// The head of the smallest non-trivial cycle involving this entry. /// - /// If we were to use that result when later trying to prove another cycle - /// participant, we can end up with unstable query results. - /// - /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for - /// an example of where this is needed. - cycle_participants: FxHashSet<CanonicalInput<'tcx>>, + /// Given the following rules, when proving `A` the head for + /// the provisional entry of `C` would be `B`. + /// ```plain + /// A :- B + /// B :- C + /// C :- A + B + C + /// ``` + head: StackDepth, + result: QueryResult<'tcx>, +} + +/// Stores the stack depth of a currently evaluated goal *and* already +/// computed results for goals which depend on other goals still on the stack. +/// +/// The provisional result may depend on whether the stack above it is inductive +/// or coinductive. Because of this, we store separate provisional results for +/// each case. If an provisional entry is not applicable, it may be the case +/// that we already have provisional result while computing a goal. In this case +/// we prefer the provisional result to potentially avoid fixpoint iterations. +/// See tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs for an example. +/// +/// The provisional cache can theoretically result in changes to the observable behavior, +/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. +#[derive(Default)] +struct ProvisionalCacheEntry<'tcx> { + stack_depth: Option<StackDepth>, + with_inductive_stack: Option<DetachedEntry<'tcx>>, + with_coinductive_stack: Option<DetachedEntry<'tcx>>, +} + +impl<'tcx> ProvisionalCacheEntry<'tcx> { + fn is_empty(&self) -> bool { + self.stack_depth.is_none() + && self.with_inductive_stack.is_none() + && self.with_coinductive_stack.is_none() + } } pub(super) struct SearchGraph<'tcx> { @@ -51,7 +104,15 @@ pub(super) struct SearchGraph<'tcx> { /// /// An element is *deeper* in the stack if its index is *lower*. stack: IndexVec<StackDepth, StackEntry<'tcx>>, - stack_entries: FxHashMap<CanonicalInput<'tcx>, StackDepth>, + provisional_cache: FxHashMap<CanonicalInput<'tcx>, ProvisionalCacheEntry<'tcx>>, + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + cycle_participants: FxHashSet<CanonicalInput<'tcx>>, } impl<'tcx> SearchGraph<'tcx> { @@ -60,7 +121,8 @@ impl<'tcx> SearchGraph<'tcx> { mode, local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize, stack: Default::default(), - stack_entries: Default::default(), + provisional_cache: Default::default(), + cycle_participants: Default::default(), } } @@ -89,7 +151,6 @@ impl<'tcx> SearchGraph<'tcx> { /// would cause us to not track overflow and recursion depth correctly. fn pop_stack(&mut self) -> StackEntry<'tcx> { let elem = self.stack.pop().unwrap(); - assert!(self.stack_entries.remove(&elem.input).is_some()); if let Some(last) = self.stack.raw.last_mut() { last.reached_depth = last.reached_depth.max(elem.reached_depth); last.encountered_overflow |= elem.encountered_overflow; @@ -109,7 +170,13 @@ impl<'tcx> SearchGraph<'tcx> { } pub(super) fn is_empty(&self) -> bool { - self.stack.is_empty() + if self.stack.is_empty() { + debug_assert!(self.provisional_cache.is_empty()); + debug_assert!(self.cycle_participants.is_empty()); + true + } else { + false + } } pub(super) fn current_goal_is_normalizes_to(&self) -> bool { @@ -146,6 +213,52 @@ impl<'tcx> SearchGraph<'tcx> { } } + fn stack_coinductive_from( + tcx: TyCtxt<'tcx>, + stack: &IndexVec<StackDepth, StackEntry<'tcx>>, + head: StackDepth, + ) -> bool { + stack.raw[head.index()..] + .iter() + .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx)) + } + + // When encountering a solver cycle, the result of the current goal + // depends on goals lower on the stack. + // + // We have to therefore be careful when caching goals. Only the final result + // of the cycle root, i.e. the lowest goal on the stack involved in this cycle, + // is moved to the global cache while all others are stored in a provisional cache. + // + // We update both the head of this cycle to rerun its evaluation until + // we reach a fixpoint and all other cycle participants to make sure that + // their result does not get moved to the global cache. + fn tag_cycle_participants( + stack: &mut IndexVec<StackDepth, StackEntry<'tcx>>, + cycle_participants: &mut FxHashSet<CanonicalInput<'tcx>>, + usage_kind: HasBeenUsed, + head: StackDepth, + ) { + stack[head].has_been_used |= usage_kind; + debug_assert!(!stack[head].has_been_used.is_empty()); + for entry in &mut stack.raw[head.index() + 1..] { + entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); + cycle_participants.insert(entry.input); + } + } + + fn clear_dependent_provisional_results( + provisional_cache: &mut FxHashMap<CanonicalInput<'tcx>, ProvisionalCacheEntry<'tcx>>, + head: StackDepth, + ) { + #[allow(rustc::potential_query_instability)] + provisional_cache.retain(|_, entry| { + entry.with_coinductive_stack.take_if(|p| p.head == head); + entry.with_inductive_stack.take_if(|p| p.head == head); + !entry.is_empty() + }); + } + /// Probably the most involved method of the whole solver. /// /// Given some goal which is proven via the `prove_goal` closure, this @@ -200,82 +313,79 @@ impl<'tcx> SearchGraph<'tcx> { return result; } - // Check whether we're in a cycle. - match self.stack_entries.entry(input) { - // No entry, we push this goal on the stack and try to prove it. - Entry::Vacant(v) => { - let depth = self.stack.next_index(); - let entry = StackEntry { - input, - available_depth, - reached_depth: depth, - cycle_root_depth: depth, - encountered_overflow: false, - has_been_used: false, - provisional_result: None, - cycle_participants: Default::default(), - }; - assert_eq!(self.stack.push(entry), depth); - v.insert(depth); - } - // We have a nested goal which relies on a goal `root` deeper in the stack. + // Check whether the goal is in the provisional cache. + // The provisional result may rely on the path to its cycle roots, + // so we have to check the path of the current goal matches that of + // the cache entry. + let cache_entry = self.provisional_cache.entry(input).or_default(); + if let Some(entry) = cache_entry + .with_coinductive_stack + .as_ref() + .filter(|p| Self::stack_coinductive_from(tcx, &self.stack, p.head)) + .or_else(|| { + cache_entry + .with_inductive_stack + .as_ref() + .filter(|p| !Self::stack_coinductive_from(tcx, &self.stack, p.head)) + }) + { + // We have a nested goal which is already in the provisional cache, use + // its result. We do not provide any usage kind as that should have been + // already set correctly while computing the cache entry. + inspect + .goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::ProvisionalCacheHit); + Self::tag_cycle_participants( + &mut self.stack, + &mut self.cycle_participants, + HasBeenUsed::empty(), + entry.head, + ); + return entry.result; + } else if let Some(stack_depth) = cache_entry.stack_depth { + debug!("encountered cycle with depth {stack_depth:?}"); + // We have a nested goal which directly relies on a goal deeper in the stack. // - // We first store that we may have to reprove `root` in case the provisional - // response is not equal to the final response. We also update the depth of all - // goals which recursively depend on our current goal to depend on `root` - // instead. + // We start by tagging all cycle participants, as that's necessary for caching. // - // Finally we can return either the provisional response for that goal if we have a - // coinductive cycle or an ambiguous result if the cycle is inductive. - Entry::Occupied(entry) => { - inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack); - - let stack_depth = *entry.get(); - debug!("encountered cycle with depth {stack_depth:?}"); - // We start by updating the root depth of all cycle participants, and - // add all cycle participants to the root. - let root_depth = self.stack[stack_depth].cycle_root_depth; - let (prev, participants) = self.stack.raw.split_at_mut(stack_depth.as_usize() + 1); - let root = &mut prev[root_depth.as_usize()]; - for entry in participants { - debug_assert!(entry.cycle_root_depth >= root_depth); - entry.cycle_root_depth = root_depth; - root.cycle_participants.insert(entry.input); - // FIXME(@lcnr): I believe that this line is needed as we could - // otherwise access a cache entry for the root of a cycle while - // computing the result for a cycle participant. This can result - // in unstable results due to incompleteness. - // - // However, a test for this would be an even more complex version of - // tests/ui/traits/next-solver/coinduction/incompleteness-unstable-result.rs. - // I did not bother to write such a test and we have no regression test - // for this. It would be good to have such a test :) - #[allow(rustc::potential_query_instability)] - root.cycle_participants.extend(entry.cycle_participants.drain()); - } + // Finally we can return either the provisional response or the initial response + // in case we're in the first fixpoint iteration for this goal. + inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack); + let is_coinductive_cycle = Self::stack_coinductive_from(tcx, &self.stack, stack_depth); + let usage_kind = if is_coinductive_cycle { + HasBeenUsed::COINDUCTIVE_CYCLE + } else { + HasBeenUsed::INDUCTIVE_CYCLE + }; + Self::tag_cycle_participants( + &mut self.stack, + &mut self.cycle_participants, + usage_kind, + stack_depth, + ); - // If we're in a cycle, we have to retry proving the cycle head - // until we reach a fixpoint. It is not enough to simply retry the - // `root` goal of this cycle. - // - // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs - // for an example. - self.stack[stack_depth].has_been_used = true; - return if let Some(result) = self.stack[stack_depth].provisional_result { - result - } else { - // If we don't have a provisional result yet we're in the first iteration, - // so we start with no constraints. - let is_coinductive = self.stack.raw[stack_depth.index()..] - .iter() - .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx)); - if is_coinductive { - Self::response_no_constraints(tcx, input, Certainty::Yes) - } else { - Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) - } - }; - } + // Return the provisional result or, if we're in the first iteration, + // start with no constraints. + return if let Some(result) = self.stack[stack_depth].provisional_result { + result + } else if is_coinductive_cycle { + Self::response_no_constraints(tcx, input, Certainty::Yes) + } else { + Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) + }; + } else { + // No entry, we push this goal on the stack and try to prove it. + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + non_root_cycle_participant: None, + encountered_overflow: false, + has_been_used: HasBeenUsed::empty(), + provisional_result: None, + }; + assert_eq!(self.stack.push(entry), depth); + cache_entry.stack_depth = Some(depth); } // This is for global caching, so we properly track query dependencies. @@ -290,29 +400,58 @@ impl<'tcx> SearchGraph<'tcx> { // point we are done. for _ in 0..self.local_overflow_limit() { let result = prove_goal(self, inspect); - - // Check whether the current goal is the root of a cycle and whether - // we have to rerun because its provisional result differed from the - // final result. let stack_entry = self.pop_stack(); debug_assert_eq!(stack_entry.input, input); - if stack_entry.has_been_used - && stack_entry.provisional_result.map_or(true, |r| r != result) - { - // If so, update its provisional result and reevaluate it. + + // If the current goal is not the root of a cycle, we are done. + if stack_entry.has_been_used.is_empty() { + return (stack_entry, result); + } + + // If it is a cycle head, we have to keep trying to prove it until + // we reach a fixpoint. We need to do so for all cycle heads, + // not only for the root. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + + // Start by clearing all provisional cache entries which depend on this + // the current goal. + Self::clear_dependent_provisional_results( + &mut self.provisional_cache, + self.stack.next_index(), + ); + + // Check whether we reached a fixpoint, either because the final result + // is equal to the provisional result of the previous iteration, or because + // this was only the root of either coinductive or inductive cycles, and the + // final result is equal to the initial response for that case. + let reached_fixpoint = if let Some(r) = stack_entry.provisional_result { + r == result + } else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::Yes) == result + } else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE { + Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) == result + } else { + false + }; + + // If we did not reach a fixpoint, update the provisional result and reevaluate. + if reached_fixpoint { + return (stack_entry, result); + } else { let depth = self.stack.push(StackEntry { - has_been_used: false, + has_been_used: HasBeenUsed::empty(), provisional_result: Some(result), ..stack_entry }); - assert_eq!(self.stack_entries.insert(input, depth), None); - } else { - return (stack_entry, result); + debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); } } debug!("canonical cycle overflow"); let current_entry = self.pop_stack(); + debug_assert!(current_entry.has_been_used.is_empty()); let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); (current_entry, result) }); @@ -322,26 +461,35 @@ impl<'tcx> SearchGraph<'tcx> { // We're now done with this goal. In case this goal is involved in a larger cycle // do not remove it from the provisional cache and update its provisional result. // We only add the root of cycles to the global cache. - // - // It is not possible for any nested goal to depend on something deeper on the - // stack, as this would have also updated the depth of the current goal. - if final_entry.cycle_root_depth == self.stack.next_index() { + if let Some(head) = final_entry.non_root_cycle_participant { + let coinductive_stack = Self::stack_coinductive_from(tcx, &self.stack, head); + + let entry = self.provisional_cache.get_mut(&input).unwrap(); + entry.stack_depth = None; + if coinductive_stack { + entry.with_coinductive_stack = Some(DetachedEntry { head, result }); + } else { + entry.with_inductive_stack = Some(DetachedEntry { head, result }); + } + } else { + self.provisional_cache.remove(&input); + let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + let cycle_participants = mem::take(&mut self.cycle_participants); // When encountering a cycle, both inductive and coinductive, we only // move the root into the global cache. We also store all other cycle // participants involved. // - // We disable the global cache entry of the root goal if a cycle + // We must not use the global cache entry of a root goal if a cycle // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `StackEntry::cycle_participants` for + // results. See the comment of `SearchGraph::cycle_participants` for // more details. - let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); self.global_cache(tcx).insert( tcx, input, proof_tree, reached_depth, final_entry.encountered_overflow, - final_entry.cycle_participants, + cycle_participants, dep_node, result, ) diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 10329c623b3..200d022c80c 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -26,7 +26,7 @@ use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgs, GenericArgsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use super::util; use super::SelectionContext; @@ -258,7 +258,7 @@ fn fulfill_implication<'tcx>( pub(super) fn specialization_graph_provider( tcx: TyCtxt<'_>, trait_id: DefId, -) -> specialization_graph::Graph { +) -> Result<&'_ specialization_graph::Graph, ErrorGuaranteed> { let mut sg = specialization_graph::Graph::new(); let overlap_mode = specialization_graph::OverlapMode::get(tcx, trait_id); @@ -271,6 +271,8 @@ pub(super) fn specialization_graph_provider( trait_impls .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); + let mut errored = Ok(()); + for impl_def_id in trait_impls { if let Some(impl_def_id) = impl_def_id.as_local() { // This is where impl overlap checking happens: @@ -283,15 +285,21 @@ pub(super) fn specialization_graph_provider( }; if let Some(overlap) = overlap { - report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg); + errored = errored.and(report_overlap_conflict( + tcx, + overlap, + impl_def_id, + used_to_be_allowed, + )); } } else { let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); sg.record_impl_from_cstore(tcx, parent, impl_def_id) } } + errored?; - sg + Ok(tcx.arena.alloc(sg)) } // This function is only used when @@ -304,36 +312,31 @@ fn report_overlap_conflict<'tcx>( overlap: OverlapError<'tcx>, impl_def_id: LocalDefId, used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, - sg: &mut specialization_graph::Graph, -) { +) -> Result<(), ErrorGuaranteed> { let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id()); let other_polarity = tcx.impl_polarity(overlap.with_impl); match (impl_polarity, other_polarity) { (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => { - report_negative_positive_conflict( + Err(report_negative_positive_conflict( tcx, &overlap, impl_def_id, impl_def_id.to_def_id(), overlap.with_impl, - sg, - ); + )) } (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => { - report_negative_positive_conflict( + Err(report_negative_positive_conflict( tcx, &overlap, impl_def_id, overlap.with_impl, impl_def_id.to_def_id(), - sg, - ); + )) } - _ => { - report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg); - } + _ => report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed), } } @@ -343,16 +346,16 @@ fn report_negative_positive_conflict<'tcx>( local_impl_def_id: LocalDefId, negative_impl_def_id: DefId, positive_impl_def_id: DefId, - sg: &mut specialization_graph::Graph, -) { - let err = tcx.dcx().create_err(NegativePositiveConflict { - impl_span: tcx.def_span(local_impl_def_id), - trait_desc: overlap.trait_ref, - self_ty: overlap.self_ty, - negative_impl_span: tcx.span_of_impl(negative_impl_def_id), - positive_impl_span: tcx.span_of_impl(positive_impl_def_id), - }); - sg.has_errored = Some(err.emit()); +) -> ErrorGuaranteed { + tcx.dcx() + .create_err(NegativePositiveConflict { + impl_span: tcx.def_span(local_impl_def_id), + trait_desc: overlap.trait_ref, + self_ty: overlap.self_ty, + negative_impl_span: tcx.span_of_impl(negative_impl_def_id), + positive_impl_span: tcx.span_of_impl(positive_impl_def_id), + }) + .emit() } fn report_conflicting_impls<'tcx>( @@ -360,8 +363,7 @@ fn report_conflicting_impls<'tcx>( overlap: OverlapError<'tcx>, impl_def_id: LocalDefId, used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, - sg: &mut specialization_graph::Graph, -) { +) -> Result<(), ErrorGuaranteed> { let impl_span = tcx.def_span(impl_def_id); // Work to be done after we've built the DiagnosticBuilder. We have to define it @@ -429,14 +431,11 @@ fn report_conflicting_impls<'tcx>( let mut err = tcx.dcx().struct_span_err(impl_span, msg); err.code(error_code!(E0119)); decorate(tcx, &overlap, impl_span, &mut err); - Some(err.emit()) + err.emit() } else { - Some( - tcx.dcx() - .span_delayed_bug(impl_span, "impl should have failed the orphan check"), - ) + tcx.dcx().span_delayed_bug(impl_span, "impl should have failed the orphan check") }; - sg.has_errored = reported; + Err(reported) } Some(kind) => { let lint = match kind { @@ -452,8 +451,9 @@ fn report_conflicting_impls<'tcx>( decorate(tcx, &overlap, impl_span, err); }, ); + Ok(()) } - }; + } } /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index e0f9fdc3827..ed5d01d7048 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -22,8 +22,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); if self.infcx.next_trait_solver() { - // FIXME(-Znext-solver): Should we resolve opaques here? - let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = *ty.kind() else { + let ty::Alias(..) = *ty.kind() else { return Ok(ty); }; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 72227a04bf1..38877f7a77f 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -963,7 +963,7 @@ pub enum PointerCoercion { /// Go from a safe fn pointer to an unsafe fn pointer. UnsafeFnPointer, - /// Go from a non-capturing closure to an fn pointer or an unsafe fn pointer. + /// Go from a non-capturing closure to a fn pointer or an unsafe fn pointer. /// It cannot convert a closure that requires unsafe. ClosureFnPointer(Safety), @@ -1037,21 +1037,24 @@ impl Place { /// locals from the function body where this place originates from. pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> { let start_ty = locals[self.local].ty; - self.projection.iter().fold(Ok(start_ty), |place_ty, elem| { - let ty = place_ty?; - match elem { - ProjectionElem::Deref => Self::deref_ty(ty), - ProjectionElem::Field(_idx, fty) => Ok(*fty), - ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { - Self::index_ty(ty) - } - ProjectionElem::Subslice { from, to, from_end } => { - Self::subslice_ty(ty, from, to, from_end) - } - ProjectionElem::Downcast(_) => Ok(ty), - ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), + self.projection.iter().fold(Ok(start_ty), |place_ty, elem| elem.ty(place_ty?)) + } +} + +impl ProjectionElem { + /// Get the expected type after applying this projection to a given place type. + pub fn ty(&self, place_ty: Ty) -> Result<Ty, Error> { + let ty = place_ty; + match &self { + ProjectionElem::Deref => Self::deref_ty(ty), + ProjectionElem::Field(_idx, fty) => Ok(*fty), + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => Self::index_ty(ty), + ProjectionElem::Subslice { from, to, from_end } => { + Self::subslice_ty(ty, from, to, from_end) } - }) + ProjectionElem::Downcast(_) => Ok(ty), + ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) => Ok(*ty), + } } fn index_ty(ty: Ty) -> Result<Ty, Error> { diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index ab57ff0f8f5..24296e9e877 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -37,7 +37,7 @@ use crate::mir::*; use crate::ty::{Const, GenericArgs, Region, Ty}; -use crate::{Opaque, Span}; +use crate::{Error, Opaque, Span}; pub trait MirVisitor { fn visit_body(&mut self, body: &Body) { @@ -76,12 +76,14 @@ pub trait MirVisitor { self.super_place(place, ptx, location) } - fn visit_projection_elem( + fn visit_projection_elem<'a>( &mut self, + place_ref: PlaceRef<'a>, elem: &ProjectionElem, ptx: PlaceContext, location: Location, ) { + let _ = place_ref; self.super_projection_elem(elem, ptx, location); } @@ -284,8 +286,9 @@ pub trait MirVisitor { let _ = ptx; self.visit_local(&place.local, ptx, location); - for elem in &place.projection { - self.visit_projection_elem(elem, ptx, location); + for (idx, elem) in place.projection.iter().enumerate() { + let place_ref = PlaceRef { local: place.local, projection: &place.projection[..idx] }; + self.visit_projection_elem(place_ref, elem, ptx, location); } } @@ -453,6 +456,19 @@ impl Location { } } +/// Reference to a place used to represent a partial projection. +pub struct PlaceRef<'a> { + pub local: Local, + pub projection: &'a [ProjectionElem], +} + +impl<'a> PlaceRef<'a> { + /// Get the type of this place. + pub fn ty(&self, locals: &[LocalDecl]) -> Result<Ty, Error> { + self.projection.iter().fold(Ok(locals[self.local].ty), |place_ty, elem| elem.ty(place_ty?)) + } +} + /// Information about a place's usage. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct PlaceContext { diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 74fa30456eb..45e82240164 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -552,7 +552,6 @@ fn handle_reserve(result: Result<(), TryReserveError>) { // `> isize::MAX` bytes will surely fail. On 32-bit and 16-bit we need to add // an extra guard for this in case we're running on a platform which can use // all 4GB in user-space, e.g., PAE or x32. - #[inline] fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { if usize::BITS < 64 && alloc_size > isize::MAX as usize { diff --git a/library/alloc/tests/autotraits.rs b/library/alloc/tests/autotraits.rs index ba5e28f7293..6b82deeac8a 100644 --- a/library/alloc/tests/autotraits.rs +++ b/library/alloc/tests/autotraits.rs @@ -55,12 +55,7 @@ fn test_btree_map() { require_send_sync(async { let _v = None::< - alloc::collections::btree_map::ExtractIf< - '_, - &u32, - &u32, - fn(&&u32, &mut &u32) -> bool, - >, + alloc::collections::btree_map::ExtractIf<'_, &u32, &u32, fn(&&u32, &mut &u32) -> bool>, >; async {}.await; }); diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 9ec6f6ae1ac..0f5e0d99eca 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1,7 +1,7 @@ use core::alloc::{Allocator, Layout}; -use core::{assert_eq, assert_ne}; use core::num::NonZeroUsize; use core::ptr::NonNull; +use core::{assert_eq, assert_ne}; use std::alloc::System; use std::assert_matches::assert_matches; use std::borrow::Cow; @@ -1212,7 +1212,7 @@ fn test_in_place_specialization_step_up_down() { assert_eq!(sink.len(), 2); let mut src: Vec<[u8; 3]> = Vec::with_capacity(17); - src.resize( 8, [0; 3]); + src.resize(8, [0; 3]); let iter = src.into_iter().map(|[a, b, _]| [a, b]); assert_in_place_trait(&iter); let sink: Vec<[u8; 2]> = iter.collect(); @@ -1221,11 +1221,7 @@ fn test_in_place_specialization_step_up_down() { let src = vec![[0u8; 4]; 256]; let srcptr = src.as_ptr(); - let iter = src - .into_iter() - .flat_map(|a| { - a.into_iter().map(|b| b.wrapping_add(1)) - }); + let iter = src.into_iter().flat_map(|a| a.into_iter().map(|b| b.wrapping_add(1))); assert_in_place_trait(&iter); let sink = iter.collect::<Vec<_>>(); assert_eq!(srcptr as *const u8, sink.as_ptr()); diff --git a/library/core/benches/num/int_pow/mod.rs b/library/core/benches/num/int_pow/mod.rs new file mode 100644 index 00000000000..063d722bdd1 --- /dev/null +++ b/library/core/benches/num/int_pow/mod.rs @@ -0,0 +1,99 @@ +use rand::Rng; +use test::{black_box, Bencher}; + +const ITERATIONS: usize = 128; // Uses an ITERATIONS * 20 Byte stack allocation +type IntType = i128; // Hardest native type to multiply +const EXPONENT_MAX: u32 = 31; +const MAX_BASE: IntType = 17; // +-17 ** 31 <= IntType::MAX + +macro_rules! pow_bench_template { + ($name:ident, $inner_macro:ident, $base_macro:ident) => { + #[bench] + fn $name(bench: &mut Bencher) { + // Frequent black_box calls can add latency and prevent optimizations, so for + // variable parameters we premake an array and pass the + // reference through black_box outside of the loop. + let mut rng = crate::bench_rng(); + let base_array: [IntType; ITERATIONS] = + core::array::from_fn(|_| rng.gen_range((-MAX_BASE..=MAX_BASE))); + let exp_array: [u32; ITERATIONS] = + core::array::from_fn(|_| rng.gen_range((0..=EXPONENT_MAX))); + + bench.iter(|| { + #[allow(unused, unused_mut)] + let mut base_iter = black_box(&base_array).into_iter(); + let mut exp_iter = black_box(&exp_array).into_iter(); + + (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { + // Sometimes constants don't propogate all the way to the + // inside of the loop, so we call a custom expression every cycle + // rather than iter::repeat(CONST) + let base: IntType = $base_macro!(base_iter); + let exp: u32 = *exp_iter.next().unwrap(); + + let r: (IntType, bool) = $inner_macro!(base, exp); + (acc.0 ^ r.0, acc.1 ^ r.1) + }) + }); + } + }; +} + +// This may panic if it overflows. +macro_rules! inner_pow { + ($base:ident, $exp:ident) => { + ($base.pow($exp), false) + }; +} + +macro_rules! inner_wrapping { + ($base:ident, $exp:ident) => { + ($base.wrapping_pow($exp), false) + }; +} + +macro_rules! inner_overflowing { + ($base:ident, $exp:ident) => { + $base.overflowing_pow($exp) + }; +} + +// This will panic if it overflows. +macro_rules! inner_checked_unwrapped { + ($base:ident, $exp:ident) => { + ($base.checked_pow($exp).unwrap(), false) + }; +} + +macro_rules! inner_saturating { + ($base:ident, $exp:ident) => { + ($base.saturating_pow($exp), false) + }; +} + +macro_rules! make_const_base { + ($name:ident, $x:literal) => { + macro_rules! $name { + ($iter:ident) => { + $x + }; + } + }; +} + +make_const_base!(const_base_m7, -7); +make_const_base!(const_base_m8, -8); + +macro_rules! variable_base { + ($iter:ident) => { + *$iter.next().unwrap() + }; +} + +pow_bench_template!(pow_variable, inner_pow, variable_base); +pow_bench_template!(wrapping_pow_variable, inner_wrapping, variable_base); +pow_bench_template!(overflowing_pow_variable, inner_overflowing, variable_base); +pow_bench_template!(checked_pow_variable, inner_checked_unwrapped, variable_base); +pow_bench_template!(saturating_pow_variable, inner_saturating, variable_base); +pow_bench_template!(pow_m7, inner_pow, const_base_m7); +pow_bench_template!(pow_m8, inner_pow, const_base_m8); diff --git a/library/core/benches/num/mod.rs b/library/core/benches/num/mod.rs index b97014d9bf9..4922ee150d9 100644 --- a/library/core/benches/num/mod.rs +++ b/library/core/benches/num/mod.rs @@ -1,6 +1,7 @@ mod dec2flt; mod flt2dec; mod int_log; +mod int_pow; use std::str::FromStr; use test::{black_box, Bencher}; diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 3df3e8ea05c..5d917dc6fbb 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1787,8 +1787,9 @@ extern "rust-intrinsic" { /// so this rounds half-way cases to the number with an even least significant digit. /// /// May raise an inexact floating-point exception if the argument is not an integer. - /// However, Rust assumes floating-point exceptions cannot be observed, so this is not something that - /// can actually be used from Rust code. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf32` and `roundevenf32`. /// /// The stabilized version of this intrinsic is /// [`f32::round_ties_even`](../../std/primitive.f32.html#method.round_ties_even) @@ -1798,8 +1799,9 @@ extern "rust-intrinsic" { /// so this rounds half-way cases to the number with an even least significant digit. /// /// May raise an inexact floating-point exception if the argument is not an integer. - /// However, Rust assumes floating-point exceptions cannot be observed, so this is not something that - /// can actually be used from Rust code. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf64` and `roundevenf64`. /// /// The stabilized version of this intrinsic is /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index f5ecf501ce9..2df38ab5848 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -104,6 +104,18 @@ macro_rules! nonzero_integers { #[inline] #[rustc_const_stable(feature = "const_nonzero_get", since = "1.34.0")] pub const fn get(self) -> $Int { + // FIXME: Remove this after LLVM supports `!range` metadata for function + // arguments https://github.com/llvm/llvm-project/issues/76628 + // + // Rustc can set range metadata only if it loads `self` from + // memory somewhere. If the value of `self` was from by-value argument + // of some not-inlined function, LLVM don't have range metadata + // to understand that the value cannot be zero. + + // SAFETY: It is an invariant of this type. + unsafe { + intrinsics::assume(self.0 != 0); + } self.0 } @@ -114,7 +126,9 @@ macro_rules! nonzero_integers { #[doc = concat!("Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`")] #[inline] fn from(nonzero: $Ty) -> Self { - nonzero.0 + // Call nonzero to keep information range information + // from get method. + nonzero.get() } } @@ -233,7 +247,7 @@ macro_rules! nonzero_leading_trailing_zeros { #[inline] pub const fn leading_zeros(self) -> u32 { // SAFETY: since `self` cannot be zero, it is safe to call `ctlz_nonzero`. - unsafe { intrinsics::ctlz_nonzero(self.0 as $Uint) as u32 } + unsafe { intrinsics::ctlz_nonzero(self.get() as $Uint) as u32 } } /// Returns the number of trailing zeros in the binary representation @@ -257,7 +271,7 @@ macro_rules! nonzero_leading_trailing_zeros { #[inline] pub const fn trailing_zeros(self) -> u32 { // SAFETY: since `self` cannot be zero, it is safe to call `cttz_nonzero`. - unsafe { intrinsics::cttz_nonzero(self.0 as $Uint) as u32 } + unsafe { intrinsics::cttz_nonzero(self.get() as $Uint) as u32 } } } @@ -515,7 +529,7 @@ macro_rules! nonzero_unsigned_operations { without modifying the original"] #[inline] pub const fn ilog10(self) -> u32 { - super::int_log10::$Int(self.0) + super::int_log10::$Int(self.get()) } /// Calculates the middle point of `self` and `rhs`. diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index b1c1456ade1..ed52de3cbec 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -1,6 +1,6 @@ -use core::{array, assert_eq}; use core::num::NonZeroUsize; use core::sync::atomic::{AtomicUsize, Ordering}; +use core::{array, assert_eq}; #[test] fn array_from_ref() { diff --git a/library/core/tests/cell.rs b/library/core/tests/cell.rs index 71b8eb29600..d6a401c2b4d 100644 --- a/library/core/tests/cell.rs +++ b/library/core/tests/cell.rs @@ -466,14 +466,14 @@ fn const_cells() { const CELL: Cell<i32> = Cell::new(3); const _: i32 = CELL.into_inner(); -/* FIXME(#110395) - const UNSAFE_CELL_FROM: UnsafeCell<i32> = UnsafeCell::from(3); - const _: i32 = UNSAFE_CELL.into_inner(); + /* FIXME(#110395) + const UNSAFE_CELL_FROM: UnsafeCell<i32> = UnsafeCell::from(3); + const _: i32 = UNSAFE_CELL.into_inner(); - const REF_CELL_FROM: RefCell<i32> = RefCell::from(3); - const _: i32 = REF_CELL.into_inner(); + const REF_CELL_FROM: RefCell<i32> = RefCell::from(3); + const _: i32 = REF_CELL.into_inner(); - const CELL_FROM: Cell<i32> = Cell::from(3); - const _: i32 = CELL.into_inner(); -*/ + const CELL_FROM: Cell<i32> = Cell::from(3); + const _: i32 = CELL.into_inner(); + */ } diff --git a/library/core/tests/error.rs b/library/core/tests/error.rs index cb7cb5441d1..5e20c34ca6c 100644 --- a/library/core/tests/error.rs +++ b/library/core/tests/error.rs @@ -1,4 +1,4 @@ -use core::error::{request_value, request_ref, Request}; +use core::error::{request_ref, request_value, Request}; // Test the `Request` API. #[derive(Debug)] diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs index c1c80c46c78..704d2461399 100644 --- a/library/core/tests/fmt/mod.rs +++ b/library/core/tests/fmt/mod.rs @@ -22,11 +22,11 @@ fn test_pointer_formats_data_pointer() { #[test] fn test_estimated_capacity() { assert_eq!(format_args!("").estimated_capacity(), 0); - assert_eq!(format_args!("{}", {""}).estimated_capacity(), 0); + assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); assert_eq!(format_args!("Hello").estimated_capacity(), 5); - assert_eq!(format_args!("Hello, {}!", {""}).estimated_capacity(), 16); - assert_eq!(format_args!("{}, hello!", {"World"}).estimated_capacity(), 0); - assert_eq!(format_args!("{}. 16-bytes piece", {"World"}).estimated_capacity(), 32); + assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); + assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); + assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); } #[test] diff --git a/library/core/tests/hash/mod.rs b/library/core/tests/hash/mod.rs index addc255de4a..3b9351457a9 100644 --- a/library/core/tests/hash/mod.rs +++ b/library/core/tests/hash/mod.rs @@ -35,7 +35,8 @@ impl Hasher for MyHasher { #[test] fn test_writer_hasher() { // FIXME(#110395) - /* const */ fn hash<T: Hash>(t: &T) -> u64 { + /* const */ + fn hash<T: Hash>(t: &T) -> u64 { let mut s = MyHasher { hash: 0 }; t.hash(&mut s); s.finish() @@ -140,7 +141,8 @@ impl Hash for Custom { #[test] fn test_custom_state() { // FIXME(#110395) - /* const */ fn hash<T: Hash>(t: &T) -> u64 { + /* const */ + fn hash<T: Hash>(t: &T) -> u64 { let mut c = CustomHasher { output: 0 }; t.hash(&mut c); c.finish() diff --git a/library/core/tests/iter/adapters/chain.rs b/library/core/tests/iter/adapters/chain.rs index 175a1b638e1..ad78a85a88d 100644 --- a/library/core/tests/iter/adapters/chain.rs +++ b/library/core/tests/iter/adapters/chain.rs @@ -42,7 +42,10 @@ fn test_iterator_chain_advance_by() { let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); assert_eq!(iter.advance_by(xs.len() + i), Ok(())); assert_eq!(iter.next(), Some(&ys[i])); - assert_eq!(iter.advance_by(100), Err(NonZeroUsize::new(100 - (ys.len() - i - 1)).unwrap())); + assert_eq!( + iter.advance_by(100), + Err(NonZeroUsize::new(100 - (ys.len() - i - 1)).unwrap()) + ); assert_eq!(iter.advance_by(0), Ok(())); } @@ -71,7 +74,10 @@ fn test_iterator_chain_advance_back_by() { let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); assert_eq!(iter.advance_back_by(i), Ok(())); assert_eq!(iter.next_back(), Some(&ys[ys.len() - i - 1])); - assert_eq!(iter.advance_back_by(100), Err(NonZeroUsize::new(100 - (len - i - 1)).unwrap())); + assert_eq!( + iter.advance_back_by(100), + Err(NonZeroUsize::new(100 - (len - i - 1)).unwrap()) + ); assert_eq!(iter.advance_back_by(0), Ok(())); } @@ -79,7 +85,10 @@ fn test_iterator_chain_advance_back_by() { let mut iter = Unfuse::new(xs).chain(Unfuse::new(ys)); assert_eq!(iter.advance_back_by(ys.len() + i), Ok(())); assert_eq!(iter.next_back(), Some(&xs[xs.len() - i - 1])); - assert_eq!(iter.advance_back_by(100), Err(NonZeroUsize::new(100 - (xs.len() - i - 1)).unwrap())); + assert_eq!( + iter.advance_back_by(100), + Err(NonZeroUsize::new(100 - (xs.len() - i - 1)).unwrap()) + ); assert_eq!(iter.advance_back_by(0), Ok(())); } diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/core/tests/iter/adapters/flatten.rs index 91809c9e5fd..f429d90cd7d 100644 --- a/library/core/tests/iter/adapters/flatten.rs +++ b/library/core/tests/iter/adapters/flatten.rs @@ -1,5 +1,5 @@ -use core::assert_eq; use super::*; +use core::assert_eq; use core::iter::*; use core::num::NonZeroUsize; diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/core/tests/iter/adapters/step_by.rs index 4c5b1dd9a6b..70c9906163a 100644 --- a/library/core/tests/iter/adapters/step_by.rs +++ b/library/core/tests/iter/adapters/step_by.rs @@ -245,7 +245,6 @@ fn test_step_by_skip() { assert_eq!((200..=255u8).step_by(10).nth(3), Some(230)); } - struct DeOpt<I: Iterator>(I); impl<I: Iterator> Iterator for DeOpt<I> { @@ -265,17 +264,15 @@ impl<I: DoubleEndedIterator> DoubleEndedIterator for DeOpt<I> { #[test] fn test_step_by_fold_range_specialization() { macro_rules! t { - ($range:expr, $var: ident, $body:tt) => { - { - // run the same tests for the non-optimized version - let mut $var = DeOpt($range); - $body - } - { - let mut $var = $range; - $body - } + ($range:expr, $var: ident, $body:tt) => {{ + // run the same tests for the non-optimized version + let mut $var = DeOpt($range); + $body } + { + let mut $var = $range; + $body + }}; } t!((1usize..5).step_by(1), r, { @@ -288,13 +285,12 @@ fn test_step_by_fold_range_specialization() { assert_eq!(r.sum::<usize>(), 2); }); - t!((0usize..5).step_by(2), r, { assert_eq!(r.next(), Some(0)); assert_eq!(r.sum::<usize>(), 6); }); - t!((usize::MAX - 6 .. usize::MAX).step_by(5), r, { + t!((usize::MAX - 6..usize::MAX).step_by(5), r, { assert_eq!(r.next(), Some(usize::MAX - 6)); assert_eq!(r.sum::<usize>(), usize::MAX - 1); }); diff --git a/library/core/tests/iter/adapters/take.rs b/library/core/tests/iter/adapters/take.rs index 3cad47c06de..ff6e362b065 100644 --- a/library/core/tests/iter/adapters/take.rs +++ b/library/core/tests/iter/adapters/take.rs @@ -93,7 +93,10 @@ fn test_take_advance_by() { assert_eq!((0..2).take(1).advance_back_by(10), Err(NonZeroUsize::new(9).unwrap())); assert_eq!((0..0).take(1).advance_back_by(1), Err(NonZeroUsize::new(1).unwrap())); assert_eq!((0..0).take(1).advance_back_by(0), Ok(())); - assert_eq!((0..usize::MAX).take(100).advance_back_by(usize::MAX), Err(NonZeroUsize::new(usize::MAX - 100).unwrap())); + assert_eq!( + (0..usize::MAX).take(100).advance_back_by(usize::MAX), + Err(NonZeroUsize::new(usize::MAX - 100).unwrap()) + ); } #[test] diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs index c3508be8598..ba54de5822b 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/core/tests/iter/adapters/zip.rs @@ -184,7 +184,7 @@ fn test_zip_nested_sideffectful() { let it = xs.iter_mut().map(|x| *x = 1).enumerate().zip(&ys); it.count(); } - let length_aware = &xs == &[1, 1, 1, 1, 0, 0]; + let length_aware = &xs == &[1, 1, 1, 1, 0, 0]; let probe_first = &xs == &[1, 1, 1, 1, 1, 0]; // either implementation is valid according to zip documentation diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index 995bbf0e261..9c1dce7b66d 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -168,7 +168,10 @@ fn test_iterator_advance_back_by() { let mut iter = v.iter(); assert_eq!(iter.advance_back_by(i), Ok(())); assert_eq!(iter.next_back().unwrap(), &v[v.len() - 1 - i]); - assert_eq!(iter.advance_back_by(100), Err(NonZeroUsize::new(100 - (v.len() - 1 - i)).unwrap())); + assert_eq!( + iter.advance_back_by(100), + Err(NonZeroUsize::new(100 - (v.len() - 1 - i)).unwrap()) + ); } assert_eq!(v.iter().advance_back_by(v.len()), Ok(())); @@ -183,7 +186,10 @@ fn test_iterator_rev_advance_back_by() { let mut iter = v.iter().rev(); assert_eq!(iter.advance_back_by(i), Ok(())); assert_eq!(iter.next_back().unwrap(), &v[i]); - assert_eq!(iter.advance_back_by(100), Err(NonZeroUsize::new(100 - (v.len() - 1 - i)).unwrap())); + assert_eq!( + iter.advance_back_by(100), + Err(NonZeroUsize::new(100 - (v.len() - 1 - i)).unwrap()) + ); } assert_eq!(v.iter().rev().advance_back_by(v.len()), Ok(())); diff --git a/library/core/tests/net/ip_addr.rs b/library/core/tests/net/ip_addr.rs index 7f7802c221a..3d13bffba92 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/core/tests/net/ip_addr.rs @@ -664,7 +664,11 @@ fn ipv6_properties() { &[0x20, 1, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global ); - check!("2001:30::", &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], global | unicast_global); + check!( + "2001:30::", + &[0x20, 1, 0, 0x30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + global | unicast_global + ); check!("2001:40::", &[0x20, 1, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unicast_global); check!( diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index 00a308b29d2..b1b9492f182 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -178,19 +178,19 @@ fn test_or_else() { assert_eq!(x.or_else(two), Some(2)); assert_eq!(x.or_else(none), None); -/* FIXME(#110395) - const FOO: Option<isize> = Some(1); - const A: Option<isize> = FOO.or_else(two); - const B: Option<isize> = FOO.or_else(none); - assert_eq!(A, Some(1)); - assert_eq!(B, Some(1)); - - const BAR: Option<isize> = None; - const C: Option<isize> = BAR.or_else(two); - const D: Option<isize> = BAR.or_else(none); - assert_eq!(C, Some(2)); - assert_eq!(D, None); -*/ + /* FIXME(#110395) + const FOO: Option<isize> = Some(1); + const A: Option<isize> = FOO.or_else(two); + const B: Option<isize> = FOO.or_else(none); + assert_eq!(A, Some(1)); + assert_eq!(B, Some(1)); + + const BAR: Option<isize> = None; + const C: Option<isize> = BAR.or_else(two); + const D: Option<isize> = BAR.or_else(none); + assert_eq!(C, Some(2)); + assert_eq!(D, None); + */ } #[test] @@ -486,15 +486,15 @@ const fn option_const_mut() { None => unreachable!(), } } -/* FIXME(const-hack) - { - let as_mut: Option<&mut usize> = Option::from(&mut option); - match as_mut { - Some(v) => *v = 42, - None => unreachable!(), + /* FIXME(const-hack) + { + let as_mut: Option<&mut usize> = Option::from(&mut option); + match as_mut { + Some(v) => *v = 42, + None => unreachable!(), + } } - } -*/ + */ } #[test] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 238f29c5980..b68f2a50b32 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -478,7 +478,11 @@ fn align_offset_various_strides() { x |= test_stride::<A9>(ptr::invalid::<A9>(ptr), align); #[repr(packed)] - struct A10(#[allow(dead_code)] u32, #[allow(dead_code)] u32, #[allow(dead_code)] u16); + struct A10( + #[allow(dead_code)] u32, + #[allow(dead_code)] u32, + #[allow(dead_code)] u16, + ); x |= test_stride::<A10>(ptr::invalid::<A10>(ptr), align); x |= test_stride::<u32>(ptr::invalid::<u32>(ptr), align); @@ -532,7 +536,11 @@ fn align_offset_various_strides_const() { test_stride::<A6>(ptr::invalid::<A6>(ptr), ptr, align); #[repr(packed)] - struct A7(#[allow(dead_code)] u32, #[allow(dead_code)] u16, #[allow(dead_code)] u8); + struct A7( + #[allow(dead_code)] u32, + #[allow(dead_code)] u16, + #[allow(dead_code)] u8, + ); test_stride::<A7>(ptr::invalid::<A7>(ptr), ptr, align); #[repr(packed)] @@ -540,11 +548,19 @@ fn align_offset_various_strides_const() { test_stride::<A8>(ptr::invalid::<A8>(ptr), ptr, align); #[repr(packed)] - struct A9(#[allow(dead_code)] u32, #[allow(dead_code)] u32, #[allow(dead_code)] u8); + struct A9( + #[allow(dead_code)] u32, + #[allow(dead_code)] u32, + #[allow(dead_code)] u8, + ); test_stride::<A9>(ptr::invalid::<A9>(ptr), ptr, align); #[repr(packed)] - struct A10(#[allow(dead_code)] u32, #[allow(dead_code)] u32, #[allow(dead_code)] u16); + struct A10( + #[allow(dead_code)] u32, + #[allow(dead_code)] u32, + #[allow(dead_code)] u16, + ); test_stride::<A10>(ptr::invalid::<A10>(ptr), ptr, align); test_stride::<u32>(ptr::invalid::<u32>(ptr), ptr, align); diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs index 24ab4be9d8c..23f07bf84b3 100644 --- a/library/core/tests/time.rs +++ b/library/core/tests/time.rs @@ -479,22 +479,22 @@ fn duration_const() { const CHECKED_MUL: Option<Duration> = Duration::SECOND.checked_mul(1); assert_eq!(CHECKED_MUL, Some(Duration::SECOND)); -/* FIXME(#110395) - const MUL_F32: Duration = Duration::SECOND.mul_f32(1.0); - assert_eq!(MUL_F32, Duration::SECOND); + /* FIXME(#110395) + const MUL_F32: Duration = Duration::SECOND.mul_f32(1.0); + assert_eq!(MUL_F32, Duration::SECOND); - const MUL_F64: Duration = Duration::SECOND.mul_f64(1.0); - assert_eq!(MUL_F64, Duration::SECOND); + const MUL_F64: Duration = Duration::SECOND.mul_f64(1.0); + assert_eq!(MUL_F64, Duration::SECOND); - const CHECKED_DIV: Option<Duration> = Duration::SECOND.checked_div(1); - assert_eq!(CHECKED_DIV, Some(Duration::SECOND)); + const CHECKED_DIV: Option<Duration> = Duration::SECOND.checked_div(1); + assert_eq!(CHECKED_DIV, Some(Duration::SECOND)); - const DIV_F32: Duration = Duration::SECOND.div_f32(1.0); - assert_eq!(DIV_F32, Duration::SECOND); + const DIV_F32: Duration = Duration::SECOND.div_f32(1.0); + assert_eq!(DIV_F32, Duration::SECOND); - const DIV_F64: Duration = Duration::SECOND.div_f64(1.0); - assert_eq!(DIV_F64, Duration::SECOND); -*/ + const DIV_F64: Duration = Duration::SECOND.div_f64(1.0); + assert_eq!(DIV_F64, Duration::SECOND); + */ const DIV_DURATION_F32: f32 = Duration::SECOND.div_duration_f32(Duration::SECOND); assert_eq!(DIV_DURATION_F32, 1.0); diff --git a/library/std/src/num.rs b/library/std/src/num.rs index 3cd5fa458e0..55f6ddcf77f 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -9,9 +9,6 @@ #[cfg(test)] mod tests; -#[cfg(test)] -mod benches; - #[stable(feature = "saturating_int_impl", since = "1.74.0")] pub use core::num::Saturating; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/num/benches.rs b/library/std/src/num/benches.rs deleted file mode 100644 index 233ea0506c0..00000000000 --- a/library/std/src/num/benches.rs +++ /dev/null @@ -1,9 +0,0 @@ -use test::Bencher; - -#[bench] -fn bench_pow_function(b: &mut Bencher) { - let v = (0..1024).collect::<Vec<u32>>(); - b.iter(|| { - v.iter().fold(0u32, |old, new| old.pow(*new as u32)); - }); -} diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 46dc9ff00bd..59f67f9901f 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -1,4 +1,4 @@ -#![cfg(not(target_env="sgx"))] +#![cfg(not(target_env = "sgx"))] use std::env; use std::fs; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 2fa5a8e5e38..2e93796d981 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -298,24 +298,18 @@ where let mut filtered = FilteredTests { tests: Vec::new(), benches: Vec::new(), next_id: 0 }; - for test in filter_tests(opts, tests) { + let mut filtered_tests = filter_tests(opts, tests); + if !opts.bench_benchmarks { + filtered_tests = convert_benchmarks_to_tests(filtered_tests); + } + + for test in filtered_tests { let mut desc = test.desc; desc.name = desc.name.with_padding(test.testfn.padding()); match test.testfn { - DynBenchFn(benchfn) => { - if opts.bench_benchmarks { - filtered.add_bench(desc, DynBenchFn(benchfn)); - } else { - filtered.add_test(desc, DynBenchAsTestFn(benchfn)); - } - } - StaticBenchFn(benchfn) => { - if opts.bench_benchmarks { - filtered.add_bench(desc, StaticBenchFn(benchfn)); - } else { - filtered.add_test(desc, StaticBenchAsTestFn(benchfn)); - } + DynBenchFn(_) | StaticBenchFn(_) => { + filtered.add_bench(desc, test.testfn); } testfn => { filtered.add_test(desc, testfn); diff --git a/rustfmt.toml b/rustfmt.toml index e292a310742..e6cc298ec44 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -13,7 +13,7 @@ ignore = [ # tests for now are not formatted, as they are sometimes pretty-printing constrained # (and generally rustfmt can move around comments in UI-testing incompatible ways) - "tests", + "/tests/", # do not format submodules # FIXME: sync submodule list with tidy/bootstrap/etc diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 95553e7ea02..e2c90cc8c84 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "autocfg" @@ -37,9 +37,9 @@ checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -79,13 +79,13 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.17" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ - "lazy_static", "memchr", "regex-automata", + "serde", ] [[package]] @@ -110,9 +110,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.7" +version = "4.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642" dependencies = [ "clap_builder", "clap_derive", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstyle", "clap_lex", @@ -130,9 +130,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.3" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7" +checksum = "97aeaa95557bd02f23fbb662f981670c3d20c5a26e69f7354b28f57092437fcd" dependencies = [ "clap", ] @@ -166,24 +166,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -192,31 +192,29 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" dependencies = [ "cfg-if", ] [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -224,15 +222,15 @@ dependencies = [ [[package]] name = "diff" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.3" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -240,9 +238,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "errno" @@ -267,27 +265,21 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi", + "windows-sys", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -295,15 +287,15 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ "aho-corasick", "bstr", - "fnv", "log", - "regex", + "regex-automata", + "regex-syntax", ] [[package]] @@ -314,36 +306,34 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "home" -version = "0.5.4" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "winapi", + "windows-sys", ] [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" dependencies = [ - "crossbeam-utils", + "crossbeam-deque", "globset", - "lazy_static", "log", "memchr", - "regex", + "regex-automata", "same-file", - "thread_local", "walkdir", "winapi-util", ] [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "junction" @@ -356,16 +346,10 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "linux-raw-sys" @@ -375,18 +359,15 @@ checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lzma-sys" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb4b7c3eddad11d3af9e86c487607d2d2442d185d848575365c4856ba96d619" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" dependencies = [ "cc", "libc", @@ -395,33 +376,24 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "ntapi" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] [[package]] name = "object" -version = "0.32.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -434,9 +406,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opener" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952" +checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005" dependencies = [ "bstr", "winapi", @@ -444,9 +416,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "pretty_assertions" @@ -460,18 +432,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -498,18 +470,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] -name = "regex" -version = "1.5.6" +name = "regex-automata" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -517,16 +489,10 @@ dependencies = [ ] [[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - -[[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustix" @@ -543,9 +509,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -558,27 +524,30 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.17" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", @@ -587,9 +556,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -598,9 +567,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -609,9 +578,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -620,9 +589,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.3" +version = "0.30.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2dbd2894d23b2d78dae768d85e323b557ac3ac71a5d917a31536d8f77ebada" +checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2" dependencies = [ "cfg-if", "core-foundation-sys", @@ -635,9 +604,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -646,42 +615,33 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "version_check" @@ -691,12 +651,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -718,9 +677,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -818,18 +777,20 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "xattr" -version = "0.2.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" dependencies = [ "libc", + "linux-raw-sys", + "rustix", ] [[package]] name = "xz2" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" dependencies = [ "lzma-sys", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 8e09f216d74..b232885c590 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -33,30 +33,34 @@ path = "src/bin/sccache-plus-cl.rs" test = false [dependencies] +# Most of the time updating these dependencies requires modifications +# to the bootstrap codebase; otherwise, some targets will fail. That's +# why these dependencies are explicitly pinned. +cc = "=1.0.73" +cmake = "=0.1.48" + build_helper = { path = "../tools/build_helper" } -cc = "1.0.69" -clap = { version = "4.4.7", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } -clap_complete = "4.4.3" -cmake = "0.1.38" +clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } +clap_complete = "4.4" fd-lock = "4.0" filetime = "0.2" -home = "0.5.4" -ignore = "0.4.10" -libc = "0.2.150" -object = { version = "0.32.0", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } -once_cell = "1.7.2" +home = "0.5" +ignore = "0.4" +libc = "0.2" +object = { version = "0.32", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } +once_cell = "1.19" opener = "0.5" -semver = "1.0.17" -serde = "1.0.137" +semver = "1.0" +serde = "1.0" # Directly use serde_derive rather than through the derive feature of serde to allow building both # in parallel and to allow serde_json and toml to start building as soon as serde has been built. -serde_derive = "1.0.137" -serde_json = "1.0.2" +serde_derive = "1.0" +serde_json = "1.0" sha2 = "0.10" tar = "0.4" -termcolor = "1.2.0" +termcolor = "1.4" toml = "0.5" -walkdir = "2" +walkdir = "2.4" xz2 = "0.1" # Dependencies needed by the build-metrics feature diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 85132e405b4..fcdd742e69c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1776,7 +1776,6 @@ impl Config { check_ci_llvm!(static_libstdcpp); check_ci_llvm!(targets); check_ci_llvm!(experimental_targets); - check_ci_llvm!(link_jobs); check_ci_llvm!(clang_cl); check_ci_llvm!(version_suffix); check_ci_llvm!(cflags); diff --git a/src/bootstrap/src/tests/config.rs b/src/bootstrap/src/tests/config.rs index 6f432343882..c65067f8e8f 100644 --- a/src/bootstrap/src/tests/config.rs +++ b/src/bootstrap/src/tests/config.rs @@ -32,9 +32,12 @@ fn download_ci_llvm() { assert_eq!(parse_llvm("rust.channel = \"dev\""), if_unchanged); assert!(!parse_llvm("rust.channel = \"stable\"")); assert_eq!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""), if_unchanged); - assert_eq!(parse_llvm( - "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\"" - ), if_unchanged); + assert_eq!( + parse_llvm( + "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\"" + ), + if_unchanged + ); assert!(!parse_llvm( "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-unchanged\"" )); diff --git a/src/bootstrap/src/tests/helpers.rs b/src/bootstrap/src/tests/helpers.rs index 163594dbb2f..2d626fad417 100644 --- a/src/bootstrap/src/tests/helpers.rs +++ b/src/bootstrap/src/tests/helpers.rs @@ -1,4 +1,4 @@ -use crate::utils::helpers::{extract_beta_rev, hex_encode, make, check_cfg_arg}; +use crate::utils::helpers::{check_cfg_arg, extract_beta_rev, hex_encode, make}; use std::path::PathBuf; #[test] diff --git a/src/ci/scripts/dump-environment.sh b/src/ci/scripts/dump-environment.sh index c6774b52ab9..812690181e9 100755 --- a/src/ci/scripts/dump-environment.sh +++ b/src/ci/scripts/dump-environment.sh @@ -1,6 +1,8 @@ #!/bin/bash # This script dumps information about the build environment to stdout. +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + set -euo pipefail IFS=$'\n\t' @@ -17,3 +19,17 @@ set +o pipefail du . | sort -nr | head -n100 set -o pipefail echo + +if isMacOS +then + # Debugging information that might be helpful for diagnosing macOS + # performance issues. + # SIP + csrutil status + # Gatekeeper + spctl --status + # Authorization policy + DevToolsSecurity -status + # Spotlight status + mdutil -avs +fi diff --git a/src/doc/unstable-book/src/compiler-flags/env.md b/src/doc/unstable-book/src/compiler-flags/env-set.md index ac6d7474a9b..e5d7206c3a3 100644 --- a/src/doc/unstable-book/src/compiler-flags/env.md +++ b/src/doc/unstable-book/src/compiler-flags/env-set.md @@ -1,4 +1,4 @@ -# `env` +# `env-set` The tracking issue for this feature is: [#118372](https://github.com/rust-lang/rust/issues/118372). @@ -11,11 +11,11 @@ from the `proc_macro` crate. This information will be stored in the dep-info files. For more information about dep-info files, take a look [here](https://doc.rust-lang.org/cargo/guide/build-cache.html#dep-info-files). -When retrieving an environment variable value, the one specified by `--env` will take +When retrieving an environment variable value, the one specified by `--env-set` will take precedence. For example, if you want have `PATH=a` in your environment and pass: ```bash -rustc --env PATH=env +rustc --env-set PATH=env ``` Then you will have: @@ -24,17 +24,17 @@ Then you will have: assert_eq!(env!("PATH"), "env"); ``` -It will trigger a new compilation if any of the `--env` argument value is different. +It will trigger a new compilation if any of the `--env-set` argument value is different. So if you first passed: ```bash ---env A=B --env X=12 +--env-set A=B --env X=12 ``` and then on next compilation: ```bash ---env A=B +--env-set A=B ``` `X` value is different (not set) so the code will be re-compiled. @@ -42,4 +42,4 @@ and then on next compilation: Please note that on Windows, environment variables are case insensitive but case preserving whereas `rustc`'s environment variables are case sensitive. For example, having `Path` in your environment (case insensitive) is different than using -`rustc --env Path=...` (case sensitive). +`rustc --env-set Path=...` (case sensitive). diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 8ffd8f67df2..e1436dcde67 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -1761,4 +1761,8 @@ _x.py() { esac } -complete -F _x.py -o nosort -o bashdefault -o default x.py +if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then + complete -F _x.py -o nosort -o bashdefault -o default x.py +else + complete -F _x.py -o bashdefault -o default x.py +fi diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index 605f9e496c7..b736f4a7956 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -69,8 +69,8 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String let mut parser = match rustc_parse::maybe_new_parser_from_source_str(&sess, file_name, snippet.clone()) { Ok(parser) => parser, - Err(diagnostics) => { - drop(diagnostics); + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); return None; } }; diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 9a900625268..82746a7ab03 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -589,7 +589,7 @@ pub(crate) fn make_test( let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) { Ok(p) => p, Err(errs) => { - drop(errs); + errs.into_iter().for_each(|err| err.cancel()); return (found_main, found_extern_crate, found_macro); } }; @@ -759,8 +759,10 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { Ok(p) => p, - Err(_) => { - // If there is an unclosed delimiter, an error will be returned by the tokentrees. + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + // If there is an unclosed delimiter, an error will be returned by the + // tokentrees. return false; } }; diff --git a/src/llvm-project b/src/llvm-project -Subproject 606bc11367b475542bd6228163424ca43b4dbdb +Subproject 700fbf978e6c5bb297d12963206f7487722de48 diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 3e428a38a34e820a461d2cc082e726d3bda71bc +Subproject 84976cd699f4aea56cb3a90ce3eedeed9e20d5a diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 99d80bec025..5ba960db66d 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -38,7 +38,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 73c25550742..012797e5ca7 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -26,7 +26,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.ref }} @@ -72,7 +72,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install i686 dependencies if: matrix.host == 'i686-unknown-linux-gnu' @@ -151,7 +151,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install toolchain run: rustup show active-toolchain @@ -175,7 +175,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install toolchain run: rustup show active-toolchain @@ -231,7 +231,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install toolchain run: rustup show active-toolchain diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 0f0e3f2db92..37f18a4c087 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -24,7 +24,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run - name: Build diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 999ee7acfe7..94f494b65c4 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 30bd476332f..05e1b3b9202 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v3 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index f82421a687b..4d32bbec914 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5105,6 +5105,7 @@ Released 2018-09-13 [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else [`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum +[`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets [`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop @@ -5294,6 +5295,7 @@ Released 2018-09-13 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite +[`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map @@ -5440,6 +5442,7 @@ Released 2018-09-13 [`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some +[`option_as_ref_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_cloned [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap [`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used @@ -5483,6 +5486,7 @@ Released 2018-09-13 [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names +[`pub_underscore_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields [`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use [`pub_with_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_with_shorthand [`pub_without_shorthand`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_without_shorthand @@ -5580,6 +5584,7 @@ Released 2018-09-13 [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc [`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core +[`str_split_at_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_split_at_newline [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -5612,6 +5617,7 @@ Released 2018-09-13 [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`test_attr_in_doctest`]: https://rust-lang.github.io/rust-clippy/master/index.html#test_attr_in_doctest [`tests_outside_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#tests_outside_test_module +[`thread_local_initializer_can_be_made_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#thread_local_initializer_can_be_made_const [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args @@ -5810,4 +5816,5 @@ Released 2018-09-13 [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items +[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior <!-- end autogenerated links to configuration documentation --> diff --git a/src/tools/clippy/COPYRIGHT b/src/tools/clippy/COPYRIGHT index 82703b18fd7..219693d63d9 100644 --- a/src/tools/clippy/COPYRIGHT +++ b/src/tools/clippy/COPYRIGHT @@ -1,6 +1,6 @@ // REUSE-IgnoreStart -Copyright 2014-2022 The Rust Project Developers +Copyright 2014-2024 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license diff --git a/src/tools/clippy/LICENSE-APACHE b/src/tools/clippy/LICENSE-APACHE index 0d62c37278e..506582c31d6 100644 --- a/src/tools/clippy/LICENSE-APACHE +++ b/src/tools/clippy/LICENSE-APACHE @@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work. same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2014-2022 The Rust Project Developers +Copyright 2014-2024 The Rust Project Developers Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/tools/clippy/LICENSE-MIT b/src/tools/clippy/LICENSE-MIT index b724b24aa83..6d8ee9afb61 100644 --- a/src/tools/clippy/LICENSE-MIT +++ b/src/tools/clippy/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2014-2022 The Rust Project Developers +Copyright (c) 2014-2024 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index 5d490645d89..fa18447090c 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. @@ -278,7 +278,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT <!-- REUSE-IgnoreStart --> -Copyright 2014-2023 The Rust Project Developers +Copyright 2014-2024 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md index 486ea3df704..e7972b0db19 100644 --- a/src/tools/clippy/book/src/README.md +++ b/src/tools/clippy/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md index b02457307d7..a048fbbd8ac 100644 --- a/src/tools/clippy/book/src/SUMMARY.md +++ b/src/tools/clippy/book/src/SUMMARY.md @@ -9,6 +9,7 @@ - [Clippy's Lints](lints.md) - [Continuous Integration](continuous_integration/README.md) - [GitHub Actions](continuous_integration/github_actions.md) + - [GitLab CI](continuous_integration/gitlab.md) - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) - [Basics](development/basics.md) diff --git a/src/tools/clippy/book/src/continuous_integration/github_actions.md b/src/tools/clippy/book/src/continuous_integration/github_actions.md index 339287a7dd9..b588c8f0f02 100644 --- a/src/tools/clippy/book/src/continuous_integration/github_actions.md +++ b/src/tools/clippy/book/src/continuous_integration/github_actions.md @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` diff --git a/src/tools/clippy/book/src/continuous_integration/gitlab.md b/src/tools/clippy/book/src/continuous_integration/gitlab.md new file mode 100644 index 00000000000..bb3ef246c2f --- /dev/null +++ b/src/tools/clippy/book/src/continuous_integration/gitlab.md @@ -0,0 +1,16 @@ +# GitLab CI + +You can add Clippy to GitLab CI by using the latest stable [rust docker image](https://hub.docker.com/_/rust), +as it is shown in the `.gitlab-ci.yml` CI configuration file below, + +```yml +# Make sure CI fails on all warnings, including Clippy lints +variables: + RUSTFLAGS: "-Dwarnings" + +clippy_check: + image: rust:latest + script: + - rustup component add clippy + - cargo clippy --all-targets --all-features +``` diff --git a/src/tools/clippy/book/src/development/macro_expansions.md b/src/tools/clippy/book/src/development/macro_expansions.md index c5eb000272d..aecca9ef72e 100644 --- a/src/tools/clippy/book/src/development/macro_expansions.md +++ b/src/tools/clippy/book/src/development/macro_expansions.md @@ -102,7 +102,7 @@ let x: Option<u32> = Some(42); m!(x, x.unwrap()); ``` -If the `m!(x, x.unwrapp());` line is expanded, we would get two expanded +If the `m!(x, x.unwrap());` line is expanded, we would get two expanded expressions: - `x.is_some()` (from the `$a.is_some()` line in the `m` macro) diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md index a8c9660da4c..dc29ab5d08d 100644 --- a/src/tools/clippy/book/src/development/type_checking.md +++ b/src/tools/clippy/book/src/development/type_checking.md @@ -133,7 +133,7 @@ in this chapter: - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) -[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html#variant.Adt +[Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Adt [AdtDef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adt/struct.AdtDef.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type @@ -144,7 +144,7 @@ in this chapter: [LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html [pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/typeck_results/struct.TypeckResults.html#method.pat_ty [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.TyKind.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html [hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 7c9a8eb1bfb..3b62ae0524a 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -805,3 +805,13 @@ for _ in &mut *rmvec {} * [`missing_errors_doc`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc) +## `pub-underscore-fields-behavior` + + +**Default Value:** `"PublicallyExported"` + +--- +**Affected lints:** +* [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields) + + diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index a4f368397ce..5477d9b83a7 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,5 +1,5 @@ use crate::msrvs::Msrv; -use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, Rename}; +use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; use crate::ClippyConfiguration; use rustc_data_structures::fx::FxHashSet; use rustc_session::Session; @@ -547,6 +547,11 @@ define_Conf! { /// /// Whether to also run the listed lints on private items. (check_private_items: bool = false), + /// Lint: PUB_UNDERSCORE_FIELDS + /// + /// Lint "public" fields in a struct that are prefixed with an underscore based on their + /// exported visibility, or whether they are marked as "pub". + (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PublicallyExported), } /// Search for the configuration file. diff --git a/src/tools/clippy/clippy_config/src/metadata.rs b/src/tools/clippy/clippy_config/src/metadata.rs index 2451fbc91e8..3ba2796e18d 100644 --- a/src/tools/clippy/clippy_config/src/metadata.rs +++ b/src/tools/clippy/clippy_config/src/metadata.rs @@ -96,6 +96,9 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> { doc_comment.make_ascii_lowercase(); let lints: Vec<String> = doc_comment .split_off(DOC_START.len()) + .lines() + .next() + .unwrap() .split(", ") .map(str::to_string) .collect(); diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index dae9f09ec00..72d5b9aff28 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -17,7 +17,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } - 1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN } + 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index df48cc3f5e3..baee09629ac 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -126,3 +126,9 @@ unimplemented_serialize! { Rename, MacroMatcher, } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] +pub enum PubUnderscoreFieldsBehaviour { + PublicallyExported, + AllPubFields, +} diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 31a42734c13..5d9cde06cd8 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -67,7 +67,7 @@ pub fn create( if pass == "early" { println!( "\n\ - NOTE: Use a late pass unless you need something specific from\ + NOTE: Use a late pass unless you need something specific from\n\ an early pass, as they lack many features and utilities" ); } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index bd12ee40628..1df5a25f674 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,12 +1,14 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{method_chain_args, sext}; -use rustc_hir::{Expr, ExprKind}; +use clippy_utils::{clip, method_chain_args, sext}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, UintTy}; use super::CAST_SIGN_LOSS; +const METHODS_RET_POSITIVE: &[&str] = &["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; + pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { if should_lint(cx, cast_op, cast_from, cast_to) { span_lint( @@ -25,33 +27,28 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast return false; } - // Don't lint for positive constants. - let const_val = constant(cx, cx.typeck_results(), cast_op); - if let Some(Constant::Int(n)) = const_val - && let ty::Int(ity) = *cast_from.kind() - && sext(cx.tcx, n, ity) >= 0 - { + // Don't lint if `cast_op` is known to be positive. + if let Sign::ZeroOrPositive = expr_sign(cx, cast_op, cast_from) { return false; } - // Don't lint for the result of methods that always return non-negative values. - if let ExprKind::MethodCall(path, ..) = cast_op.kind { - let mut method_name = path.ident.name.as_str(); - let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; - - if method_name == "unwrap" - && let Some(arglist) = method_chain_args(cast_op, &["unwrap"]) - && let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind - { - method_name = inner_path.ident.name.as_str(); - } - - if allowed_methods.iter().any(|&name| method_name == name) { - return false; - } + let (mut uncertain_count, mut negative_count) = (0, 0); + // Peel off possible binary expressions, e.g. x * x * y => [x, x, y] + let Some(exprs) = exprs_with_selected_binop_peeled(cast_op) else { + // Assume cast sign lose if we cannot determine the sign of `cast_op` + return true; + }; + for expr in exprs { + let ty = cx.typeck_results().expr_ty(expr); + match expr_sign(cx, expr, ty) { + Sign::Negative => negative_count += 1, + Sign::Uncertain => uncertain_count += 1, + Sign::ZeroOrPositive => (), + }; } - true + // Lint if there are odd number of uncertain or negative results + uncertain_count % 2 == 1 || negative_count % 2 == 1 }, (false, true) => !cast_to.is_signed(), @@ -59,3 +56,97 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast (_, _) => false, } } + +fn get_const_int_eval(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Option<i128> { + if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + && let ty::Int(ity) = *ty.kind() + { + return Some(sext(cx.tcx, n, ity)); + } + None +} + +enum Sign { + ZeroOrPositive, + Negative, + Uncertain, +} + +fn expr_sign(cx: &LateContext<'_>, expr: &Expr<'_>, ty: Ty<'_>) -> Sign { + // Try evaluate this expr first to see if it's positive + if let Some(val) = get_const_int_eval(cx, expr, ty) { + return if val >= 0 { Sign::ZeroOrPositive } else { Sign::Negative }; + } + // Calling on methods that always return non-negative values. + if let ExprKind::MethodCall(path, caller, args, ..) = expr.kind { + let mut method_name = path.ident.name.as_str(); + + if method_name == "unwrap" + && let Some(arglist) = method_chain_args(expr, &["unwrap"]) + && let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind + { + method_name = inner_path.ident.name.as_str(); + } + + if method_name == "pow" + && let [arg] = args + { + return pow_call_result_sign(cx, caller, arg); + } else if METHODS_RET_POSITIVE.iter().any(|&name| method_name == name) { + return Sign::ZeroOrPositive; + } + } + + Sign::Uncertain +} + +/// Return the sign of the `pow` call's result. +/// +/// If the caller is a positive number, the result is always positive, +/// If the `power_of` is a even number, the result is always positive as well, +/// Otherwise a [`Sign::Uncertain`] will be returned. +fn pow_call_result_sign(cx: &LateContext<'_>, caller: &Expr<'_>, power_of: &Expr<'_>) -> Sign { + let caller_ty = cx.typeck_results().expr_ty(caller); + if let Some(caller_val) = get_const_int_eval(cx, caller, caller_ty) + && caller_val >= 0 + { + return Sign::ZeroOrPositive; + } + + if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), power_of) + && clip(cx.tcx, n, UintTy::U32) % 2 == 0 + { + return Sign::ZeroOrPositive; + } + + Sign::Uncertain +} + +/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`], +/// which the result could always be positive under certain condition. +/// +/// Other operators such as `+`/`-` causing the result's sign hard to determine, which we will +/// return `None` +fn exprs_with_selected_binop_peeled<'a>(expr: &'a Expr<'_>) -> Option<Vec<&'a Expr<'a>>> { + #[inline] + fn collect_operands<'a>(expr: &'a Expr<'a>, operands: &mut Vec<&'a Expr<'a>>) -> Option<()> { + match expr.kind { + ExprKind::Binary(op, lhs, rhs) => { + if matches!(op.node, BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem) { + collect_operands(lhs, operands); + operands.push(rhs); + } else { + // Things are complicated when there are other binary ops exist, + // abort checking by returning `None` for now. + return None; + } + }, + _ => operands.push(expr), + } + Some(()) + } + + let mut res = vec![]; + collect_operands(expr, &mut res)?; + Some(res) +} diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index eae9dfac064..20230106d53 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -150,7 +150,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, crate::empty_drop::EMPTY_DROP_INFO, crate::empty_enum::EMPTY_ENUM_INFO, - crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO, + crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO, + crate::empty_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO, crate::endian_bytes::BIG_ENDIAN_BYTES_INFO, crate::endian_bytes::HOST_ENDIAN_BYTES_INFO, crate::endian_bytes::LITTLE_ENDIAN_BYTES_INFO, @@ -385,6 +386,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::JOIN_ABSOLUTE_PATHS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, + crate::methods::MANUAL_IS_VARIANT_AND_INFO, crate::methods::MANUAL_NEXT_BACK_INFO, crate::methods::MANUAL_OK_OR_INFO, crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, @@ -408,6 +410,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::NO_EFFECT_REPLACE_INFO, crate::methods::OBFUSCATED_IF_ELSE_INFO, crate::methods::OK_EXPECT_INFO, + crate::methods::OPTION_AS_REF_CLONED_INFO, crate::methods::OPTION_AS_REF_DEREF_INFO, crate::methods::OPTION_FILTER_MAP_INFO, crate::methods::OPTION_MAP_OR_ERR_OK_INFO, @@ -433,6 +436,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, + crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_SPLITN_INFO, @@ -576,6 +580,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, + crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, crate::question_mark::QUESTION_MARK_INFO, crate::question_mark_used::QUESTION_MARK_USED_INFO, @@ -648,6 +653,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, + crate::thread_local_initializer_can_be_made_const::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 64a924a776a..712bc075650 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -4,7 +4,7 @@ use clippy_utils::{get_parent_node, numeric_literal}; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, HirId, ItemKind, Lit, Node, Stmt, StmtKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnRetTy, HirId, ItemKind, Lit, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty}; @@ -122,13 +122,42 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { + ExprKind::Block( + Block { + stmts, expr: Some(_), .. + }, + _, + ) => { + if let Some(parent) = self.cx.tcx.hir().find_parent(expr.hir_id) + && let Some(fn_sig) = parent.fn_sig() + && let FnRetTy::Return(_ty) = fn_sig.decl.output + { + // We cannot check the exact type since it's a `hir::Ty`` which does not implement `is_numeric` + self.ty_bounds.push(ExplicitTyBound(true)); + for stmt in *stmts { + self.visit_stmt(stmt); + } + self.ty_bounds.pop(); + // Ignore return expr since we know its type was inferred from return ty + return; + } + }, + + // Ignore return expr since we know its type was inferred from return ty + ExprKind::Ret(_) => return, + ExprKind::Call(func, args) => { if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) { for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) { - // Push found arg type, then visit arg. - self.ty_bounds.push((*bound).into()); - self.visit_expr(expr); - self.ty_bounds.pop(); + // If is from macro, try to use last bound type (typically pushed when visiting stmt), + // otherwise push found arg type, then visit arg, + if expr.span.from_expansion() { + self.visit_expr(expr); + } else { + self.ty_bounds.push((*bound).into()); + self.visit_expr(expr); + self.ty_bounds.pop(); + } } return; } @@ -137,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { ExprKind::MethodCall(_, receiver, args, _) => { if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = self.cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); - for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { + for (expr, bound) in iter::zip(iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) { self.ty_bounds.push((*bound).into()); self.visit_expr(expr); self.ty_bounds.pop(); diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index a744b69ecb4..8b018220c17 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -53,7 +53,7 @@ pub fn check( let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { Ok(p) => p, Err(errs) => { - drop(errs); + errs.into_iter().for_each(|err| err.cancel()); return (false, test_attr_spans); }, }; diff --git a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index 3cf67b3ecbf..969df6d85b5 100644 --- a/src/tools/clippy/clippy_lints/src/empty_structs_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use rustc_ast::ast::{Item, ItemKind, VariantData}; +use rustc_ast::ast::{Item, ItemKind, Variant, VariantData}; use rustc_errors::Applicability; use rustc_lexer::TokenKind; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -27,9 +27,38 @@ declare_clippy_lint! { restriction, "finds struct declarations with empty brackets" } -declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]); -impl EarlyLintPass for EmptyStructsWithBrackets { +declare_clippy_lint! { + /// ### What it does + /// Finds enum variants without fields that are declared with empty brackets. + /// + /// ### Why is this bad? + /// Empty brackets while defining enum variants are redundant and can be omitted. + /// + /// ### Example + /// ```no_run + /// enum MyEnum { + /// HasData(u8), + /// HasNoData(), // redundant parentheses + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// enum MyEnum { + /// HasData(u8), + /// HasNoData, + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + restriction, + "finds enum variants with empty brackets" +} + +declare_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); + +impl EarlyLintPass for EmptyWithBrackets { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { let span_after_ident = item.span.with_lo(item.ident.span.hi()); @@ -53,6 +82,27 @@ impl EarlyLintPass for EmptyStructsWithBrackets { ); } } + + fn check_variant(&mut self, cx: &EarlyContext<'_>, variant: &Variant) { + let span_after_ident = variant.span.with_lo(variant.ident.span.hi()); + + if has_brackets(&variant.data) && has_no_fields(cx, &variant.data, span_after_ident) { + span_lint_and_then( + cx, + EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + span_after_ident, + "enum variant has empty brackets", + |diagnostic| { + diagnostic.span_suggestion_hidden( + span_after_ident, + "remove the brackets", + "", + Applicability::MaybeIncorrect, + ); + }, + ); + } + } } fn has_no_ident_token(braces_span_str: &str) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index cd6c46a71a8..8f48941c4a9 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -74,6 +74,11 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { return; } + // `bool::then()` and `bool::then_some()` are not const + if in_constant(cx, expr.hir_id) { + return; + } + let ctxt = expr.span.ctxt(); if let Some(higher::If { diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 655f4b82aa4..17b6256f982 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Lints subtraction between an [`Instant`] and a [`Duration`]. + /// Lints subtraction between an `Instant` and a `Duration`. /// /// ### Why is this bad? /// Unchecked subtraction could cause underflow on certain platforms, leading to @@ -57,9 +57,6 @@ declare_clippy_lint! { /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); /// ``` - /// - /// [`Duration`]: std::time::Duration - /// [`Instant::now()`]: std::time::Instant::now; #[clippy::version = "1.67.0"] pub UNCHECKED_DURATION_SUBTRACTION, pedantic, diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index a9f1612ff05..276c1abb60c 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -1,6 +1,7 @@ //! lint on enum variants that are prefixed or suffixed by the same characters use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; +use clippy_utils::is_bool; use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; @@ -231,6 +232,10 @@ fn check_fields(cx: &LateContext<'_>, threshold: u64, item: &Item<'_>, fields: & (false, _) => ("pre", prefix), (true, false) => ("post", postfix), }; + if fields.iter().all(|field| is_bool(field.ty)) { + // If all fields are booleans, we don't want to emit this lint. + return; + } span_lint_and_help( cx, STRUCT_FIELD_NAMES, diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs index c9dc48668f2..903d3a2ab89 100644 --- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs +++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs @@ -5,7 +5,8 @@ use clippy_utils::ty::{implements_trait, make_normalized_projection}; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir::{FnRetTy, ImplItemKind, ImplicitSelfKind, ItemKind, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use rustc_span::{sym, Symbol}; @@ -152,7 +153,8 @@ fn adt_has_inherent_method(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol impl LateLintPass<'_> for IterWithoutIntoIter { fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) { - if let ItemKind::Impl(imp) = item.kind + if !in_external_macro(cx.sess(), item.span) + && let ItemKind::Impl(imp) = item.kind && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind && let Some(trait_ref) = imp.of_trait && trait_ref @@ -219,7 +221,8 @@ impl {self_ty_without_ref} {{ _ => return, }; - if let ImplItemKind::Fn(sig, _) = item.kind + if !in_external_macro(cx.sess(), item.span) + && let ImplItemKind::Fn(sig, _) = item.kind && let FnRetTy::Return(ret) = sig.decl.output && is_nameable_in_impl_trait(ret) && cx.tcx.generics_of(item_did).params.is_empty() diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 755a4ff525d..efdd3925949 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -115,7 +115,7 @@ mod duplicate_mod; mod else_if_without_else; mod empty_drop; mod empty_enum; -mod empty_structs_with_brackets; +mod empty_with_brackets; mod endian_bytes; mod entry; mod enum_clike; @@ -272,6 +272,7 @@ mod permissions_set_readonly_false; mod precedence; mod ptr; mod ptr_offset_with_cast; +mod pub_underscore_fields; mod pub_use; mod question_mark; mod question_mark_used; @@ -322,6 +323,7 @@ mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod tests_outside_test_module; +mod thread_local_initializer_can_be_made_const; mod to_digit_is_some; mod trailing_empty_array; mod trait_bounds; @@ -571,6 +573,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { verbose_bit_mask_threshold, warn_on_all_wildcard_imports, check_private_items, + pub_underscore_fields_behavior, blacklisted_names: _, cyclomatic_complexity_threshold: _, @@ -947,7 +950,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }) }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); - store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); + store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); @@ -1080,7 +1083,15 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity)); store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences)); store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)); - store.register_late_pass(|_| Box::new(unconditional_recursion::UnconditionalRecursion)); + store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default()); + store.register_late_pass(move |_| { + Box::new(pub_underscore_fields::PubUnderscoreFields { + behavior: pub_underscore_fields_behavior, + }) + }); + store.register_late_pass(move |_| { + Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv())) + }); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index 8a0955147bb..29957e423b0 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -96,8 +96,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo ExprKind::Path(qpath) => cx .qpath_res(qpath, fm_arg.hir_id) .opt_def_id() - .map(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)) - .unwrap_or_default(), + .is_some_and(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)), // Detect `|x| x.ok()` ExprKind::Closure(Closure { body, .. }) => { if let Body { diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 7cfd3d346b6..e489899c19e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -26,13 +26,14 @@ fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(body.value); - let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { if ident.name == method_name && let hir::ExprKind::Path(path) = &receiver.kind && let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id) + && !body.params.is_empty() { + let arg_id = body.params[0].pat.hir_id; return arg_id == *local; } false diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index ade8e3155fa..9f84321ced4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -1,87 +1,197 @@ +use clippy_utils::ty::get_iterator_item_ty; +use hir::ExprKind; use rustc_lint::{LateContext, LintContext}; use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; -use clippy_utils::{is_trait_method, peel_blocks, span_contains_comment}; +use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::QPath; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; use std::borrow::Cow; -fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { - match &expr.kind { - hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, - hir::ExprKind::Path(QPath::Resolved(_, segments)) => { - segments.segments.last().unwrap().ident.name == method_name +/// +/// Returns true if the expression is a method call to `method_name` +/// e.g. `a.method_name()` or `Option::method_name`. +/// +/// The type-checker verifies for us that the method accepts the right kind of items +/// (e.g. `Option::is_some` accepts `Option<_>`), so we don't need to check that. +/// +/// How to capture each case: +/// +/// `.filter(|a| { std::option::Option::is_some(a) })` +/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a closure, getting unwrapped and +/// recursively checked. +/// `std::option::Option::is_some(a)` +/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a call. It unwraps to a path with +/// `QPath::TypeRelative`. Since this is a type relative path, we need to check the method name, the +/// type, and that the parameter of the closure is passed in the call. This part is the dual of +/// `receiver.method_name()` below. +/// +/// `filter(std::option::Option::is_some);` +/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- this is a type relative path, like above, we check the +/// type and the method name. +/// +/// `filter(|a| a.is_some());` +/// ^^^^^^^^^^^^^^^ <- this is a method call inside a closure, +/// we check that the parameter of the closure is the receiver of the method call and don't allow +/// any other parameters. +fn is_method( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + type_symbol: Symbol, + method_name: Symbol, + params: &[&hir::Pat<'_>], +) -> bool { + fn pat_is_recv(ident: Ident, param: &hir::Pat<'_>) -> bool { + match param.kind { + hir::PatKind::Binding(_, _, other, _) => ident == other, + hir::PatKind::Ref(pat, _) => pat_is_recv(ident, pat), + _ => false, + } + } + match expr.kind { + hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, recv, ..) => { + // compare the identifier of the receiver to the parameter + // we are in a filter => closure has a single parameter and a single, non-block + // expression, this means that the parameter shadows all outside variables with + // the same name => avoid FPs. If the parameter is not the receiver, then this hits + // outside variables => avoid FP + if ident.name == method_name + && let ExprKind::Path(QPath::Resolved(None, path)) = recv.kind + && let &[seg] = path.segments + && params.iter().any(|p| pat_is_recv(seg.ident, p)) + { + return true; + } + false + }, + // This is used to check for complete paths via `|a| std::option::Option::is_some(a)` + // this then unwraps to a path with `QPath::TypeRelative` + // we pass the params as they've been passed to the current call through the closure + hir::ExprKind::Call(expr, [param]) => { + // this will hit the `QPath::TypeRelative` case and check that the method name is correct + if is_method(cx, expr, type_symbol, method_name, params) + // we then check that this is indeed passing the parameter of the closure + && let ExprKind::Path(QPath::Resolved(None, path)) = param.kind + && let &[seg] = path.segments + && params.iter().any(|p| pat_is_recv(seg.ident, p)) + { + return true; + } + false + }, + hir::ExprKind::Path(QPath::TypeRelative(ty, mname)) => { + let ty = cx.typeck_results().node_type(ty.hir_id); + if let Some(did) = cx.tcx.get_diagnostic_item(type_symbol) + && ty.ty_adt_def() == cx.tcx.type_of(did).skip_binder().ty_adt_def() + { + return mname.ident.name == method_name; + } + false }, - hir::ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(body.value); - let arg_id = body.params[0].pat.hir_id; - match closure_expr.kind { - hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => { - if ident.name == method_name - && let hir::ExprKind::Path(path) = &receiver.kind - && let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id) - { - return arg_id == *local; - } - false - }, - _ => false, - } + let params = body.params.iter().map(|param| param.pat).collect::<Vec<_>>(); + is_method(cx, closure_expr, type_symbol, method_name, params.as_slice()) }, _ => false, } } fn parent_is_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - if let hir::Node::Expr(parent_expr) = cx.tcx.hir().get_parent(expr.hir_id) { - is_method(cx, parent_expr, rustc_span::sym::map) - } else { - false + if let Some(expr) = get_parent_expr(cx, expr) + && is_trait_method(cx, expr, sym::Iterator) + && let hir::ExprKind::MethodCall(path, _, _, _) = expr.kind + && path.ident.name == rustc_span::sym::map + { + return true; } + false } -#[allow(clippy::too_many_arguments)] -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, filter_span: Span) { - let is_iterator = is_trait_method(cx, expr, sym::Iterator); - let parent_is_not_map = !parent_is_map(cx, expr); +enum FilterType { + IsSome, + IsOk, +} - if is_iterator - && parent_is_not_map - && is_method(cx, filter_arg, sym!(is_some)) - && !span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) +/// Returns the `FilterType` of the expression if it is a filter over an Iter<Option> or +/// Iter<Result> with the parent expression not being a map, and not having a comment in the span of +/// the filter. If it is not a filter over an Iter<Option> or Iter<Result> then it returns None +/// +/// How this is done: +/// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs` +/// 2. we check that we are in a trait method. Therefore we are in an +/// `(x as Iterator).filter({filter_arg})` method call. +/// 3. we check that the parent expression is not a map. This is because we don't want to lint +/// twice, and we already have a specialized lint for that. +/// 4. we check that the span of the filter does not contain a comment. +/// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and +/// Result. +/// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or +/// `is_ok`. +/// 7. if all of the above are true, then we return the `FilterType` +fn expression_type( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + filter_arg: &hir::Expr<'_>, + filter_span: Span, +) -> Option<FilterType> { + if !is_trait_method(cx, expr, sym::Iterator) + || parent_is_map(cx, expr) + || span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) { - span_lint_and_sugg( + return None; + } + if let hir::ExprKind::MethodCall(_, receiver, _, _) = expr.kind + && let receiver_ty = cx.typeck_results().expr_ty(receiver) + && let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty) + { + if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Option) + && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() + && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() + && is_method(cx, filter_arg, sym::Option, sym!(is_some), &[]) + { + return Some(FilterType::IsSome); + } + + if let Some(opt_defid) = cx.tcx.get_diagnostic_item(sym::Result) + && let opt_ty = cx.tcx.type_of(opt_defid).skip_binder() + && iter_item_ty.ty_adt_def() == opt_ty.ty_adt_def() + && is_method(cx, filter_arg, sym::Result, sym!(is_ok), &[]) + { + return Some(FilterType::IsOk); + } + } + None +} + +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, filter_span: Span) { + // we are in a filter inside an iterator + match expression_type(cx, expr, filter_arg, filter_span) { + None => (), + Some(FilterType::IsOk) => span_lint_and_sugg( cx, - ITER_FILTER_IS_SOME, + ITER_FILTER_IS_OK, filter_span.with_hi(expr.span.hi()), - "`filter` for `is_some` on iterator over `Option`", + "`filter` for `is_ok` on iterator over `Result`s", "consider using `flatten` instead", reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(), Applicability::HasPlaceholders, - ); - } - if is_iterator - && parent_is_not_map - && is_method(cx, filter_arg, sym!(is_ok)) - && !span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) - { - span_lint_and_sugg( + ), + Some(FilterType::IsSome) => span_lint_and_sugg( cx, - ITER_FILTER_IS_OK, + ITER_FILTER_IS_SOME, filter_span.with_hi(expr.span.hi()), - "`filter` for `is_ok` on iterator over `Result`s", + "`filter` for `is_some` on iterator over `Option`", "consider using `flatten` instead", reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(), Applicability::HasPlaceholders, - ); + ), } } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs new file mode 100644 index 00000000000..d29acd4622a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -0,0 +1,59 @@ +use clippy_config::msrvs::{Msrv, OPTION_RESULT_IS_VARIANT_AND}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_span::{sym, Span}; + +use super::MANUAL_IS_VARIANT_AND; + +pub(super) fn check<'tcx>( + cx: &LateContext<'_>, + expr: &'tcx rustc_hir::Expr<'_>, + map_recv: &'tcx rustc_hir::Expr<'_>, + map_arg: &'tcx rustc_hir::Expr<'_>, + map_span: Span, + msrv: &Msrv, +) { + // Don't lint if: + + // 1. the `expr` is generated by a macro + if expr.span.from_expansion() { + return; + } + + // 2. the caller of `map()` is neither `Option` nor `Result` + let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Option); + let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(map_recv), sym::Result); + if !is_option && !is_result { + return; + } + + // 3. the caller of `unwrap_or_default` is neither `Option<bool>` nor `Result<bool, _>` + if !cx.typeck_results().expr_ty(expr).is_bool() { + return; + } + + // 4. msrv doesn't meet `OPTION_RESULT_IS_VARIANT_AND` + if !msrv.meets(OPTION_RESULT_IS_VARIANT_AND) { + return; + } + + let lint_msg = if is_option { + "called `map(<f>).unwrap_or_default()` on an `Option` value" + } else { + "called `map(<f>).unwrap_or_default()` on a `Result` value" + }; + let suggestion = if is_option { "is_some_and" } else { "is_ok_and" }; + + span_lint_and_sugg( + cx, + MANUAL_IS_VARIANT_AND, + expr.span.with_lo(map_span.lo()), + lint_msg, + "use", + format!("{}({})", suggestion, snippet(cx, map_arg.span, "..")), + Applicability::MachineApplicable, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index cc6eeaa86e5..f9f636bbbf7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -2,9 +2,10 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, peel_blocks}; +use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; @@ -14,60 +15,110 @@ use rustc_span::{sym, Span}; use super::MAP_CLONE; +// If this `map` is called on an `Option` or a `Result` and the previous call is `as_ref`, we don't +// run this lint because it would overlap with `useless_asref` which provides a better suggestion +// in this case. +fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> bool { + if is_diag_trait_item(cx, method_id, sym::Iterator) { + return true; + } + // We check if it's an `Option` or a `Result`. + if let Some(id) = cx.tcx.impl_of_method(method_id) { + let identity = cx.tcx.type_of(id).instantiate_identity(); + if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { + return false; + } + } else { + return false; + } + // We check if the previous method call is `as_ref`. + if let hir::ExprKind::MethodCall(path1, receiver, _, _) = &e.kind + && let hir::ExprKind::MethodCall(path2, _, _, _) = &receiver.kind + { + return path2.ident.name != sym::as_ref || path1.ident.name != sym::map; + } + + true +} + pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && (cx.tcx.impl_of_method(method_id).map_or(false, |id| { - is_type_diagnostic_item(cx, cx.tcx.type_of(id).instantiate_identity(), sym::Option) - }) || is_diag_trait_item(cx, method_id, sym::Iterator)) - && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind + && should_run_lint(cx, e, method_id) { - let closure_body = cx.tcx.hir().body(body); - let closure_expr = peel_blocks(closure_body.value); - match closure_body.params[0].pat.kind { - hir::PatKind::Ref(inner, hir::Mutability::Not) => { - if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind { - if ident_eq(name, closure_expr) { - lint_explicit_closure(cx, e.span, recv.span, true, msrv); - } - } - }, - hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => { - match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { - if ident_eq(name, inner) { - if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { + match arg.kind { + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + let closure_body = cx.tcx.hir().body(body); + let closure_expr = peel_blocks(closure_body.value); + match closure_body.params[0].pat.kind { + hir::PatKind::Ref(inner, hir::Mutability::Not) => { + if let hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) = inner.kind { + if ident_eq(name, closure_expr) { lint_explicit_closure(cx, e.span, recv.span, true, msrv); } } }, - hir::ExprKind::MethodCall(method, obj, [], _) => { - if ident_eq(name, obj) && method.ident.name == sym::clone - && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) - && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) - // no autoderefs - && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) - { - let obj_ty = cx.typeck_results().expr_ty(obj); - if let ty::Ref(_, ty, mutability) = obj_ty.kind() { - if matches!(mutability, Mutability::Not) { - let copy = is_copy(cx, *ty); - lint_explicit_closure(cx, e.span, recv.span, copy, msrv); + hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => { + match closure_expr.kind { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { + if ident_eq(name, inner) { + if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { + lint_explicit_closure(cx, e.span, recv.span, true, msrv); + } } - } else { - lint_needless_cloning(cx, e.span, recv.span); - } + }, + hir::ExprKind::MethodCall(method, obj, [], _) => { + if ident_eq(name, obj) && method.ident.name == sym::clone + && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + // no autoderefs + && !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + let obj_ty = cx.typeck_results().expr_ty(obj); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, *ty); + lint_explicit_closure(cx, e.span, recv.span, copy, msrv); + } + } else { + lint_needless_cloning(cx, e.span, recv.span); + } + } + }, + hir::ExprKind::Call(call, [_]) => { + if let hir::ExprKind::Path(qpath) = call.kind { + handle_path(cx, call, &qpath, e, recv); + } + }, + _ => {}, } }, _ => {}, } }, + hir::ExprKind::Path(qpath) => handle_path(cx, arg, &qpath, e, recv), _ => {}, } } } +fn handle_path( + cx: &LateContext<'_>, + arg: &hir::Expr<'_>, + qpath: &hir::QPath<'_>, + e: &hir::Expr<'_>, + recv: &hir::Expr<'_>, +) { + if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id() + && match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + { + // FIXME: It would be better to infer the type to check if it's copyable or not + // to suggest to use `.copied()` instead of `.cloned()` where applicable. + lint_path(cx, e.span, recv.span); + } +} + fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind { path.segments.len() == 1 && path.segments[0].ident == name @@ -88,6 +139,23 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } +fn lint_path(cx: &LateContext<'_>, replace: Span, root: Span) { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + MAP_CLONE, + replace, + "you are explicitly cloning with `.map()`", + "consider calling the dedicated `cloned` method", + format!( + "{}.cloned()", + snippet_with_applicability(cx, root, "..", &mut applicability), + ), + applicability, + ); +} + fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index c1e126137df..89ea3597dc0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -51,6 +51,7 @@ mod iter_skip_zero; mod iter_with_drain; mod iterator_step_by_zero; mod join_absolute_paths; +mod manual_is_variant_and; mod manual_next_back; mod manual_ok_or; mod manual_saturating_arithmetic; @@ -70,6 +71,7 @@ mod no_effect_replace; mod obfuscated_if_else; mod ok_expect; mod open_options; +mod option_as_ref_cloned; mod option_as_ref_deref; mod option_map_or_err_ok; mod option_map_or_none; @@ -93,6 +95,7 @@ mod single_char_pattern; mod single_char_push_string; mod skip_while_next; mod stable_sort_primitive; +mod str_split; mod str_splitn; mod string_extend_chars; mod string_lit_chars_any; @@ -2589,11 +2592,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for usage of `x.get(0)` instead of - /// `x.first()`. + /// `x.first()` or `x.front()`. /// /// ### Why is this bad? - /// Using `x.first()` is easier to read and has the same - /// result. + /// Using `x.first()` for `Vec`s and slices or `x.front()` + /// for `VecDeque`s is easier to read and has the same result. /// /// ### Example /// ```no_run @@ -2609,7 +2612,7 @@ declare_clippy_lint! { #[clippy::version = "1.63.0"] pub GET_FIRST, style, - "Using `x.get(0)` when `x.first()` is simpler" + "Using `x.get(0)` when `x.first()` or `x.front()` is simpler" } declare_clippy_lint! { @@ -3784,7 +3787,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// This pattern is often followed by manual unwrapping of the `Option`. The simplification - /// results in more readable and succint code without the need for manual unwrapping. + /// results in more readable and succinct code without the need for manual unwrapping. /// /// ### Example /// ```no_run @@ -3810,7 +3813,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// This pattern is often followed by manual unwrapping of `Result`. The simplification - /// results in more readable and succint code without the need for manual unwrapping. + /// results in more readable and succinct code without the need for manual unwrapping. /// /// ### Example /// ```no_run @@ -3829,6 +3832,87 @@ declare_clippy_lint! { "filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`" } +declare_clippy_lint! { + /// Checks for usage of `option.map(f).unwrap_or_default()` and `result.map(f).unwrap_or_default()` where f is a function or closure that returns the `bool` type. + /// + /// ### Why is this bad? + /// Readability. These can be written more concisely as `option.is_some_and(f)` and `result.is_ok_and(f)`. + /// + /// ### Example + /// ```no_run + /// # let option = Some(1); + /// # let result: Result<usize, ()> = Ok(1); + /// option.map(|a| a > 10).unwrap_or_default(); + /// result.map(|a| a > 10).unwrap_or_default(); + /// ``` + /// Use instead: + /// ```no_run + /// # let option = Some(1); + /// # let result: Result<usize, ()> = Ok(1); + /// option.is_some_and(|a| a > 10); + /// result.is_ok_and(|a| a > 10); + /// ``` + #[clippy::version = "1.76.0"] + pub MANUAL_IS_VARIANT_AND, + pedantic, + "using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for usages of `str.trim().split("\n")` and `str.trim().split("\r\n")`. + /// + /// ### Why is this bad? + /// + /// Hard-coding the line endings makes the code less compatible. `str.lines` should be used instead. + /// + /// ### Example + /// ```no_run + /// "some\ntext\nwith\nnewlines\n".trim().split('\n'); + /// ``` + /// Use instead: + /// ```no_run + /// "some\ntext\nwith\nnewlines\n".lines(); + /// ``` + /// + /// ### Known Problems + /// + /// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or + /// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is + /// valid. + /// ``` + #[clippy::version = "1.76.0"] + pub STR_SPLIT_AT_NEWLINE, + pedantic, + "splitting a trimmed string at hard-coded newlines" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `.as_ref().cloned()` and `.as_mut().cloned()` on `Option`s + /// + /// ### Why is this bad? + /// This can be written more concisely by cloning the `Option` directly. + /// + /// ### Example + /// ```no_run + /// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> { + /// bar.as_ref().cloned() + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn foo(bar: &Option<Vec<u8>>) -> Option<Vec<u8>> { + /// bar.clone() + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub OPTION_AS_REF_CLONED, + pedantic, + "cloning an `Option` via `as_ref().cloned()`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -3983,6 +4067,9 @@ impl_lint_pass!(Methods => [ RESULT_FILTER_MAP, ITER_FILTER_IS_SOME, ITER_FILTER_IS_OK, + MANUAL_IS_VARIANT_AND, + STR_SPLIT_AT_NEWLINE, + OPTION_AS_REF_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4230,7 +4317,10 @@ impl Methods { ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv), + ("cloned", []) => { + cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv); + option_as_ref_cloned::check(cx, recv, span); + }, ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { @@ -4569,6 +4659,9 @@ impl Methods { ("sort_unstable_by", [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, + ("split", [arg]) => { + str_split::check(cx, expr, recv, arg); + }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); @@ -4664,7 +4757,13 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - ("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => { + ("unwrap_or_default", []) => { + if let Some(("map", m_recv, [arg], span, _)) = method_call(recv) { + manual_is_variant_and::check(cx, expr, m_recv, arg, span, &self.msrv); + } + unnecessary_literal_unwrap::check(cx, expr, recv, name, args); + }, + ("unwrap_unchecked" | "unwrap_err_unchecked", []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, ("unwrap_or_else", [u_arg]) => { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs new file mode 100644 index 00000000000..d7fec360fa2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -0,0 +1,24 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::{sym, Span}; + +use super::{method_call, OPTION_AS_REF_CLONED}; + +pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { + if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(as_ref_recv).peel_refs(), sym::Option) + { + span_lint_and_sugg( + cx, + OPTION_AS_REF_CLONED, + as_ref_ident_span.to(cloned_ident_span), + &format!("cloning an `Option<_>` using `.{method}().cloned()`"), + "this can be written more concisely by cloning the `Option<_>` directly", + "clone".into(), + Applicability::MachineApplicable, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index 47c9438c588..624597ffca9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -72,7 +72,7 @@ pub(super) fn check<'tcx>( } // is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead - let suggest_is_some_and = msrv.meets(msrvs::OPTION_IS_SOME_AND) + let suggest_is_some_and = msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) && matches!(&unwrap_arg.kind, ExprKind::Lit(lit) if matches!(lit.node, rustc_ast::LitKind::Bool(false))); diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs index 094ead9f4ad..29f44ec2a4d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs @@ -33,7 +33,7 @@ pub(super) fn check( && path.chars().all(char::is_alphanumeric) { let mut sugg = snippet(cx, recv.span, "..").into_owned(); - if msrv.meets(msrvs::OPTION_IS_SOME_AND) { + if msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) { let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); } else { let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); diff --git a/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs b/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs index 2a2feedd2b4..24de1979c63 100644 --- a/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs +++ b/src/tools/clippy/clippy_lints/src/methods/redundant_as_str.rs @@ -13,7 +13,11 @@ pub(super) fn check( as_str_span: Span, other_method_span: Span, ) { - if cx.typeck_results().expr_ty(recv).ty_adt_def().is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string()) + if cx + .typeck_results() + .expr_ty(recv) + .ty_adt_def() + .is_some_and(|adt| Some(adt.did()) == cx.tcx.lang_items().string()) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs new file mode 100644 index 00000000000..3586e11f56a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::visitors::is_const_evaluatable; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use super::STR_SPLIT_AT_NEWLINE; + +pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &'a Expr<'_>, split_arg: &'_ Expr<'_>) { + // We're looking for `A.trim().split(B)`, where the adjusted type of `A` is `&str` (e.g. an + // expression returning `String`), and `B` is a `Pattern` that hard-codes a newline (either `"\n"` + // or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most + // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. + if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind + && trim_method_name.ident.as_str() == "trim" + && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() + && !is_const_evaluatable(cx, trim_recv) + && let ExprKind::Lit(split_lit) = split_arg.kind + && (matches!(split_lit.node, LitKind::Char('\n')) + || matches!(split_lit.node, LitKind::Str(sym, _) if (sym.as_str() == "\n" || sym.as_str() == "\r\n"))) + { + let mut app = Applicability::MaybeIncorrect; + span_lint_and_sugg( + cx, + STR_SPLIT_AT_NEWLINE, + expr.span, + "using `str.trim().split()` with hard-coded newlines", + "use `str.lines()` instead", + format!( + "{}.lines()", + snippet_with_context(cx, trim_recv.span, expr.span.ctxt(), "..", &mut app).0 + ), + app, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 637368e9361..64fcd9f8f45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,13 +3,13 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; +use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_lang_item, peel_mid_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, Node}; +use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_hir_typeck::{FnCtxt, Inherited}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -246,6 +246,19 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb && let Some(receiver_snippet) = snippet_opt(cx, receiver.span) && let Some(arg_snippet) = snippet_opt(cx, argument_expr.span) { + // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` + // implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to + // add `.as_ref()` to the suggestion. + let as_ref = if is_type_lang_item(cx, cx.typeck_results().expr_ty(expr), LangItem::String) + && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref) + && cx.get_associated_type(cx.typeck_results().expr_ty(receiver), deref_trait_id, "Target") + != Some(cx.tcx.types.str_) + { + ".as_ref()" + } else { + "" + }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. @@ -255,7 +268,7 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb parent.span, &format!("unnecessary use of `{method_name}`"), "use", - format!("{receiver_snippet}.split({arg_snippet})"), + format!("{receiver_snippet}{as_ref}.split({arg_snippet})"), Applicability::MaybeIncorrect, ); return true; @@ -445,7 +458,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< { let bound_fn_sig = cx.tcx.fn_sig(callee_def_id); let fn_sig = bound_fn_sig.skip_binder(); - if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id) + if let Some(arg_index) = recv + .into_iter() + .chain(call_args) + .position(|arg| arg.hir_id == expr.hir_id) && let param_ty = fn_sig.input(arg_index).skip_binder() && let ty::Param(ParamTy { index: param_index , ..}) = *param_ty.kind() // https://github.com/rust-lang/rust-clippy/issues/9504 and https://github.com/rust-lang/rust-clippy/issues/10021 diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index 84ee64e88a6..66727e5a29d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -1,19 +1,52 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::walk_ptrs_ty_depth; -use clippy_utils::{get_parent_expr, is_trait_method}; +use clippy_utils::{get_parent_expr, is_diag_trait_item, match_def_path, paths, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_span::{sym, Span}; + +use core::ops::ControlFlow; use super::USELESS_ASREF; +/// Returns the first type inside the `Option`/`Result` type passed as argument. +fn get_enum_ty(enum_ty: Ty<'_>) -> Option<Ty<'_>> { + struct ContainsTyVisitor { + level: usize, + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTyVisitor { + type BreakTy = Ty<'tcx>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + self.level += 1; + if self.level == 1 { + t.super_visit_with(self) + } else { + ControlFlow::Break(t) + } + } + } + + match enum_ty.visit_with(&mut ContainsTyVisitor { level: 0 }) { + ControlFlow::Break(ty) => Some(ty), + ControlFlow::Continue(()) => None, + } +} + /// Checks for the `USELESS_ASREF` lint. pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" // check if the call is to the actual `AsRef` or `AsMut` trait - if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) { + let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) else { + return; + }; + + if is_diag_trait_item(cx, def_id, sym::AsRef) || is_diag_trait_item(cx, def_id, sym::AsMut) { // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); @@ -39,5 +72,89 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, applicability, ); } + } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name]) + || match_def_path(cx, def_id, &["core", "result", "Result", call_name]) + { + let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs(); + let res_ty = cx.typeck_results().expr_ty(expr).peel_refs(); + + if let Some(rcv_ty) = get_enum_ty(rcv_ty) + && let Some(res_ty) = get_enum_ty(res_ty) + // If the only thing the `as_mut`/`as_ref` call is doing is adding references and not + // changing the type, then we can move forward. + && rcv_ty.peel_refs() == res_ty.peel_refs() + && let Some(parent) = get_parent_expr(cx, expr) + && let hir::ExprKind::MethodCall(segment, _, args, _) = parent.kind + && segment.ident.span != expr.span + // We check that the called method name is `map`. + && segment.ident.name == sym::map + // And that it only has one argument. + && let [arg] = args + && is_calling_clone(cx, arg) + { + lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name); + } + } +} + +fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool { + // We check it's calling the `clone` method of the `Clone` trait. + if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() { + match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD) + } else { + false } } + +fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { + match arg.kind { + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { + // If it's a closure, we need to check what is called. + let closure_body = cx.tcx.hir().body(body); + let closure_expr = peel_blocks(closure_body.value); + match closure_expr.kind { + hir::ExprKind::MethodCall(method, obj, [], _) => { + if method.ident.name == sym::clone + && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + // We check it's the `Clone` trait. + && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id) + // no autoderefs + && !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + true + } else { + false + } + }, + hir::ExprKind::Call(call, [_]) => { + if let hir::ExprKind::Path(qpath) = call.kind { + check_qpath(cx, qpath, call.hir_id) + } else { + false + } + }, + _ => false, + } + }, + hir::ExprKind::Path(qpath) => check_qpath(cx, qpath, arg.hir_id), + _ => false, + } +} + +fn lint_as_ref_clone(cx: &LateContext<'_>, span: Span, recvr: &hir::Expr<'_>, call_name: &str) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + USELESS_ASREF, + span, + &format!("this call to `{call_name}.map(...)` does nothing"), + "try", + format!( + "{}.clone()", + snippet_with_applicability(cx, recvr.span, "..", &mut applicability) + ), + applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index 9ad4250a141..3a0305b4d55 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -74,6 +74,7 @@ pub(super) fn get_hint_if_single_char_arg( match ch { "'" => "\\'", r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` _ => ch, } ); diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 0f18e943451..bbc4d0a0f9a 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -331,7 +331,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>> slice, } if indexes.len() > 1 => { // if we have found an `assert!`, let's also check that it's actually right - // and if it convers the highest index and if not, suggest the correct length + // and if it covers the highest index and if not, suggest the correct length let sugg = match comparison { // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks. // The user probably meant `v.len() > 5` diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index c1f6c71a63e..c6c188854fd 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -13,21 +13,23 @@ use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does /// Checks for imports that do not rename the item as specified - /// in the `enforce-import-renames` config option. + /// in the `enforced-import-renames` config option. /// /// Note: Even though this lint is warn-by-default, it will only trigger if - /// import renames are defined in the clippy.toml file. + /// import renames are defined in the `clippy.toml` file. /// /// ### Why is this bad? - /// Consistency is important, if a project has defined import - /// renames they should be followed. More practically, some item names are too - /// vague outside of their defining scope this can enforce a more meaningful naming. + /// Consistency is important; if a project has defined import renames, then they should be + /// followed. More practically, some item names are too vague outside of their defining scope, + /// in which case this can enforce a more meaningful naming. /// /// ### Example /// An example clippy.toml configuration: /// ```toml /// # clippy.toml - /// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }] + /// enforced-import-renames = [ + /// { path = "serde_json::Value", rename = "JsonValue" }, + /// ] /// ``` /// /// ```rust,ignore diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index a23e12f7a18..4ae4fc9b096 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -6,7 +6,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -105,8 +105,28 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { match ty.kind() { ty::Bool => Some("AtomicBool"), - ty::Uint(_) => Some("AtomicUsize"), - ty::Int(_) => Some("AtomicIsize"), + ty::Uint(uint_ty) => { + match uint_ty { + UintTy::U8 => Some("AtomicU8"), + UintTy::U16 => Some("AtomicU16"), + UintTy::U32 => Some("AtomicU32"), + UintTy::U64 => Some("AtomicU64"), + UintTy::Usize => Some("AtomicUsize"), + // There's no `AtomicU128`. + UintTy::U128 => None, + } + }, + ty::Int(int_ty) => { + match int_ty { + IntTy::I8 => Some("AtomicI8"), + IntTy::I16 => Some("AtomicI16"), + IntTy::I32 => Some("AtomicI32"), + IntTy::I64 => Some("AtomicI64"), + IntTy::Isize => Some("AtomicIsize"), + // There's no `AtomicI128`. + IntTy::I128 => None, + } + }, ty::RawPtr(_) => Some("AtomicPtr"), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 5978da83199..6bbe427ea29 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -165,7 +165,7 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { } fn is_operator_overridden(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - // It's very hard or impossable to check whether overridden operator have side-effect this lint. + // It's very hard or impossible to check whether overridden operator have side-effect this lint. // So, this function assume user-defined operator is overridden with an side-effect. // The definition of user-defined structure here is ADT-type, // Althrough this will weaken the ability of this lint, less error lint-fix happen. diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 49e9e2c00cc..2701d6bdca3 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -53,14 +53,10 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) + && let Some(snip) = snippet_opt(cx, param.span) + && !(snip.starts_with("0o") || snip.starts_with("0b")) { - let Some(snip) = snippet_opt(cx, param.span) else { - return; - }; - - if !snip.starts_with("0o") { - show_error(cx, param); - } + show_error(cx, param); } }, ExprKind::Call(func, [param]) => { @@ -70,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) && let Some(snip) = snippet_opt(cx, param.span) - && !snip.starts_with("0o") + && !(snip.starts_with("0o") || snip.starts_with("0b")) { show_error(cx, param); } diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs index 8ecb038627f..f671517c134 100644 --- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs @@ -18,82 +18,118 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if !is_allowed(cx, op, left, right) { - match op { - BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check_op( - cx, - left, - 0, - expr.span, - peel_hir_expr_refs(right).0.span, - needs_parenthesis(cx, expr, right), - ); - check_op( - cx, - right, - 0, - expr.span, - peel_hir_expr_refs(left).0.span, - Parens::Unneeded, - ); - }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check_op( - cx, - right, - 0, - expr.span, - peel_hir_expr_refs(left).0.span, - Parens::Unneeded, - ); - }, - BinOpKind::Mul => { - check_op( - cx, - left, - 1, - expr.span, - peel_hir_expr_refs(right).0.span, - needs_parenthesis(cx, expr, right), - ); - check_op( - cx, - right, - 1, - expr.span, - peel_hir_expr_refs(left).0.span, - Parens::Unneeded, - ); - }, - BinOpKind::Div => check_op( + return; + } + + // we need to know whether a ref is coerced to a value + // if a ref is coerced, then the suggested lint must deref it + // e.g. `let _: i32 = x+0` with `x: &i32` should be replaced with `let _: i32 = *x`. + // we do this by checking the _kind_ of the type of the expression + // if it's a ref, we then check whether it is erased, and that's it. + let (peeled_left_span, left_is_coerced_to_value) = { + let expr = peel_hir_expr_refs(left).0; + let span = expr.span; + let is_coerced = expr_is_erased_ref(cx, expr); + (span, is_coerced) + }; + + let (peeled_right_span, right_is_coerced_to_value) = { + let expr = peel_hir_expr_refs(right).0; + let span = expr.span; + let is_coerced = expr_is_erased_ref(cx, expr); + (span, is_coerced) + }; + + match op { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + check_op( + cx, + left, + 0, + expr.span, + peeled_right_span, + needs_parenthesis(cx, expr, right), + right_is_coerced_to_value, + ); + check_op( + cx, + right, + 0, + expr.span, + peeled_left_span, + Parens::Unneeded, + left_is_coerced_to_value, + ); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { + check_op( + cx, + right, + 0, + expr.span, + peeled_left_span, + Parens::Unneeded, + left_is_coerced_to_value, + ); + }, + BinOpKind::Mul => { + check_op( + cx, + left, + 1, + expr.span, + peeled_right_span, + needs_parenthesis(cx, expr, right), + right_is_coerced_to_value, + ); + check_op( cx, right, 1, expr.span, - peel_hir_expr_refs(left).0.span, + peeled_left_span, Parens::Unneeded, - ), - BinOpKind::BitAnd => { - check_op( - cx, - left, - -1, - expr.span, - peel_hir_expr_refs(right).0.span, - needs_parenthesis(cx, expr, right), - ); - check_op( - cx, - right, - -1, - expr.span, - peel_hir_expr_refs(left).0.span, - Parens::Unneeded, - ); - }, - BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), - _ => (), - } + left_is_coerced_to_value, + ); + }, + BinOpKind::Div => check_op( + cx, + right, + 1, + expr.span, + peeled_left_span, + Parens::Unneeded, + left_is_coerced_to_value, + ), + BinOpKind::BitAnd => { + check_op( + cx, + left, + -1, + expr.span, + peeled_right_span, + needs_parenthesis(cx, expr, right), + right_is_coerced_to_value, + ); + check_op( + cx, + right, + -1, + expr.span, + peeled_left_span, + Parens::Unneeded, + left_is_coerced_to_value, + ); + }, + BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), + _ => (), + } +} + +fn expr_is_erased_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.typeck_results().expr_ty(expr).kind() { + ty::Ref(r, ..) => r.is_erased(), + _ => false, } } @@ -144,11 +180,11 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) } fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool { - // This lint applies to integers - !cx.typeck_results().expr_ty(left).peel_refs().is_integral() - || !cx.typeck_results().expr_ty(right).peel_refs().is_integral() + // This lint applies to integers and their references + cx.typeck_results().expr_ty(left).peel_refs().is_integral() + && cx.typeck_results().expr_ty(right).peel_refs().is_integral() // `1 << 0` is a common pattern in bit manipulation code - || (cmp == BinOpKind::Shl + && !(cmp == BinOpKind::Shl && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0)) && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))) } @@ -161,11 +197,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, } { - span_ineffective_operation(cx, span, arg, Parens::Unneeded); + span_ineffective_operation(cx, span, arg, Parens::Unneeded, false); } } -fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) { +fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), @@ -178,18 +214,28 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa 1 => v == 1, _ => unreachable!(), } { - span_ineffective_operation(cx, span, arg, parens); + span_ineffective_operation(cx, span, arg, parens, is_erased); } } } -fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) { +fn span_ineffective_operation( + cx: &LateContext<'_>, + span: Span, + arg: Span, + parens: Parens, + is_ref_coerced_to_val: bool, +) { let mut applicability = Applicability::MachineApplicable; let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability); - + let expr_snippet = if is_ref_coerced_to_val { + format!("*{expr_snippet}") + } else { + expr_snippet.into_owned() + }; let suggestion = match parens { Parens::Needed => format!("({expr_snippet})"), - Parens::Unneeded => expr_snippet.into_owned(), + Parens::Unneeded => expr_snippet, }; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs new file mode 100644 index 00000000000..00465ce4381 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -0,0 +1,83 @@ +use clippy_config::types::PubUnderscoreFieldsBehaviour; +use clippy_utils::attrs::is_doc_hidden; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_path_lang_item; +use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks whether any field of the struct is prefixed with an `_` (underscore) and also marked + /// `pub` (public) + /// + /// ### Why is this bad? + /// Fields prefixed with an `_` are inferred as unused, which suggests it should not be marked + /// as `pub`, because marking it as `pub` infers it will be used. + /// + /// ### Example + /// ```rust + /// struct FileHandle { + /// pub _descriptor: usize, + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct FileHandle { + /// _descriptor: usize, + /// } + /// ``` + /// + /// OR + /// + /// ```rust + /// struct FileHandle { + /// pub descriptor: usize, + /// } + /// ``` + #[clippy::version = "1.77.0"] + pub PUB_UNDERSCORE_FIELDS, + pedantic, + "struct field prefixed with underscore and marked public" +} + +pub struct PubUnderscoreFields { + pub behavior: PubUnderscoreFieldsBehaviour, +} +impl_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]); + +impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + // This lint only pertains to structs. + let ItemKind::Struct(variant_data, _) = &item.kind else { + return; + }; + + let is_visible = |field: &FieldDef<'_>| match self.behavior { + PubUnderscoreFieldsBehaviour::PublicallyExported => cx.effective_visibilities.is_reachable(field.def_id), + PubUnderscoreFieldsBehaviour::AllPubFields => { + // If there is a visibility span then the field is marked pub in some way. + !field.vis_span.is_empty() + }, + }; + + for field in variant_data.fields() { + // Only pertains to fields that start with an underscore, and are public. + if field.ident.as_str().starts_with('_') && is_visible(field) + // We ignore fields that have `#[doc(hidden)]`. + && !is_doc_hidden(cx.tcx.hir().attrs(field.hir_id)) + // We ignore fields that are `PhantomData`. + && !is_path_lang_item(cx, field.ty, LangItem::PhantomData) + { + span_lint_and_help( + cx, + PUB_UNDERSCORE_FIELDS, + field.vis_span.to(field.ident.span), + "field marked as public but also inferred as unused because it's prefixed with `_`", + None, + "consider removing the underscore, or making the field private", + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 9469888a4d4..bd3128a0f97 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -75,12 +75,7 @@ enum IfBlockType<'hir> { /// An `if x.is_xxx() { a } else { b } ` expression. /// /// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b) - IfIs( - &'hir Expr<'hir>, - Ty<'hir>, - Symbol, - &'hir Expr<'hir>, - ), + IfIs(&'hir Expr<'hir>, Ty<'hir>, Symbol, &'hir Expr<'hir>), /// An `if let Xxx(a) = b { c } else { d }` expression. /// /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c), diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs index 90834d784a5..b4278d879e5 100644 --- a/src/tools/clippy/clippy_lints/src/serde_api.rs +++ b/src/tools/clippy/clippy_lints/src/serde_api.rs @@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for mis-uses of the serde API. + /// Checks for misuses of the serde API. /// /// ### Why is this bad? /// Serde is very finnicky about how its API should be diff --git a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs new file mode 100644 index 00000000000..9fee4c06200 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs @@ -0,0 +1,102 @@ +use clippy_config::msrvs::Msrv; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::fn_has_unsatisfiable_preds; +use clippy_utils::qualify_min_const_fn::is_min_const_fn; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{intravisit, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::sym::thread_local_macro; + +declare_clippy_lint! { + /// ### What it does + /// Suggests to use `const` in `thread_local!` macro if possible. + /// ### Why is this bad? + /// + /// The `thread_local!` macro wraps static declarations and makes them thread-local. + /// It supports using a `const` keyword that may be used for declarations that can + /// be evaluated as a constant expression. This can enable a more efficient thread + /// local implementation that can avoid lazy initialization. For types that do not + /// need to be dropped, this can enable an even more efficient implementation that + /// does not need to track any additional state. + /// + /// https://doc.rust-lang.org/std/macro.thread_local.html + /// + /// ### Example + /// ```no_run + /// // example code where clippy issues a warning + /// thread_local! { + /// static BUF: String = String::new(); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// // example code which does not raise clippy warning + /// thread_local! { + /// static BUF: String = const { String::new() }; + /// } + /// ``` + #[clippy::version = "1.76.0"] + pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, + perf, + "suggest using `const` in `thread_local!` macro" +} + +pub struct ThreadLocalInitializerCanBeMadeConst { + msrv: Msrv, +} + +impl ThreadLocalInitializerCanBeMadeConst { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]); + +impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: rustc_hir::intravisit::FnKind<'tcx>, + _: &'tcx rustc_hir::FnDecl<'tcx>, + body: &'tcx rustc_hir::Body<'tcx>, + span: rustc_span::Span, + defid: rustc_span::def_id::LocalDefId, + ) { + if in_external_macro(cx.sess(), span) + && let Some(callee) = span.source_callee() + && let Some(macro_def_id) = callee.macro_def_id + && cx.tcx.is_diagnostic_item(thread_local_macro, macro_def_id) + && let intravisit::FnKind::ItemFn(..) = fn_kind + // Building MIR for `fn`s with unsatisfiable preds results in ICE. + && !fn_has_unsatisfiable_preds(cx, defid.to_def_id()) + && let mir = cx.tcx.optimized_mir(defid.to_def_id()) + && let Ok(()) = is_min_const_fn(cx.tcx, mir, &self.msrv) + // this is the `__init` function emitted by the `thread_local!` macro + // when the `const` keyword is not used. We avoid checking the `__init` directly + // as that is not a public API. + // we know that the function is const-qualifiable, so now we need only to get the + // initializer expression to span-lint it. + && let ExprKind::Block(block, _) = body.value.kind + && let Some(ret_expr) = block.expr + && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }") + && initializer_snippet != "thread_local! { ... }" + { + span_lint_and_sugg( + cx, + THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, + ret_expr.span, + "initializer for `thread_local` value can be made `const`", + "replace with", + format!("const {{ {initializer_snippet} }}"), + Applicability::MachineApplicable, + ); + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs index 01a23c515f5..c44f5150dd1 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_normalizable; -use clippy_utils::{path_to_local, path_to_local_id}; +use clippy_utils::{eq_expr_value, path_to_local}; use rustc_abi::WrappingRange; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node}; @@ -25,6 +25,52 @@ fn range_fully_contained(from: WrappingRange, to: WrappingRange) -> bool { to.contains(from.start) && to.contains(from.end) } +/// Checks if a given expression is a binary operation involving a local variable or is made up of +/// other (nested) binary expressions involving the local. There must be at least one local +/// reference that is the same as `local_expr`. +/// +/// This is used as a heuristic to detect if a variable +/// is checked to be within the valid range of a transmuted type. +/// All of these would return true: +/// * `x < 4` +/// * `x < 4 && x > 1` +/// * `x.field < 4 && x.field > 1` (given `x.field`) +/// * `x.field < 4 && unrelated()` +/// * `(1..=3).contains(&x)` +fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Binary(_, lhs, rhs) => { + binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs) + }, + ExprKind::MethodCall(path, receiver, [arg], _) + if path.ident.name == sym!(contains) + // ... `contains` called on some kind of range + && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def() + && let lang_items = cx.tcx.lang_items() + && [ + lang_items.range_from_struct(), + lang_items.range_inclusive_struct(), + lang_items.range_struct(), + lang_items.range_to_inclusive_struct(), + lang_items.range_to_struct() + ].into_iter().any(|did| did == Some(receiver_adt.did())) => + { + eq_expr_value(cx, local_expr, arg.peel_borrows()) + }, + _ => eq_expr_value(cx, local_expr, expr), + } +} + +/// Checks if an expression is a path to a local variable (with optional projections), e.g. +/// `x.field[0].field2` would return true. +fn is_local_with_projections(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(_) => path_to_local(expr).is_some(), + ExprKind::Field(expr, _) | ExprKind::Index(expr, ..) => is_local_with_projections(expr), + _ => false, + } +} + pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, @@ -36,9 +82,8 @@ pub(super) fn check<'tcx>( && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind && cx.typeck_results().expr_ty(receiver).is_bool() && path.ident.name == sym!(then_some) - && let ExprKind::Binary(_, lhs, rhs) = receiver.kind - && let Some(local_id) = path_to_local(transmutable) - && (path_to_local_id(lhs, local_id) || path_to_local_id(rhs, local_id)) + && is_local_with_projections(transmutable) + && binops_with_local(cx, transmutable, receiver) && is_normalizable(cx, cx.param_env, from_ty) && is_normalizable(cx, cx.param_env, to_ty) // we only want to lint if the target type has a niche that is larger than the one of the source type diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index db378cfd755..a6f03c85b4f 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -37,9 +37,7 @@ pub(super) fn check<'tcx>( to_ty = to_sub_ty; continue; }, - (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) - if reduced_tys.from_fat_ptr => - { + (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index b1fa30aa068..e90306ded61 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -1,14 +1,21 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{get_trait_def_id, path_res}; +use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id}; use rustc_ast::BinOpKind; -use rustc_hir::def::Res; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, Item, ItemKind, Node}; +use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind}; +use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; -use rustc_session::declare_lint_pass; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, Span}; +use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; declare_clippy_lint! { /// ### What it does @@ -41,7 +48,26 @@ declare_clippy_lint! { "detect unconditional recursion in some traits implementation" } -declare_lint_pass!(UnconditionalRecursion => [UNCONDITIONAL_RECURSION]); +#[derive(Default)] +pub struct UnconditionalRecursion { + /// The key is the `DefId` of the type implementing the `Default` trait and the value is the + /// `DefId` of the return call. + default_impl_for_type: FxHashMap<DefId, DefId>, +} + +impl_lint_pass!(UnconditionalRecursion => [UNCONDITIONAL_RECURSION]); + +fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) { + span_lint_and_then( + cx, + UNCONDITIONAL_RECURSION, + method_span, + "function cannot return without recursing", + |diag| { + diag.span_note(expr.span, "recursive call site"); + }, + ); +} fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> { match ty.peel_refs().kind() { @@ -51,84 +77,329 @@ fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> { } } -fn is_local(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(path_res(cx, expr), Res::Local(_)) +fn get_hir_ty_def_id(tcx: TyCtxt<'_>, hir_ty: rustc_hir::Ty<'_>) -> Option<DefId> { + let TyKind::Path(qpath) = hir_ty.kind else { return None }; + match qpath { + QPath::Resolved(_, path) => path.res.opt_def_id(), + QPath::TypeRelative(_, _) => { + let ty = hir_ty_to_ty(tcx, &hir_ty); + + match ty.kind() { + ty::Alias(ty::Projection, proj) => { + Res::<HirId>::Def(DefKind::Trait, proj.trait_ref(tcx).def_id).opt_def_id() + }, + _ => None, + } + }, + QPath::LangItem(..) => None, + } } -impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { +fn get_return_calls_in_body<'tcx>(body: &'tcx Body<'tcx>) -> Vec<&'tcx Expr<'tcx>> { + let mut visitor = ReturnsVisitor::default(); + + visitor.visit_body(body); + visitor.returns +} + +fn has_conditional_return(body: &Body<'_>, expr: &Expr<'_>) -> bool { + match get_return_calls_in_body(body).as_slice() { + [] => false, + [return_expr] => return_expr.hir_id != expr.hir_id, + _ => true, + } +} + +fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Option<DefId> { + let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id); + if let Some(( + _, + Node::Item(Item { + kind: ItemKind::Impl(impl_), + owner_id, + .. + }), + )) = cx.tcx.hir().parent_iter(hir_id).next() + // We exclude `impl` blocks generated from rustc's proc macros. + && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + // It is a implementation of a trait. + && let Some(trait_) = impl_.of_trait + { + trait_.trait_def_id() + } else { + None + } +} + +#[allow(clippy::unnecessary_def_path)] +fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) { + let args = cx + .tcx + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder()) + .inputs(); + // That has two arguments. + if let [self_arg, other_arg] = args + && let Some(self_arg) = get_ty_def_id(*self_arg) + && let Some(other_arg) = get_ty_def_id(*other_arg) + // The two arguments are of the same type. + && self_arg == other_arg + && let Some(trait_def_id) = get_impl_trait_def_id(cx, method_def_id) + // The trait is `PartialEq`. + && Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"]) + { + let to_check_op = if name.name == sym::eq { + BinOpKind::Eq + } else { + BinOpKind::Ne + }; + let is_bad = match expr.kind { + ExprKind::Binary(op, left, right) if op.node == to_check_op => { + // Then we check if the left-hand element is of the same type as `self`. + if let Some(left_ty) = cx.typeck_results().expr_ty_opt(left) + && let Some(left_id) = get_ty_def_id(left_ty) + && self_arg == left_id + && let Some(right_ty) = cx.typeck_results().expr_ty_opt(right) + && let Some(right_id) = get_ty_def_id(right_ty) + && other_arg == right_id + { + true + } else { + false + } + }, + ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { + if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && trait_id == trait_def_id + { + true + } else { + false + } + }, + _ => false, + }; + if is_bad { + span_error(cx, method_span, expr); + } + } +} + +#[allow(clippy::unnecessary_def_path)] +fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) { + let args = cx + .tcx + .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder()) + .inputs(); + // That has one argument. + if let [_self_arg] = args + && let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id) + && let Some(( + _, + Node::Item(Item { + kind: ItemKind::Impl(impl_), + owner_id, + .. + }), + )) = cx.tcx.hir().parent_iter(hir_id).next() + // We exclude `impl` blocks generated from rustc's proc macros. + && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) + // It is a implementation of a trait. + && let Some(trait_) = impl_.of_trait + && let Some(trait_def_id) = trait_.trait_def_id() + // The trait is `ToString`. + && Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"]) + { + let is_bad = match expr.kind { + ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { + if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && trait_id == trait_def_id + { + true + } else { + false + } + }, + _ => false, + }; + if is_bad { + span_error(cx, method_span, expr); + } + } +} + +fn is_default_method_on_current_ty(tcx: TyCtxt<'_>, qpath: QPath<'_>, implemented_ty_id: DefId) -> bool { + match qpath { + QPath::Resolved(_, path) => match path.segments { + [first, .., last] => last.ident.name == kw::Default && first.res.opt_def_id() == Some(implemented_ty_id), + _ => false, + }, + QPath::TypeRelative(ty, segment) => { + if segment.ident.name != kw::Default { + return false; + } + if matches!( + ty.kind, + TyKind::Path(QPath::Resolved( + _, + hir::Path { + res: Res::SelfTyAlias { .. }, + .. + }, + )) + ) { + return true; + } + get_hir_ty_def_id(tcx, *ty) == Some(implemented_ty_id) + }, + QPath::LangItem(..) => false, + } +} + +struct CheckCalls<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + map: Map<'tcx>, + implemented_ty_id: DefId, + found_default_call: bool, + method_span: Span, +} + +impl<'a, 'tcx> Visitor<'tcx> for CheckCalls<'a, 'tcx> +where + 'tcx: 'a, +{ + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.map + } + #[allow(clippy::unnecessary_def_path)] + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if self.found_default_call { + return; + } + walk_expr(self, expr); + + if let ExprKind::Call(f, _) = expr.kind + && let ExprKind::Path(qpath) = f.kind + && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) + && let Some(method_def_id) = path_def_id(self.cx, f) + && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) + && Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"]) + { + self.found_default_call = true; + span_error(self.cx, self.method_span, expr); + } + } +} + +impl UnconditionalRecursion { + #[allow(clippy::unnecessary_def_path)] + fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) { + if self.default_impl_for_type.is_empty() + && let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"]) + { + let impls = cx.tcx.trait_impls_of(default_trait_id); + for (ty, impl_def_ids) in impls.non_blanket_impls() { + let Some(self_def_id) = ty.def() else { continue }; + for impl_def_id in impl_def_ids { + if !cx.tcx.has_attr(*impl_def_id, sym::automatically_derived) && + let Some(assoc_item) = cx + .tcx + .associated_items(impl_def_id) + .in_definition_order() + // We're not interested in foreign implementations of the `Default` trait. + .find(|item| { + item.kind == AssocKind::Fn && item.def_id.is_local() && item.name == kw::Default + }) + && let Some(body_node) = cx.tcx.hir().get_if_local(assoc_item.def_id) + && let Some(body_id) = body_node.body_id() + && let body = cx.tcx.hir().body(body_id) + // We don't want to keep it if it has conditional return. + && let [return_expr] = get_return_calls_in_body(body).as_slice() + && let ExprKind::Call(call_expr, _) = return_expr.kind + // We need to use typeck here to infer the actual function being called. + && let body_def_id = cx.tcx.hir().enclosing_body_owner(call_expr.hir_id) + && let Some(body_owner) = cx.tcx.hir().maybe_body_owned_by(body_def_id) + && let typeck = cx.tcx.typeck_body(body_owner) + && let Some(call_def_id) = typeck.type_dependent_def_id(call_expr.hir_id) + { + self.default_impl_for_type.insert(self_def_id, call_def_id); + } + } + } + } + } + + fn check_default_new<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + decl: &FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + method_span: Span, + method_def_id: LocalDefId, + ) { + // We're only interested into static methods. + if decl.implicit_self.has_implicit_self() { + return; + } + // We don't check trait implementations. + if get_impl_trait_def_id(cx, method_def_id).is_some() { + return; + } + + let hir_id = cx.tcx.local_def_id_to_hir_id(method_def_id); + if let Some(( + _, + Node::Item(Item { + kind: ItemKind::Impl(impl_), + .. + }), + )) = cx.tcx.hir().parent_iter(hir_id).next() + && let Some(implemented_ty_id) = get_hir_ty_def_id(cx.tcx, *impl_.self_ty) + && { + self.init_default_impl_for_type_if_needed(cx); + true + } + && let Some(return_def_id) = self.default_impl_for_type.get(&implemented_ty_id) + && method_def_id.to_def_id() == *return_def_id + { + let mut c = CheckCalls { + cx, + map: cx.tcx.hir(), + implemented_ty_id, + found_default_call: false, + method_span, + }; + walk_body(&mut c, body); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { fn check_fn( &mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, - _decl: &'tcx FnDecl<'tcx>, + decl: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, method_span: Span, - def_id: LocalDefId, + method_def_id: LocalDefId, ) { // If the function is a method... if let FnKind::Method(name, _) = kind - // That has two arguments. - && let [self_arg, other_arg] = cx - .tcx - .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder()) - .inputs() - && let Some(self_arg) = get_ty_def_id(*self_arg) - && let Some(other_arg) = get_ty_def_id(*other_arg) - // The two arguments are of the same type. - && self_arg == other_arg - && let hir_id = cx.tcx.local_def_id_to_hir_id(def_id) - && let Some(( - _, - Node::Item(Item { - kind: ItemKind::Impl(impl_), - owner_id, - .. - }), - )) = cx.tcx.hir().parent_iter(hir_id).next() - // We exclude `impl` blocks generated from rustc's proc macros. - && !cx.tcx.has_attr(*owner_id, sym::automatically_derived) - // It is a implementation of a trait. - && let Some(trait_) = impl_.of_trait - && let Some(trait_def_id) = trait_.trait_def_id() - // The trait is `PartialEq`. - && Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"]) + && let expr = expr_or_init(cx, body.value).peel_blocks() + // Doesn't have a conditional return. + && !has_conditional_return(body, expr) { - let to_check_op = if name.name == sym::eq { - BinOpKind::Eq - } else { - BinOpKind::Ne - }; - let expr = body.value.peel_blocks(); - let is_bad = match expr.kind { - ExprKind::Binary(op, left, right) if op.node == to_check_op => { - is_local(cx, left) && is_local(cx, right) - }, - ExprKind::MethodCall(segment, receiver, &[arg], _) if segment.ident.name == name.name => { - if is_local(cx, receiver) - && is_local(cx, &arg) - && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) - && trait_id == trait_def_id - { - true - } else { - false - } - }, - _ => false, - }; - if is_bad { - span_lint_and_then( - cx, - UNCONDITIONAL_RECURSION, - method_span, - "function cannot return without recursing", - |diag| { - diag.span_note(expr.span, "recursive call site"); - }, - ); + if name.name == sym::eq || name.name == sym::ne { + check_partial_eq(cx, method_span, method_def_id, name, expr); + } else if name.name == sym::to_string { + check_to_string(cx, method_span, method_def_id, name, expr); } + self.check_default_new(cx, decl, body, method_span, method_def_id); } } } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index e5bc3b5a25f..add4b3e5637 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -681,11 +681,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos .filter(|(_, text)| !text.is_empty()); let (line_start, line) = lines.next()?; + let mut in_codeblock = false; // Check for a sequence of line comments. if line.starts_with("//") { let (mut line, mut line_start) = (line, line_start); loop { - if line.to_ascii_uppercase().contains("SAFETY:") { + // Don't lint if the safety comment is part of a codeblock in a doc comment. + // It may or may not be required, and we can't very easily check it (and we shouldn't, since + // the safety comment isn't referring to the node we're currently checking) + if line.trim_start_matches("///").trim_start().starts_with("```") { + in_codeblock = !in_codeblock; + } + + if line.to_ascii_uppercase().contains("SAFETY:") && !in_codeblock { return Some(start_pos + BytePos(u32::try_from(line_start).unwrap())); } match lines.next() { diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index ef67f4b04b5..ed0958197f8 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -13,12 +13,31 @@ use rustc_middle::ty; use super::LET_UNIT_VALUE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + // skip `let () = { ... }` + if let PatKind::Tuple(fields, ..) = local.pat.kind + && fields.is_empty() + { + return; + } + if let Some(init) = local.init && !local.pat.span.from_expansion() && !in_external_macro(cx.sess(), local.span) && !is_from_async_await(local.span) && cx.typeck_results().pat_ty(local.pat).is_unit() { + // skip `let awa = ()` + if let ExprKind::Tup([]) = init.kind { + return; + } + + // skip `let _: () = { ... }` + if let Some(ty) = local.ty + && let TyKind::Tup([]) = ty.kind + { + return; + } + if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer)) || matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none())) && expr_needs_inferred_result(cx, init) @@ -34,7 +53,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { |diag| { diag.span_suggestion( local.pat.span, - "use a wild (`_`) binding", + "use a wildcard binding", "_", Applicability::MaybeIncorrect, // snippet ); diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 5e13c73f035..2c33c93412a 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -11,8 +11,8 @@ use clippy_utils::{get_parent_expr, higher, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::{sym, DesugaringKind, Span}; @@ -79,7 +79,6 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { // this is to avoid compile errors when doing the suggestion here: let _: Vec<_> = vec![..]; && local.ty.is_none() && let PatKind::Binding(_, id, ..) = local.pat.kind - && is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(expr.peel_borrows()))) { let only_slice_uses = for_each_local_use_after_expr(cx, id, expr.hir_id, |expr| { // allow indexing into a vec and some set of allowed method calls that exist on slices, too @@ -185,6 +184,11 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) { + // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also + if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { + return; + } + #[expect(clippy::cast_possible_truncation)] if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { return; @@ -241,12 +245,3 @@ fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { let ty = cx.typeck_results().expr_ty_adjusted(expr); cx.layout_of(ty).map_or(0, |l| l.size.bytes()) } - -/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`). -fn vec_type(ty: Ty<'_>) -> Ty<'_> { - if let ty::Adt(_, args) = ty.kind() { - args.type_at(0) - } else { - panic!("The type of `vec!` is a not a struct?"); - } -} diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 9b0dac6af25..b82bd1d7e7c 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -142,7 +142,7 @@ impl LateLintPass<'_> for WildcardImports { } else { // In this case, the `use_path.span` ends right before the `::*`, so we need to // extend it up to the `*`. Since it is hard to find the `*` in weird - // formattings like `use _ :: *;`, we extend it up to, but not including the + // formatting like `use _ :: *;`, we extend it up to, but not including the // `;`. In nested imports, like `use _::{inner::*, _}` there is no `;` and we // can just use the end of the item span let mut span = use_path.span.with_hi(item.span.hi()); diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 4e71c6483e6..6c40029a9de 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -99,7 +99,10 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res { + if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _) + | Res::SelfCtor(_) + | Res::SelfTyAlias { .. } = res + { cx.typeck_results() .expr_ty(e) .has_significant_drop(cx.tcx, cx.param_env) @@ -173,6 +176,13 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS self.eagerness |= NoChange; return; }, + #[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778 + ExprKind::Struct(path, ..) => { + if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + self.eagerness = ForceNoChange; + return; + } + }, ExprKind::Path(ref path) => { if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { self.eagerness = ForceNoChange; @@ -291,7 +301,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS | ExprKind::Closure { .. } | ExprKind::Field(..) | ExprKind::AddrOf(..) - | ExprKind::Struct(..) | ExprKind::Repeat(..) | ExprKind::Block(Block { stmts: [], .. }, _) | ExprKind::OffsetOf(..) => (), diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index a23105691bf..07b72e3f570 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -865,7 +865,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { for arm in arms { self.hash_pat(arm.pat); - if let Some(ref e) = arm.guard { + if let Some(e) = arm.guard { self.hash_expr(e); } self.hash_expr(arm.body); diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 46ce4ffdce5..c475e7b7c43 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -420,7 +420,7 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) ast_format_args .get()? .get(&format_args_expr.span.with_parent(None)) - .map(Rc::clone) + .cloned() }) } diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index da71fc3aaa8..adca2ca1c3e 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -38,7 +38,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { ExprKind::Call(callee, args) => { let lhs = expr_type_certainty(cx, callee); - let rhs = if type_is_inferrable_from_arguments(cx, expr) { + let rhs = if type_is_inferable_from_arguments(cx, expr) { meet(args.iter().map(|arg| expr_type_certainty(cx, arg))) } else { Certainty::Uncertain @@ -57,7 +57,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id); }; let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false); - let rhs = if type_is_inferrable_from_arguments(cx, expr) { + let rhs = if type_is_inferable_from_arguments(cx, expr) { meet( std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))), ) @@ -279,7 +279,7 @@ fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: & } #[allow(clippy::cast_possible_truncation)] -fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let Some(callee_def_id) = (match expr.kind { ExprKind::Call(callee, _) => { let callee_ty = cx.typeck_results().expr_ty(callee); diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 5a2526fd267..2589d46e7da 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-12-28" +channel = "nightly-2024-01-11" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md index e197ea048a0..56f62b867a6 100644 --- a/src/tools/clippy/rustc_tools_util/README.md +++ b/src/tools/clippy/rustc_tools_util/README.md @@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under: <!-- REUSE-IgnoreStart --> -Copyright 2014-2022 The Rust Project Developers +Copyright 2014-2024 The Rust Project Developers Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license diff --git a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/all_pub_fields/clippy.toml b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/all_pub_fields/clippy.toml new file mode 100644 index 00000000000..95835d101b1 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/all_pub_fields/clippy.toml @@ -0,0 +1 @@ +pub-underscore-fields-behavior = "AllPubFields" \ No newline at end of file diff --git a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml new file mode 100644 index 00000000000..94a0d3554bc --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/exported/clippy.toml @@ -0,0 +1 @@ +pub-underscore-fields-behavior = "PublicallyExported" \ No newline at end of file diff --git a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr new file mode 100644 index 00000000000..c6bd499fd8c --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.all_pub_fields.stderr @@ -0,0 +1,60 @@ +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:15:9 + | +LL | pub _b: u8, + | ^^^^^^ + | + = help: consider removing the underscore, or making the field private + = note: `-D clippy::pub-underscore-fields` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]` + +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:23:13 + | +LL | pub(in crate::inner) _f: Option<()>, + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing the underscore, or making the field private + +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:27:13 + | +LL | pub _g: String, + | ^^^^^^ + | + = help: consider removing the underscore, or making the field private + +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:34:9 + | +LL | pub _a: usize, + | ^^^^^^ + | + = help: consider removing the underscore, or making the field private + +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:41:9 + | +LL | pub _c: i64, + | ^^^^^^ + | + = help: consider removing the underscore, or making the field private + +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:44:9 + | +LL | pub _e: Option<u8>, + | ^^^^^^ + | + = help: consider removing the underscore, or making the field private + +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:57:9 + | +LL | pub(crate) _b: Option<String>, + | ^^^^^^^^^^^^^ + | + = help: consider removing the underscore, or making the field private + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.exported.stderr b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.exported.stderr new file mode 100644 index 00000000000..b76f6b3d388 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.exported.stderr @@ -0,0 +1,12 @@ +error: field marked as public but also inferred as unused because it's prefixed with `_` + --> $DIR/pub_underscore_fields.rs:15:9 + | +LL | pub _b: u8, + | ^^^^^^ + | + = help: consider removing the underscore, or making the field private + = note: `-D clippy::pub-underscore-fields` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::pub_underscore_fields)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs new file mode 100644 index 00000000000..1d8fee7daad --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/pub_underscore_fields/pub_underscore_fields.rs @@ -0,0 +1,66 @@ +//@revisions: exported all_pub_fields +//@[all_pub_fields] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/pub_underscore_fields/all_pub_fields +//@[exported] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/pub_underscore_fields/exported + +#![allow(unused)] +#![warn(clippy::pub_underscore_fields)] + +use std::marker::PhantomData; + +pub mod inner { + use std::marker; + + pub struct PubSuper { + pub(super) a: usize, + pub _b: u8, + _c: i32, + pub _mark: marker::PhantomData<u8>, + } + + mod inner2 { + pub struct PubModVisibility { + pub(in crate::inner) e: bool, + pub(in crate::inner) _f: Option<()>, + } + + struct PrivateStructPubField { + pub _g: String, + } + } +} + +fn main() { + pub struct StructWithOneViolation { + pub _a: usize, + } + + // should handle structs with multiple violations + pub struct StructWithMultipleViolations { + a: u8, + _b: usize, + pub _c: i64, + #[doc(hidden)] + pub d: String, + pub _e: Option<u8>, + } + + // shouldn't warn on anonymous fields + pub struct AnonymousFields(pub usize, i32); + + // don't warn on empty structs + pub struct Empty1; + pub struct Empty2(); + pub struct Empty3 {}; + + pub struct PubCrate { + pub(crate) a: String, + pub(crate) _b: Option<String>, + } + + // shouldn't warn on fields named pub + pub struct NamedPub { + r#pub: bool, + _pub: String, + pub(crate) _mark: PhantomData<u8>, + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 12828cf9dec..ed4fb84df1a 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -49,6 +49,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect missing-docs-in-crate-items msrv pass-by-value-size-limit + pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold @@ -124,6 +125,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect missing-docs-in-crate-items msrv pass-by-value-size-limit + pub-underscore-fields-behavior semicolon-inside-block-ignore-singleline semicolon-outside-block-ignore-multiline single-char-binding-names-threshold diff --git a/src/tools/clippy/tests/ui/as_conversions.rs b/src/tools/clippy/tests/ui/as_conversions.rs index 192eb51ea99..8499c0ac5a0 100644 --- a/src/tools/clippy/tests/ui/as_conversions.rs +++ b/src/tools/clippy/tests/ui/as_conversions.rs @@ -17,7 +17,7 @@ fn main() { with_span!( span - fn coverting() { + fn converting() { let x = 0u32 as u64; } ); diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs index 1ca18170f8a..e9476c80ccb 100644 --- a/src/tools/clippy/tests/ui/cast.rs +++ b/src/tools/clippy/tests/ui/cast.rs @@ -365,3 +365,52 @@ fn avoid_subtract_overflow(q: u32) { fn issue11426() { (&42u8 >> 0xa9008fb6c9d81e42_0e25730562a601c8_u128) as usize; } + +fn issue11642() { + fn square(x: i16) -> u32 { + let x = x as i32; + (x * x) as u32; + x.pow(2) as u32; + (-2_i32).pow(2) as u32 + } + + let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; + + (-2_i32).pow(3) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + + let x: i32 = 10; + (x * x) as u32; + (x * x * x) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + + let y: i16 = -2; + (y * y * y * y * -2) as u16; + //~^ ERROR: casting `i16` to `u16` may lose the sign of the value + (y * y * y * y * 2) as u16; + (y * y * y * 2) as u16; + //~^ ERROR: casting `i16` to `u16` may lose the sign of the value + (y * y * y * -2) as u16; + //~^ ERROR: casting `i16` to `u16` may lose the sign of the value + + fn foo(a: i32, b: i32, c: i32) -> u32 { + (a * a * b * b * c * c) as u32; + (a * b * c) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + (a * -b * c) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + (a * b * c * c) as u32; + (a * -2) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + (a * b * c * -2) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + (a / b) as u32; + (a / b * c) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + (a / b + b * c) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + a.pow(3) as u32; + //~^ ERROR: casting `i32` to `u32` may lose the sign of the value + (a.abs() * b.pow(2) / c.abs()) as u32 + } +} diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr index bc74f7b728e..4e37af7f378 100644 --- a/src/tools/clippy/tests/ui/cast.stderr +++ b/src/tools/clippy/tests/ui/cast.stderr @@ -444,5 +444,77 @@ help: ... or use `try_from` and handle the error accordingly LL | let c = u8::try_from(q / 1000); | ~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 51 previous errors +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:379:5 + | +LL | (-2_i32).pow(3) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:384:5 + | +LL | (x * x * x) as u32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting `i16` to `u16` may lose the sign of the value + --> $DIR/cast.rs:388:5 + | +LL | (y * y * y * y * -2) as u16; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `i16` to `u16` may lose the sign of the value + --> $DIR/cast.rs:391:5 + | +LL | (y * y * y * 2) as u16; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `i16` to `u16` may lose the sign of the value + --> $DIR/cast.rs:393:5 + | +LL | (y * y * y * -2) as u16; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:398:9 + | +LL | (a * b * c) as u32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:400:9 + | +LL | (a * -b * c) as u32; + | ^^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:403:9 + | +LL | (a * -2) as u32; + | ^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:405:9 + | +LL | (a * b * c * -2) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:408:9 + | +LL | (a / b * c) as u32; + | ^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:410:9 + | +LL | (a / b + b * c) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: casting `i32` to `u32` may lose the sign of the value + --> $DIR/cast.rs:412:9 + | +LL | a.pow(3) as u32; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 63 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-8821.fixed b/src/tools/clippy/tests/ui/crashes/ice-8821.fixed deleted file mode 100644 index a25bb46f9ff..00000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-8821.fixed +++ /dev/null @@ -1,10 +0,0 @@ -#![warn(clippy::let_unit_value)] - -fn f() {} -static FN: fn() = f; - -fn main() { - FN(); - //~^ ERROR: this let-binding has unit value - //~| NOTE: `-D clippy::let-unit-value` implied by `-D warnings` -} diff --git a/src/tools/clippy/tests/ui/crashes/ice-8821.rs b/src/tools/clippy/tests/ui/crashes/ice-8821.rs index 082f7c92646..fb87b79aeed 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-8821.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-8821.rs @@ -5,6 +5,4 @@ static FN: fn() = f; fn main() { let _: () = FN(); - //~^ ERROR: this let-binding has unit value - //~| NOTE: `-D clippy::let-unit-value` implied by `-D warnings` } diff --git a/src/tools/clippy/tests/ui/crashes/ice-8821.stderr b/src/tools/clippy/tests/ui/crashes/ice-8821.stderr deleted file mode 100644 index 94ebb20918e..00000000000 --- a/src/tools/clippy/tests/ui/crashes/ice-8821.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: this let-binding has unit value - --> $DIR/ice-8821.rs:7:5 - | -LL | let _: () = FN(); - | ^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `FN();` - | - = note: `-D clippy::let-unit-value` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed index 9072d233563..00f0d764434 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.fixed @@ -73,9 +73,7 @@ mod nested_local { mod function_def { fn ret_f64() -> f64 { - // Even though the output type is specified, - // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. - 1.0_f64 + 1. } fn test() { diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs index 256b94f6c05..942cedac275 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.rs @@ -73,8 +73,6 @@ mod nested_local { mod function_def { fn ret_f64() -> f64 { - // Even though the output type is specified, - // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. 1. } diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr index 7ea2e3e6819..c95679c9eb8 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_f64.stderr @@ -86,66 +86,60 @@ LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:78:9 - | -LL | 1. - | ^^ help: consider adding suffix: `1.0_f64` - -error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:84:27 + --> $DIR/default_numeric_fallback_f64.rs:82:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:88:29 + --> $DIR/default_numeric_fallback_f64.rs:86:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:102:21 + --> $DIR/default_numeric_fallback_f64.rs:100:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:105:32 + --> $DIR/default_numeric_fallback_f64.rs:103:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:123:28 + --> $DIR/default_numeric_fallback_f64.rs:121:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:126:36 + --> $DIR/default_numeric_fallback_f64.rs:124:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:144:24 + --> $DIR/default_numeric_fallback_f64.rs:142:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:164:23 + --> $DIR/default_numeric_fallback_f64.rs:162:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:174:25 + --> $DIR/default_numeric_fallback_f64.rs:172:25 | LL | inline!(let x = 22.;); | ^^^ help: consider adding suffix: `22.0_f64` | = note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 24 previous errors +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed index 920cd9f8f77..c364c683057 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.fixed @@ -74,9 +74,7 @@ mod nested_local { mod function_def { fn ret_i32() -> i32 { - // Even though the output type is specified, - // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. - 1_i32 + 1 } fn test() { @@ -186,4 +184,36 @@ fn check_expect_suppression() { let x = 21; } +mod type_already_inferred { + // Should NOT lint if bound to return type + fn ret_i32() -> i32 { + 1 + } + + // Should NOT lint if bound to return type + fn ret_if_i32(b: bool) -> i32 { + if b { 100 } else { 0 } + } + + // Should NOT lint if bound to return type + fn ret_i32_tuple() -> (i32, i32) { + (0, 1) + } + + // Should NOT lint if bound to return type + fn ret_stmt(b: bool) -> (i32, i32) { + if b { + return (0, 1); + } + (0, 0) + } + + #[allow(clippy::useless_vec)] + fn vec_macro() { + // Should NOT lint in `vec!` call if the type was already stated + let data_i32: Vec<i32> = vec![1, 2, 3]; + let data_i32 = vec![1_i32, 2_i32, 3_i32]; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs index bdb7b5f47bc..ffa7b961d1c 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.rs @@ -74,8 +74,6 @@ mod nested_local { mod function_def { fn ret_i32() -> i32 { - // Even though the output type is specified, - // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. 1 } @@ -186,4 +184,36 @@ fn check_expect_suppression() { let x = 21; } +mod type_already_inferred { + // Should NOT lint if bound to return type + fn ret_i32() -> i32 { + 1 + } + + // Should NOT lint if bound to return type + fn ret_if_i32(b: bool) -> i32 { + if b { 100 } else { 0 } + } + + // Should NOT lint if bound to return type + fn ret_i32_tuple() -> (i32, i32) { + (0, 1) + } + + // Should NOT lint if bound to return type + fn ret_stmt(b: bool) -> (i32, i32) { + if b { + return (0, 1); + } + (0, 0) + } + + #[allow(clippy::useless_vec)] + fn vec_macro() { + // Should NOT lint in `vec!` call if the type was already stated + let data_i32: Vec<i32> = vec![1, 2, 3]; + let data_i32 = vec![1, 2, 3]; + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr index b03b8b84c33..7663977fb65 100644 --- a/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr +++ b/src/tools/clippy/tests/ui/default_numeric_fallback_i32.stderr @@ -98,66 +98,78 @@ LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:79:9 - | -LL | 1 - | ^ help: consider adding suffix: `1_i32` - -error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:85:27 + --> $DIR/default_numeric_fallback_i32.rs:83:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:29 + --> $DIR/default_numeric_fallback_i32.rs:87:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:103:21 + --> $DIR/default_numeric_fallback_i32.rs:101:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:106:32 + --> $DIR/default_numeric_fallback_i32.rs:104:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:124:28 + --> $DIR/default_numeric_fallback_i32.rs:122:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:127:36 + --> $DIR/default_numeric_fallback_i32.rs:125:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:145:24 + --> $DIR/default_numeric_fallback_i32.rs:143:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:165:23 + --> $DIR/default_numeric_fallback_i32.rs:163:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:175:25 + --> $DIR/default_numeric_fallback_i32.rs:173:25 | LL | inline!(let x = 22;); | ^^ help: consider adding suffix: `22_i32` | = note: this error originates in the macro `__inline_mac_fn_internal` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 26 previous errors +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:215:29 + | +LL | let data_i32 = vec![1, 2, 3]; + | ^ help: consider adding suffix: `1_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:215:32 + | +LL | let data_i32 = vec![1, 2, 3]; + | ^ help: consider adding suffix: `2_i32` + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback_i32.rs:215:35 + | +LL | let data_i32 = vec![1, 2, 3]; + | ^ help: consider adding suffix: `3_i32` + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/eager_transmute.fixed b/src/tools/clippy/tests/ui/eager_transmute.fixed index e06aa2cc9fd..bece09bba1a 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.fixed +++ b/src/tools/clippy/tests/ui/eager_transmute.fixed @@ -12,16 +12,49 @@ enum Opcode { Div = 3, } +struct Data { + foo: &'static [u8], + bar: &'static [u8], +} + fn int_to_opcode(op: u8) -> Option<Opcode> { (op < 4).then(|| unsafe { std::mem::transmute(op) }) } -fn f(op: u8, unrelated: u8) { +fn f(op: u8, op2: Data, unrelated: u8) { true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); (unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); (op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) }); (op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) }); (op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) }); + + let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) }); + + // lint even when the transmutable goes through field/array accesses + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) }); + + // don't lint: wrong index used in the transmute + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[1]) }); + + // don't lint: no check for the transmutable in the condition + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op2.bar[0]) }); + + // don't lint: wrong variable + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) }); + + // range contains checks + let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + + // unrelated binding in contains + let _: Option<Opcode> = (1..=3) + .contains(&unrelated) + .then_some(unsafe { std::mem::transmute(op) }); } unsafe fn f2(op: u8) { diff --git a/src/tools/clippy/tests/ui/eager_transmute.rs b/src/tools/clippy/tests/ui/eager_transmute.rs index 89ccdf583f3..a82bd578f76 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.rs +++ b/src/tools/clippy/tests/ui/eager_transmute.rs @@ -12,16 +12,49 @@ enum Opcode { Div = 3, } +struct Data { + foo: &'static [u8], + bar: &'static [u8], +} + fn int_to_opcode(op: u8) -> Option<Opcode> { (op < 4).then_some(unsafe { std::mem::transmute(op) }) } -fn f(op: u8, unrelated: u8) { +fn f(op: u8, op2: Data, unrelated: u8) { true.then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); (unrelated < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); (op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); (op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); (op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); + + let _: Option<Opcode> = (op > 0 && op < 10).then_some(unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then_some(unsafe { std::mem::transmute(op) }); + + // lint even when the transmutable goes through field/array accesses + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[0]) }); + + // don't lint: wrong index used in the transmute + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[1]) }); + + // don't lint: no check for the transmutable in the condition + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op2.bar[0]) }); + + // don't lint: wrong variable + let _: Option<Opcode> = (op2.foo[0] > 0 && op2.bar[1] < 10).then_some(unsafe { std::mem::transmute(op) }); + + // range contains checks + let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + + // unrelated binding in contains + let _: Option<Opcode> = (1..=3) + .contains(&unrelated) + .then_some(unsafe { std::mem::transmute(op) }); } unsafe fn f2(op: u8) { diff --git a/src/tools/clippy/tests/ui/eager_transmute.stderr b/src/tools/clippy/tests/ui/eager_transmute.stderr index 5eb163c5fcb..5f42eec544f 100644 --- a/src/tools/clippy/tests/ui/eager_transmute.stderr +++ b/src/tools/clippy/tests/ui/eager_transmute.stderr @@ -1,5 +1,5 @@ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:16:33 + --> $DIR/eager_transmute.rs:21:33 | LL | (op < 4).then_some(unsafe { std::mem::transmute(op) }) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | (op < 4).then(|| unsafe { std::mem::transmute(op) }) | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:22:33 + --> $DIR/eager_transmute.rs:27:33 | LL | (op < 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | (op < 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) }); | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:23:33 + --> $DIR/eager_transmute.rs:28:33 | LL | (op > 4).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | (op > 4).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) }); | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:24:34 + --> $DIR/eager_transmute.rs:29:34 | LL | (op == 0).then_some(unsafe { std::mem::transmute::<_, Opcode>(op) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,106 @@ LL | (op == 0).then(|| unsafe { std::mem::transmute::<_, Opcode>(op) }); | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:28:24 + --> $DIR/eager_transmute.rs:31:68 + | +LL | let _: Option<Opcode> = (op > 0 && op < 10).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (op > 0 && op < 10).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:32:86 + | +LL | let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (op > 0 && op < 10 && unrelated == 0).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:35:84 + | +LL | let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then_some(unsafe { std::mem::transmute(op2.foo[0]) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (op2.foo[0] > 0 && op2.foo[0] < 10).then(|| unsafe { std::mem::transmute(op2.foo[0]) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:47:70 + | +LL | let _: Option<Opcode> = (1..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (1..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:48:83 + | +LL | let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = ((1..=3).contains(&op) || op == 4).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:49:69 + | +LL | let _: Option<Opcode> = (1..3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (1..3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:50:68 + | +LL | let _: Option<Opcode> = (1..).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (1..).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:51:68 + | +LL | let _: Option<Opcode> = (..3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (..3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:52:69 + | +LL | let _: Option<Opcode> = (..=3).contains(&op).then_some(unsafe { std::mem::transmute(op) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `bool::then` to only transmute if the condition holds + | +LL | let _: Option<Opcode> = (..=3).contains(&op).then(|| unsafe { std::mem::transmute(op) }); + | ~~~~ ++ + +error: this transmute is always evaluated eagerly, even if the condition is false + --> $DIR/eager_transmute.rs:61:24 | LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +155,7 @@ LL | (op < 4).then(|| std::mem::transmute::<_, Opcode>(op)); | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:57:60 + --> $DIR/eager_transmute.rs:90:60 | LL | let _: Option<NonZeroU8> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,7 +166,7 @@ LL | let _: Option<NonZeroU8> = (v1 > 0).then(|| unsafe { std::mem::transmut | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:63:86 + --> $DIR/eager_transmute.rs:96:86 | LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -78,7 +177,7 @@ LL | let _: Option<NonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| u | ~~~~ ++ error: this transmute is always evaluated eagerly, even if the condition is false - --> $DIR/eager_transmute.rs:69:93 + --> $DIR/eager_transmute.rs:102:93 | LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then_some(unsafe { std::mem::transmute(v2) }); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,5 +187,5 @@ help: consider using `bool::then` to only transmute if the condition holds LL | let _: Option<NonZeroNonMaxU8> = (v2 < NonZeroU8::new(255).unwrap()).then(|| unsafe { std::mem::transmute(v2) }); | ~~~~ ++ -error: aborting due to 8 previous errors +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed new file mode 100644 index 00000000000..1a5e78dd47f --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.fixed @@ -0,0 +1,27 @@ +#![warn(clippy::empty_enum_variants_with_brackets)] +#![allow(dead_code)] + +pub enum PublicTestEnum { + NonEmptyBraces { x: i32, y: i32 }, // No error + NonEmptyParentheses(i32, i32), // No error + EmptyBraces, //~ ERROR: enum variant has empty brackets + EmptyParentheses, //~ ERROR: enum variant has empty brackets +} + +enum TestEnum { + NonEmptyBraces { x: i32, y: i32 }, // No error + NonEmptyParentheses(i32, i32), // No error + EmptyBraces, //~ ERROR: enum variant has empty brackets + EmptyParentheses, //~ ERROR: enum variant has empty brackets + AnotherEnum, // No error +} + +enum TestEnumWithFeatures { + NonEmptyBraces { + #[cfg(feature = "thisisneverenabled")] + x: i32, + }, // No error + NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs new file mode 100644 index 00000000000..ca20b969a24 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.rs @@ -0,0 +1,27 @@ +#![warn(clippy::empty_enum_variants_with_brackets)] +#![allow(dead_code)] + +pub enum PublicTestEnum { + NonEmptyBraces { x: i32, y: i32 }, // No error + NonEmptyParentheses(i32, i32), // No error + EmptyBraces {}, //~ ERROR: enum variant has empty brackets + EmptyParentheses(), //~ ERROR: enum variant has empty brackets +} + +enum TestEnum { + NonEmptyBraces { x: i32, y: i32 }, // No error + NonEmptyParentheses(i32, i32), // No error + EmptyBraces {}, //~ ERROR: enum variant has empty brackets + EmptyParentheses(), //~ ERROR: enum variant has empty brackets + AnotherEnum, // No error +} + +enum TestEnumWithFeatures { + NonEmptyBraces { + #[cfg(feature = "thisisneverenabled")] + x: i32, + }, // No error + NonEmptyParentheses(#[cfg(feature = "thisisneverenabled")] i32), // No error +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr new file mode 100644 index 00000000000..6b2f286147f --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_enum_variants_with_brackets.stderr @@ -0,0 +1,36 @@ +error: enum variant has empty brackets + --> $DIR/empty_enum_variants_with_brackets.rs:7:16 + | +LL | EmptyBraces {}, + | ^^^ + | + = note: `-D clippy::empty-enum-variants-with-brackets` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_enum_variants_with_brackets)]` + = help: remove the brackets + +error: enum variant has empty brackets + --> $DIR/empty_enum_variants_with_brackets.rs:8:21 + | +LL | EmptyParentheses(), + | ^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> $DIR/empty_enum_variants_with_brackets.rs:14:16 + | +LL | EmptyBraces {}, + | ^^^ + | + = help: remove the brackets + +error: enum variant has empty brackets + --> $DIR/empty_enum_variants_with_brackets.rs:15:21 + | +LL | EmptyParentheses(), + | ^^ + | + = help: remove the brackets + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/identity_op.fixed b/src/tools/clippy/tests/ui/identity_op.fixed index f3b4b1fffa0..660e9a21b18 100644 --- a/src/tools/clippy/tests/ui/identity_op.fixed +++ b/src/tools/clippy/tests/ui/identity_op.fixed @@ -6,7 +6,9 @@ clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::borrow_deref_ref, + clippy::deref_addrof )] use std::fmt::Write as _; @@ -40,32 +42,45 @@ fn main() { let x = 0; x; + //~^ ERROR: this operation has no effect x; + //~^ ERROR: this operation has no effect x + 1; x; + //~^ ERROR: this operation has no effect 1 + x; x - ZERO; //no error, as we skip lookups (for now) x; + //~^ ERROR: this operation has no effect ((ZERO)) | x; //no error, as we skip lookups (for now) x; + //~^ ERROR: this operation has no effect x; + //~^ ERROR: this operation has no effect x / ONE; //no error, as we skip lookups (for now) x / 2; //no false positive x & NEG_ONE; //no error, as we skip lookups (for now) x; + //~^ ERROR: this operation has no effect let u: u8 = 0; u; + //~^ ERROR: this operation has no effect 1 << 0; // no error, this case is allowed, see issue 3430 42; + //~^ ERROR: this operation has no effect 1; + //~^ ERROR: this operation has no effect 42; + //~^ ERROR: this operation has no effect x; + //~^ ERROR: this operation has no effect x; + //~^ ERROR: this operation has no effect let mut a = A(String::new()); let b = a << 0; // no error: non-integer @@ -73,10 +88,15 @@ fn main() { 1 * Meter; // no error: non-integer 2; + //~^ ERROR: this operation has no effect -2; + //~^ ERROR: this operation has no effect 2 + x; + //~^ ERROR: this operation has no effect -2 + x; + //~^ ERROR: this operation has no effect x + 1; + //~^ ERROR: this operation has no effect (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error @@ -85,38 +105,110 @@ fn main() { let a = 0; let b = true; (if b { 1 } else { 2 }); + //~^ ERROR: this operation has no effect (if b { 1 } else { 2 }) + if b { 3 } else { 4 }; + //~^ ERROR: this operation has no effect (match a { 0 => 10, _ => 20 }); + //~^ ERROR: this operation has no effect (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 }; + //~^ ERROR: this operation has no effect (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 }; + //~^ ERROR: this operation has no effect (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 }; + //~^ ERROR: this operation has no effect (if b { 1 } else { 2 }); + //~^ ERROR: this operation has no effect ({ a }) + 3; + //~^ ERROR: this operation has no effect ({ a } * 2); + //~^ ERROR: this operation has no effect (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 }; + //~^ ERROR: this operation has no effect fn f(_: i32) { todo!(); } + f(a + { 8 * 5 }); + //~^ ERROR: this operation has no effect f(if b { 1 } else { 2 } + 3); + //~^ ERROR: this operation has no effect + const _: i32 = { 2 * 4 } + 3; + //~^ ERROR: this operation has no effect const _: i32 = { 1 + 2 * 3 } + 3; + //~^ ERROR: this operation has no effect a as usize; + //~^ ERROR: this operation has no effect let _ = a as usize; + //~^ ERROR: this operation has no effect ({ a } as usize); + //~^ ERROR: this operation has no effect 2 * { a }; + //~^ ERROR: this operation has no effect (({ a } + 4)); + //~^ ERROR: this operation has no effect 1; + //~^ ERROR: this operation has no effect // Issue #9904 let x = 0i32; let _: i32 = x; + //~^ ERROR: this operation has no effect } pub fn decide(a: bool, b: bool) -> u32 { (if a { 1 } else { 2 }) + if b { 3 } else { 5 } } + +/// The following tests are from / for issue #12050 +/// In short, the lint didn't work for coerced references, +/// e.g. let x = &0; let y = x + 0; +/// because the suggested fix was `let y = x;` but +/// it should have been `let y = *x;` +fn issue_12050() { + { + let x = &0i32; + let _: i32 = *x; + //~^ ERROR: this operation has no effect + let _: i32 = *x; + //~^ ERROR: this operation has no effect + } + { + let x = &&0i32; + let _: i32 = **x; + //~^ ERROR: this operation has no effect + let x = &&0i32; + let _: i32 = **x; + //~^ ERROR: this operation has no effect + } + { + // this is just silly + let x = &&&0i32; + let _: i32 = ***x; + //~^ ERROR: this operation has no effect + let _: i32 = ***x; + //~^ ERROR: this operation has no effect + let x = 0i32; + let _: i32 = *&x; + //~^ ERROR: this operation has no effect + let _: i32 = **&&x; + //~^ ERROR: this operation has no effect + let _: i32 = *&*&x; + //~^ ERROR: this operation has no effect + let _: i32 = **&&*&x; + //~^ ERROR: this operation has no effect + } + { + // this is getting ridiculous, but we should still see the same + // error message so let's just keep going + let x = &0i32; + let _: i32 = ***&&*&x; + //~^ ERROR: this operation has no effect + let _: i32 = ***&&*&x; + //~^ ERROR: this operation has no effect + } +} diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs index 631aa3b0215..bbef531e9dc 100644 --- a/src/tools/clippy/tests/ui/identity_op.rs +++ b/src/tools/clippy/tests/ui/identity_op.rs @@ -6,7 +6,9 @@ clippy::unnecessary_operation, clippy::op_ref, clippy::double_parens, - clippy::uninlined_format_args + clippy::uninlined_format_args, + clippy::borrow_deref_ref, + clippy::deref_addrof )] use std::fmt::Write as _; @@ -40,32 +42,45 @@ fn main() { let x = 0; x + 0; + //~^ ERROR: this operation has no effect x + (1 - 1); + //~^ ERROR: this operation has no effect x + 1; 0 + x; + //~^ ERROR: this operation has no effect 1 + x; x - ZERO; //no error, as we skip lookups (for now) x | (0); + //~^ ERROR: this operation has no effect ((ZERO)) | x; //no error, as we skip lookups (for now) x * 1; + //~^ ERROR: this operation has no effect 1 * x; + //~^ ERROR: this operation has no effect x / ONE; //no error, as we skip lookups (for now) x / 2; //no false positive x & NEG_ONE; //no error, as we skip lookups (for now) -1 & x; + //~^ ERROR: this operation has no effect let u: u8 = 0; u & 255; + //~^ ERROR: this operation has no effect 1 << 0; // no error, this case is allowed, see issue 3430 42 << 0; + //~^ ERROR: this operation has no effect 1 >> 0; + //~^ ERROR: this operation has no effect 42 >> 0; + //~^ ERROR: this operation has no effect &x >> 0; + //~^ ERROR: this operation has no effect x >> &0; + //~^ ERROR: this operation has no effect let mut a = A(String::new()); let b = a << 0; // no error: non-integer @@ -73,10 +88,15 @@ fn main() { 1 * Meter; // no error: non-integer 2 % 3; + //~^ ERROR: this operation has no effect -2 % 3; + //~^ ERROR: this operation has no effect 2 % -3 + x; + //~^ ERROR: this operation has no effect -2 % -3 + x; + //~^ ERROR: this operation has no effect x + 1 % 3; + //~^ ERROR: this operation has no effect (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error @@ -85,38 +105,110 @@ fn main() { let a = 0; let b = true; 0 + if b { 1 } else { 2 }; + //~^ ERROR: this operation has no effect 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; + //~^ ERROR: this operation has no effect 0 + match a { 0 => 10, _ => 20 }; + //~^ ERROR: this operation has no effect 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + //~^ ERROR: this operation has no effect 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + //~^ ERROR: this operation has no effect 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + //~^ ERROR: this operation has no effect (if b { 1 } else { 2 }) + 0; + //~^ ERROR: this operation has no effect 0 + { a } + 3; + //~^ ERROR: this operation has no effect 0 + { a } * 2; + //~^ ERROR: this operation has no effect 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + //~^ ERROR: this operation has no effect fn f(_: i32) { todo!(); } + f(1 * a + { 8 * 5 }); + //~^ ERROR: this operation has no effect f(0 + if b { 1 } else { 2 } + 3); + //~^ ERROR: this operation has no effect + const _: i32 = { 2 * 4 } + 0 + 3; + //~^ ERROR: this operation has no effect const _: i32 = 0 + { 1 + 2 * 3 } + 3; + //~^ ERROR: this operation has no effect 0 + a as usize; + //~^ ERROR: this operation has no effect let _ = 0 + a as usize; + //~^ ERROR: this operation has no effect 0 + { a } as usize; + //~^ ERROR: this operation has no effect 2 * (0 + { a }); + //~^ ERROR: this operation has no effect 1 * ({ a } + 4); + //~^ ERROR: this operation has no effect 1 * 1; + //~^ ERROR: this operation has no effect // Issue #9904 let x = 0i32; let _: i32 = &x + 0; + //~^ ERROR: this operation has no effect } pub fn decide(a: bool, b: bool) -> u32 { 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } } + +/// The following tests are from / for issue #12050 +/// In short, the lint didn't work for coerced references, +/// e.g. let x = &0; let y = x + 0; +/// because the suggested fix was `let y = x;` but +/// it should have been `let y = *x;` +fn issue_12050() { + { + let x = &0i32; + let _: i32 = *x + 0; + //~^ ERROR: this operation has no effect + let _: i32 = x + 0; + //~^ ERROR: this operation has no effect + } + { + let x = &&0i32; + let _: i32 = **x + 0; + //~^ ERROR: this operation has no effect + let x = &&0i32; + let _: i32 = *x + 0; + //~^ ERROR: this operation has no effect + } + { + // this is just silly + let x = &&&0i32; + let _: i32 = ***x + 0; + //~^ ERROR: this operation has no effect + let _: i32 = **x + 0; + //~^ ERROR: this operation has no effect + let x = 0i32; + let _: i32 = *&x + 0; + //~^ ERROR: this operation has no effect + let _: i32 = **&&x + 0; + //~^ ERROR: this operation has no effect + let _: i32 = *&*&x + 0; + //~^ ERROR: this operation has no effect + let _: i32 = **&&*&x + 0; + //~^ ERROR: this operation has no effect + } + { + // this is getting ridiculous, but we should still see the same + // error message so let's just keep going + let x = &0i32; + let _: i32 = **&&*&x + 0; + //~^ ERROR: this operation has no effect + let _: i32 = **&&*&x + 0; + //~^ ERROR: this operation has no effect + } +} diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr index 2a61327f15f..6bb980035c1 100644 --- a/src/tools/clippy/tests/ui/identity_op.stderr +++ b/src/tools/clippy/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: this operation has no effect - --> $DIR/identity_op.rs:42:5 + --> $DIR/identity_op.rs:44:5 | LL | x + 0; | ^^^^^ help: consider reducing it to: `x` @@ -8,238 +8,310 @@ LL | x + 0; = help: to override `-D warnings` add `#[allow(clippy::identity_op)]` error: this operation has no effect - --> $DIR/identity_op.rs:43:5 + --> $DIR/identity_op.rs:46:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:45:5 + --> $DIR/identity_op.rs:49:5 | LL | 0 + x; | ^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:48:5 + --> $DIR/identity_op.rs:53:5 | LL | x | (0); | ^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:51:5 + --> $DIR/identity_op.rs:57:5 | LL | x * 1; | ^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:52:5 + --> $DIR/identity_op.rs:59:5 | LL | 1 * x; | ^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:58:5 + --> $DIR/identity_op.rs:66:5 | LL | -1 & x; | ^^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:61:5 + --> $DIR/identity_op.rs:70:5 | LL | u & 255; | ^^^^^^^ help: consider reducing it to: `u` error: this operation has no effect - --> $DIR/identity_op.rs:64:5 + --> $DIR/identity_op.rs:74:5 | LL | 42 << 0; | ^^^^^^^ help: consider reducing it to: `42` error: this operation has no effect - --> $DIR/identity_op.rs:65:5 + --> $DIR/identity_op.rs:76:5 | LL | 1 >> 0; | ^^^^^^ help: consider reducing it to: `1` error: this operation has no effect - --> $DIR/identity_op.rs:66:5 + --> $DIR/identity_op.rs:78:5 | LL | 42 >> 0; | ^^^^^^^ help: consider reducing it to: `42` error: this operation has no effect - --> $DIR/identity_op.rs:67:5 + --> $DIR/identity_op.rs:80:5 | LL | &x >> 0; | ^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:68:5 + --> $DIR/identity_op.rs:82:5 | LL | x >> &0; | ^^^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:75:5 + --> $DIR/identity_op.rs:90:5 | LL | 2 % 3; | ^^^^^ help: consider reducing it to: `2` error: this operation has no effect - --> $DIR/identity_op.rs:76:5 + --> $DIR/identity_op.rs:92:5 | LL | -2 % 3; | ^^^^^^ help: consider reducing it to: `-2` error: this operation has no effect - --> $DIR/identity_op.rs:77:5 + --> $DIR/identity_op.rs:94:5 | LL | 2 % -3 + x; | ^^^^^^ help: consider reducing it to: `2` error: this operation has no effect - --> $DIR/identity_op.rs:78:5 + --> $DIR/identity_op.rs:96:5 | LL | -2 % -3 + x; | ^^^^^^^ help: consider reducing it to: `-2` error: this operation has no effect - --> $DIR/identity_op.rs:79:9 + --> $DIR/identity_op.rs:98:9 | LL | x + 1 % 3; | ^^^^^ help: consider reducing it to: `1` error: this operation has no effect - --> $DIR/identity_op.rs:87:5 + --> $DIR/identity_op.rs:107:5 | LL | 0 + if b { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` error: this operation has no effect - --> $DIR/identity_op.rs:88:5 + --> $DIR/identity_op.rs:109:5 | LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` error: this operation has no effect - --> $DIR/identity_op.rs:89:5 + --> $DIR/identity_op.rs:111:5 | LL | 0 + match a { 0 => 10, _ => 20 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` error: this operation has no effect - --> $DIR/identity_op.rs:90:5 + --> $DIR/identity_op.rs:113:5 | LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` error: this operation has no effect - --> $DIR/identity_op.rs:91:5 + --> $DIR/identity_op.rs:115:5 | LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` error: this operation has no effect - --> $DIR/identity_op.rs:92:5 + --> $DIR/identity_op.rs:117:5 | LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` error: this operation has no effect - --> $DIR/identity_op.rs:93:5 + --> $DIR/identity_op.rs:119:5 | LL | (if b { 1 } else { 2 }) + 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` error: this operation has no effect - --> $DIR/identity_op.rs:95:5 + --> $DIR/identity_op.rs:122:5 | LL | 0 + { a } + 3; | ^^^^^^^^^ help: consider reducing it to: `({ a })` error: this operation has no effect - --> $DIR/identity_op.rs:96:5 + --> $DIR/identity_op.rs:124:5 | LL | 0 + { a } * 2; | ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)` error: this operation has no effect - --> $DIR/identity_op.rs:97:5 + --> $DIR/identity_op.rs:126:5 | LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })` error: this operation has no effect - --> $DIR/identity_op.rs:102:7 + --> $DIR/identity_op.rs:133:7 | LL | f(1 * a + { 8 * 5 }); | ^^^^^ help: consider reducing it to: `a` error: this operation has no effect - --> $DIR/identity_op.rs:103:7 + --> $DIR/identity_op.rs:135:7 | LL | f(0 + if b { 1 } else { 2 } + 3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }` error: this operation has no effect - --> $DIR/identity_op.rs:104:20 + --> $DIR/identity_op.rs:138:20 | LL | const _: i32 = { 2 * 4 } + 0 + 3; | ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }` error: this operation has no effect - --> $DIR/identity_op.rs:105:20 + --> $DIR/identity_op.rs:140:20 | LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3; | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }` error: this operation has no effect - --> $DIR/identity_op.rs:107:5 + --> $DIR/identity_op.rs:143:5 | LL | 0 + a as usize; | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` error: this operation has no effect - --> $DIR/identity_op.rs:108:13 + --> $DIR/identity_op.rs:145:13 | LL | let _ = 0 + a as usize; | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` error: this operation has no effect - --> $DIR/identity_op.rs:109:5 + --> $DIR/identity_op.rs:147:5 | LL | 0 + { a } as usize; | ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)` error: this operation has no effect - --> $DIR/identity_op.rs:111:9 + --> $DIR/identity_op.rs:150:9 | LL | 2 * (0 + { a }); | ^^^^^^^^^^^ help: consider reducing it to: `{ a }` error: this operation has no effect - --> $DIR/identity_op.rs:112:5 + --> $DIR/identity_op.rs:152:5 | LL | 1 * ({ a } + 4); | ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))` error: this operation has no effect - --> $DIR/identity_op.rs:113:5 + --> $DIR/identity_op.rs:154:5 | LL | 1 * 1; | ^^^^^ help: consider reducing it to: `1` error: this operation has no effect - --> $DIR/identity_op.rs:117:18 + --> $DIR/identity_op.rs:159:18 | LL | let _: i32 = &x + 0; | ^^^^^^ help: consider reducing it to: `x` error: this operation has no effect - --> $DIR/identity_op.rs:121:5 + --> $DIR/identity_op.rs:164:5 | LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` -error: aborting due to 40 previous errors +error: this operation has no effect + --> $DIR/identity_op.rs:175:22 + | +LL | let _: i32 = *x + 0; + | ^^^^^^ help: consider reducing it to: `*x` + +error: this operation has no effect + --> $DIR/identity_op.rs:177:22 + | +LL | let _: i32 = x + 0; + | ^^^^^ help: consider reducing it to: `*x` + +error: this operation has no effect + --> $DIR/identity_op.rs:182:22 + | +LL | let _: i32 = **x + 0; + | ^^^^^^^ help: consider reducing it to: `**x` + +error: this operation has no effect + --> $DIR/identity_op.rs:185:22 + | +LL | let _: i32 = *x + 0; + | ^^^^^^ help: consider reducing it to: `**x` + +error: this operation has no effect + --> $DIR/identity_op.rs:191:22 + | +LL | let _: i32 = ***x + 0; + | ^^^^^^^^ help: consider reducing it to: `***x` + +error: this operation has no effect + --> $DIR/identity_op.rs:193:22 + | +LL | let _: i32 = **x + 0; + | ^^^^^^^ help: consider reducing it to: `***x` + +error: this operation has no effect + --> $DIR/identity_op.rs:196:22 + | +LL | let _: i32 = *&x + 0; + | ^^^^^^^ help: consider reducing it to: `*&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:198:22 + | +LL | let _: i32 = **&&x + 0; + | ^^^^^^^^^ help: consider reducing it to: `**&&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:200:22 + | +LL | let _: i32 = *&*&x + 0; + | ^^^^^^^^^ help: consider reducing it to: `*&*&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:202:22 + | +LL | let _: i32 = **&&*&x + 0; + | ^^^^^^^^^^^ help: consider reducing it to: `**&&*&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:209:22 + | +LL | let _: i32 = **&&*&x + 0; + | ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:211:22 + | +LL | let _: i32 = **&&*&x + 0; + | ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x` + +error: aborting due to 52 previous errors diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs index abc92459148..ccde154bd56 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs @@ -130,3 +130,8 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { Ok(()) } + +const fn issue12103(x: u32) -> Option<u32> { + // Should not issue an error in `const` context + if x > 42 { Some(150) } else { None } +} diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs index 448d0114dff..c8b9076041a 100644 --- a/src/tools/clippy/tests/ui/into_iter_without_iter.rs +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs @@ -1,5 +1,7 @@ //@no-rustfix +//@aux-build:proc_macros.rs #![warn(clippy::into_iter_without_iter)] +extern crate proc_macros; use std::iter::IntoIterator; @@ -111,6 +113,43 @@ impl IntoIterator for &Alias { } } +// Fine to lint, the impls comes from a local macro. +pub struct Issue12037; +macro_rules! generate_impl { + () => { + impl<'a> IntoIterator for &'a Issue12037 { + type IntoIter = std::slice::Iter<'a, u8>; + type Item = &'a u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } + }; +} +generate_impl!(); + +// Impl comes from an external crate +proc_macros::external! { + pub struct ImplWithForeignSpan; + impl<'a> IntoIterator for &'a ImplWithForeignSpan { + type IntoIter = std::slice::Iter<'a, u8>; + type Item = &'a u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } + } +} + +pub struct Allowed; +#[allow(clippy::into_iter_without_iter)] +impl<'a> IntoIterator for &'a Allowed { + type IntoIter = std::slice::Iter<'a, u8>; + type Item = &'a u8; + fn into_iter(self) -> Self::IntoIter { + todo!() + } +} + fn main() {} pub mod issue11635 { diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.stderr b/src/tools/clippy/tests/ui/into_iter_without_iter.stderr index 70f3f82a936..a232c7cecc5 100644 --- a/src/tools/clippy/tests/ui/into_iter_without_iter.stderr +++ b/src/tools/clippy/tests/ui/into_iter_without_iter.stderr @@ -1,5 +1,5 @@ error: `IntoIterator` implemented for a reference type without an `iter` method - --> $DIR/into_iter_without_iter.rs:7:1 + --> $DIR/into_iter_without_iter.rs:9:1 | LL | / impl<'a> IntoIterator for &'a S1 { LL | | @@ -23,7 +23,7 @@ LL + } | error: `IntoIterator` implemented for a reference type without an `iter_mut` method - --> $DIR/into_iter_without_iter.rs:15:1 + --> $DIR/into_iter_without_iter.rs:17:1 | LL | / impl<'a> IntoIterator for &'a mut S1 { LL | | @@ -45,7 +45,7 @@ LL + } | error: `IntoIterator` implemented for a reference type without an `iter` method - --> $DIR/into_iter_without_iter.rs:25:1 + --> $DIR/into_iter_without_iter.rs:27:1 | LL | / impl<'a, T> IntoIterator for &'a S2<T> { LL | | @@ -67,7 +67,7 @@ LL + } | error: `IntoIterator` implemented for a reference type without an `iter_mut` method - --> $DIR/into_iter_without_iter.rs:33:1 + --> $DIR/into_iter_without_iter.rs:35:1 | LL | / impl<'a, T> IntoIterator for &'a mut S2<T> { LL | | @@ -89,7 +89,7 @@ LL + } | error: `IntoIterator` implemented for a reference type without an `iter_mut` method - --> $DIR/into_iter_without_iter.rs:84:1 + --> $DIR/into_iter_without_iter.rs:86:1 | LL | / impl<'a, T> IntoIterator for &mut S4<'a, T> { LL | | @@ -110,5 +110,31 @@ LL + } LL + } | -error: aborting due to 5 previous errors +error: `IntoIterator` implemented for a reference type without an `iter` method + --> $DIR/into_iter_without_iter.rs:120:9 + | +LL | / impl<'a> IntoIterator for &'a Issue12037 { +LL | | type IntoIter = std::slice::Iter<'a, u8>; +LL | | type Item = &'a u8; +LL | | fn into_iter(self) -> Self::IntoIter { +LL | | todo!() +LL | | } +LL | | } + | |_________^ +... +LL | generate_impl!(); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `generate_impl` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider implementing `iter` + | +LL ~ +LL + impl Issue12037 { +LL + fn iter(&self) -> std::slice::Iter<'a, u8> { +LL + <&Self as IntoIterator>::into_iter(self) +LL + } +LL + } + | + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/iter_filter_is_ok.fixed b/src/tools/clippy/tests/ui/iter_filter_is_ok.fixed index a5ca41528af..80db8b29c18 100644 --- a/src/tools/clippy/tests/ui/iter_filter_is_ok.fixed +++ b/src/tools/clippy/tests/ui/iter_filter_is_ok.fixed @@ -1,21 +1,72 @@ #![warn(clippy::iter_filter_is_ok)] +#![allow( + clippy::map_identity, + clippy::result_filter_map, + clippy::needless_borrow, + clippy::redundant_closure +)] fn main() { - let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); - //~^ HELP: consider using `flatten` instead - let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); - //~^ HELP: consider using `flatten` instead + { + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } - #[rustfmt::skip] - let _ = vec![Ok(1), Err(2)].into_iter().flatten(); - //~^ HELP: consider using `flatten` instead + { + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + .flatten(); + //~^ HELP: consider using `flatten` instead + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + .flatten(); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } +} + +fn avoid_linting_when_filter_has_side_effects() { // Don't lint below let mut counter = 0; let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { counter += 1; o.is_ok() }); +} + +fn avoid_linting_when_commented() { let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { // Roses are red, // Violets are blue, @@ -24,3 +75,131 @@ fn main() { o.is_ok() }); } + +fn ice_12058() { + // check that checking the parent node doesn't cause an ICE + // by indexing the parameters of a closure without parameters + Some(1).or_else(|| { + vec![Ok(1), Err(())].into_iter().filter(|z| *z != Ok(2)); + None + }); +} + +fn avoid_linting_map() { + // should not lint + let _ = vec![Ok(1), Err(())] + .into_iter() + .filter(|o| o.is_ok()) + .map(|o| o.unwrap()); + + // should not lint + let _ = vec![Ok(1), Err(())].into_iter().filter(|o| o.is_ok()).map(|o| o); +} + +fn avoid_false_positive_due_to_is_ok_and_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_ok(&self) -> bool { + true + } + } + + impl Iterator for Foo { + type Item = Foo; + fn next(&mut self) -> Option<Self::Item> { + Some(Foo::default()) + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_ok); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_ok()); +} + +fn avoid_false_positive_due_to_is_ok_and_into_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_ok(&self) -> bool { + true + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_ok); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_ok()); +} + +fn avoid_fp_for_trivial() { + let _ = vec![Ok(1), Err(()), Ok(3)] + .into_iter() + // should not lint + .filter(|o| (Err(()) as Result<i32, ()>).is_ok()); +} + +fn avoid_false_positive_due_to_method_name() { + fn is_ok(x: &Result<i32, i32>) -> bool { + x.is_ok() + } + + vec![Ok(1), Err(2), Ok(3)].into_iter().filter(is_ok); + // should not lint +} + +fn avoid_fp_due_to_trait_type() { + struct Foo { + bar: i32, + } + impl Foo { + fn is_ok(obj: &Result<i32, i32>) -> bool { + obj.is_ok() + } + } + vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Foo::is_ok); + // should not lint +} + +fn avoid_fp_with_call_to_outside_var() { + let outside: Result<i32, ()> = Ok(1); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_ok()); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| Result::is_ok(&outside)); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| std::result::Result::is_ok(&outside)); +} + +fn avoid_fp_with_call_to_outside_var_mix_match_types() { + let outside = Some(1); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_some()); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| Option::is_some(&outside)); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| std::option::Option::is_some(&outside)); +} diff --git a/src/tools/clippy/tests/ui/iter_filter_is_ok.rs b/src/tools/clippy/tests/ui/iter_filter_is_ok.rs index e4e73f5ada1..89b083b84f3 100644 --- a/src/tools/clippy/tests/ui/iter_filter_is_ok.rs +++ b/src/tools/clippy/tests/ui/iter_filter_is_ok.rs @@ -1,21 +1,72 @@ #![warn(clippy::iter_filter_is_ok)] +#![allow( + clippy::map_identity, + clippy::result_filter_map, + clippy::needless_borrow, + clippy::redundant_closure +)] fn main() { - let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok); - //~^ HELP: consider using `flatten` instead - let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok()); - //~^ HELP: consider using `flatten` instead + { + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok); + //~^ HELP: consider using `flatten` instead + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok()); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() }); + //~^ HELP: consider using `flatten` instead + } - #[rustfmt::skip] - let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() }); - //~^ HELP: consider using `flatten` instead + { + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok()); + //~^ HELP: consider using `flatten` instead + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok()); + //~^ HELP: consider using `flatten` instead + + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2)].into_iter().filter(|&o| { o.is_ok() }); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + .filter(std::result::Result::is_ok); + //~^ HELP: consider using `flatten` instead + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + .filter(|a| std::result::Result::is_ok(a)); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| { std::result::Result::is_ok(a) }); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok()); + //~^ HELP: consider using `flatten` instead + + let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok()); + //~^ HELP: consider using `flatten` instead + + #[rustfmt::skip] + let _ = vec![Ok(1), Err(2)].into_iter().filter(|ref o| { o.is_ok() }); + //~^ HELP: consider using `flatten` instead + } +} + +fn avoid_linting_when_filter_has_side_effects() { // Don't lint below let mut counter = 0; let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { counter += 1; o.is_ok() }); +} + +fn avoid_linting_when_commented() { let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { // Roses are red, // Violets are blue, @@ -24,3 +75,131 @@ fn main() { o.is_ok() }); } + +fn ice_12058() { + // check that checking the parent node doesn't cause an ICE + // by indexing the parameters of a closure without parameters + Some(1).or_else(|| { + vec![Ok(1), Err(())].into_iter().filter(|z| *z != Ok(2)); + None + }); +} + +fn avoid_linting_map() { + // should not lint + let _ = vec![Ok(1), Err(())] + .into_iter() + .filter(|o| o.is_ok()) + .map(|o| o.unwrap()); + + // should not lint + let _ = vec![Ok(1), Err(())].into_iter().filter(|o| o.is_ok()).map(|o| o); +} + +fn avoid_false_positive_due_to_is_ok_and_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_ok(&self) -> bool { + true + } + } + + impl Iterator for Foo { + type Item = Foo; + fn next(&mut self) -> Option<Self::Item> { + Some(Foo::default()) + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_ok); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_ok()); +} + +fn avoid_false_positive_due_to_is_ok_and_into_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_ok(&self) -> bool { + true + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_ok); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_ok()); +} + +fn avoid_fp_for_trivial() { + let _ = vec![Ok(1), Err(()), Ok(3)] + .into_iter() + // should not lint + .filter(|o| (Err(()) as Result<i32, ()>).is_ok()); +} + +fn avoid_false_positive_due_to_method_name() { + fn is_ok(x: &Result<i32, i32>) -> bool { + x.is_ok() + } + + vec![Ok(1), Err(2), Ok(3)].into_iter().filter(is_ok); + // should not lint +} + +fn avoid_fp_due_to_trait_type() { + struct Foo { + bar: i32, + } + impl Foo { + fn is_ok(obj: &Result<i32, i32>) -> bool { + obj.is_ok() + } + } + vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Foo::is_ok); + // should not lint +} + +fn avoid_fp_with_call_to_outside_var() { + let outside: Result<i32, ()> = Ok(1); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_ok()); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| Result::is_ok(&outside)); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| std::result::Result::is_ok(&outside)); +} + +fn avoid_fp_with_call_to_outside_var_mix_match_types() { + let outside = Some(1); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_some()); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| Option::is_some(&outside)); + + let _ = vec![Ok(1), Err(2), Ok(3)] + .into_iter() + // should not lint + .filter(|o| std::option::Option::is_some(&outside)); +} diff --git a/src/tools/clippy/tests/ui/iter_filter_is_ok.stderr b/src/tools/clippy/tests/ui/iter_filter_is_ok.stderr index f3acbe38d8a..d99e2e0446d 100644 --- a/src/tools/clippy/tests/ui/iter_filter_is_ok.stderr +++ b/src/tools/clippy/tests/ui/iter_filter_is_ok.stderr @@ -1,23 +1,77 @@ error: `filter` for `is_ok` on iterator over `Result`s - --> $DIR/iter_filter_is_ok.rs:4:52 + --> $DIR/iter_filter_is_ok.rs:11:56 | -LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(Result::is_ok); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` | = note: `-D clippy::iter-filter-is-ok` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_filter_is_ok)]` error: `filter` for `is_ok` on iterator over `Result`s - --> $DIR/iter_filter_is_ok.rs:6:52 + --> $DIR/iter_filter_is_ok.rs:13:56 | -LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| a.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_ok` on iterator over `Result`s - --> $DIR/iter_filter_is_ok.rs:10:45 + --> $DIR/iter_filter_is_ok.rs:16:49 | -LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` +LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|o| { o.is_ok() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` -error: aborting due to 3 previous errors +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:21:56 + | +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:24:56 + | +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|&a| a.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:28:49 + | +LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|&o| { o.is_ok() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:35:14 + | +LL | .filter(std::result::Result::is_ok); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:40:14 + | +LL | .filter(|a| std::result::Result::is_ok(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:43:56 + | +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|a| { std::result::Result::is_ok(a) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:48:56 + | +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:51:56 + | +LL | let _ = vec![Ok(1), Err(2), Ok(3)].into_iter().filter(|ref a| a.is_ok()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_ok` on iterator over `Result`s + --> $DIR/iter_filter_is_ok.rs:55:49 + | +LL | let _ = vec![Ok(1), Err(2)].into_iter().filter(|ref o| { o.is_ok() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/iter_filter_is_some.fixed b/src/tools/clippy/tests/ui/iter_filter_is_some.fixed index c3fa93f0ab2..abc3a47fa46 100644 --- a/src/tools/clippy/tests/ui/iter_filter_is_some.fixed +++ b/src/tools/clippy/tests/ui/iter_filter_is_some.fixed @@ -1,23 +1,70 @@ #![warn(clippy::iter_filter_is_some)] +#![allow( + clippy::map_identity, + clippy::result_filter_map, + clippy::needless_borrow, + clippy::option_filter_map, + clippy::redundant_closure +)] + +use std::collections::HashMap; fn main() { - let _ = vec![Some(1)].into_iter().flatten(); - //~^ HELP: consider using `flatten` instead - let _ = vec![Some(1)].into_iter().flatten(); - //~^ HELP: consider using `flatten` instead + { + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .flatten(); + //~^ HELP: consider using `flatten` instead + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .flatten(); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead - #[rustfmt::skip] - let _ = vec![Some(1)].into_iter().flatten(); - //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().flatten(); + //~^ HELP: consider using `flatten` instead + } +} +fn avoid_linting_when_filter_has_side_effects() { // Don't lint below let mut counter = 0; - let _ = vec![Some(1)].into_iter().filter(|o| { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { counter += 1; o.is_some() }); +} - let _ = vec![Some(1)].into_iter().filter(|o| { +fn avoid_linting_when_commented() { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { // Roses are red, // Violets are blue, // `Err` is not an `Option`, @@ -25,3 +72,169 @@ fn main() { o.is_some() }); } + +fn ice_12058() { + // check that checking the parent node doesn't cause an ICE + // by indexing the parameters of a closure without parameters + Some(1).or_else(|| { + vec![Some(1), None, Some(3)].into_iter().filter(|z| *z != Some(2)); + None + }); +} + +fn avoid_linting_map() { + // should not lint + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .filter(|o| o.is_some()) + .map(|o| o.unwrap()); + + // should not lint + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .filter(|o| o.is_some()) + .map(|o| o); +} + +fn avoid_false_positive_due_to_is_some_and_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_some(&self) -> bool { + true + } + } + + impl Iterator for Foo { + type Item = Foo; + fn next(&mut self) -> Option<Self::Item> { + Some(Foo::default()) + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_some); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_some()); +} + +fn avoid_false_positive_due_to_is_some_and_into_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_some(&self) -> bool { + true + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_some); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_some()); +} + +fn avoid_unpack_fp() { + let _ = vec![(Some(1), None), (None, Some(3))] + .into_iter() + // should not lint + .filter(|(a, _)| a.is_some()); + let _ = vec![(Some(1), None), (None, Some(3))] + .into_iter() + // should not lint + .filter(|(a, _)| a.is_some()) + .collect::<Vec<_>>(); + + let m = HashMap::from([(1, 1)]); + let _ = vec![1, 2, 4].into_iter().filter(|a| m.get(a).is_some()); + // should not lint +} + +fn avoid_fp_for_external() { + let value = HashMap::from([(1, 1)]); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| value.get(&1).is_some()); + + let value = Option::Some(1); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| value.is_some()); +} + +fn avoid_fp_for_trivial() { + let value = HashMap::from([(1, 1)]); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| Some(1).is_some()); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| None::<i32>.is_some()); +} + +fn avoid_false_positive_due_to_method_name() { + fn is_some(x: &Option<i32>) -> bool { + x.is_some() + } + + vec![Some(1), None, Some(3)].into_iter().filter(is_some); + // should not lint +} + +fn avoid_fp_due_to_trait_type() { + struct Foo { + bar: i32, + } + impl Foo { + fn is_some(obj: &Option<i32>) -> bool { + obj.is_some() + } + } + vec![Some(1), None, Some(3)].into_iter().filter(Foo::is_some); + // should not lint +} + +fn avoid_fp_with_call_to_outside_var() { + let outside = Some(1); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_some()); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| Option::is_some(&outside)); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| std::option::Option::is_some(&outside)); +} + +fn avoid_fp_with_call_to_outside_var_mix_match_types() { + let outside: Result<i32, ()> = Ok(1); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_ok()); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| Result::is_ok(&outside)); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| std::result::Result::is_ok(&outside)); +} diff --git a/src/tools/clippy/tests/ui/iter_filter_is_some.rs b/src/tools/clippy/tests/ui/iter_filter_is_some.rs index b023776abe4..c74775a82ba 100644 --- a/src/tools/clippy/tests/ui/iter_filter_is_some.rs +++ b/src/tools/clippy/tests/ui/iter_filter_is_some.rs @@ -1,23 +1,70 @@ #![warn(clippy::iter_filter_is_some)] +#![allow( + clippy::map_identity, + clippy::result_filter_map, + clippy::needless_borrow, + clippy::option_filter_map, + clippy::redundant_closure +)] + +use std::collections::HashMap; fn main() { - let _ = vec![Some(1)].into_iter().filter(Option::is_some); - //~^ HELP: consider using `flatten` instead - let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()); - //~^ HELP: consider using `flatten` instead + { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(Option::is_some); + //~^ HELP: consider using `flatten` instead + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|a| a.is_some()); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { o.is_some() }); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .filter(std::option::Option::is_some); + //~^ HELP: consider using `flatten` instead + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .filter(|a| std::option::Option::is_some(a)); + //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|a| { std::option::Option::is_some(a) }); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|&a| a.is_some()); + //~^ HELP: consider using `flatten` instead + + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|&o| { o.is_some() }); + //~^ HELP: consider using `flatten` instead + } + + { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|ref a| a.is_some()); + //~^ HELP: consider using `flatten` instead - #[rustfmt::skip] - let _ = vec![Some(1)].into_iter().filter(|o| { o.is_some() }); - //~^ HELP: consider using `flatten` instead + #[rustfmt::skip] + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|ref o| { o.is_some() }); + //~^ HELP: consider using `flatten` instead + } +} +fn avoid_linting_when_filter_has_side_effects() { // Don't lint below let mut counter = 0; - let _ = vec![Some(1)].into_iter().filter(|o| { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { counter += 1; o.is_some() }); +} - let _ = vec![Some(1)].into_iter().filter(|o| { +fn avoid_linting_when_commented() { + let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { // Roses are red, // Violets are blue, // `Err` is not an `Option`, @@ -25,3 +72,169 @@ fn main() { o.is_some() }); } + +fn ice_12058() { + // check that checking the parent node doesn't cause an ICE + // by indexing the parameters of a closure without parameters + Some(1).or_else(|| { + vec![Some(1), None, Some(3)].into_iter().filter(|z| *z != Some(2)); + None + }); +} + +fn avoid_linting_map() { + // should not lint + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .filter(|o| o.is_some()) + .map(|o| o.unwrap()); + + // should not lint + let _ = vec![Some(1), None, Some(3)] + .into_iter() + .filter(|o| o.is_some()) + .map(|o| o); +} + +fn avoid_false_positive_due_to_is_some_and_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_some(&self) -> bool { + true + } + } + + impl Iterator for Foo { + type Item = Foo; + fn next(&mut self) -> Option<Self::Item> { + Some(Foo::default()) + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_some); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_some()); +} + +fn avoid_false_positive_due_to_is_some_and_into_iterator_impl() { + #[derive(Default, Clone)] + struct Foo {} + + impl Foo { + fn is_some(&self) -> bool { + true + } + } + + let data = vec![Foo::default()]; + // should not lint + let _ = data.clone().into_iter().filter(Foo::is_some); + // should not lint + let _ = data.clone().into_iter().filter(|f| f.is_some()); +} + +fn avoid_unpack_fp() { + let _ = vec![(Some(1), None), (None, Some(3))] + .into_iter() + // should not lint + .filter(|(a, _)| a.is_some()); + let _ = vec![(Some(1), None), (None, Some(3))] + .into_iter() + // should not lint + .filter(|(a, _)| a.is_some()) + .collect::<Vec<_>>(); + + let m = HashMap::from([(1, 1)]); + let _ = vec![1, 2, 4].into_iter().filter(|a| m.get(a).is_some()); + // should not lint +} + +fn avoid_fp_for_external() { + let value = HashMap::from([(1, 1)]); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| value.get(&1).is_some()); + + let value = Option::Some(1); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| value.is_some()); +} + +fn avoid_fp_for_trivial() { + let value = HashMap::from([(1, 1)]); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| Some(1).is_some()); + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| None::<i32>.is_some()); +} + +fn avoid_false_positive_due_to_method_name() { + fn is_some(x: &Option<i32>) -> bool { + x.is_some() + } + + vec![Some(1), None, Some(3)].into_iter().filter(is_some); + // should not lint +} + +fn avoid_fp_due_to_trait_type() { + struct Foo { + bar: i32, + } + impl Foo { + fn is_some(obj: &Option<i32>) -> bool { + obj.is_some() + } + } + vec![Some(1), None, Some(3)].into_iter().filter(Foo::is_some); + // should not lint +} + +fn avoid_fp_with_call_to_outside_var() { + let outside = Some(1); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_some()); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| Option::is_some(&outside)); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| std::option::Option::is_some(&outside)); +} + +fn avoid_fp_with_call_to_outside_var_mix_match_types() { + let outside: Result<i32, ()> = Ok(1); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| outside.is_ok()); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| Result::is_ok(&outside)); + + let _ = vec![Some(1), None, Some(3)] + .into_iter() + // should not lint + .filter(|o| std::result::Result::is_ok(&outside)); +} diff --git a/src/tools/clippy/tests/ui/iter_filter_is_some.stderr b/src/tools/clippy/tests/ui/iter_filter_is_some.stderr index 1f2b10036fe..2eb00633e88 100644 --- a/src/tools/clippy/tests/ui/iter_filter_is_some.stderr +++ b/src/tools/clippy/tests/ui/iter_filter_is_some.stderr @@ -1,23 +1,65 @@ error: `filter` for `is_some` on iterator over `Option` - --> $DIR/iter_filter_is_some.rs:4:39 + --> $DIR/iter_filter_is_some.rs:14:58 | -LL | let _ = vec![Some(1)].into_iter().filter(Option::is_some); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(Option::is_some); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` | = note: `-D clippy::iter-filter-is-some` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_filter_is_some)]` error: `filter` for `is_some` on iterator over `Option` - --> $DIR/iter_filter_is_some.rs:6:39 + --> $DIR/iter_filter_is_some.rs:16:58 | -LL | let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|a| a.is_some()); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` error: `filter` for `is_some` on iterator over `Option` - --> $DIR/iter_filter_is_some.rs:10:39 + --> $DIR/iter_filter_is_some.rs:19:58 | -LL | let _ = vec![Some(1)].into_iter().filter(|o| { o.is_some() }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|o| { o.is_some() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` -error: aborting due to 3 previous errors +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:26:14 + | +LL | .filter(std::option::Option::is_some); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:31:14 + | +LL | .filter(|a| std::option::Option::is_some(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:34:58 + | +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|a| { std::option::Option::is_some(a) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:39:58 + | +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|&a| a.is_some()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:43:58 + | +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|&o| { o.is_some() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:48:58 + | +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|ref a| a.is_some()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `is_some` on iterator over `Option` + --> $DIR/iter_filter_is_some.rs:52:58 + | +LL | let _ = vec![Some(1), None, Some(3)].into_iter().filter(|ref o| { o.is_some() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.rs b/src/tools/clippy/tests/ui/iter_without_into_iter.rs index 29f526b455c..3054d848efb 100644 --- a/src/tools/clippy/tests/ui/iter_without_into_iter.rs +++ b/src/tools/clippy/tests/ui/iter_without_into_iter.rs @@ -1,5 +1,7 @@ //@no-rustfix +//@aux-build:proc_macros.rs #![warn(clippy::iter_without_into_iter)] +extern crate proc_macros; pub struct S1; impl S1 { @@ -121,4 +123,33 @@ impl S12 { } } +pub struct Issue12037; +macro_rules! generate_impl { + () => { + impl Issue12037 { + fn iter(&self) -> std::slice::Iter<'_, u8> { + todo!() + } + } + }; +} +generate_impl!(); + +proc_macros::external! { + pub struct ImplWithForeignSpan; + impl ImplWithForeignSpan { + fn iter(&self) -> std::slice::Iter<'_, u8> { + todo!() + } + } +} + +pub struct Allowed; +impl Allowed { + #[allow(clippy::iter_without_into_iter)] + pub fn iter(&self) -> std::slice::Iter<'_, u8> { + todo!() + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr index af5afd47bfc..4cf20e2aa56 100644 --- a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr +++ b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr @@ -1,5 +1,5 @@ error: `iter` method without an `IntoIterator` impl for `&S1` - --> $DIR/iter_without_into_iter.rs:6:5 + --> $DIR/iter_without_into_iter.rs:8:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { LL | | @@ -22,7 +22,7 @@ LL + } | error: `iter_mut` method without an `IntoIterator` impl for `&mut S1` - --> $DIR/iter_without_into_iter.rs:10:5 + --> $DIR/iter_without_into_iter.rs:12:5 | LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { LL | | @@ -43,7 +43,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&S3<'a>` - --> $DIR/iter_without_into_iter.rs:26:5 + --> $DIR/iter_without_into_iter.rs:28:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'_, u8> { LL | | @@ -64,7 +64,7 @@ LL + } | error: `iter_mut` method without an `IntoIterator` impl for `&mut S3<'a>` - --> $DIR/iter_without_into_iter.rs:30:5 + --> $DIR/iter_without_into_iter.rs:32:5 | LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, u8> { LL | | @@ -85,7 +85,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&S8<T>` - --> $DIR/iter_without_into_iter.rs:67:5 + --> $DIR/iter_without_into_iter.rs:69:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'static, T> { LL | | todo!() @@ -105,7 +105,7 @@ LL + } | error: `iter` method without an `IntoIterator` impl for `&S9<T>` - --> $DIR/iter_without_into_iter.rs:75:5 + --> $DIR/iter_without_into_iter.rs:77:5 | LL | / pub fn iter(&self) -> std::slice::Iter<'_, T> { LL | | @@ -126,7 +126,7 @@ LL + } | error: `iter_mut` method without an `IntoIterator` impl for `&mut S9<T>` - --> $DIR/iter_without_into_iter.rs:79:5 + --> $DIR/iter_without_into_iter.rs:81:5 | LL | / pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> { LL | | @@ -146,5 +146,29 @@ LL + } LL + } | -error: aborting due to 7 previous errors +error: `iter` method without an `IntoIterator` impl for `&Issue12037` + --> $DIR/iter_without_into_iter.rs:130:13 + | +LL | / fn iter(&self) -> std::slice::Iter<'_, u8> { +LL | | todo!() +LL | | } + | |_____________^ +... +LL | generate_impl!(); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `generate_impl` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider implementing `IntoIterator` for `&Issue12037` + | +LL ~ +LL + impl IntoIterator for &Issue12037 { +LL + type IntoIter = std::slice::Iter<'_, u8>; +LL + type Item = &u8; +LL + fn into_iter(self) -> Self::IntoIter { +LL + self.iter() +LL + } +LL + } + | + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/let_unit.fixed b/src/tools/clippy/tests/ui/let_unit.fixed index f98ce9d50a9..4d41b5e5e50 100644 --- a/src/tools/clippy/tests/ui/let_unit.fixed +++ b/src/tools/clippy/tests/ui/let_unit.fixed @@ -13,7 +13,14 @@ fn main() { let _y = 1; // this is fine let _z = ((), 1); // this as well if true { - (); + // do not lint this, since () is explicit + let _a = (); + let () = dummy(); + let () = (); + () = dummy(); + () = (); + let _a: () = (); + let _a: () = dummy(); } consume_units_with_for_loop(); // should be fine as well @@ -23,6 +30,8 @@ fn main() { let_and_return!(()) // should be fine } +fn dummy() {} + // Related to issue #1964 fn consume_units_with_for_loop() { // `for_let_unit` lint should not be triggered by consuming them using for loop. @@ -74,40 +83,29 @@ fn _returns_generic() { x.then(|| T::default()) } - let _: () = f(); // Ok - let _: () = f(); // Lint. + let _: () = f(); + let x: () = f(); - let _: () = f2(0i32); // Ok - let _: () = f2(0i32); // Lint. + let _: () = f2(0i32); + let x: () = f2(0i32); - f3(()); // Lint - f3(()); // Lint + let _: () = f3(()); + let x: () = f3(()); - // Should lint: - // fn f4<T>(mut x: Vec<T>) -> T { - // x.pop().unwrap() - // } - // let _: () = f4(vec![()]); - // let x: () = f4(vec![()]); + fn f4<T>(mut x: Vec<T>) -> T { + x.pop().unwrap() + } + let _: () = f4(vec![()]); + let x: () = f4(vec![()]); - // Ok let _: () = { let x = 5; f2(x) }; - let _: () = if true { f() } else { f2(0) }; // Ok - let _: () = if true { f() } else { f2(0) }; // Lint - - // Ok - let _: () = match Some(0) { - None => f2(1), - Some(0) => f(), - Some(1) => f2(3), - Some(_) => f2('x'), - }; + let _: () = if true { f() } else { f2(0) }; + let x: () = if true { f() } else { f2(0) }; - // Lint match Some(0) { None => f2(1), Some(0) => f(), @@ -155,7 +153,7 @@ fn _returns_generic() { { let _: () = x; let _: () = y; - z; + let _: () = z; let _: () = x1; let _: () = x2; let _: () = opt; diff --git a/src/tools/clippy/tests/ui/let_unit.rs b/src/tools/clippy/tests/ui/let_unit.rs index 6d942ca8908..daa660be25e 100644 --- a/src/tools/clippy/tests/ui/let_unit.rs +++ b/src/tools/clippy/tests/ui/let_unit.rs @@ -13,7 +13,14 @@ fn main() { let _y = 1; // this is fine let _z = ((), 1); // this as well if true { + // do not lint this, since () is explicit let _a = (); + let () = dummy(); + let () = (); + () = dummy(); + () = (); + let _a: () = (); + let _a: () = dummy(); } consume_units_with_for_loop(); // should be fine as well @@ -23,6 +30,8 @@ fn main() { let_and_return!(()) // should be fine } +fn dummy() {} + // Related to issue #1964 fn consume_units_with_for_loop() { // `for_let_unit` lint should not be triggered by consuming them using for loop. @@ -74,41 +83,30 @@ fn _returns_generic() { x.then(|| T::default()) } - let _: () = f(); // Ok - let x: () = f(); // Lint. + let _: () = f(); + let x: () = f(); - let _: () = f2(0i32); // Ok - let x: () = f2(0i32); // Lint. + let _: () = f2(0i32); + let x: () = f2(0i32); - let _: () = f3(()); // Lint - let x: () = f3(()); // Lint + let _: () = f3(()); + let x: () = f3(()); - // Should lint: - // fn f4<T>(mut x: Vec<T>) -> T { - // x.pop().unwrap() - // } - // let _: () = f4(vec![()]); - // let x: () = f4(vec![()]); + fn f4<T>(mut x: Vec<T>) -> T { + x.pop().unwrap() + } + let _: () = f4(vec![()]); + let x: () = f4(vec![()]); - // Ok let _: () = { let x = 5; f2(x) }; - let _: () = if true { f() } else { f2(0) }; // Ok - let x: () = if true { f() } else { f2(0) }; // Lint - - // Ok - let _: () = match Some(0) { - None => f2(1), - Some(0) => f(), - Some(1) => f2(3), - Some(_) => f2('x'), - }; + let _: () = if true { f() } else { f2(0) }; + let x: () = if true { f() } else { f2(0) }; - // Lint - let _: () = match Some(0) { + let x = match Some(0) { None => f2(1), Some(0) => f(), Some(1) => f2(3), diff --git a/src/tools/clippy/tests/ui/let_unit.stderr b/src/tools/clippy/tests/ui/let_unit.stderr index de106f50e0e..00a3c439ba0 100644 --- a/src/tools/clippy/tests/ui/let_unit.stderr +++ b/src/tools/clippy/tests/ui/let_unit.stderr @@ -8,13 +8,7 @@ LL | let _x = println!("x"); = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]` error: this let-binding has unit value - --> $DIR/let_unit.rs:16:9 - | -LL | let _a = (); - | ^^^^^^^^^^^^ help: omit the `let` binding: `();` - -error: this let-binding has unit value - --> $DIR/let_unit.rs:51:5 + --> $DIR/let_unit.rs:60:5 | LL | / let _ = v LL | | .into_iter() @@ -37,45 +31,9 @@ LL + .unwrap(); | error: this let-binding has unit value - --> $DIR/let_unit.rs:78:5 - | -LL | let x: () = f(); // Lint. - | ^^^^-^^^^^^^^^^^ - | | - | help: use a wild (`_`) binding: `_` - -error: this let-binding has unit value - --> $DIR/let_unit.rs:81:5 + --> $DIR/let_unit.rs:109:5 | -LL | let x: () = f2(0i32); // Lint. - | ^^^^-^^^^^^^^^^^^^^^^ - | | - | help: use a wild (`_`) binding: `_` - -error: this let-binding has unit value - --> $DIR/let_unit.rs:83:5 - | -LL | let _: () = f3(()); // Lint - | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` - -error: this let-binding has unit value - --> $DIR/let_unit.rs:84:5 - | -LL | let x: () = f3(()); // Lint - | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` - -error: this let-binding has unit value - --> $DIR/let_unit.rs:100:5 - | -LL | let x: () = if true { f() } else { f2(0) }; // Lint - | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: use a wild (`_`) binding: `_` - -error: this let-binding has unit value - --> $DIR/let_unit.rs:111:5 - | -LL | / let _: () = match Some(0) { +LL | / let x = match Some(0) { LL | | None => f2(1), LL | | Some(0) => f(), LL | | Some(1) => f2(3), @@ -93,11 +51,5 @@ LL + Some(_) => (), LL + }; | -error: this let-binding has unit value - --> $DIR/let_unit.rs:158:13 - | -LL | let _: () = z; - | ^^^^^^^^^^^^^^ help: omit the `let` binding: `z;` - -error: aborting due to 10 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed new file mode 100644 index 00000000000..8c34b51103c --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed @@ -0,0 +1,51 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::manual_is_variant_and)] + +#[macro_use] +extern crate option_helpers; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_default()` use. + // Single line case. + let _ = opt.is_some_and(|x| x > 1); + // Multi-line cases. + let _ = opt.is_some_and(|x| { + x > 1 + }); + let _ = opt.is_some_and(|x| x > 1); + let _ = opt + .is_some_and(|x| x > 1); + + // won't fix because the return type of the closure is not `bool` + let _ = opt.map(|x| x + 1).unwrap_or_default(); + + let opt2 = Some('a'); + let _ = opt2.is_some_and(char::is_alphanumeric); // should lint + let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint +} + +#[rustfmt::skip] +fn result_methods() { + let res: Result<i32, ()> = Ok(1); + + // multi line cases + let _ = res.is_ok_and(|x| { + x > 1 + }); + let _ = res.is_ok_and(|x| x > 1); + + // won't fix because the return type of the closure is not `bool` + let _ = res.map(|x| x + 1).unwrap_or_default(); + + let res2: Result<char, ()> = Ok('a'); + let _ = res2.is_ok_and(char::is_alphanumeric); // should lint + let _ = opt_map!(res2, |x| x == 'a').unwrap_or_default(); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.rs b/src/tools/clippy/tests/ui/manual_is_variant_and.rs new file mode 100644 index 00000000000..25b2489d942 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.rs @@ -0,0 +1,57 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::manual_is_variant_and)] + +#[macro_use] +extern crate option_helpers; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_default()` use. + // Single line case. + let _ = opt.map(|x| x > 1) + // Should lint even though this call is on a separate line. + .unwrap_or_default(); + // Multi-line cases. + let _ = opt.map(|x| { + x > 1 + } + ).unwrap_or_default(); + let _ = opt.map(|x| x > 1).unwrap_or_default(); + let _ = opt + .map(|x| x > 1) + .unwrap_or_default(); + + // won't fix because the return type of the closure is not `bool` + let _ = opt.map(|x| x + 1).unwrap_or_default(); + + let opt2 = Some('a'); + let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint + let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint +} + +#[rustfmt::skip] +fn result_methods() { + let res: Result<i32, ()> = Ok(1); + + // multi line cases + let _ = res.map(|x| { + x > 1 + } + ).unwrap_or_default(); + let _ = res.map(|x| x > 1) + .unwrap_or_default(); + + // won't fix because the return type of the closure is not `bool` + let _ = res.map(|x| x + 1).unwrap_or_default(); + + let res2: Result<char, ()> = Ok('a'); + let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint + let _ = opt_map!(res2, |x| x == 'a').unwrap_or_default(); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr new file mode 100644 index 00000000000..c243de098dc --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr @@ -0,0 +1,82 @@ +error: called `map(<f>).unwrap_or_default()` on an `Option` value + --> $DIR/manual_is_variant_and.rs:13:17 + | +LL | let _ = opt.map(|x| x > 1) + | _________________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_default(); + | |____________________________^ help: use: `is_some_and(|x| x > 1)` + | + = note: `-D clippy::manual-is-variant-and` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_is_variant_and)]` + +error: called `map(<f>).unwrap_or_default()` on an `Option` value + --> $DIR/manual_is_variant_and.rs:17:17 + | +LL | let _ = opt.map(|x| { + | _________________^ +LL | | x > 1 +LL | | } +LL | | ).unwrap_or_default(); + | |_________________________^ + | +help: use + | +LL ~ let _ = opt.is_some_and(|x| { +LL + x > 1 +LL ~ }); + | + +error: called `map(<f>).unwrap_or_default()` on an `Option` value + --> $DIR/manual_is_variant_and.rs:21:17 + | +LL | let _ = opt.map(|x| x > 1).unwrap_or_default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(|x| x > 1)` + +error: called `map(<f>).unwrap_or_default()` on an `Option` value + --> $DIR/manual_is_variant_and.rs:23:10 + | +LL | .map(|x| x > 1) + | __________^ +LL | | .unwrap_or_default(); + | |____________________________^ help: use: `is_some_and(|x| x > 1)` + +error: called `map(<f>).unwrap_or_default()` on an `Option` value + --> $DIR/manual_is_variant_and.rs:30:18 + | +LL | let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(char::is_alphanumeric)` + +error: called `map(<f>).unwrap_or_default()` on a `Result` value + --> $DIR/manual_is_variant_and.rs:39:17 + | +LL | let _ = res.map(|x| { + | _________________^ +LL | | x > 1 +LL | | } +LL | | ).unwrap_or_default(); + | |_________________________^ + | +help: use + | +LL ~ let _ = res.is_ok_and(|x| { +LL + x > 1 +LL ~ }); + | + +error: called `map(<f>).unwrap_or_default()` on a `Result` value + --> $DIR/manual_is_variant_and.rs:43:17 + | +LL | let _ = res.map(|x| x > 1) + | _________________^ +LL | | .unwrap_or_default(); + | |____________________________^ help: use: `is_ok_and(|x| x > 1)` + +error: called `map(<f>).unwrap_or_default()` on a `Result` value + --> $DIR/manual_is_variant_and.rs:50:18 + | +LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed index dd979013d3c..08b155a1aea 100644 --- a/src/tools/clippy/tests/ui/map_clone.fixed +++ b/src/tools/clippy/tests/ui/map_clone.fixed @@ -4,6 +4,8 @@ clippy::iter_cloned_collect, clippy::many_single_char_names, clippy::redundant_clone, + clippy::redundant_closure, + clippy::useless_asref, clippy::useless_vec )] @@ -60,4 +62,26 @@ fn main() { let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); } + + let x = Some(String::new()); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + + // Testing with `Result` now. + let x: Result<String, ()> = Ok(String::new()); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.cloned(); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.cloned(); + + // We ensure that no warning is emitted here because `useless_asref` is taking over. + let x = Some(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); + let x: Result<String, ()> = Ok(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); } diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs index 96cba71965f..901d9b278b4 100644 --- a/src/tools/clippy/tests/ui/map_clone.rs +++ b/src/tools/clippy/tests/ui/map_clone.rs @@ -4,6 +4,8 @@ clippy::iter_cloned_collect, clippy::many_single_char_names, clippy::redundant_clone, + clippy::redundant_closure, + clippy::useless_asref, clippy::useless_vec )] @@ -60,4 +62,26 @@ fn main() { let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); } + + let x = Some(String::new()); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| String::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.map(Clone::clone); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.map(String::clone); + //~^ ERROR: you are explicitly cloning with `.map()` + + // Testing with `Result` now. + let x: Result<String, ()> = Ok(String::new()); + let x = x.as_ref(); // We do this to prevent triggering the `useless_asref` lint. + let y = x.map(|x| String::clone(x)); + //~^ ERROR: you are explicitly cloning with `.map()` + let y = x.map(|x| String::clone(x)); + + // We ensure that no warning is emitted here because `useless_asref` is taking over. + let x = Some(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); + let x: Result<String, ()> = Ok(String::new()); + let y = x.as_ref().map(|x| String::clone(x)); } diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr index eb11f084887..9d7e9317b58 100644 --- a/src/tools/clippy/tests/ui/map_clone.stderr +++ b/src/tools/clippy/tests/ui/map_clone.stderr @@ -1,5 +1,5 @@ error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:11:22 + --> $DIR/map_clone.rs:13:22 | LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` @@ -8,34 +8,64 @@ LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_clone)]` error: you are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:12:26 + --> $DIR/map_clone.rs:14:26 | LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:13:23 + --> $DIR/map_clone.rs:15:23 | LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:15:26 + --> $DIR/map_clone.rs:17:26 | LL | let _: Option<u64> = Some(&16).map(|b| *b); | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:16:25 + --> $DIR/map_clone.rs:18:25 | LL | let _: Option<u8> = Some(&1).map(|x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` error: you are needlessly cloning iterator elements - --> $DIR/map_clone.rs:27:29 + --> $DIR/map_clone.rs:29:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call -error: aborting due to 6 previous errors +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:68:13 + | +LL | let y = x.map(|x| String::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:70:13 + | +LL | let y = x.map(Clone::clone); + | ^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:72:13 + | +LL | let y = x.map(String::clone); + | ^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:78:13 + | +LL | let y = x.map(|x| String::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: you are explicitly cloning with `.map()` + --> $DIR/map_clone.rs:80:13 + | +LL | let y = x.map(|x| String::clone(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index db20ba29f3d..c057eba4aca 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -1,9 +1,5 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut -)] +#![allow(unused_mut, clippy::redundant_allocation, clippy::needless_pass_by_ref_mut)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index d7e56130245..36019652006 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -1,9 +1,5 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut -)] +#![allow(unused_mut, clippy::redundant_allocation, clippy::needless_pass_by_ref_mut)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr index 39446bf6cd9..98175dbd458 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.stderr +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:15:1 + --> $DIR/must_use_candidates.rs:11:1 | LL | pub fn pure(i: u8) -> u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8` @@ -8,25 +8,25 @@ LL | pub fn pure(i: u8) -> u8 { = help: to override `-D warnings` add `#[allow(clippy::must_use_candidate)]` error: this method could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:20:5 + --> $DIR/must_use_candidates.rs:16:5 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8` error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:51:1 + --> $DIR/must_use_candidates.rs:47:1 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool` error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:63:1 + --> $DIR/must_use_candidates.rs:59:1 | LL | pub fn rcd(_x: Rc<u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool` error: this function could have a `#[must_use]` attribute - --> $DIR/must_use_candidates.rs:71:1 + --> $DIR/must_use_candidates.rs:67:1 | LL | pub fn arcd(_x: Arc<u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool` diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 198b95d8c94..3a51538b742 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -18,9 +18,24 @@ fn main() { Mutex::new(&mut x as *mut u32); //~^ ERROR: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want Mutex::new(0u32); - //~^ ERROR: consider using an `AtomicUsize` instead of a `Mutex` here; if you just wan + //~^ ERROR: consider using an `AtomicU32` instead of a `Mutex` here; if you just wan //~| NOTE: `-D clippy::mutex-integer` implied by `-D warnings` Mutex::new(0i32); - //~^ ERROR: consider using an `AtomicIsize` instead of a `Mutex` here; if you just wan + //~^ ERROR: consider using an `AtomicI32` instead of a `Mutex` here; if you just wan Mutex::new(0f32); // there are no float atomics, so this should not lint + Mutex::new(0u8); + //~^ ERROR: consider using an `AtomicU8` instead of a `Mutex` here; if you just wan + Mutex::new(0i16); + //~^ ERROR: consider using an `AtomicI16` instead of a `Mutex` here; if you just wan + let _x: Mutex<i8> = Mutex::new(0); + //~^ ERROR: consider using an `AtomicI8` instead of a `Mutex` here; if you just wan + const X: i64 = 0; + Mutex::new(X); + //~^ ERROR: consider using an `AtomicI64` instead of a `Mutex` here; if you just wan + + // there are no 128 atomics, so these two should not lint + { + Mutex::new(0u128); + let _x: Mutex<i128> = Mutex::new(0); + } } diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index 483e1ce15f6..91f73d30b53 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -31,7 +31,7 @@ error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: consider using an `AtomicU32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:20:5 | LL | Mutex::new(0u32); @@ -40,11 +40,35 @@ LL | Mutex::new(0u32); = note: `-D clippy::mutex-integer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutex_integer)]` -error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` +error: consider using an `AtomicI32` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:23:5 | LL | Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: consider using an `AtomicU8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + --> $DIR/mutex_atomic.rs:26:5 + | +LL | Mutex::new(0u8); + | ^^^^^^^^^^^^^^^ + +error: consider using an `AtomicI16` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + --> $DIR/mutex_atomic.rs:28:5 + | +LL | Mutex::new(0i16); + | ^^^^^^^^^^^^^^^^ + +error: consider using an `AtomicI8` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + --> $DIR/mutex_atomic.rs:30:25 + | +LL | let _x: Mutex<i8> = Mutex::new(0); + | ^^^^^^^^^^^^^ + +error: consider using an `AtomicI64` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + --> $DIR/mutex_atomic.rs:33:5 + | +LL | Mutex::new(X); + | ^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed index 245d36cb734..237f5f5b97a 100644 --- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed @@ -25,9 +25,13 @@ fn main() { permissions.set_mode(0o644); permissions.set_mode(0o704); + // no error + permissions.set_mode(0b111_000_100); // DirBuilderExt::mode let mut builder = DirBuilder::new(); builder.mode(0o755); builder.mode(0o406); + // no error + permissions.set_mode(0b111000100); } diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs index d1559cba554..c8da5dbcec2 100644 --- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs @@ -25,9 +25,13 @@ fn main() { permissions.set_mode(644); permissions.set_mode(0o704); + // no error + permissions.set_mode(0b111_000_100); // DirBuilderExt::mode let mut builder = DirBuilder::new(); builder.mode(755); builder.mode(0o406); + // no error + permissions.set_mode(0b111000100); } diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr b/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr index 78c8f1a2fcf..83688c1b451 100644 --- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.stderr @@ -20,7 +20,7 @@ LL | permissions.set_mode(644); | ^^^ help: consider using an octal literal instead: `0o644` error: using a non-octal value to set unix file permissions - --> $DIR/non_octal_unix_permissions.rs:31:18 + --> $DIR/non_octal_unix_permissions.rs:33:18 | LL | builder.mode(755); | ^^^ help: consider using an octal literal instead: `0o755` diff --git a/src/tools/clippy/tests/ui/option_as_ref_cloned.fixed b/src/tools/clippy/tests/ui/option_as_ref_cloned.fixed new file mode 100644 index 00000000000..394dad219f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_cloned.fixed @@ -0,0 +1,21 @@ +#![warn(clippy::option_as_ref_cloned)] +#![allow(clippy::clone_on_copy)] + +fn main() { + let mut x = Some(String::new()); + + let _: Option<String> = x.clone(); + let _: Option<String> = x.clone(); + + let y = x.as_ref(); + let _: Option<&String> = y.clone(); + + macro_rules! cloned_recv { + () => { + x.as_ref() + }; + } + + // Don't lint when part of the expression is from a macro + let _: Option<String> = cloned_recv!().cloned(); +} diff --git a/src/tools/clippy/tests/ui/option_as_ref_cloned.rs b/src/tools/clippy/tests/ui/option_as_ref_cloned.rs new file mode 100644 index 00000000000..7243957927b --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_cloned.rs @@ -0,0 +1,21 @@ +#![warn(clippy::option_as_ref_cloned)] +#![allow(clippy::clone_on_copy)] + +fn main() { + let mut x = Some(String::new()); + + let _: Option<String> = x.as_ref().cloned(); + let _: Option<String> = x.as_mut().cloned(); + + let y = x.as_ref(); + let _: Option<&String> = y.as_ref().cloned(); + + macro_rules! cloned_recv { + () => { + x.as_ref() + }; + } + + // Don't lint when part of the expression is from a macro + let _: Option<String> = cloned_recv!().cloned(); +} diff --git a/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr b/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr new file mode 100644 index 00000000000..ea03da3b69f --- /dev/null +++ b/src/tools/clippy/tests/ui/option_as_ref_cloned.stderr @@ -0,0 +1,37 @@ +error: cloning an `Option<_>` using `.as_ref().cloned()` + --> $DIR/option_as_ref_cloned.rs:7:31 + | +LL | let _: Option<String> = x.as_ref().cloned(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::option-as-ref-cloned` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::option_as_ref_cloned)]` +help: this can be written more concisely by cloning the `Option<_>` directly + | +LL | let _: Option<String> = x.clone(); + | ~~~~~ + +error: cloning an `Option<_>` using `.as_mut().cloned()` + --> $DIR/option_as_ref_cloned.rs:8:31 + | +LL | let _: Option<String> = x.as_mut().cloned(); + | ^^^^^^^^^^^^^^^ + | +help: this can be written more concisely by cloning the `Option<_>` directly + | +LL | let _: Option<String> = x.clone(); + | ~~~~~ + +error: cloning an `Option<_>` using `.as_ref().cloned()` + --> $DIR/option_as_ref_cloned.rs:11:32 + | +LL | let _: Option<&String> = y.as_ref().cloned(); + | ^^^^^^^^^^^^^^^ + | +help: this can be written more concisely by cloning the `Option<_>` directly + | +LL | let _: Option<&String> = y.clone(); + | ~~~~~ + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_as_str.fixed b/src/tools/clippy/tests/ui/redundant_as_str.fixed index a38523a7c79..4185b402226 100644 --- a/src/tools/clippy/tests/ui/redundant_as_str.fixed +++ b/src/tools/clippy/tests/ui/redundant_as_str.fixed @@ -11,7 +11,7 @@ fn main() { let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); - // These methods are not redundant, and are equivelant to + // These methods are not redundant, and are equivalent to // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); diff --git a/src/tools/clippy/tests/ui/redundant_as_str.rs b/src/tools/clippy/tests/ui/redundant_as_str.rs index 33adb609996..7a74d8a55de 100644 --- a/src/tools/clippy/tests/ui/redundant_as_str.rs +++ b/src/tools/clippy/tests/ui/redundant_as_str.rs @@ -11,7 +11,7 @@ fn main() { let _no_as_str = string.as_bytes(); let _no_as_str = string.is_empty(); - // These methods are not redundant, and are equivelant to + // These methods are not redundant, and are equivalent to // doing dereferencing the string and applying the method let _not_redundant = string.as_str().escape_unicode(); let _not_redundant = string.as_str().trim(); diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index 1ea0d65bf1e..4fb6c08bb44 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -113,7 +113,7 @@ fn trivial_regex() { // #6005: unicode classes in bytes::Regex let a_byte_of_unicode = BRegex::new(r"\p{C}"); - // start and end word boundry, introduced in regex 0.10 + // start and end word boundary, introduced in regex 0.10 let _ = BRegex::new(r"\<word\>"); let _ = BRegex::new(r"\b{start}word\b{end}"); } diff --git a/src/tools/clippy/tests/ui/single_char_pattern.fixed b/src/tools/clippy/tests/ui/single_char_pattern.fixed index 79e7eda4070..9573fdbcfde 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.fixed +++ b/src/tools/clippy/tests/ui/single_char_pattern.fixed @@ -42,6 +42,8 @@ fn main() { x.split('\n'); x.split('\''); x.split('\''); + // Issue #11973: Don't escape `"` in `'"'` + x.split('"'); let h = HashSet::<String>::new(); h.contains("X"); // should not warn diff --git a/src/tools/clippy/tests/ui/single_char_pattern.rs b/src/tools/clippy/tests/ui/single_char_pattern.rs index 81962c0a6e9..8a04480dbc6 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.rs +++ b/src/tools/clippy/tests/ui/single_char_pattern.rs @@ -42,6 +42,8 @@ fn main() { x.split("\n"); x.split("'"); x.split("\'"); + // Issue #11973: Don't escape `"` in `'"'` + x.split("\""); let h = HashSet::<String>::new(); h.contains("X"); // should not warn diff --git a/src/tools/clippy/tests/ui/single_char_pattern.stderr b/src/tools/clippy/tests/ui/single_char_pattern.stderr index 6e57ab3489f..781ab316d9d 100644 --- a/src/tools/clippy/tests/ui/single_char_pattern.stderr +++ b/src/tools/clippy/tests/ui/single_char_pattern.stderr @@ -182,58 +182,64 @@ LL | x.split("\'"); | ^^^^ help: try using a `char` instead: `'\''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:49:31 + --> $DIR/single_char_pattern.rs:46:13 + | +LL | x.split("\""); + | ^^^^ help: try using a `char` instead: `'"'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:51:31 | LL | x.replace(';', ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:50:19 + --> $DIR/single_char_pattern.rs:52:19 | LL | x.starts_with("\x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'\x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:57:13 + --> $DIR/single_char_pattern.rs:59:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:58:13 + --> $DIR/single_char_pattern.rs:60:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:59:13 + --> $DIR/single_char_pattern.rs:61:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:60:13 + --> $DIR/single_char_pattern.rs:62:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'\''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:61:13 + --> $DIR/single_char_pattern.rs:63:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:63:13 + --> $DIR/single_char_pattern.rs:65:13 | LL | x.split(r#"\"#); | ^^^^^^ help: try using a `char` instead: `'\\'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:64:13 + --> $DIR/single_char_pattern.rs:66:13 | LL | x.split(r"\"); | ^^^^ help: try using a `char` instead: `'\\'` -error: aborting due to 39 previous errors +error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui/str_split.fixed b/src/tools/clippy/tests/ui/str_split.fixed new file mode 100644 index 00000000000..4f33241da7a --- /dev/null +++ b/src/tools/clippy/tests/ui/str_split.fixed @@ -0,0 +1,145 @@ +#![warn(clippy::str_split_at_newline)] + +use core::str::Split; +use std::ops::Deref; + +struct NotStr<'a> { + s: &'a str, +} + +impl<'a> NotStr<'a> { + fn trim(&'a self) -> &'a str { + self.s + } +} + +struct DerefsIntoNotStr<'a> { + not_str: &'a NotStr<'a>, +} + +impl<'a> Deref for DerefsIntoNotStr<'a> { + type Target = NotStr<'a>; + + fn deref(&self) -> &Self::Target { + self.not_str + } +} + +struct DerefsIntoStr<'a> { + s: &'a str, +} + +impl<'a> Deref for DerefsIntoStr<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.s + } +} + +macro_rules! trim_split { + ( $x:expr, $y:expr ) => { + $x.trim().split($y); + }; +} + +macro_rules! make_str { + ( $x: expr ) => { + format!("x={}", $x) + }; +} + +fn main() { + let s1 = "hello\nworld\n"; + let s2 = s1.to_owned(); + + // CASES THAT SHOULD EMIT A LINT + + // Splitting a `str` variable at "\n" or "\r\n" after trimming should warn + let _ = s1.lines(); + #[allow(clippy::single_char_pattern)] + let _ = s1.lines(); + let _ = s1.lines(); + + // Splitting a `String` variable at "\n" or "\r\n" after trimming should warn + let _ = s2.lines(); + #[allow(clippy::single_char_pattern)] + let _ = s2.lines(); + let _ = s2.lines(); + + // Splitting a variable that derefs into `str` at "\n" or "\r\n" after trimming should warn. + let s3 = DerefsIntoStr { s: s1 }; + let _ = s3.lines(); + #[allow(clippy::single_char_pattern)] + let _ = s3.lines(); + let _ = s3.lines(); + + // If the `&str` is generated by a macro then the macro should not be expanded in the suggested fix. + let _ = make_str!(s1).lines(); + + // CASES THAT SHOULD NOT EMIT A LINT + + // Splitting a `str` constant at "\n" or "\r\n" after trimming should not warn + let _ = "hello\nworld\n".trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = "hello\nworld\n".trim().split("\n"); + let _ = "hello\nworld\n".trim().split("\r\n"); + + // Splitting a `str` variable at "\n" or "\r\n" without trimming should not warn, since it is not + // equivalent + let _ = s1.split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s1.split("\n"); + let _ = s1.split("\r\n"); + + // Splitting a `String` variable at "\n" or "\r\n" without trimming should not warn. + let _ = s2.split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s2.split("\n"); + + // Splitting a variable that derefs into `str` at "\n" or "\r\n" without trimming should not warn. + let _ = s3.split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s3.split("\n"); + let _ = s3.split("\r\n"); + let _ = s2.split("\r\n"); + + // Splitting a `str` variable at other separators should not warn + let _ = s1.trim().split('\r'); + #[allow(clippy::single_char_pattern)] + let _ = s1.trim().split("\r"); + let _ = s1.trim().split("\n\r"); + let _ = s1.trim().split("\r \n"); + + // Splitting a `String` variable at other separators should not warn + let _ = s2.trim().split('\r'); + #[allow(clippy::single_char_pattern)] + let _ = s2.trim().split("\r"); + let _ = s2.trim().split("\n\r"); + + // Splitting a variable that derefs into `str` at other separators should not warn + let _ = s3.trim().split('\r'); + #[allow(clippy::single_char_pattern)] + let _ = s3.trim().split("\r"); + let _ = s3.trim().split("\n\r"); + let _ = s3.trim().split("\r \n"); + let _ = s2.trim().split("\r \n"); + + // Using `trim` and `split` on other types should not warn + let not_str = NotStr { s: s1 }; + let _ = not_str.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = not_str.trim().split("\n"); + let _ = not_str.trim().split("\r\n"); + let derefs_into_not_str = DerefsIntoNotStr { not_str: ¬_str }; + let _ = derefs_into_not_str.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = derefs_into_not_str.trim().split("\n"); + let _ = derefs_into_not_str.trim().split("\r\n"); + + // Code generated by macros should not create a warning + trim_split!(s1, "\r\n"); + trim_split!("hello\nworld\n", "\r\n"); + trim_split!(s2, "\r\n"); + trim_split!(s3, "\r\n"); +} diff --git a/src/tools/clippy/tests/ui/str_split.rs b/src/tools/clippy/tests/ui/str_split.rs new file mode 100644 index 00000000000..f24caa61c30 --- /dev/null +++ b/src/tools/clippy/tests/ui/str_split.rs @@ -0,0 +1,145 @@ +#![warn(clippy::str_split_at_newline)] + +use core::str::Split; +use std::ops::Deref; + +struct NotStr<'a> { + s: &'a str, +} + +impl<'a> NotStr<'a> { + fn trim(&'a self) -> &'a str { + self.s + } +} + +struct DerefsIntoNotStr<'a> { + not_str: &'a NotStr<'a>, +} + +impl<'a> Deref for DerefsIntoNotStr<'a> { + type Target = NotStr<'a>; + + fn deref(&self) -> &Self::Target { + self.not_str + } +} + +struct DerefsIntoStr<'a> { + s: &'a str, +} + +impl<'a> Deref for DerefsIntoStr<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.s + } +} + +macro_rules! trim_split { + ( $x:expr, $y:expr ) => { + $x.trim().split($y); + }; +} + +macro_rules! make_str { + ( $x: expr ) => { + format!("x={}", $x) + }; +} + +fn main() { + let s1 = "hello\nworld\n"; + let s2 = s1.to_owned(); + + // CASES THAT SHOULD EMIT A LINT + + // Splitting a `str` variable at "\n" or "\r\n" after trimming should warn + let _ = s1.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s1.trim().split("\n"); + let _ = s1.trim().split("\r\n"); + + // Splitting a `String` variable at "\n" or "\r\n" after trimming should warn + let _ = s2.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s2.trim().split("\n"); + let _ = s2.trim().split("\r\n"); + + // Splitting a variable that derefs into `str` at "\n" or "\r\n" after trimming should warn. + let s3 = DerefsIntoStr { s: s1 }; + let _ = s3.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s3.trim().split("\n"); + let _ = s3.trim().split("\r\n"); + + // If the `&str` is generated by a macro then the macro should not be expanded in the suggested fix. + let _ = make_str!(s1).trim().split('\n'); + + // CASES THAT SHOULD NOT EMIT A LINT + + // Splitting a `str` constant at "\n" or "\r\n" after trimming should not warn + let _ = "hello\nworld\n".trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = "hello\nworld\n".trim().split("\n"); + let _ = "hello\nworld\n".trim().split("\r\n"); + + // Splitting a `str` variable at "\n" or "\r\n" without trimming should not warn, since it is not + // equivalent + let _ = s1.split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s1.split("\n"); + let _ = s1.split("\r\n"); + + // Splitting a `String` variable at "\n" or "\r\n" without trimming should not warn. + let _ = s2.split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s2.split("\n"); + + // Splitting a variable that derefs into `str` at "\n" or "\r\n" without trimming should not warn. + let _ = s3.split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = s3.split("\n"); + let _ = s3.split("\r\n"); + let _ = s2.split("\r\n"); + + // Splitting a `str` variable at other separators should not warn + let _ = s1.trim().split('\r'); + #[allow(clippy::single_char_pattern)] + let _ = s1.trim().split("\r"); + let _ = s1.trim().split("\n\r"); + let _ = s1.trim().split("\r \n"); + + // Splitting a `String` variable at other separators should not warn + let _ = s2.trim().split('\r'); + #[allow(clippy::single_char_pattern)] + let _ = s2.trim().split("\r"); + let _ = s2.trim().split("\n\r"); + + // Splitting a variable that derefs into `str` at other separators should not warn + let _ = s3.trim().split('\r'); + #[allow(clippy::single_char_pattern)] + let _ = s3.trim().split("\r"); + let _ = s3.trim().split("\n\r"); + let _ = s3.trim().split("\r \n"); + let _ = s2.trim().split("\r \n"); + + // Using `trim` and `split` on other types should not warn + let not_str = NotStr { s: s1 }; + let _ = not_str.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = not_str.trim().split("\n"); + let _ = not_str.trim().split("\r\n"); + let derefs_into_not_str = DerefsIntoNotStr { not_str: ¬_str }; + let _ = derefs_into_not_str.trim().split('\n'); + #[allow(clippy::single_char_pattern)] + let _ = derefs_into_not_str.trim().split("\n"); + let _ = derefs_into_not_str.trim().split("\r\n"); + + // Code generated by macros should not create a warning + trim_split!(s1, "\r\n"); + trim_split!("hello\nworld\n", "\r\n"); + trim_split!(s2, "\r\n"); + trim_split!(s3, "\r\n"); +} diff --git a/src/tools/clippy/tests/ui/str_split.stderr b/src/tools/clippy/tests/ui/str_split.stderr new file mode 100644 index 00000000000..ee0a9653711 --- /dev/null +++ b/src/tools/clippy/tests/ui/str_split.stderr @@ -0,0 +1,65 @@ +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:59:13 + | +LL | let _ = s1.trim().split('\n'); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | + = note: `-D clippy::str-split-at-newline` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::str_split_at_newline)]` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:61:13 + | +LL | let _ = s1.trim().split("\n"); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:62:13 + | +LL | let _ = s1.trim().split("\r\n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:65:13 + | +LL | let _ = s2.trim().split('\n'); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:67:13 + | +LL | let _ = s2.trim().split("\n"); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:68:13 + | +LL | let _ = s2.trim().split("\r\n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:72:13 + | +LL | let _ = s3.trim().split('\n'); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:74:13 + | +LL | let _ = s3.trim().split("\n"); + | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:75:13 + | +LL | let _ = s3.trim().split("\r\n"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + +error: using `str.trim().split()` with hard-coded newlines + --> $DIR/str_split.rs:78:13 + | +LL | let _ = make_str!(s1).trim().split('\n'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `make_str!(s1).lines()` + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/struct_fields.rs b/src/tools/clippy/tests/ui/struct_fields.rs index 8b1a1446e3c..7c9e9d8ed26 100644 --- a/src/tools/clippy/tests/ui/struct_fields.rs +++ b/src/tools/clippy/tests/ui/struct_fields.rs @@ -39,14 +39,14 @@ struct DataStruct { struct DoublePrefix { //~^ ERROR: all fields have the same prefix: `some_data` some_data_a: bool, - some_data_b: bool, + some_data_b: i8, some_data_c: bool, } struct DoublePostfix { //~^ ERROR: all fields have the same postfix: `some_data` a_some_data: bool, - b_some_data: bool, + b_some_data: i8, c_some_data: bool, } @@ -54,18 +54,18 @@ struct DoublePostfix { struct NotSnakeCase { //~^ ERROR: all fields have the same postfix: `someData` a_someData: bool, - b_someData: bool, + b_someData: i8, c_someData: bool, } #[allow(non_snake_case)] struct NotSnakeCase2 { //~^ ERROR: all fields have the same prefix: `someData` someData_c: bool, - someData_b: bool, + someData_b: i8, someData_a_b: bool, } -// no error, threshold is 3 fiels by default +// no error, threshold is 3 fields by default struct Fooo { foo: u8, bar: u8, @@ -328,4 +328,18 @@ external! { } +// Should not warn +struct Config { + use_foo: bool, + use_bar: bool, + use_baz: bool, +} + +struct Use { + use_foo: bool, + //~^ ERROR: field name starts with the struct's name + use_bar: bool, + use_baz: bool, +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/struct_fields.stderr b/src/tools/clippy/tests/ui/struct_fields.stderr index 4ca57715b18..d2bdbd17d5c 100644 --- a/src/tools/clippy/tests/ui/struct_fields.stderr +++ b/src/tools/clippy/tests/ui/struct_fields.stderr @@ -45,7 +45,7 @@ error: all fields have the same prefix: `some_data` LL | / struct DoublePrefix { LL | | LL | | some_data_a: bool, -LL | | some_data_b: bool, +LL | | some_data_b: i8, LL | | some_data_c: bool, LL | | } | |_^ @@ -58,7 +58,7 @@ error: all fields have the same postfix: `some_data` LL | / struct DoublePostfix { LL | | LL | | a_some_data: bool, -LL | | b_some_data: bool, +LL | | b_some_data: i8, LL | | c_some_data: bool, LL | | } | |_^ @@ -71,7 +71,7 @@ error: all fields have the same postfix: `someData` LL | / struct NotSnakeCase { LL | | LL | | a_someData: bool, -LL | | b_someData: bool, +LL | | b_someData: i8, LL | | c_someData: bool, LL | | } | |_^ @@ -84,7 +84,7 @@ error: all fields have the same prefix: `someData` LL | / struct NotSnakeCase2 { LL | | LL | | someData_c: bool, -LL | | someData_b: bool, +LL | | someData_b: i8, LL | | someData_a_b: bool, LL | | } | |_^ @@ -261,5 +261,23 @@ LL | mk_struct_full_def!(PrefixData, some_data, some_meta, some_other); = help: remove the prefixes = note: this error originates in the macro `mk_struct_full_def` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 21 previous errors +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:339:5 + | +LL | use_foo: bool, + | ^^^^^^^^^^^^^ + +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:341:5 + | +LL | use_bar: bool, + | ^^^^^^^^^^^^^ + +error: field name starts with the struct's name + --> $DIR/struct_fields.rs:342:5 + | +LL | use_baz: bool, + | ^^^^^^^^^^^^^ + +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.fixed b/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.fixed new file mode 100644 index 00000000000..bbde25b0a88 --- /dev/null +++ b/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::thread_local_initializer_can_be_made_const)] + +use std::cell::RefCell; + +fn main() { + // lint and suggest const + thread_local! { + static BUF_1: RefCell<String> = const { RefCell::new(String::new()) }; + } + //~^^ ERROR: initializer for `thread_local` value can be made `const` + + // don't lint + thread_local! { + static BUF_2: RefCell<String> = const { RefCell::new(String::new()) }; + } + + thread_local! { + static SIMPLE:i32 = const { 1 }; + } + //~^^ ERROR: initializer for `thread_local` value can be made `const` + + // lint and suggest const for all non const items + thread_local! { + static BUF_3_CAN_BE_MADE_CONST: RefCell<String> = const { RefCell::new(String::new()) }; + static CONST_MIXED_WITH:i32 = const { 1 }; + static BUF_4_CAN_BE_MADE_CONST: RefCell<String> = const { RefCell::new(String::new()) }; + } + //~^^^^ ERROR: initializer for `thread_local` value can be made `const` + //~^^^ ERROR: initializer for `thread_local` value can be made `const` +} diff --git a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.rs b/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.rs new file mode 100644 index 00000000000..3d7aacf2f09 --- /dev/null +++ b/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.rs @@ -0,0 +1,30 @@ +#![warn(clippy::thread_local_initializer_can_be_made_const)] + +use std::cell::RefCell; + +fn main() { + // lint and suggest const + thread_local! { + static BUF_1: RefCell<String> = RefCell::new(String::new()); + } + //~^^ ERROR: initializer for `thread_local` value can be made `const` + + // don't lint + thread_local! { + static BUF_2: RefCell<String> = const { RefCell::new(String::new()) }; + } + + thread_local! { + static SIMPLE:i32 = 1; + } + //~^^ ERROR: initializer for `thread_local` value can be made `const` + + // lint and suggest const for all non const items + thread_local! { + static BUF_3_CAN_BE_MADE_CONST: RefCell<String> = RefCell::new(String::new()); + static CONST_MIXED_WITH:i32 = const { 1 }; + static BUF_4_CAN_BE_MADE_CONST: RefCell<String> = RefCell::new(String::new()); + } + //~^^^^ ERROR: initializer for `thread_local` value can be made `const` + //~^^^ ERROR: initializer for `thread_local` value can be made `const` +} diff --git a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.stderr b/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.stderr new file mode 100644 index 00000000000..b35bd306b52 --- /dev/null +++ b/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.stderr @@ -0,0 +1,29 @@ +error: initializer for `thread_local` value can be made `const` + --> $DIR/thread_local_initializer_can_be_made_const.rs:8:41 + | +LL | static BUF_1: RefCell<String> = RefCell::new(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` + | + = note: `-D clippy::thread-local-initializer-can-be-made-const` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::thread_local_initializer_can_be_made_const)]` + +error: initializer for `thread_local` value can be made `const` + --> $DIR/thread_local_initializer_can_be_made_const.rs:18:29 + | +LL | static SIMPLE:i32 = 1; + | ^ help: replace with: `const { 1 }` + +error: initializer for `thread_local` value can be made `const` + --> $DIR/thread_local_initializer_can_be_made_const.rs:24:59 + | +LL | static BUF_3_CAN_BE_MADE_CONST: RefCell<String> = RefCell::new(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` + +error: initializer for `thread_local` value can be made `const` + --> $DIR/thread_local_initializer_can_be_made_const.rs:26:59 + | +LL | static BUF_4_CAN_BE_MADE_CONST: RefCell<String> = RefCell::new(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.rs b/src/tools/clippy/tests/ui/unconditional_recursion.rs index 1169118de83..e1a2d6a90b8 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.rs +++ b/src/tools/clippy/tests/ui/unconditional_recursion.rs @@ -1,7 +1,7 @@ //@no-rustfix #![warn(clippy::unconditional_recursion)] -#![allow(clippy::partialeq_ne_impl)] +#![allow(clippy::partialeq_ne_impl, clippy::default_constructed_unit_structs)] enum Foo { A, @@ -158,6 +158,112 @@ struct S5; impl_partial_eq!(S5); //~^ ERROR: function cannot return without recursing +struct S6 { + field: String, +} + +impl PartialEq for S6 { + fn eq(&self, other: &Self) -> bool { + let mine = &self.field; + let theirs = &other.field; + mine == theirs // Should not warn! + } +} + +struct S7<'a> { + field: &'a S7<'a>, +} + +impl<'a> PartialEq for S7<'a> { + fn eq(&self, other: &Self) -> bool { + //~^ ERROR: function cannot return without recursing + let mine = &self.field; + let theirs = &other.field; + mine == theirs + } +} + +struct S8 { + num: i32, + field: Option<Box<S8>>, +} + +impl PartialEq for S8 { + fn eq(&self, other: &Self) -> bool { + if self.num != other.num { + return false; + } + + let (this, other) = match (self.field.as_deref(), other.field.as_deref()) { + (Some(x1), Some(x2)) => (x1, x2), + (None, None) => return true, + _ => return false, + }; + + this == other + } +} + +struct S9; + +impl std::string::ToString for S9 { + fn to_string(&self) -> String { + //~^ ERROR: function cannot return without recursing + self.to_string() + } +} + +struct S10; + +impl std::string::ToString for S10 { + fn to_string(&self) -> String { + //~^ ERROR: function cannot return without recursing + let x = self; + x.to_string() + } +} + +struct S11; + +impl std::string::ToString for S11 { + fn to_string(&self) -> String { + //~^ ERROR: function cannot return without recursing + (self as &Self).to_string() + } +} + +struct S12; + +impl std::default::Default for S12 { + fn default() -> Self { + Self::new() + } +} + +impl S12 { + fn new() -> Self { + //~^ ERROR: function cannot return without recursing + Self::default() + } + + fn bar() -> Self { + // Should not warn! + Self::default() + } +} + +#[derive(Default)] +struct S13 { + f: u32, +} + +impl S13 { + fn new() -> Self { + // Shoud not warn! + Self::default() + } +} + fn main() { // test code goes here } diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.stderr b/src/tools/clippy/tests/ui/unconditional_recursion.stderr index 1fb01c00f19..5d82e2a9f31 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.stderr +++ b/src/tools/clippy/tests/ui/unconditional_recursion.stderr @@ -23,6 +23,39 @@ LL | self.eq(other) = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:210:5 + | +LL | fn to_string(&self) -> String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | self.to_string() + | ---------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:219:5 + | +LL | fn to_string(&self) -> String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +... +LL | x.to_string() + | ------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:229:5 + | +LL | fn to_string(&self) -> String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | (self as &Self).to_string() + | --------------------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + +error: function cannot return without recursing --> $DIR/unconditional_recursion.rs:12:5 | LL | / fn ne(&self, other: &Self) -> bool { @@ -55,6 +88,34 @@ LL | self == other | ^^^^^^^^^^^^^ error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:28:5 + | +LL | / fn ne(&self, other: &Self) -> bool { +LL | | self != &Foo2::B // no error here +LL | | } + | |_____^ + | +note: recursive call site + --> $DIR/unconditional_recursion.rs:29:9 + | +LL | self != &Foo2::B // no error here + | ^^^^^^^^^^^^^^^^ + +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:31:5 + | +LL | / fn eq(&self, other: &Self) -> bool { +LL | | self == &Foo2::B // no error here +LL | | } + | |_____^ + | +note: recursive call site + --> $DIR/unconditional_recursion.rs:32:9 + | +LL | self == &Foo2::B // no error here + | ^^^^^^^^^^^^^^^^ + +error: function cannot return without recursing --> $DIR/unconditional_recursion.rs:42:5 | LL | / fn ne(&self, other: &Self) -> bool { @@ -247,5 +308,37 @@ LL | impl_partial_eq!(S5); | -------------------- in this macro invocation = note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 19 previous errors +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:178:5 + | +LL | / fn eq(&self, other: &Self) -> bool { +LL | | +LL | | let mine = &self.field; +LL | | let theirs = &other.field; +LL | | mine == theirs +LL | | } + | |_____^ + | +note: recursive call site + --> $DIR/unconditional_recursion.rs:182:9 + | +LL | mine == theirs + | ^^^^^^^^^^^^^^ + +error: function cannot return without recursing + --> $DIR/unconditional_recursion.rs:244:5 + | +LL | / fn new() -> Self { +LL | | +LL | | Self::default() +LL | | } + | |_____^ + | +note: recursive call site + --> $DIR/unconditional_recursion.rs:246:9 + | +LL | Self::default() + | ^^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed index cba32ce6b77..c0ccd41c19a 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed @@ -7,10 +7,12 @@ #[warn(clippy::if_not_else)] #[warn(clippy::unnecessary_cast)] #[warn(clippy::useless_transmute)] -// Shouldn't suggest rustc lint name(`dead_code`) -#[warn(clippy::eq_op)] +// Should suggest rustc lint name(`dead_code`) +#[warn(dead_code)] // Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) #[warn(clippy::unused_self)] // Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) #[warn(clippy::redundant_static_lifetimes)] +// issue #118183, should report `missing_docs` from rustc lint +#[warn(missing_docs)] fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs index c15d541974f..7a59ad364aa 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs @@ -7,10 +7,12 @@ #[warn(clippy::if_not_els)] #[warn(clippy::UNNecsaRy_cAst)] #[warn(clippy::useles_transute)] -// Shouldn't suggest rustc lint name(`dead_code`) +// Should suggest rustc lint name(`dead_code`) #[warn(clippy::dead_cod)] // Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) #[warn(clippy::unused_colle)] // Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) #[warn(clippy::const_static_lifetim)] +// issue #118183, should report `missing_docs` from rustc lint +#[warn(clippy::missing_docs)] fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr index ee82db31c2c..432c7f72e32 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -35,7 +35,12 @@ error: unknown lint: `clippy::dead_cod` --> $DIR/unknown_clippy_lints.rs:11:8 | LL | #[warn(clippy::dead_cod)] - | ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::eq_op` + | ^^^^^^^^^^^^^^^^ + | +help: a lint with a similar name exists in `rustc` lints + | +LL | #[warn(dead_code)] + | ~~~~~~~~~ error: unknown lint: `clippy::unused_colle` --> $DIR/unknown_clippy_lints.rs:13:8 @@ -49,5 +54,16 @@ error: unknown lint: `clippy::const_static_lifetim` LL | #[warn(clippy::const_static_lifetim)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes` -error: aborting due to 8 previous errors +error: unknown lint: `clippy::missing_docs` + --> $DIR/unknown_clippy_lints.rs:17:8 + | +LL | #[warn(clippy::missing_docs)] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: a lint with a similar name exists in `rustc` lints + | +LL | #[warn(missing_docs)] + | ~~~~~~~~~~~~ + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 66598f89208..d8031c484e5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -47,6 +47,18 @@ impl Drop for Issue9427FollowUp { } } +struct Issue9427Followup2 { + ptr: *const (), +} +impl Issue9427Followup2 { + fn from_owned(ptr: *const ()) -> Option<Self> { + (!ptr.is_null()).then(|| Self { ptr }) + } +} +impl Drop for Issue9427Followup2 { + fn drop(&mut self) {} +} + struct Issue10437; impl Deref for Issue10437 { type Target = u32; @@ -128,6 +140,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop + let _ = false.then(|| Issue9427Followup2 { ptr: std::ptr::null() }); // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 5045fcd790e..ea55b1d9a90 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -47,6 +47,18 @@ impl Drop for Issue9427FollowUp { } } +struct Issue9427Followup2 { + ptr: *const (), +} +impl Issue9427Followup2 { + fn from_owned(ptr: *const ()) -> Option<Self> { + (!ptr.is_null()).then(|| Self { ptr }) + } +} +impl Drop for Issue9427Followup2 { + fn drop(&mut self) {} +} + struct Issue10437; impl Deref for Issue10437 { type Target = u32; @@ -128,6 +140,7 @@ fn main() { // Should not lint - bool let _ = (0 == 1).then(|| Issue9427(0)); // Issue9427 has a significant drop let _ = false.then(|| Issue9427FollowUp); // Issue9427FollowUp has a significant drop + let _ = false.then(|| Issue9427Followup2 { ptr: std::ptr::null() }); // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index 466664aee6c..6ff2691a461 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:71:13 + --> $DIR/unnecessary_lazy_eval.rs:83:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^-------------------- @@ -10,7 +10,7 @@ LL | let _ = opt.unwrap_or_else(|| 2); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:72:13 + --> $DIR/unnecessary_lazy_eval.rs:84:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^--------------------------------- @@ -18,7 +18,7 @@ LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:85:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^------------------------------------- @@ -26,7 +26,7 @@ LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:75:13 + --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^--------------------- @@ -34,7 +34,7 @@ LL | let _ = opt.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:76:13 + --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^------------------- @@ -42,7 +42,7 @@ LL | let _ = opt.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:77:13 + --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = opt.or_else(|| None); | ^^^^---------------- @@ -50,7 +50,7 @@ LL | let _ = opt.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:78:13 + --> $DIR/unnecessary_lazy_eval.rs:90:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^------------------------ @@ -58,7 +58,7 @@ LL | let _ = opt.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:79:13 + --> $DIR/unnecessary_lazy_eval.rs:91:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^---------------- @@ -66,7 +66,7 @@ LL | let _ = opt.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:80:13 + --> $DIR/unnecessary_lazy_eval.rs:92:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | ^^^^^^^^^^^^^^^^^------------------------------- @@ -74,7 +74,7 @@ LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); | help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:81:13 + --> $DIR/unnecessary_lazy_eval.rs:93:13 | LL | let _ = cond.then(|| astronomers_pi); | ^^^^^----------------------- @@ -82,7 +82,7 @@ LL | let _ = cond.then(|| astronomers_pi); | help: use `then_some(..)` instead: `then_some(astronomers_pi)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:82:13 + --> $DIR/unnecessary_lazy_eval.rs:94:13 | LL | let _ = true.then(|| -> _ {}); | ^^^^^---------------- @@ -90,7 +90,7 @@ LL | let _ = true.then(|| -> _ {}); | help: use `then_some(..)` instead: `then_some({})` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:83:13 + --> $DIR/unnecessary_lazy_eval.rs:95:13 | LL | let _ = true.then(|| {}); | ^^^^^----------- @@ -98,7 +98,7 @@ LL | let _ = true.then(|| {}); | help: use `then_some(..)` instead: `then_some({})` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:87:13 + --> $DIR/unnecessary_lazy_eval.rs:99:13 | LL | let _ = Some(1).unwrap_or_else(|| *r); | ^^^^^^^^--------------------- @@ -106,7 +106,7 @@ LL | let _ = Some(1).unwrap_or_else(|| *r); | help: use `unwrap_or(..)` instead: `unwrap_or(*r)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:89:13 + --> $DIR/unnecessary_lazy_eval.rs:101:13 | LL | let _ = Some(1).unwrap_or_else(|| *b); | ^^^^^^^^--------------------- @@ -114,7 +114,7 @@ LL | let _ = Some(1).unwrap_or_else(|| *b); | help: use `unwrap_or(..)` instead: `unwrap_or(*b)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:91:13 + --> $DIR/unnecessary_lazy_eval.rs:103:13 | LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r); | ^^^^^^^^^^^^^^^^^--------------------- @@ -122,7 +122,7 @@ LL | let _ = Some(1).as_ref().unwrap_or_else(|| &r); | help: use `unwrap_or(..)` instead: `unwrap_or(&r)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:92:13 + --> $DIR/unnecessary_lazy_eval.rs:104:13 | LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b); | ^^^^^^^^^^^^^^^^^--------------------- @@ -130,7 +130,7 @@ LL | let _ = Some(1).as_ref().unwrap_or_else(|| &b); | help: use `unwrap_or(..)` instead: `unwrap_or(&b)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:95:13 + --> $DIR/unnecessary_lazy_eval.rs:107:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^-------------------- @@ -138,7 +138,7 @@ LL | let _ = Some(10).unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:96:13 + --> $DIR/unnecessary_lazy_eval.rs:108:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^--------------------- @@ -146,7 +146,7 @@ LL | let _ = Some(10).and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:97:28 + --> $DIR/unnecessary_lazy_eval.rs:109:28 | LL | let _: Option<usize> = None.or_else(|| ext_opt); | ^^^^^------------------- @@ -154,7 +154,7 @@ LL | let _: Option<usize> = None.or_else(|| ext_opt); | help: use `or(..)` instead: `or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:98:13 + --> $DIR/unnecessary_lazy_eval.rs:110:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^------------------------ @@ -162,7 +162,7 @@ LL | let _ = None.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:99:35 + --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); | ^^^^^---------------- @@ -170,7 +170,7 @@ LL | let _: Result<usize, usize> = None.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:100:28 + --> $DIR/unnecessary_lazy_eval.rs:112:28 | LL | let _: Option<usize> = None.or_else(|| None); | ^^^^^---------------- @@ -178,7 +178,7 @@ LL | let _: Option<usize> = None.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:103:13 + --> $DIR/unnecessary_lazy_eval.rs:115:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^-------------------- @@ -186,7 +186,7 @@ LL | let _ = deep.0.unwrap_or_else(|| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:104:13 + --> $DIR/unnecessary_lazy_eval.rs:116:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^--------------------- @@ -194,7 +194,7 @@ LL | let _ = deep.0.and_then(|_| ext_opt); | help: use `and(..)` instead: `and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:105:13 + --> $DIR/unnecessary_lazy_eval.rs:117:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^---------------- @@ -202,7 +202,7 @@ LL | let _ = deep.0.or_else(|| None); | help: use `or(..)` instead: `or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:106:13 + --> $DIR/unnecessary_lazy_eval.rs:118:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^------------------------ @@ -210,7 +210,7 @@ LL | let _ = deep.0.get_or_insert_with(|| 2); | help: use `get_or_insert(..)` instead: `get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:107:13 + --> $DIR/unnecessary_lazy_eval.rs:119:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^---------------- @@ -218,7 +218,7 @@ LL | let _ = deep.0.ok_or_else(|| 2); | help: use `ok_or(..)` instead: `ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:137:28 + --> $DIR/unnecessary_lazy_eval.rs:150:28 | LL | let _: Option<usize> = None.or_else(|| Some(3)); | ^^^^^------------------- @@ -226,7 +226,7 @@ LL | let _: Option<usize> = None.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:138:13 + --> $DIR/unnecessary_lazy_eval.rs:151:13 | LL | let _ = deep.0.or_else(|| Some(3)); | ^^^^^^^------------------- @@ -234,7 +234,7 @@ LL | let _ = deep.0.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:139:13 + --> $DIR/unnecessary_lazy_eval.rs:152:13 | LL | let _ = opt.or_else(|| Some(3)); | ^^^^------------------- @@ -242,7 +242,7 @@ LL | let _ = opt.or_else(|| Some(3)); | help: use `or(..)` instead: `or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:145:13 + --> $DIR/unnecessary_lazy_eval.rs:158:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^--------------------- @@ -250,7 +250,7 @@ LL | let _ = res2.unwrap_or_else(|_| 2); | help: use `unwrap_or(..)` instead: `unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:146:13 + --> $DIR/unnecessary_lazy_eval.rs:159:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^---------------------------------- @@ -258,7 +258,7 @@ LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:147:13 + --> $DIR/unnecessary_lazy_eval.rs:160:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^-------------------------------------- @@ -266,7 +266,7 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:169:35 + --> $DIR/unnecessary_lazy_eval.rs:182:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); | ^^^^-------------------- @@ -274,7 +274,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(2)); | help: use `and(..)` instead: `and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:170:35 + --> $DIR/unnecessary_lazy_eval.rs:183:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); | ^^^^--------------------------------- @@ -282,7 +282,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi)); | help: use `and(..)` instead: `and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:171:35 + --> $DIR/unnecessary_lazy_eval.rs:184:35 | LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)); | ^^^^------------------------------------- @@ -290,7 +290,7 @@ LL | let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field)) | help: use `and(..)` instead: `and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:173:35 + --> $DIR/unnecessary_lazy_eval.rs:186:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); | ^^^^------------------ @@ -298,7 +298,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(2)); | help: use `or(..)` instead: `or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:174:35 + --> $DIR/unnecessary_lazy_eval.rs:187:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); | ^^^^------------------------------- @@ -306,7 +306,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi)); | help: use `or(..)` instead: `or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:175:35 + --> $DIR/unnecessary_lazy_eval.rs:188:35 | LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^----------------------------------- @@ -314,7 +314,7 @@ LL | let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:176:35 + --> $DIR/unnecessary_lazy_eval.rs:189:35 | LL | let _: Result<usize, usize> = res. | ___________________________________^ @@ -329,7 +329,7 @@ LL | | or_else(|_| Ok(ext_str.some_field)); | help: use `or(..)` instead: `or(Ok(ext_str.some_field))` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:206:14 + --> $DIR/unnecessary_lazy_eval.rs:219:14 | LL | let _x = false.then(|| i32::MAX + 1); | ^^^^^^--------------------- @@ -337,7 +337,7 @@ LL | let _x = false.then(|| i32::MAX + 1); | help: use `then_some(..)` instead: `then_some(i32::MAX + 1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:208:14 + --> $DIR/unnecessary_lazy_eval.rs:221:14 | LL | let _x = false.then(|| i32::MAX * 2); | ^^^^^^--------------------- @@ -345,7 +345,7 @@ LL | let _x = false.then(|| i32::MAX * 2); | help: use `then_some(..)` instead: `then_some(i32::MAX * 2)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:210:14 + --> $DIR/unnecessary_lazy_eval.rs:223:14 | LL | let _x = false.then(|| i32::MAX - 1); | ^^^^^^--------------------- @@ -353,7 +353,7 @@ LL | let _x = false.then(|| i32::MAX - 1); | help: use `then_some(..)` instead: `then_some(i32::MAX - 1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:212:14 + --> $DIR/unnecessary_lazy_eval.rs:225:14 | LL | let _x = false.then(|| i32::MIN - 1); | ^^^^^^--------------------- @@ -361,7 +361,7 @@ LL | let _x = false.then(|| i32::MIN - 1); | help: use `then_some(..)` instead: `then_some(i32::MIN - 1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:214:14 + --> $DIR/unnecessary_lazy_eval.rs:227:14 | LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); | ^^^^^^------------------------------------- @@ -369,7 +369,7 @@ LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); | help: use `then_some(..)` instead: `then_some((1 + 2 * 3 - 2 / 3 + 9) << 2)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:216:14 + --> $DIR/unnecessary_lazy_eval.rs:229:14 | LL | let _x = false.then(|| 255u8 << 7); | ^^^^^^------------------- @@ -377,7 +377,7 @@ LL | let _x = false.then(|| 255u8 << 7); | help: use `then_some(..)` instead: `then_some(255u8 << 7)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:218:14 + --> $DIR/unnecessary_lazy_eval.rs:231:14 | LL | let _x = false.then(|| 255u8 << 8); | ^^^^^^------------------- @@ -385,7 +385,7 @@ LL | let _x = false.then(|| 255u8 << 8); | help: use `then_some(..)` instead: `then_some(255u8 << 8)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:220:14 + --> $DIR/unnecessary_lazy_eval.rs:233:14 | LL | let _x = false.then(|| 255u8 >> 8); | ^^^^^^------------------- @@ -393,7 +393,7 @@ LL | let _x = false.then(|| 255u8 >> 8); | help: use `then_some(..)` instead: `then_some(255u8 >> 8)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:223:14 + --> $DIR/unnecessary_lazy_eval.rs:236:14 | LL | let _x = false.then(|| i32::MAX + -1); | ^^^^^^---------------------- @@ -401,7 +401,7 @@ LL | let _x = false.then(|| i32::MAX + -1); | help: use `then_some(..)` instead: `then_some(i32::MAX + -1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:225:14 + --> $DIR/unnecessary_lazy_eval.rs:238:14 | LL | let _x = false.then(|| -i32::MAX); | ^^^^^^------------------ @@ -409,7 +409,7 @@ LL | let _x = false.then(|| -i32::MAX); | help: use `then_some(..)` instead: `then_some(-i32::MAX)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:227:14 + --> $DIR/unnecessary_lazy_eval.rs:240:14 | LL | let _x = false.then(|| -i32::MIN); | ^^^^^^------------------ @@ -417,7 +417,7 @@ LL | let _x = false.then(|| -i32::MIN); | help: use `then_some(..)` instead: `then_some(-i32::MIN)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:230:14 + --> $DIR/unnecessary_lazy_eval.rs:243:14 | LL | let _x = false.then(|| 255 >> -7); | ^^^^^^------------------ @@ -425,7 +425,7 @@ LL | let _x = false.then(|| 255 >> -7); | help: use `then_some(..)` instead: `then_some(255 >> -7)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:232:14 + --> $DIR/unnecessary_lazy_eval.rs:245:14 | LL | let _x = false.then(|| 255 << -1); | ^^^^^^------------------ @@ -433,7 +433,7 @@ LL | let _x = false.then(|| 255 << -1); | help: use `then_some(..)` instead: `then_some(255 << -1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:234:14 + --> $DIR/unnecessary_lazy_eval.rs:247:14 | LL | let _x = false.then(|| 1 / 0); | ^^^^^^-------------- @@ -441,7 +441,7 @@ LL | let _x = false.then(|| 1 / 0); | help: use `then_some(..)` instead: `then_some(1 / 0)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:236:14 + --> $DIR/unnecessary_lazy_eval.rs:249:14 | LL | let _x = false.then(|| x << -1); | ^^^^^^---------------- @@ -449,7 +449,7 @@ LL | let _x = false.then(|| x << -1); | help: use `then_some(..)` instead: `then_some(x << -1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:238:14 + --> $DIR/unnecessary_lazy_eval.rs:251:14 | LL | let _x = false.then(|| x << 2); | ^^^^^^--------------- @@ -457,7 +457,7 @@ LL | let _x = false.then(|| x << 2); | help: use `then_some(..)` instead: `then_some(x << 2)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:248:14 + --> $DIR/unnecessary_lazy_eval.rs:261:14 | LL | let _x = false.then(|| x / 0); | ^^^^^^-------------- @@ -465,7 +465,7 @@ LL | let _x = false.then(|| x / 0); | help: use `then_some(..)` instead: `then_some(x / 0)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:250:14 + --> $DIR/unnecessary_lazy_eval.rs:263:14 | LL | let _x = false.then(|| x % 0); | ^^^^^^-------------- @@ -473,7 +473,7 @@ LL | let _x = false.then(|| x % 0); | help: use `then_some(..)` instead: `then_some(x % 0)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:253:14 + --> $DIR/unnecessary_lazy_eval.rs:266:14 | LL | let _x = false.then(|| 1 / -1); | ^^^^^^--------------- @@ -481,7 +481,7 @@ LL | let _x = false.then(|| 1 / -1); | help: use `then_some(..)` instead: `then_some(1 / -1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:255:14 + --> $DIR/unnecessary_lazy_eval.rs:268:14 | LL | let _x = false.then(|| i32::MIN / -1); | ^^^^^^---------------------- @@ -489,7 +489,7 @@ LL | let _x = false.then(|| i32::MIN / -1); | help: use `then_some(..)` instead: `then_some(i32::MIN / -1)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:258:14 + --> $DIR/unnecessary_lazy_eval.rs:271:14 | LL | let _x = false.then(|| i32::MIN / 0); | ^^^^^^--------------------- @@ -497,7 +497,7 @@ LL | let _x = false.then(|| i32::MIN / 0); | help: use `then_some(..)` instead: `then_some(i32::MIN / 0)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:260:14 + --> $DIR/unnecessary_lazy_eval.rs:273:14 | LL | let _x = false.then(|| 4 / 2); | ^^^^^^-------------- @@ -505,7 +505,7 @@ LL | let _x = false.then(|| 4 / 2); | help: use `then_some(..)` instead: `then_some(4 / 2)` error: unnecessary closure used with `bool::then` - --> $DIR/unnecessary_lazy_eval.rs:268:14 + --> $DIR/unnecessary_lazy_eval.rs:281:14 | LL | let _x = false.then(|| f1 + f2); | ^^^^^^---------------- diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs index d9a7ad8e56c..bdc6fa0f46b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs +++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs @@ -73,4 +73,25 @@ mod issue_10084 { } } +mod issue_12048 { + pub const X: u8 = 0; + + /// Returns a pointer to five. + /// + /// # Examples + /// + /// ``` + /// use foo::point_to_five; + /// + /// let five_pointer = point_to_five(); + /// // Safety: this pointer always points to a valid five. + /// let five = unsafe { *five_pointer }; + /// assert_eq!(five, 5); + /// ``` + pub fn point_to_five() -> *const u8 { + static FIVE: u8 = 5; + &FIVE + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed index 53dc3c43e2f..f87c898f9b7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed @@ -1,4 +1,18 @@ -#[allow(clippy::single_char_pattern)] +#![allow(clippy::single_char_pattern)] + +struct Issue12068; + +impl AsRef<str> for Issue12068 { + fn as_ref(&self) -> &str { + "" + } +} + +impl ToString for Issue12068 { + fn to_string(&self) -> String { + String::new() + } +} fn main() { let _ = "a".split('a').next().unwrap(); @@ -9,6 +23,8 @@ fn main() { //~^ ERROR: unnecessary use of `to_owned` let _ = "a".split("a").next().unwrap(); //~^ ERROR: unnecessary use of `to_owned` + let _ = Issue12068.as_ref().split('a').next().unwrap(); + //~^ ERROR: unnecessary use of `to_string` let _ = [1].split(|x| *x == 2).next().unwrap(); //~^ ERROR: unnecessary use of `to_vec` diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs index 62400e7eee1..db5719e5880 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs @@ -1,4 +1,18 @@ -#[allow(clippy::single_char_pattern)] +#![allow(clippy::single_char_pattern)] + +struct Issue12068; + +impl AsRef<str> for Issue12068 { + fn as_ref(&self) -> &str { + "" + } +} + +impl ToString for Issue12068 { + fn to_string(&self) -> String { + String::new() + } +} fn main() { let _ = "a".to_string().split('a').next().unwrap(); @@ -9,6 +23,8 @@ fn main() { //~^ ERROR: unnecessary use of `to_owned` let _ = "a".to_owned().split("a").next().unwrap(); //~^ ERROR: unnecessary use of `to_owned` + let _ = Issue12068.to_string().split('a').next().unwrap(); + //~^ ERROR: unnecessary use of `to_string` let _ = [1].to_vec().split(|x| *x == 2).next().unwrap(); //~^ ERROR: unnecessary use of `to_vec` diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr index cfb3766d15e..4cfaeed3384 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr @@ -1,5 +1,5 @@ error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned_on_split.rs:4:13 + --> $DIR/unnecessary_to_owned_on_split.rs:18:13 | LL | let _ = "a".to_string().split('a').next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')` @@ -8,46 +8,52 @@ LL | let _ = "a".to_string().split('a').next().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned_on_split.rs:6:13 + --> $DIR/unnecessary_to_owned_on_split.rs:20:13 | LL | let _ = "a".to_string().split("a").next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:8:13 + --> $DIR/unnecessary_to_owned_on_split.rs:22:13 | LL | let _ = "a".to_owned().split('a').next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:10:13 + --> $DIR/unnecessary_to_owned_on_split.rs:24:13 | LL | let _ = "a".to_owned().split("a").next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")` +error: unnecessary use of `to_string` + --> $DIR/unnecessary_to_owned_on_split.rs:26:13 + | +LL | let _ = Issue12068.to_string().split('a').next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Issue12068.as_ref().split('a')` + error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned_on_split.rs:13:13 + --> $DIR/unnecessary_to_owned_on_split.rs:29:13 | LL | let _ = [1].to_vec().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned_on_split.rs:15:13 + --> $DIR/unnecessary_to_owned_on_split.rs:31:13 | LL | let _ = [1].to_vec().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:17:13 + --> $DIR/unnecessary_to_owned_on_split.rs:33:13 | LL | let _ = [1].to_owned().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned_on_split.rs:19:13 + --> $DIR/unnecessary_to_owned_on_split.rs:35:13 | LL | let _ = [1].to_owned().split(|x| *x == 2).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed index f6770558bd8..88b95095bc0 100644 --- a/src/tools/clippy/tests/ui/useless_asref.fixed +++ b/src/tools/clippy/tests/ui/useless_asref.fixed @@ -2,7 +2,9 @@ #![allow( clippy::explicit_auto_deref, clippy::uninlined_format_args, - clippy::needless_pass_by_ref_mut + clippy::map_clone, + clippy::needless_pass_by_ref_mut, + clippy::redundant_closure )] use std::fmt::Debug; @@ -132,6 +134,16 @@ fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) { foo_rt(mru.as_ref()); } +fn foo() { + let x = Some(String::new()); + let z = x.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.clone(); + //~^ ERROR: this call to `as_ref.map(...)` does nothing +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs index 0996218076b..504dc1f5cbf 100644 --- a/src/tools/clippy/tests/ui/useless_asref.rs +++ b/src/tools/clippy/tests/ui/useless_asref.rs @@ -2,7 +2,9 @@ #![allow( clippy::explicit_auto_deref, clippy::uninlined_format_args, - clippy::needless_pass_by_ref_mut + clippy::map_clone, + clippy::needless_pass_by_ref_mut, + clippy::redundant_closure )] use std::fmt::Debug; @@ -132,6 +134,16 @@ fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) { foo_rt(mru.as_ref()); } +fn foo() { + let x = Some(String::new()); + let z = x.as_ref().map(String::clone); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.as_ref().map(|z| z.clone()); + //~^ ERROR: this call to `as_ref.map(...)` does nothing + let z = x.as_ref().map(|z| String::clone(z)); + //~^ ERROR: this call to `as_ref.map(...)` does nothing +} + fn main() { not_ok(); ok(); diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr index 163eb7b1437..deb5d90f2f6 100644 --- a/src/tools/clippy/tests/ui/useless_asref.stderr +++ b/src/tools/clippy/tests/ui/useless_asref.stderr @@ -1,5 +1,5 @@ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:46:18 + --> $DIR/useless_asref.rs:48:18 | LL | foo_rstr(rstr.as_ref()); | ^^^^^^^^^^^^^ help: try: `rstr` @@ -11,64 +11,82 @@ LL | #![deny(clippy::useless_asref)] | ^^^^^^^^^^^^^^^^^^^^^ error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:48:20 + --> $DIR/useless_asref.rs:50:20 | LL | foo_rslice(rslice.as_ref()); | ^^^^^^^^^^^^^^^ help: try: `rslice` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:52:21 + --> $DIR/useless_asref.rs:54:21 | LL | foo_mrslice(mrslice.as_mut()); | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:54:20 + --> $DIR/useless_asref.rs:56:20 | LL | foo_rslice(mrslice.as_ref()); | ^^^^^^^^^^^^^^^^ help: try: `mrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:61:20 + --> $DIR/useless_asref.rs:63:20 | LL | foo_rslice(rrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^ help: try: `rrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:63:18 + --> $DIR/useless_asref.rs:65:18 | LL | foo_rstr(rrrrrstr.as_ref()); | ^^^^^^^^^^^^^^^^^ help: try: `rrrrrstr` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:68:21 + --> $DIR/useless_asref.rs:70:21 | LL | foo_mrslice(mrrrrrslice.as_mut()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:70:20 + --> $DIR/useless_asref.rs:72:20 | LL | foo_rslice(mrrrrrslice.as_ref()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `mrrrrrslice` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:74:16 + --> $DIR/useless_asref.rs:76:16 | LL | foo_rrrrmr((&&&&MoreRef).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&&&&MoreRef)` error: this call to `as_mut` does nothing - --> $DIR/useless_asref.rs:124:13 + --> $DIR/useless_asref.rs:126:13 | LL | foo_mrt(mrt.as_mut()); | ^^^^^^^^^^^^ help: try: `mrt` error: this call to `as_ref` does nothing - --> $DIR/useless_asref.rs:126:12 + --> $DIR/useless_asref.rs:128:12 | LL | foo_rt(mrt.as_ref()); | ^^^^^^^^^^^^ help: try: `mrt` -error: aborting due to 11 previous errors +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:139:13 + | +LL | let z = x.as_ref().map(String::clone); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:141:13 + | +LL | let z = x.as_ref().map(|z| z.clone()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` + +error: this call to `as_ref.map(...)` does nothing + --> $DIR/useless_asref.rs:143:13 + | +LL | let z = x.as_ref().map(|z| String::clone(z)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.clone()` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index ed8387b7eb2..ce00fde2f99 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -241,7 +241,7 @@ mod issue11300 { foo2::<(), _>([1, 2, 3].into_iter()); // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, - // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unnecessary. foo3([1, 2, 3]); } @@ -253,7 +253,7 @@ mod issue11300 { S1.foo([1, 2]); - // ICE that occured in itertools + // ICE that occurred in itertools trait Itertools { fn interleave_shortest<J>(self, other: J) where diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 991a7762fc6..39979619586 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -241,7 +241,7 @@ mod issue11300 { foo2::<(), _>([1, 2, 3].into_iter()); // This should lint. Removing the `.into_iter()` means that `I` gets substituted with `[i32; 3]`, - // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unncessary. + // and `i32: Helper2<[i32, 3]>` is true, so this call is indeed unnecessary. foo3([1, 2, 3].into_iter()); } @@ -253,7 +253,7 @@ mod issue11300 { S1.foo([1, 2].into_iter()); - // ICE that occured in itertools + // ICE that occurred in itertools trait Itertools { fn interleave_shortest<J>(self, other: J) where diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed index 81b8bd7da77..b318fd42f7c 100644 --- a/src/tools/clippy/tests/ui/vec.fixed +++ b/src/tools/clippy/tests/ui/vec.fixed @@ -210,3 +210,10 @@ fn issue11861() { // should not lint m!(vec![1]); } + +fn issue_11958() { + fn f(_s: &[String]) {} + + // should not lint, `String` is not `Copy` + f(&vec!["test".to_owned(); 2]); +} diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs index 5aca9b2925c..08ad6efa37f 100644 --- a/src/tools/clippy/tests/ui/vec.rs +++ b/src/tools/clippy/tests/ui/vec.rs @@ -210,3 +210,10 @@ fn issue11861() { // should not lint m!(vec![1]); } + +fn issue_11958() { + fn f(_s: &[String]) {} + + // should not lint, `String` is not `Copy` + f(&vec!["test".to_owned(); 2]); +} diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 96085bcf9ee..a05765b3981 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -28,7 +28,6 @@ users_on_vacation = ["blyxyas"] "*" = [ "@Manishearth", "@llogiq", - "@giraffate", "@xFrednet", "@Alexendoo", "@dswij", diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 61aa9c12822..ebc704f785f 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -65b323b168daa6c723a8a5b4ddf939f3b12e0329 +f1f8687b06a5908dd096f51da32347b3313279db diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index aead55a6996..e8e10f64ad2 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -165,7 +165,6 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { level: SymbolExportLevel::C, kind: SymbolExportKind::Text, used: false, - used_compiler: false, }, )) }), diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 7045a7dd9ce..31226cf8c30 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_ast::token::TokenKind; use rustc_ast::{ast, attr, ptr}; -use rustc_errors::Diagnostic; +use rustc_errors::DiagnosticBuilder; use rustc_parse::{new_parser_from_file, parser::Parser as RawParser}; use rustc_span::{sym, Span}; use thin_vec::ThinVec; @@ -65,7 +65,7 @@ impl<'a> ParserBuilder<'a> { fn parser( sess: &'a rustc_session::parse::ParseSess, input: Input, - ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> { + ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<DiagnosticBuilder<'a>>>> { match input { Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || { new_parser_from_file(sess, file, None) diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index f4fb5073dfd..6dc3eac44d4 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -4,7 +4,9 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter}; use rustc_errors::translation::Translate; -use rustc_errors::{ColorConfig, DiagCtxt, Diagnostic, Level as DiagnosticLevel}; +use rustc_errors::{ + ColorConfig, DiagCtxt, Diagnostic, DiagnosticBuilder, Level as DiagnosticLevel, +}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ source_map::{FilePathMapping, SourceMap}, @@ -283,9 +285,9 @@ impl ParseSess { // Methods that should be restricted within the parse module. impl ParseSess { - pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) { + pub(super) fn emit_diagnostics(&self, diagnostics: Vec<DiagnosticBuilder<'_>>) { for diagnostic in diagnostics { - self.parse_sess.dcx.emit_diagnostic(diagnostic); + diagnostic.emit(); } } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 62d48315d43..c198c116282 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -194,6 +194,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "aho-corasick", "allocator-api2", // FIXME: only appears in Cargo.lock due to https://github.com/rust-lang/cargo/issues/10801 "annotate-snippets", + "anstyle", "ar_archive_writer", "arrayvec", "autocfg", @@ -391,7 +392,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "windows_x86_64_gnullvm", "windows_x86_64_msvc", "writeable", - "yansi-term", // this is a false-positive: it's only used by rustfmt, but because it's enabled through a feature, tidy thinks it's used by rustc as well. "yoke", "yoke-derive", "zerocopy", diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 32e271d4a56..ba569078f3d 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1849; -const ROOT_ENTRY_LIMIT: usize = 868; +const ROOT_ENTRY_LIMIT: usize = 869; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/codegen/issues/issue-119422.rs b/tests/codegen/issues/issue-119422.rs new file mode 100644 index 00000000000..9c99d96317d --- /dev/null +++ b/tests/codegen/issues/issue-119422.rs @@ -0,0 +1,83 @@ +//! This test checks that compiler don't generate useless compares to zeros +//! for NonZero integer types. + +// compile-flags: -O --edition=2021 -Zmerge-functions=disabled +// only-64bit (because the LLVM type of i64 for usize shows up) + +#![crate_type = "lib"] + +use core::num::*; +use core::ptr::NonNull; + +// CHECK-LABEL: @check_non_null +#[no_mangle] +pub fn check_non_null(x: NonNull<u8>) -> bool { + // CHECK: ret i1 false + x.as_ptr().is_null() +} + +// CHECK-LABEL: @equals_zero_is_false_u8 +#[no_mangle] +pub fn equals_zero_is_false_u8(x: NonZeroU8) -> bool { + // CHECK-NOT: br + // CHECK: ret i1 false + // CHECK-NOT: br + x.get() == 0 +} + +// CHECK-LABEL: @not_equals_zero_is_true_u8 +#[no_mangle] +pub fn not_equals_zero_is_true_u8(x: NonZeroU8) -> bool { + // CHECK-NOT: br + // CHECK: ret i1 true + // CHECK-NOT: br + x.get() != 0 +} + +// CHECK-LABEL: @equals_zero_is_false_i8 +#[no_mangle] +pub fn equals_zero_is_false_i8(x: NonZeroI8) -> bool { + // CHECK-NOT: br + // CHECK: ret i1 false + // CHECK-NOT: br + x.get() == 0 +} + +// CHECK-LABEL: @not_equals_zero_is_true_i8 +#[no_mangle] +pub fn not_equals_zero_is_true_i8(x: NonZeroI8) -> bool { + // CHECK-NOT: br + // CHECK: ret i1 true + // CHECK-NOT: br + x.get() != 0 +} + +// CHECK-LABEL: @usize_try_from_u32 +#[no_mangle] +pub fn usize_try_from_u32(x: NonZeroU32) -> NonZeroUsize { + // CHECK-NOT: br + // CHECK: zext i32 %{{.*}} to i64 + // CHECK-NOT: br + // CHECK: ret i64 + x.try_into().unwrap() +} + +// CHECK-LABEL: @isize_try_from_i32 +#[no_mangle] +pub fn isize_try_from_i32(x: NonZeroI32) -> NonZeroIsize { + // CHECK-NOT: br + // CHECK: sext i32 %{{.*}} to i64 + // CHECK-NOT: br + // CHECK: ret i64 + x.try_into().unwrap() +} + +// CHECK-LABEL: @u64_from_nonzero_is_not_zero +#[no_mangle] +pub fn u64_from_nonzero_is_not_zero(x: NonZeroU64)->bool { + // CHECK-NOT: br + // CHECK: ret i1 false + // CHECK-NOT: br + let v: u64 = x.into(); + v == 0 +} diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff index 6e681b4f970..5f2a096bb1f 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-abort.diff @@ -61,7 +61,6 @@ + StorageDead(_10); + StorageDead(_11); + nop; - nop; StorageDead(_8); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff index e987969d313..a49546f158c 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.panic-unwind.diff @@ -61,7 +61,6 @@ + StorageDead(_10); + StorageDead(_11); + nop; - nop; StorageDead(_8); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff index 6e681b4f970..5f2a096bb1f 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-abort.diff @@ -61,7 +61,6 @@ + StorageDead(_10); + StorageDead(_11); + nop; - nop; StorageDead(_8); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff index e987969d313..a49546f158c 100644 --- a/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff +++ b/tests/mir-opt/pre-codegen/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.panic-unwind.diff @@ -61,7 +61,6 @@ + StorageDead(_10); + StorageDead(_11); + nop; - nop; StorageDead(_8); StorageDead(_3); StorageDead(_1); diff --git a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-abort.diff b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-abort.diff index 64a435f2245..c3076fb67c2 100644 --- a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-abort.diff +++ b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-abort.diff @@ -6,8 +6,8 @@ let _1: (); bb0: { -- switchInt(const false) -> [0: bb3, otherwise: bb1]; -+ goto -> bb3; +- switchInt(const false) -> [0: bb2, otherwise: bb1]; ++ goto -> bb2; } bb1: { @@ -15,14 +15,6 @@ } bb2: { - goto -> bb4; - } - - bb3: { - goto -> bb4; - } - - bb4: { return; } } diff --git a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-unwind.diff b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-unwind.diff index 146e00686ed..6c346e20e58 100644 --- a/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-unwind.diff +++ b/tests/mir-opt/simplify_if.main.SimplifyConstCondition-after-const-prop.panic-unwind.diff @@ -6,8 +6,8 @@ let _1: (); bb0: { -- switchInt(const false) -> [0: bb3, otherwise: bb1]; -+ goto -> bb3; +- switchInt(const false) -> [0: bb2, otherwise: bb1]; ++ goto -> bb2; } bb1: { @@ -15,14 +15,6 @@ } bb2: { - goto -> bb4; - } - - bb3: { - goto -> bb4; - } - - bb4: { return; } } diff --git a/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff b/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff index 825babe7994..d7ade041e4c 100644 --- a/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff +++ b/tests/mir-opt/simplify_match.main.GVN.panic-abort.diff @@ -11,19 +11,15 @@ bb0: { _2 = const false; -- switchInt(_2) -> [0: bb1, otherwise: bb2]; -+ switchInt(const false) -> [0: bb1, otherwise: bb2]; +- switchInt(_2) -> [0: bb2, otherwise: bb1]; ++ switchInt(const false) -> [0: bb2, otherwise: bb1]; } bb1: { - goto -> bb3; + _0 = noop() -> [return: bb2, unwind unreachable]; } bb2: { - _0 = noop() -> [return: bb3, unwind unreachable]; - } - - bb3: { return; } } diff --git a/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff b/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff index 24ab6c39788..931c5c43c4c 100644 --- a/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff +++ b/tests/mir-opt/simplify_match.main.GVN.panic-unwind.diff @@ -11,19 +11,15 @@ bb0: { _2 = const false; -- switchInt(_2) -> [0: bb1, otherwise: bb2]; -+ switchInt(const false) -> [0: bb1, otherwise: bb2]; +- switchInt(_2) -> [0: bb2, otherwise: bb1]; ++ switchInt(const false) -> [0: bb2, otherwise: bb1]; } bb1: { - goto -> bb3; + _0 = noop() -> [return: bb2, unwind continue]; } bb2: { - _0 = noop() -> [return: bb3, unwind continue]; - } - - bb3: { return; } } diff --git a/tests/run-make/libtest-padding/Makefile b/tests/run-make/libtest-padding/Makefile new file mode 100644 index 00000000000..42bc1192925 --- /dev/null +++ b/tests/run-make/libtest-padding/Makefile @@ -0,0 +1,14 @@ +# ignore-cross-compile because we run the compiled code +# needs-unwind because #[bench] and -Cpanic=abort requires -Zpanic-abort-tests +include ../tools.mk + +NORMALIZE=sed 's%[0-9,]\{1,\} ns/iter (+/- [0-9,]\{1,\})%?? ns/iter (+/- ??)%' | sed 's%finished in [0-9\.]\{1,\}%finished in ??%' + +all: + $(RUSTC) --test tests.rs + + $(call RUN,tests) --test-threads=1 | $(NORMALIZE) > "$(TMPDIR)"/test.stdout + $(RUSTC_TEST_OP) "$(TMPDIR)"/test.stdout test.stdout + + $(call RUN,tests) --test-threads=1 --bench | $(NORMALIZE) > "$(TMPDIR)"/bench.stdout + $(RUSTC_TEST_OP) "$(TMPDIR)"/bench.stdout bench.stdout diff --git a/tests/run-make/libtest-padding/bench.stdout b/tests/run-make/libtest-padding/bench.stdout new file mode 100644 index 00000000000..3d55401c93a --- /dev/null +++ b/tests/run-make/libtest-padding/bench.stdout @@ -0,0 +1,9 @@ + +running 4 tests +test short_test_name ... ignored +test this_is_a_really_long_test_name ... ignored +test short_bench_name ... bench: ?? ns/iter (+/- ??) +test this_is_a_really_long_bench_name ... bench: ?? ns/iter (+/- ??) + +test result: ok. 0 passed; 0 failed; 2 ignored; 2 measured; 0 filtered out; finished in ??s + diff --git a/tests/run-make/libtest-padding/test.stdout b/tests/run-make/libtest-padding/test.stdout new file mode 100644 index 00000000000..8ffc810f6cd --- /dev/null +++ b/tests/run-make/libtest-padding/test.stdout @@ -0,0 +1,9 @@ + +running 4 tests +test short_bench_name ... ok +test short_test_name ... ok +test this_is_a_really_long_bench_name ... ok +test this_is_a_really_long_test_name ... ok + +test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in ??s + diff --git a/tests/run-make/libtest-padding/tests.rs b/tests/run-make/libtest-padding/tests.rs new file mode 100644 index 00000000000..cadf07237e9 --- /dev/null +++ b/tests/run-make/libtest-padding/tests.rs @@ -0,0 +1,18 @@ +#![feature(test)] +extern crate test; + +#[test] +fn short_test_name() {} + +#[test] +fn this_is_a_really_long_test_name() {} + +#[bench] +fn short_bench_name(b: &mut test::Bencher) { + b.iter(|| 1); +} + +#[bench] +fn this_is_a_really_long_bench_name(b: &mut test::Bencher) { + b.iter(|| 1); +} diff --git a/tests/run-make/no-builtins-lto/Makefile b/tests/run-make/no-builtins-lto/Makefile index c7be4836466..c8f05d9918b 100644 --- a/tests/run-make/no-builtins-lto/Makefile +++ b/tests/run-make/no-builtins-lto/Makefile @@ -1,15 +1,9 @@ include ../tools.mk -# only-x86_64 - -# We want to check that `no_builtins` is correctly participating in LTO. -# First, verify that the `foo::foo` symbol can be found when linking. -# Next, verify that `memcpy` can be customized using `no_builtins` under LTO. -# Others will use the built-in memcpy. - all: - $(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 foo.rs - $(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 no_builtins.rs - $(RUSTC) main.rs -C lto -C opt-level=2 -C debuginfo=0 -C save-temps -C metadata=1 -C codegen-units=1 - "$(LLVM_BIN_DIR)"/llvm-dis $(TMPDIR)/main.main.*-cgu.0.rcgu.lto.input.bc -o $(TMPDIR)/lto.ll - cat "$(TMPDIR)"/lto.ll | "$(LLVM_FILECHECK)" filecheck.lto.txt + # Compile a `#![no_builtins]` rlib crate + $(RUSTC) no_builtins.rs + # Build an executable that depends on that crate using LTO. The no_builtins crate doesn't + # participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by + # grepping the linker arguments. + $(RUSTC) main.rs -C lto --print link-args | $(CGREP) 'libno_builtins.rlib' diff --git a/tests/run-make/no-builtins-lto/filecheck.lto.txt b/tests/run-make/no-builtins-lto/filecheck.lto.txt deleted file mode 100644 index 79dc3a51501..00000000000 --- a/tests/run-make/no-builtins-lto/filecheck.lto.txt +++ /dev/null @@ -1,17 +0,0 @@ -CHECK: define{{.*}} void @bar -CHECK-NEXT: call void @no_builtins -CHECK-NEXT: call void @llvm.memcpy - -CHECK: define{{.*}} i32 @main -CHECK: call void @bar - -CHECK: define{{.*}} void @foo -CHECK-NEXT: call void @llvm.memcpy - -CHECK: define{{.*}} void @no_builtins -CHECK-SAME: #[[ATTR:[0-9]+]] { -CHECK: call void @foo -CHECK-NEXT: call{{.*}} @memcpy - -CHECK: attributes #[[ATTR]] -CHECK-SAME: no-builtins diff --git a/tests/run-make/no-builtins-lto/foo.rs b/tests/run-make/no-builtins-lto/foo.rs deleted file mode 100644 index f09ac40b152..00000000000 --- a/tests/run-make/no-builtins-lto/foo.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![feature(lang_items, no_core)] -#![no_std] -#![no_core] -#![crate_type = "lib"] - -#[inline(never)] -#[no_mangle] -pub unsafe fn foo(dest: *mut u8, src: *const u8) { - // should call `@llvm.memcpy`. - memcpy(dest, src, 1024); -} - -#[no_mangle] -#[inline(never)] -pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, _n: usize) -> *mut u8 { - *dest = 0; - return src as *mut u8; -} - -#[lang = "sized"] -trait Sized {} -#[lang = "copy"] -trait Copy {} -impl Copy for *mut u8 {} -impl Copy for *const u8 {} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} diff --git a/tests/run-make/no-builtins-lto/main.rs b/tests/run-make/no-builtins-lto/main.rs index 4421a2afbce..890c999c8cc 100644 --- a/tests/run-make/no-builtins-lto/main.rs +++ b/tests/run-make/no-builtins-lto/main.rs @@ -1,29 +1,3 @@ -#![feature(no_core, start, lang_items)] -#![no_std] -// We use `no_core` to reduce the LTO products is small enough. -#![no_core] - extern crate no_builtins; -extern crate foo; - -#[cfg_attr(unix, link(name = "c"))] -#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] -extern "C" {} - -#[start] -fn main(_: isize, p: *const *const u8) -> isize { - // Make sure the symbols are retained. - unsafe { bar(*p as *mut u8, *p); } - 0 -} - -#[no_mangle] -#[inline(never)] -pub unsafe extern "C" fn bar(dest: *mut u8, src: *const u8) { - no_builtins::no_builtins(dest, src); - // should call `@llvm.memcpy` - foo::memcpy(dest, src, 1024); -} -#[lang = "eh_personality"] -fn eh_personality() {} +fn main() {} diff --git a/tests/run-make/no-builtins-lto/no_builtins.rs b/tests/run-make/no-builtins-lto/no_builtins.rs index 33ed68e3aee..5d001031a57 100644 --- a/tests/run-make/no-builtins-lto/no_builtins.rs +++ b/tests/run-make/no-builtins-lto/no_builtins.rs @@ -1,15 +1,2 @@ -#![feature(lang_items, no_core)] -#![no_std] -#![no_core] #![crate_type = "lib"] #![no_builtins] - -extern crate foo; - -#[no_mangle] -pub unsafe fn no_builtins(dest: *mut u8, src: *const u8) { - // There should be no "undefined reference to `foo::foo'". - foo::foo(dest, src); - // should call `@memcpy` instead of `@llvm.memcpy`. - foo::memcpy(dest, src, 1024); -} diff --git a/tests/run-make/no-builtins-symbols/Makefile b/tests/run-make/no-builtins-symbols/Makefile deleted file mode 100644 index 4bb35c1d486..00000000000 --- a/tests/run-make/no-builtins-symbols/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -include ../tools.mk - -# only-x86_64-unknown-linux-gnu - -all: - $(RUSTC) main.rs -o $(TMPDIR)/main - [ "$$("$(LLVM_BIN_DIR)"/llvm-nm -U $(TMPDIR)/main | grep -c __fixunssfti)" -eq "0" ] diff --git a/tests/run-make/no-builtins-symbols/main.rs b/tests/run-make/no-builtins-symbols/main.rs deleted file mode 100644 index f328e4d9d04..00000000000 --- a/tests/run-make/no-builtins-symbols/main.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/tests/run-make/wasm-builtins-import/Makefile b/tests/run-make/wasm-spurious-import/Makefile index ff9dfeac6d0..ff9dfeac6d0 100644 --- a/tests/run-make/wasm-builtins-import/Makefile +++ b/tests/run-make/wasm-spurious-import/Makefile diff --git a/tests/run-make/wasm-builtins-import/main.rs b/tests/run-make/wasm-spurious-import/main.rs index 5eb99df6ff7..fcbead5e28b 100644 --- a/tests/run-make/wasm-builtins-import/main.rs +++ b/tests/run-make/wasm-spurious-import/main.rs @@ -8,8 +8,7 @@ fn my_panic(_info: &core::panic::PanicInfo) -> ! { #[no_mangle] pub fn multer(a: i128, b: i128) -> i128 { - // Trigger usage of the __multi3 compiler intrinsic which then leads to an imported function - // such as panic or __multi3 (externally defined) in case of a bug. We verify that - // no imports exist in our verifier. + // Trigger usage of the __multi3 compiler intrinsic which then leads to an imported + // panic function in case of a bug. We verify that no imports exist in our verifier. a * b } diff --git a/tests/run-make/wasm-builtins-import/verify.js b/tests/run-make/wasm-spurious-import/verify.js index d3b2101b662..d3b2101b662 100644 --- a/tests/run-make/wasm-builtins-import/verify.js +++ b/tests/run-make/wasm-spurious-import/verify.js diff --git a/tests/rustdoc-ui/ice-bug-report-url.stderr b/tests/rustdoc-ui/ice-bug-report-url.stderr index 869fcd20fac..06a52691310 100644 --- a/tests/rustdoc-ui/ice-bug-report-url.stderr +++ b/tests/rustdoc-ui/ice-bug-report-url.stderr @@ -1,4 +1,4 @@ -error: expected one of `->`, `where`, or `{`, found `<eof>` +error: internal compiler error: expected one of `->`, `where`, or `{`, found `<eof>` --> $DIR/ice-bug-report-url.rs:14:10 | LL | fn wrong() diff --git a/tests/rustdoc-ui/unable-fulfill-trait.rs b/tests/rustdoc-ui/unable-fulfill-trait.rs index 10887ab1903..a69f74b09ac 100644 --- a/tests/rustdoc-ui/unable-fulfill-trait.rs +++ b/tests/rustdoc-ui/unable-fulfill-trait.rs @@ -4,7 +4,6 @@ pub struct Foo<'a, 'b, T> { field1: dyn Bar<'a, 'b,>, //~^ ERROR //~| ERROR - //~| ERROR } pub trait Bar<'x, 's, U> diff --git a/tests/rustdoc-ui/unable-fulfill-trait.stderr b/tests/rustdoc-ui/unable-fulfill-trait.stderr index d7735a4fd11..72f35cb9224 100644 --- a/tests/rustdoc-ui/unable-fulfill-trait.stderr +++ b/tests/rustdoc-ui/unable-fulfill-trait.stderr @@ -5,7 +5,7 @@ LL | field1: dyn Bar<'a, 'b,>, | ^^^ expected 1 generic argument | note: trait defined here, with 1 generic parameter: `U` - --> $DIR/unable-fulfill-trait.rs:10:11 + --> $DIR/unable-fulfill-trait.rs:9:11 | LL | pub trait Bar<'x, 's, U> | ^^^ - @@ -20,24 +20,7 @@ error[E0227]: ambiguous lifetime bound, explicit lifetime bound required LL | field1: dyn Bar<'a, 'b,>, | ^^^^^^^^^^^^^^^^ -error[E0478]: lifetime bound not satisfied - --> $DIR/unable-fulfill-trait.rs:4:13 - | -LL | field1: dyn Bar<'a, 'b,>, - | ^^^^^^^^^^^^^^^^ - | -note: lifetime parameter instantiated with the lifetime `'b` as defined here - --> $DIR/unable-fulfill-trait.rs:3:20 - | -LL | pub struct Foo<'a, 'b, T> { - | ^^ -note: but lifetime parameter must outlive the lifetime `'a` as defined here - --> $DIR/unable-fulfill-trait.rs:3:16 - | -LL | pub struct Foo<'a, 'b, T> { - | ^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0107, E0227, E0478. +Some errors have detailed explanations: E0107, E0227. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr index 3acec9c085a..e3eddf489b0 100644 --- a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr @@ -30,7 +30,7 @@ LL | type Item = &[T]; = help: add `#![feature(inherent_associated_types)]` to the crate attributes to enable error[E0223]: ambiguous associated type - --> $DIR/issue-109071.rs:16:22 + --> $DIR/issue-109071.rs:15:22 | LL | fn T() -> Option<Self::Item> {} | ^^^^^^^^^^ diff --git a/tests/ui/associated-inherent-types/issue-109071.rs b/tests/ui/associated-inherent-types/issue-109071.rs index a897aaebc58..cbe8cce0924 100644 --- a/tests/ui/associated-inherent-types/issue-109071.rs +++ b/tests/ui/associated-inherent-types/issue-109071.rs @@ -9,13 +9,11 @@ impl<T> Windows { //~ ERROR: missing generics for struct `Windows` //[no_gate]~^ ERROR: inherent associated types are unstable fn next() -> Option<Self::Item> {} - //[with_gate]~^ ERROR type annotations needed } impl<T> Windows<T> { fn T() -> Option<Self::Item> {} - //[no_gate]~^ ERROR: ambiguous associated type - //[with_gate]~^^ ERROR type annotations needed + //~^ ERROR: ambiguous associated type } fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr index d413c65dccb..a7d17e2d5eb 100644 --- a/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.with_gate.stderr @@ -20,19 +20,20 @@ help: add missing generic argument LL | impl<T> Windows<T> { | +++ -error[E0282]: type annotations needed - --> $DIR/issue-109071.rs:11:18 - | -LL | fn next() -> Option<Self::Item> {} - | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` - -error[E0282]: type annotations needed - --> $DIR/issue-109071.rs:16:15 +error[E0223]: ambiguous associated type + --> $DIR/issue-109071.rs:15:22 | LL | fn T() -> Option<Self::Item> {} - | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` + | ^^^^^^^^^^ + | +help: use fully-qualified syntax + | +LL | fn T() -> Option<<Windows<T> as IntoAsyncIterator>::Item> {} + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | fn T() -> Option<<Windows<T> as IntoIterator>::Item> {} + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0282, E0637. +Some errors have detailed explanations: E0107, E0223, E0637. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/associated-types/issue-23595-1.rs b/tests/ui/associated-types/issue-23595-1.rs index 9222f5b6650..579fde34f53 100644 --- a/tests/ui/associated-types/issue-23595-1.rs +++ b/tests/ui/associated-types/issue-23595-1.rs @@ -5,9 +5,8 @@ use std::ops::Index; trait Hierarchy { type Value; type ChildKey; - type Children = dyn Index<Self::ChildKey, Output=dyn Hierarchy>; + type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>; //~^ ERROR: the value of the associated types - //~| ERROR: the size for values of type fn data(&self) -> Option<(Self::Value, Self::Children)>; } diff --git a/tests/ui/associated-types/issue-23595-1.stderr b/tests/ui/associated-types/issue-23595-1.stderr index 46906ab3fb7..694b68ef090 100644 --- a/tests/ui/associated-types/issue-23595-1.stderr +++ b/tests/ui/associated-types/issue-23595-1.stderr @@ -1,27 +1,13 @@ error[E0191]: the value of the associated types `Value`, `ChildKey` and `Children` in `Hierarchy` must be specified - --> $DIR/issue-23595-1.rs:8:58 + --> $DIR/issue-23595-1.rs:8:60 | LL | type Value; | ---------- `Value` defined here LL | type ChildKey; | ------------- `ChildKey` defined here -LL | type Children = dyn Index<Self::ChildKey, Output=dyn Hierarchy>; - | ------------- `Children` defined here ^^^^^^^^^ help: specify the associated types: `Hierarchy<Value = Type, ChildKey = Type, Children = Type>` +LL | type Children = dyn Index<Self::ChildKey, Output = dyn Hierarchy>; + | ------------- `Children` defined here ^^^^^^^^^ help: specify the associated types: `Hierarchy<Value = Type, ChildKey = Type, Children = Type>` -error[E0277]: the size for values of type `(dyn Index<<Self as Hierarchy>::ChildKey, Output = (dyn Hierarchy + 'static)> + 'static)` cannot be known at compilation time - --> $DIR/issue-23595-1.rs:8:21 - | -LL | type Children = dyn Index<Self::ChildKey, Output=dyn Hierarchy>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `(dyn Index<<Self as Hierarchy>::ChildKey, Output = (dyn Hierarchy + 'static)> + 'static)` -note: required by a bound in `Hierarchy::Children` - --> $DIR/issue-23595-1.rs:8:5 - | -LL | type Children = dyn Index<Self::ChildKey, Output=dyn Hierarchy>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Hierarchy::Children` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0191, E0277. -For more information about an error, try `rustc --explain E0191`. +For more information about this error, try `rustc --explain E0191`. diff --git a/tests/ui/check-cfg/compact-values.stderr b/tests/ui/check-cfg/compact-values.stderr index 10276af4d8f..661ee7bff52 100644 --- a/tests/ui/check-cfg/compact-values.stderr +++ b/tests/ui/check-cfg/compact-values.stderr @@ -5,7 +5,6 @@ LL | #[cfg(target(os = "linux", pointer_width = "X"))] | ^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_pointer_width` are: `16`, `32`, `64` - = help: to expect this configuration use `--check-cfg=cfg(target_pointer_width, values("X"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index 27af8212026..040f727a840 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -18,7 +18,6 @@ LL | #[cfg(test = "value")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("value"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition name: `feature` diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index a5aa80ef8e5..3cde11cf0f8 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -18,7 +18,6 @@ LL | #[cfg(test = "value")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("value"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index a5aa80ef8e5..3cde11cf0f8 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -18,7 +18,6 @@ LL | #[cfg(test = "value")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("value"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr index 0a7bd81b8aa..e8cf29ae982 100644 --- a/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-values.empty_cfg.stderr @@ -7,7 +7,6 @@ LL | #[cfg(test = "value")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("value"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-values.without_names.stderr b/tests/ui/check-cfg/exhaustive-values.without_names.stderr index 0a7bd81b8aa..e8cf29ae982 100644 --- a/tests/ui/check-cfg/exhaustive-values.without_names.stderr +++ b/tests/ui/check-cfg/exhaustive-values.without_names.stderr @@ -7,7 +7,6 @@ LL | #[cfg(test = "value")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("value"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/no-expected-values.empty.stderr b/tests/ui/check-cfg/no-expected-values.empty.stderr index ae55c95c0b1..0c0dbe9bac6 100644 --- a/tests/ui/check-cfg/no-expected-values.empty.stderr +++ b/tests/ui/check-cfg/no-expected-values.empty.stderr @@ -20,7 +20,6 @@ LL | #[cfg(test = "foo")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("foo"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/no-expected-values.mixed.stderr b/tests/ui/check-cfg/no-expected-values.mixed.stderr index ae55c95c0b1..0c0dbe9bac6 100644 --- a/tests/ui/check-cfg/no-expected-values.mixed.stderr +++ b/tests/ui/check-cfg/no-expected-values.mixed.stderr @@ -20,7 +20,6 @@ LL | #[cfg(test = "foo")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("foo"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/no-expected-values.simple.stderr b/tests/ui/check-cfg/no-expected-values.simple.stderr index ae55c95c0b1..0c0dbe9bac6 100644 --- a/tests/ui/check-cfg/no-expected-values.simple.stderr +++ b/tests/ui/check-cfg/no-expected-values.simple.stderr @@ -20,7 +20,6 @@ LL | #[cfg(test = "foo")] | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("foo"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 4f708e62cd3..9d134dcfcfd 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -7,7 +7,6 @@ LL | debug_assertions = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `debug_assertions` - = help: to expect this configuration use `--check-cfg=cfg(debug_assertions, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default @@ -20,7 +19,6 @@ LL | doc = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `doc` - = help: to expect this configuration use `--check-cfg=cfg(doc, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -32,7 +30,6 @@ LL | doctest = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `doctest` - = help: to expect this configuration use `--check-cfg=cfg(doctest, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -44,7 +41,6 @@ LL | miri = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `miri` - = help: to expect this configuration use `--check-cfg=cfg(miri, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -56,7 +52,6 @@ LL | overflow_checks = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `overflow_checks` - = help: to expect this configuration use `--check-cfg=cfg(overflow_checks, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -66,7 +61,6 @@ LL | panic = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `panic` are: `abort`, `unwind` - = help: to expect this configuration use `--check-cfg=cfg(panic, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -78,7 +72,6 @@ LL | proc_macro = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `proc_macro` - = help: to expect this configuration use `--check-cfg=cfg(proc_macro, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -88,7 +81,6 @@ LL | relocation_model = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `relocation_model` are: `dynamic-no-pic`, `pic`, `pie`, `ropi`, `ropi-rwpi`, `rwpi`, `static` - = help: to expect this configuration use `--check-cfg=cfg(relocation_model, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -98,7 +90,6 @@ LL | sanitize = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `sanitize` are: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread` - = help: to expect this configuration use `--check-cfg=cfg(sanitize, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -108,7 +99,6 @@ LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elf`, `fortanix`, `ilp32`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, `x32` - = help: to expect this configuration use `--check-cfg=cfg(target_abi, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -118,7 +108,6 @@ LL | target_arch = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_arch` are: `aarch64`, `arm`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64` - = help: to expect this configuration use `--check-cfg=cfg(target_arch, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -128,7 +117,6 @@ LL | target_endian = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_endian` are: `big`, `little` - = help: to expect this configuration use `--check-cfg=cfg(target_endian, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -138,7 +126,6 @@ LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_env` are: ``, `eabihf`, `gnu`, `gnueabihf`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `psx`, `relibc`, `sgx`, `uclibc` - = help: to expect this configuration use `--check-cfg=cfg(target_env, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -148,7 +135,6 @@ LL | target_family = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_family` are: `unix`, `wasm`, `windows` - = help: to expect this configuration use `--check-cfg=cfg(target_family, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -158,7 +144,6 @@ LL | target_feature = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fast-unaligned-access`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt` - = help: to expect this configuration use `--check-cfg=cfg(target_feature, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -168,7 +153,6 @@ LL | target_has_atomic = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_has_atomic` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr` - = help: to expect this configuration use `--check-cfg=cfg(target_has_atomic, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -178,7 +162,6 @@ LL | target_has_atomic_equal_alignment = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_has_atomic_equal_alignment` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr` - = help: to expect this configuration use `--check-cfg=cfg(target_has_atomic_equal_alignment, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -188,7 +171,6 @@ LL | target_has_atomic_load_store = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_has_atomic_load_store` are: (none), `128`, `16`, `32`, `64`, `8`, `ptr` - = help: to expect this configuration use `--check-cfg=cfg(target_has_atomic_load_store, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -198,7 +180,6 @@ LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous` - = help: to expect this configuration use `--check-cfg=cfg(target_os, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -208,7 +189,6 @@ LL | target_pointer_width = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_pointer_width` are: `16`, `32`, `64` - = help: to expect this configuration use `--check-cfg=cfg(target_pointer_width, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -220,7 +200,6 @@ LL | target_thread_local = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `target_thread_local` - = help: to expect this configuration use `--check-cfg=cfg(target_thread_local, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -230,7 +209,6 @@ LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, `wrs` - = help: to expect this configuration use `--check-cfg=cfg(target_vendor, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -242,7 +220,6 @@ LL | test = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `test` - = help: to expect this configuration use `--check-cfg=cfg(test, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -254,7 +231,6 @@ LL | unix = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `unix` - = help: to expect this configuration use `--check-cfg=cfg(unix, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -266,7 +242,6 @@ LL | windows = "_UNEXPECTED_VALUE", | help: remove the value | = note: no expected value for `windows` - = help: to expect this configuration use `--check-cfg=cfg(windows, values("_UNEXPECTED_VALUE"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: unexpected `cfg` condition value: `linuz` @@ -278,7 +253,6 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | help: there is a expected value with a similar name: `"linux"` | = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `openbsd`, `psp`, `redox`, `solaris`, `solid_asp3`, `teeos`, `tvos`, `uefi`, `unknown`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous` - = help: to expect this configuration use `--check-cfg=cfg(target_os, values("linuz"))` = note: see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration warning: 26 warnings emitted diff --git a/tests/ui/const-generics/issues/issue-71381.full.stderr b/tests/ui/const-generics/issues/issue-71381.full.stderr index b6460e0017f..5d780074696 100644 --- a/tests/ui/const-generics/issues/issue-71381.full.stderr +++ b/tests/ui/const-generics/issues/issue-71381.full.stderr @@ -7,7 +7,7 @@ LL | pub fn call_me<Args: Sized, const IDX: usize, const FN: unsafe extern " = note: type parameters may not be used in the type of const parameters error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:23:40 + --> $DIR/issue-71381.rs:22:40 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` diff --git a/tests/ui/const-generics/issues/issue-71381.min.stderr b/tests/ui/const-generics/issues/issue-71381.min.stderr index e16d3b7a8a4..5d780074696 100644 --- a/tests/ui/const-generics/issues/issue-71381.min.stderr +++ b/tests/ui/const-generics/issues/issue-71381.min.stderr @@ -7,29 +7,13 @@ LL | pub fn call_me<Args: Sized, const IDX: usize, const FN: unsafe extern " = note: type parameters may not be used in the type of const parameters error[E0770]: the type of const parameters must not depend on other generic parameters - --> $DIR/issue-71381.rs:23:40 + --> $DIR/issue-71381.rs:22:40 | LL | const FN: unsafe extern "C" fn(Args), | ^^^^ the type must not depend on the parameter `Args` | = note: type parameters may not be used in the type of const parameters -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:14:61 - | -LL | pub fn call_me<Args: Sized, const IDX: usize, const FN: unsafe extern "C" fn(Args)>(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:23:19 - | -LL | const FN: unsafe extern "C" fn(Args), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71381.rs b/tests/ui/const-generics/issues/issue-71381.rs index 8a878efb42a..75ad4545371 100644 --- a/tests/ui/const-generics/issues/issue-71381.rs +++ b/tests/ui/const-generics/issues/issue-71381.rs @@ -12,8 +12,7 @@ unsafe extern "C" fn pass(args: PassArg) { impl Test { pub fn call_me<Args: Sized, const IDX: usize, const FN: unsafe extern "C" fn(Args)>(&self) { - //[min]~^ ERROR: using function pointers as const generic parameters is forbidden - //~^^ ERROR: the type of const parameters must not depend on other generic parameters + //~^ ERROR: the type of const parameters must not depend on other generic parameters self.0 = Self::trampiline::<Args, IDX, FN> as _ } @@ -21,8 +20,7 @@ impl Test { Args: Sized, const IDX: usize, const FN: unsafe extern "C" fn(Args), - //[min]~^ ERROR: using function pointers as const generic parameters is forbidden - //~^^ ERROR: the type of const parameters must not depend on other generic parameters + //~^ ERROR: the type of const parameters must not depend on other generic parameters >( args: Args, ) { diff --git a/tests/ui/const-generics/issues/issue-71611.min.stderr b/tests/ui/const-generics/issues/issue-71611.min.stderr index b01936f4d25..6f6a9fc21a6 100644 --- a/tests/ui/const-generics/issues/issue-71611.min.stderr +++ b/tests/ui/const-generics/issues/issue-71611.min.stderr @@ -6,14 +6,6 @@ LL | fn func<A, const F: fn(inner: A)>(outer: A) { | = note: type parameters may not be used in the type of const parameters -error: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71611.rs:5:21 - | -LL | fn func<A, const F: fn(inner: A)>(outer: A) { - | ^^^^^^^^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71611.rs b/tests/ui/const-generics/issues/issue-71611.rs index c917f66818b..92930092482 100644 --- a/tests/ui/const-generics/issues/issue-71611.rs +++ b/tests/ui/const-generics/issues/issue-71611.rs @@ -3,8 +3,7 @@ #![cfg_attr(full, allow(incomplete_features))] fn func<A, const F: fn(inner: A)>(outer: A) { - //[min]~^ ERROR: using function pointers as const generic parameters is forbidden - //~^^ ERROR: the type of const parameters must not depend on other generic parameters + //~^ ERROR: the type of const parameters must not depend on other generic parameters F(outer); } diff --git a/tests/ui/consts/const-err4.rs b/tests/ui/consts/const-err-enum-discriminant.rs index 107dc3f8234..ebb3e551ba8 100644 --- a/tests/ui/consts/const-err4.rs +++ b/tests/ui/consts/const-err-enum-discriminant.rs @@ -1,4 +1,3 @@ -// stderr-per-bitwidth #[derive(Copy, Clone)] union Foo { a: isize, diff --git a/tests/ui/consts/const-err4.32bit.stderr b/tests/ui/consts/const-err-enum-discriminant.stderr index 582a848ca60..7cf34595dc9 100644 --- a/tests/ui/consts/const-err4.32bit.stderr +++ b/tests/ui/consts/const-err-enum-discriminant.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const-err4.rs:9:21 + --> $DIR/const-err-enum-discriminant.rs:8:21 | LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], | ^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory diff --git a/tests/ui/consts/const-err4.64bit.stderr b/tests/ui/consts/const-err4.64bit.stderr deleted file mode 100644 index 582a848ca60..00000000000 --- a/tests/ui/consts/const-err4.64bit.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0080]: evaluation of constant value failed - --> $DIR/const-err4.rs:9:21 - | -LL | Boo = [unsafe { Foo { b: () }.a }; 4][3], - | ^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. 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 01fb8153cf3..c748af608d1 100644 --- a/tests/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/tests/ui/consts/const-eval/const-eval-query-stack.stderr @@ -1,4 +1,4 @@ -error[E0080]: evaluation of constant value failed +error: internal compiler error[E0080]: evaluation of constant value failed --> $DIR/const-eval-query-stack.rs:16:16 | LL | const X: i32 = 1 / 0; diff --git a/tests/ui/coroutine/clone-rpit.next.stderr b/tests/ui/coroutine/clone-rpit.next.stderr new file mode 100644 index 00000000000..2dbdbcc7b05 --- /dev/null +++ b/tests/ui/coroutine/clone-rpit.next.stderr @@ -0,0 +1,52 @@ +error[E0391]: cycle detected when type-checking `foo` + --> $DIR/clone-rpit.rs:12:1 + | +LL | pub fn foo<'a, 'b>() -> impl Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires coroutine witness types for `foo::{closure#0}`... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ +note: ...which requires promoting constants in MIR for `foo::{closure#0}`... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ +note: ...which requires preparing `foo::{closure#0}` for borrow checking... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ +note: ...which requires checking if `foo::{closure#0}` contains FFI-unwind calls... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ +note: ...which requires building MIR for `foo::{closure#0}`... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ +note: ...which requires match-checking `foo::{closure#0}`... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ +note: ...which requires type-checking `foo::{closure#0}`... + --> $DIR/clone-rpit.rs:13:5 + | +LL | move |_: ()| { + | ^^^^^^^^^^^^ + = note: ...which again requires type-checking `foo`, completing the cycle +note: cycle used when computing type of opaque `foo::{opaque#0}` + --> $DIR/clone-rpit.rs:12:25 + | +LL | pub fn foo<'a, 'b>() -> impl Clone { + | ^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/coroutine/clone-rpit.rs b/tests/ui/coroutine/clone-rpit.rs index cbd28f88fcb..22a553c83d6 100644 --- a/tests/ui/coroutine/clone-rpit.rs +++ b/tests/ui/coroutine/clone-rpit.rs @@ -1,6 +1,7 @@ // revisions: current next //[next] compile-flags: -Znext-solver -// check-pass +//[current] check-pass +//[next] known-bug: trait-system-refactor-initiative#82 #![feature(coroutines, coroutine_trait, coroutine_clone)] diff --git a/tests/ui/derives/issue-97343.rs b/tests/ui/derives/issue-97343.rs index 91f0aa376e9..6f0e4d55aeb 100644 --- a/tests/ui/derives/issue-97343.rs +++ b/tests/ui/derives/issue-97343.rs @@ -2,7 +2,6 @@ use std::fmt::Debug; #[derive(Debug)] pub struct Irrelevant<Irrelevant> { //~ ERROR type arguments are not allowed on type parameter - //~^ ERROR `Irrelevant` must be used irrelevant: Irrelevant, } diff --git a/tests/ui/derives/issue-97343.stderr b/tests/ui/derives/issue-97343.stderr index 45612ae6f47..efb2fb70f5a 100644 --- a/tests/ui/derives/issue-97343.stderr +++ b/tests/ui/derives/issue-97343.stderr @@ -16,16 +16,6 @@ LL | pub struct Irrelevant<Irrelevant> { | ^^^^^^^^^^ = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0210]: type parameter `Irrelevant` must be used as the type parameter for some local type (e.g., `MyStruct<Irrelevant>`) - --> $DIR/issue-97343.rs:4:23 - | -LL | pub struct Irrelevant<Irrelevant> { - | ^^^^^^^^^^ type parameter `Irrelevant` must be used as the type parameter for some local type - | - = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local - = note: only traits defined in the current crate can be implemented for a type parameter - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0109, E0210. -For more information about an error, try `rustc --explain E0109`. +For more information about this error, try `rustc --explain E0109`. diff --git a/tests/ui/error-codes/E0227.rs b/tests/ui/error-codes/E0227.rs index 4dd4da55fa3..bab6d8af476 100644 --- a/tests/ui/error-codes/E0227.rs +++ b/tests/ui/error-codes/E0227.rs @@ -6,8 +6,6 @@ trait FooBar<'foo, 'bar>: Foo<'foo> + Bar<'bar> {} struct Baz<'foo, 'bar> { baz: dyn FooBar<'foo, 'bar>, //~^ ERROR ambiguous lifetime bound, explicit lifetime bound required - //~| ERROR lifetime bound not satisfied } -fn main() { -} +fn main() {} diff --git a/tests/ui/error-codes/E0227.stderr b/tests/ui/error-codes/E0227.stderr index 6338034a022..c77a2e98af7 100644 --- a/tests/ui/error-codes/E0227.stderr +++ b/tests/ui/error-codes/E0227.stderr @@ -4,24 +4,6 @@ error[E0227]: ambiguous lifetime bound, explicit lifetime bound required LL | baz: dyn FooBar<'foo, 'bar>, | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0478]: lifetime bound not satisfied - --> $DIR/E0227.rs:7:10 - | -LL | baz: dyn FooBar<'foo, 'bar>, - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: lifetime parameter instantiated with the lifetime `'bar` as defined here - --> $DIR/E0227.rs:6:18 - | -LL | struct Baz<'foo, 'bar> { - | ^^^^ -note: but lifetime parameter must outlive the lifetime `'foo` as defined here - --> $DIR/E0227.rs:6:12 - | -LL | struct Baz<'foo, 'bar> { - | ^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0227, E0478. -For more information about an error, try `rustc --explain E0227`. +For more information about this error, try `rustc --explain E0227`. diff --git a/tests/ui/extenv/extenv-env-overload.rs b/tests/ui/extenv/extenv-env-overload.rs index b82bb2fe966..8b3b565fe83 100644 --- a/tests/ui/extenv/extenv-env-overload.rs +++ b/tests/ui/extenv/extenv-env-overload.rs @@ -1,6 +1,6 @@ // run-pass // rustc-env:MY_VAR=tadam -// compile-flags: --env MY_VAR=123abc -Zunstable-options +// compile-flags: --env-set MY_VAR=123abc -Zunstable-options // This test ensures that variables provided with `--env` take precedence over // variables from environment. diff --git a/tests/ui/extenv/extenv-env.rs b/tests/ui/extenv/extenv-env.rs index 9fda52b8941..051ea214c1b 100644 --- a/tests/ui/extenv/extenv-env.rs +++ b/tests/ui/extenv/extenv-env.rs @@ -1,4 +1,4 @@ -// compile-flags: --env FOO=123abc -Zunstable-options +// compile-flags: --env-set FOO=123abc -Zunstable-options // run-pass fn main() { assert_eq!(env!("FOO"), "123abc"); diff --git a/tests/ui/extenv/extenv-not-env.rs b/tests/ui/extenv/extenv-not-env.rs index d6c4a43b003..b0355e073e4 100644 --- a/tests/ui/extenv/extenv-not-env.rs +++ b/tests/ui/extenv/extenv-not-env.rs @@ -1,6 +1,6 @@ // run-pass // rustc-env:MY_ENV=/ -// Ensures that variables not defined through `--env` are still available. +// Ensures that variables not defined through `--env-set` are still available. fn main() { assert!(!env!("MY_ENV").is_empty()); diff --git a/tests/ui/extern-flag/empty-extern-arg.stderr b/tests/ui/extern-flag/empty-extern-arg.stderr index 54b5e66fc21..79efcc5d8b0 100644 --- a/tests/ui/extern-flag/empty-extern-arg.stderr +++ b/tests/ui/extern-flag/empty-extern-arg.stderr @@ -2,10 +2,10 @@ error: extern location for std does not exist: error: `#[panic_handler]` function required, but not found -error: language item required, but not found: `eh_personality` +error: unwinding panics are not supported without std | - = note: this can occur when a binary crate with `#![no_std]` is compiled for a target where `eh_personality` is defined in the standard library - = help: you may be able to compile for a target that doesn't need `eh_personality`, specify a target with `--target` or in `.cargo/config` + = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem error: aborting due to 3 previous errors diff --git a/tests/ui/feature-gates/env-flag.rs b/tests/ui/feature-gates/env-flag.rs index 9dfda2584fb..598773cf3e4 100644 --- a/tests/ui/feature-gates/env-flag.rs +++ b/tests/ui/feature-gates/env-flag.rs @@ -1,3 +1,3 @@ -// compile-flags: --env A=B +// compile-flags: --env-set A=B fn main() {} diff --git a/tests/ui/feature-gates/env-flag.stderr b/tests/ui/feature-gates/env-flag.stderr index 5cb18cef9fb..a9fa1b65ea1 100644 --- a/tests/ui/feature-gates/env-flag.stderr +++ b/tests/ui/feature-gates/env-flag.stderr @@ -1,2 +1,2 @@ -error: the `-Z unstable-options` flag must also be passed to enable the flag `env` +error: the `-Z unstable-options` flag must also be passed to enable the flag `env-set` diff --git a/tests/ui/generic-associated-types/issue-71176.rs b/tests/ui/generic-associated-types/issue-71176.rs index e58b6f6091e..f0e162d825f 100644 --- a/tests/ui/generic-associated-types/issue-71176.rs +++ b/tests/ui/generic-associated-types/issue-71176.rs @@ -9,9 +9,6 @@ impl Provider for () { struct Holder<B> { inner: Box<dyn Provider<A = B>>, //~^ ERROR: missing generics for associated type - //~| ERROR: missing generics for associated type - //~| ERROR: missing generics for associated type - //~| ERROR: the trait `Provider` cannot be made into an object } fn main() { diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index a1913bb618b..ed837f34753 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -14,57 +14,6 @@ help: add missing lifetime argument LL | inner: Box<dyn Provider<A<'a> = B>>, | ++++ -error[E0107]: missing generics for associated type `Provider::A` - --> $DIR/issue-71176.rs:10:27 - | -LL | inner: Box<dyn Provider<A = B>>, - | ^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-71176.rs:2:10 - | -LL | type A<'a>; - | ^ -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | inner: Box<dyn Provider<A<'a> = B>>, - | ++++ - -error[E0107]: missing generics for associated type `Provider::A` - --> $DIR/issue-71176.rs:10:27 - | -LL | inner: Box<dyn Provider<A = B>>, - | ^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-71176.rs:2:10 - | -LL | type A<'a>; - | ^ -- - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: add missing lifetime argument - | -LL | inner: Box<dyn Provider<A<'a> = B>>, - | ++++ - -error[E0038]: the trait `Provider` cannot be made into an object - --> $DIR/issue-71176.rs:10:14 - | -LL | inner: Box<dyn Provider<A = B>>, - | ^^^^^^^^^^^^^^^^^^^ `Provider` cannot be made into an object - | -note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> - --> $DIR/issue-71176.rs:2:10 - | -LL | trait Provider { - | -------- this trait cannot be made into an object... -LL | type A<'a>; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = help: only type `()` implements the trait, consider using it directly instead - -error: aborting due to 4 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/unknown-lifetime-ice-119827.rs b/tests/ui/generic-associated-types/unknown-lifetime-ice-119827.rs new file mode 100644 index 00000000000..cd3acf9bc41 --- /dev/null +++ b/tests/ui/generic-associated-types/unknown-lifetime-ice-119827.rs @@ -0,0 +1,16 @@ +trait Foo { + type Context<'c> + where + Self: 'c; +} + +impl Foo for Box<dyn Foo> {} +//~^ ERROR `Foo` cannot be made into an object +//~| ERROR `Foo` cannot be made into an object +//~| ERROR cycle detected +//~| ERROR cycle detected +//~| ERROR cycle detected +//~| ERROR the trait bound `Box<(dyn Foo + 'static)>: Foo` is not satisfied +//~| ERROR not all trait items implemented + +fn main() {} diff --git a/tests/ui/generic-associated-types/unknown-lifetime-ice-119827.stderr b/tests/ui/generic-associated-types/unknown-lifetime-ice-119827.stderr new file mode 100644 index 00000000000..8e6b69f7461 --- /dev/null +++ b/tests/ui/generic-associated-types/unknown-lifetime-ice-119827.stderr @@ -0,0 +1,119 @@ +error[E0391]: cycle detected when computing type of `<impl at $DIR/unknown-lifetime-ice-119827.rs:7:1: 7:26>` + --> $DIR/unknown-lifetime-ice-119827.rs:7:1 + | +LL | impl Foo for Box<dyn Foo> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires finding trait impls of `Foo`... + --> $DIR/unknown-lifetime-ice-119827.rs:1:1 + | +LL | trait Foo { + | ^^^^^^^^^ + = note: ...which again requires computing type of `<impl at $DIR/unknown-lifetime-ice-119827.rs:7:1: 7:26>`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/unknown-lifetime-ice-119827.rs:1:1 + | +LL | / trait Foo { +LL | | type Context<'c> +LL | | where +LL | | Self: 'c; +... | +LL | | +LL | | fn main() {} + | |____________^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0391]: cycle detected when computing type of `<impl at $DIR/unknown-lifetime-ice-119827.rs:7:1: 7:26>` + --> $DIR/unknown-lifetime-ice-119827.rs:7:1 + | +LL | impl Foo for Box<dyn Foo> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which immediately requires computing type of `<impl at $DIR/unknown-lifetime-ice-119827.rs:7:1: 7:26>` again +note: cycle used when collecting item types in top-level module + --> $DIR/unknown-lifetime-ice-119827.rs:1:1 + | +LL | / trait Foo { +LL | | type Context<'c> +LL | | where +LL | | Self: 'c; +... | +LL | | +LL | | fn main() {} + | |____________^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0391]: cycle detected when computing type of `<impl at $DIR/unknown-lifetime-ice-119827.rs:7:1: 7:26>` + --> $DIR/unknown-lifetime-ice-119827.rs:7:1 + | +LL | impl Foo for Box<dyn Foo> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which immediately requires computing type of `<impl at $DIR/unknown-lifetime-ice-119827.rs:7:1: 7:26>` again +note: cycle used when collecting item types in top-level module + --> $DIR/unknown-lifetime-ice-119827.rs:1:1 + | +LL | / trait Foo { +LL | | type Context<'c> +LL | | where +LL | | Self: 'c; +... | +LL | | +LL | | fn main() {} + | |____________^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/unknown-lifetime-ice-119827.rs:7:22 + | +LL | impl Foo for Box<dyn Foo> {} + | ^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/unknown-lifetime-ice-119827.rs:2:10 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | type Context<'c> + | ^^^^^^^ ...because it contains the generic associated type `Context` + = help: consider moving `Context` to another trait + = help: only type `{type error}` implements the trait, consider using it directly instead + +error[E0277]: the trait bound `Box<(dyn Foo + 'static)>: Foo` is not satisfied + --> $DIR/unknown-lifetime-ice-119827.rs:7:14 + | +LL | impl Foo for Box<dyn Foo> {} + | ^^^^^^^^^^^^ the trait `Foo` is not implemented for `Box<(dyn Foo + 'static)>` + | + = help: the trait `Foo` is implemented for `Box<(dyn Foo + 'static)>` + +error[E0038]: the trait `Foo` cannot be made into an object + --> $DIR/unknown-lifetime-ice-119827.rs:7:14 + | +LL | impl Foo for Box<dyn Foo> {} + | ^^^^^^^^^^^^ `Foo` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/unknown-lifetime-ice-119827.rs:2:10 + | +LL | trait Foo { + | --- this trait cannot be made into an object... +LL | type Context<'c> + | ^^^^^^^ ...because it contains the generic associated type `Context` + = help: consider moving `Context` to another trait + = help: only type `std::boxed::Box<(dyn Foo + 'static)>` implements the trait, consider using it directly instead + +error[E0046]: not all trait items implemented, missing: `Context` + --> $DIR/unknown-lifetime-ice-119827.rs:7:1 + | +LL | type Context<'c> + | ---------------- `Context` from trait +... +LL | impl Foo for Box<dyn Foo> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Context` in implementation + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0038, E0046, E0277, E0391. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/hygiene/panic-location.run.stderr b/tests/ui/hygiene/panic-location.run.stderr index 5c552411da7..5824ef31211 100644 --- a/tests/ui/hygiene/panic-location.run.stderr +++ b/tests/ui/hygiene/panic-location.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at library/alloc/src/raw_vec.rs:571:5: +thread 'main' panicked at library/alloc/src/raw_vec.rs:570:5: capacity overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/ui/impl-trait/eagerly-reveal-in-local-body.rs b/tests/ui/impl-trait/eagerly-reveal-in-local-body.rs new file mode 100644 index 00000000000..a08c2c8765b --- /dev/null +++ b/tests/ui/impl-trait/eagerly-reveal-in-local-body.rs @@ -0,0 +1,13 @@ +// check-pass +// compile-flags: -Znext-solver + +#![feature(type_alias_impl_trait)] + +fn main() { + type Tait = impl Sized; + struct S { + i: i32, + } + let x: Tait = S { i: 0 }; + println!("{}", x.i); +} diff --git a/tests/ui/impl-trait/issues/issue-86800.stderr b/tests/ui/impl-trait/issues/issue-86800.stderr index 07ba8eb021b..7af4846a959 100644 --- a/tests/ui/impl-trait/issues/issue-86800.stderr +++ b/tests/ui/impl-trait/issues/issue-86800.stderr @@ -4,7 +4,7 @@ error: unconstrained opaque type LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -error[E0792]: expected generic lifetime parameter, found `'_` +error: internal compiler error[E0792]: expected generic lifetime parameter, found `'_` --> $DIR/issue-86800.rs:39:5 | LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>; diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr index 11b3c4ef007..df457c13e70 100644 --- a/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr @@ -1,5 +1,5 @@ error[E0733]: recursion in a coroutine requires boxing - --> $DIR/recursive-coroutine-indirect.rs:6:5 + --> $DIR/recursive-coroutine-indirect.rs:10:5 | LL | move || { | ^^^^^^^ diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr index 11b3c4ef007..df457c13e70 100644 --- a/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr @@ -1,5 +1,5 @@ error[E0733]: recursion in a coroutine requires boxing - --> $DIR/recursive-coroutine-indirect.rs:6:5 + --> $DIR/recursive-coroutine-indirect.rs:10:5 | LL | move || { | ^^^^^^^ diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.rs b/tests/ui/impl-trait/recursive-coroutine-indirect.rs index 4f8d4d33050..99b6be3358f 100644 --- a/tests/ui/impl-trait/recursive-coroutine-indirect.rs +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.rs @@ -1,5 +1,9 @@ // revisions: current next //[next] compile-flags: -Znext-solver + +//[next] build-fail +// Deeply normalizing writeback results of opaques makes this into a post-mono error :( + #![feature(coroutines)] #![allow(unconditional_recursion)] fn coroutine_hold() -> impl Sized { diff --git a/tests/ui/issues/issue-3214.rs b/tests/ui/issues/issue-3214.rs index 03d8d6ba246..b2c27f5be95 100644 --- a/tests/ui/issues/issue-3214.rs +++ b/tests/ui/issues/issue-3214.rs @@ -5,7 +5,6 @@ fn foo<T>() { impl<T> Drop for Foo<T> { //~^ ERROR struct takes 0 generic arguments but 1 generic argument - //~| ERROR `T` is not constrained fn drop(&mut self) {} } } diff --git a/tests/ui/issues/issue-3214.stderr b/tests/ui/issues/issue-3214.stderr index 26ac6d39f60..5b57c1baf90 100644 --- a/tests/ui/issues/issue-3214.stderr +++ b/tests/ui/issues/issue-3214.stderr @@ -22,13 +22,7 @@ note: struct defined here, with 0 generic parameters LL | struct Foo { | ^^^ -error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates - --> $DIR/issue-3214.rs:6:10 - | -LL | impl<T> Drop for Foo<T> { - | ^ unconstrained type parameter - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0107, E0207, E0401. +Some errors have detailed explanations: E0107, E0401. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/lang-items/required-lang-item.rs b/tests/ui/lang-items/required-lang-item.rs index 3b17c5b7255..7f8933ac2ad 100644 --- a/tests/ui/lang-items/required-lang-item.rs +++ b/tests/ui/lang-items/required-lang-item.rs @@ -1,11 +1,10 @@ -// build-fail +// edition: 2018 #![feature(lang_items, no_core)] #![no_core] +#![no_main] #[lang="copy"] pub trait Copy { } #[lang="sized"] pub trait Sized { } -// error-pattern:requires `start` lang_item - -fn main() {} +async fn x() {} //~ ERROR requires `ResumeTy` lang_item diff --git a/tests/ui/lang-items/required-lang-item.stderr b/tests/ui/lang-items/required-lang-item.stderr index bb53d336bb2..13c07ee6529 100644 --- a/tests/ui/lang-items/required-lang-item.stderr +++ b/tests/ui/lang-items/required-lang-item.stderr @@ -1,4 +1,8 @@ -error: requires `start` lang_item +error: requires `ResumeTy` lang_item + --> $DIR/required-lang-item.rs:10:14 + | +LL | async fn x() {} + | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/layout/issue-84108.rs b/tests/ui/layout/issue-84108.rs index af21d1d6210..44d6ac8db72 100644 --- a/tests/ui/layout/issue-84108.rs +++ b/tests/ui/layout/issue-84108.rs @@ -8,8 +8,6 @@ static FOO: (dyn AsRef<OsStr>, u8) = ("hello", 42); const BAR: (&Path, [u8], usize) = ("hello", [], 42); //~^ ERROR cannot find type `Path` in this scope -//~| ERROR the size for values of type `[u8]` cannot be known at compilation time -//~| ERROR mismatched types static BAZ: ([u8], usize) = ([], 0); //~^ ERROR the size for values of type `[u8]` cannot be known at compilation time diff --git a/tests/ui/layout/issue-84108.stderr b/tests/ui/layout/issue-84108.stderr index d6d75851034..58bddb069fc 100644 --- a/tests/ui/layout/issue-84108.stderr +++ b/tests/ui/layout/issue-84108.stderr @@ -21,25 +21,7 @@ LL + use std::path::Path; | error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/issue-84108.rs:9:12 - | -LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); - | ^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `[u8]` - = note: only the last element of a tuple may have a dynamically sized type - -error[E0308]: mismatched types - --> $DIR/issue-84108.rs:9:45 - | -LL | const BAR: (&Path, [u8], usize) = ("hello", [], 42); - | ^^ expected `[u8]`, found `[_; 0]` - | - = note: expected slice `[u8]` - found array `[_; 0]` - -error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/issue-84108.rs:14:13 + --> $DIR/issue-84108.rs:12:13 | LL | static BAZ: ([u8], usize) = ([], 0); | ^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -48,7 +30,7 @@ LL | static BAZ: ([u8], usize) = ([], 0); = note: only the last element of a tuple may have a dynamically sized type error[E0308]: mismatched types - --> $DIR/issue-84108.rs:14:30 + --> $DIR/issue-84108.rs:12:30 | LL | static BAZ: ([u8], usize) = ([], 0); | ^^ expected `[u8]`, found `[_; 0]` @@ -56,7 +38,7 @@ LL | static BAZ: ([u8], usize) = ([], 0); = note: expected slice `[u8]` found array `[_; 0]` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0308, E0412. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/lifetimes/unusual-rib-combinations.rs b/tests/ui/lifetimes/unusual-rib-combinations.rs index 2f5ba98445b..a2461cff932 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.rs +++ b/tests/ui/lifetimes/unusual-rib-combinations.rs @@ -28,6 +28,5 @@ fn d<const C: S>() {} trait Foo<'a> {} struct Bar<const N: &'a (dyn for<'a> Foo<'a>)>; //~^ ERROR the type of const parameters must not depend on other generic parameters -//~| ERROR `&dyn for<'a> Foo<'a>` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/lifetimes/unusual-rib-combinations.stderr b/tests/ui/lifetimes/unusual-rib-combinations.stderr index 92a2ef2f432..e3b70232ef8 100644 --- a/tests/ui/lifetimes/unusual-rib-combinations.stderr +++ b/tests/ui/lifetimes/unusual-rib-combinations.stderr @@ -58,16 +58,7 @@ LL | fn d<const C: S>() {} = note: the only supported types are integers, `bool` and `char` = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types -error: `&dyn for<'a> Foo<'a>` is forbidden as the type of a const generic parameter - --> $DIR/unusual-rib-combinations.rs:29:21 - | -LL | struct Bar<const N: &'a (dyn for<'a> Foo<'a>)>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: the only supported types are integers, `bool` and `char` - = help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors Some errors have detailed explanations: E0106, E0214, E0308, E0770. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/lint/unused/issue-117142-invalid-remove-parens.rs b/tests/ui/lint/unused/issue-117142-invalid-remove-parens.rs new file mode 100644 index 00000000000..8af9e6f3d95 --- /dev/null +++ b/tests/ui/lint/unused/issue-117142-invalid-remove-parens.rs @@ -0,0 +1,19 @@ +// check-pass +#![warn(unused_parens)] + +fn main() { + let a: i32 = 1; + let b: i64 = 1; + + if b + a as (i64) < 0 { + println!(":D"); + } + if b + b + a as (i64) < 0 { + println!(":D"); + } + let c = a + b as (i32) < 0; + let mut x = false; + x |= false || (b as (i32) < 0); + + let d = 1 + 2 + 3 * 4 as (i32) < 10; +} diff --git a/tests/ui/no_std/no-std-no-start-binary.rs b/tests/ui/no_std/no-std-no-start-binary.rs new file mode 100644 index 00000000000..ce1c871f6a6 --- /dev/null +++ b/tests/ui/no_std/no-std-no-start-binary.rs @@ -0,0 +1,13 @@ +// compile-flags: -Cpanic=abort --emit link +// error-pattern:using `fn main` requires the standard library + +// Make sure that we don't emit an error message mentioning internal lang items. + +#![no_std] + +#[panic_handler] +fn handler(_info: &core::panic::PanicInfo<'_>) -> ! { + loop {} +} + +fn main() {} diff --git a/tests/ui/no_std/no-std-no-start-binary.stderr b/tests/ui/no_std/no-std-no-start-binary.stderr new file mode 100644 index 00000000000..dd06c234da2 --- /dev/null +++ b/tests/ui/no_std/no-std-no-start-binary.stderr @@ -0,0 +1,6 @@ +error: using `fn main` requires the standard library + | + = help: use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/no_std/no-std-unwind-binary.rs b/tests/ui/no_std/no-std-unwind-binary.rs new file mode 100644 index 00000000000..7a9dfd7a48d --- /dev/null +++ b/tests/ui/no_std/no-std-unwind-binary.rs @@ -0,0 +1,15 @@ +// error-pattern:unwinding panics are not supported without std +// needs-unwind +// compile-flags: -Cpanic=unwind + +// Make sure that we don't emit an error message mentioning internal lang items. + +#![no_std] +#![no_main] + +#[panic_handler] +fn handler(_info: &core::panic::PanicInfo<'_>) -> ! { + loop {} +} + +fn main() {} diff --git a/tests/ui/no_std/no-std-unwind-binary.stderr b/tests/ui/no_std/no-std-unwind-binary.stderr new file mode 100644 index 00000000000..a3b54fe33ab --- /dev/null +++ b/tests/ui/no_std/no-std-unwind-binary.stderr @@ -0,0 +1,7 @@ +error: unwinding panics are not supported without std + | + = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem + +error: aborting due to 1 previous error + diff --git a/tests/ui/panic-handler/weak-lang-item.rs b/tests/ui/panic-handler/weak-lang-item.rs index 14a07a9ef1b..296a2c1514f 100644 --- a/tests/ui/panic-handler/weak-lang-item.rs +++ b/tests/ui/panic-handler/weak-lang-item.rs @@ -1,6 +1,6 @@ // aux-build:weak-lang-items.rs // error-pattern: `#[panic_handler]` function required, but not found -// error-pattern: language item required, but not found: `eh_personality` +// error-pattern: unwinding panics are not supported without std // needs-unwind since it affects the error output // ignore-emscripten missing eh_catch_typeinfo lang item diff --git a/tests/ui/panic-handler/weak-lang-item.stderr b/tests/ui/panic-handler/weak-lang-item.stderr index 202f3309d03..de351d2c3e4 100644 --- a/tests/ui/panic-handler/weak-lang-item.stderr +++ b/tests/ui/panic-handler/weak-lang-item.stderr @@ -12,10 +12,10 @@ LL | extern crate core as other_core; error: `#[panic_handler]` function required, but not found -error: language item required, but not found: `eh_personality` +error: unwinding panics are not supported without std | - = note: this can occur when a binary crate with `#![no_std]` is compiled for a target where `eh_personality` is defined in the standard library - = help: you may be able to compile for a target that doesn't need `eh_personality`, specify a target with `--target` or in `.cargo/config` + = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding + = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem error: aborting due to 3 previous errors diff --git a/tests/ui/panics/default-backtrace-ice.stderr b/tests/ui/panics/default-backtrace-ice.stderr index 4b00f135047..9d27cb22ae9 100644 --- a/tests/ui/panics/default-backtrace-ice.stderr +++ b/tests/ui/panics/default-backtrace-ice.stderr @@ -1,4 +1,4 @@ -error[E0425]: cannot find value `missing_ident` in this scope +error: internal compiler error[E0425]: cannot find value `missing_ident` in this scope --> $DIR/default-backtrace-ice.rs:21:13 | LL | fn main() { missing_ident; } diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs index 8012cb652bd..f6aa39df27d 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs @@ -5,4 +5,3 @@ struct Apple((Apple, Option(Banana ? Citron))); //~| ERROR expected one of `)` or `,`, found `Citron` //~| ERROR cannot find type `Citron` in this scope [E0412] //~| ERROR parenthesized type parameters may only be used with a `Fn` trait [E0214] -//~| ERROR recursive type `Apple` has infinite size [E0072] diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr index b0d8b03ae08..71d2d7b7975 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr @@ -34,18 +34,7 @@ help: use angle brackets instead LL | struct Apple((Apple, Option<Banana ? Citron>)); | ~ ~ -error[E0072]: recursive type `Apple` has infinite size - --> $DIR/issue-103748-ICE-wrong-braces.rs:3:1 - | -LL | struct Apple((Apple, Option(Banana ? Citron))); - | ^^^^^^^^^^^^ ----- recursive without indirection - | -help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle - | -LL | struct Apple((Box<Apple>, Option(Banana ? Citron))); - | ++++ + - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0072, E0214, E0412. -For more information about an error, try `rustc --explain E0072`. +Some errors have detailed explanations: E0214, E0412. +For more information about an error, try `rustc --explain E0214`. diff --git a/tests/ui/pattern/usefulness/integer-ranges/issue-117648-overlapping_range_endpoints-false-positive.rs b/tests/ui/pattern/usefulness/integer-ranges/issue-117648-overlapping_range_endpoints-false-positive.rs new file mode 100644 index 00000000000..37fcb4b4af9 --- /dev/null +++ b/tests/ui/pattern/usefulness/integer-ranges/issue-117648-overlapping_range_endpoints-false-positive.rs @@ -0,0 +1,9 @@ +// check-pass +fn main() { + match (0i8, 0i8) { + (0, _) => {} + (..=-1, ..=0) => {} + (1.., 0..) => {} + (1.., ..=-1) | (..=-1, 1..) => {} + } +} diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs index 33c1dfd39d4..7e56880a87f 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs +++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs @@ -44,13 +44,13 @@ fn main() { match (0u8, true) { (0..=10, true) => {} (10..20, true) => {} //~ ERROR multiple patterns overlap on their endpoints - (10..20, false) => {} //~ ERROR multiple patterns overlap on their endpoints + (10..20, false) => {} _ => {} } match (true, 0u8) { (true, 0..=10) => {} (true, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints - (false, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints + (false, 10..20) => {} _ => {} } match Some(0u8) { @@ -58,4 +58,11 @@ fn main() { Some(10..20) => {} //~ ERROR multiple patterns overlap on their endpoints _ => {} } + + // The lint has false negatives when we skip some cases because of relevancy. + match (true, true, 0u8) { + (true, _, 0..=10) => {} + (_, true, 10..20) => {} + _ => {} + } } diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr index a87205d76d1..aa37bd9bc9c 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr @@ -85,17 +85,6 @@ LL | (10..20, true) => {} = note: you likely meant to write mutually exclusive ranges error: multiple patterns overlap on their endpoints - --> $DIR/overlapping_range_endpoints.rs:47:10 - | -LL | (0..=10, true) => {} - | ------ this range overlaps on `10_u8`... -LL | (10..20, true) => {} -LL | (10..20, false) => {} - | ^^^^^^ ... with this range - | - = note: you likely meant to write mutually exclusive ranges - -error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:52:16 | LL | (true, 0..=10) => {} @@ -106,17 +95,6 @@ LL | (true, 10..20) => {} = note: you likely meant to write mutually exclusive ranges error: multiple patterns overlap on their endpoints - --> $DIR/overlapping_range_endpoints.rs:53:17 - | -LL | (true, 0..=10) => {} - | ------ this range overlaps on `10_u8`... -LL | (true, 10..20) => {} -LL | (false, 10..20) => {} - | ^^^^^^ ... with this range - | - = note: you likely meant to write mutually exclusive ranges - -error: multiple patterns overlap on their endpoints --> $DIR/overlapping_range_endpoints.rs:58:14 | LL | Some(0..=10) => {} @@ -126,5 +104,5 @@ LL | Some(10..20) => {} | = note: you likely meant to write mutually exclusive ranges -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/proc-macro/env.rs b/tests/ui/proc-macro/env.rs index 1b1d1873eb3..c0edda4f7df 100644 --- a/tests/ui/proc-macro/env.rs +++ b/tests/ui/proc-macro/env.rs @@ -1,7 +1,7 @@ // aux-build:env.rs // run-pass // rustc-env: THE_CONST=1 -// compile-flags: -Zunstable-options --env THE_CONST=12 --env ANOTHER=4 +// compile-flags: -Zunstable-options --env-set THE_CONST=12 --env-set ANOTHER=4 #![crate_name = "foo"] diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs index 4cbc36f4650..52ecbcc9e2c 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs @@ -39,7 +39,6 @@ impl<T: Trait<u32, String>> Struct<T> {} trait YetAnotherTrait {} impl<T: Trait<u32, Assoc=String>, U> YetAnotherTrait for Struct<T, U> {} //~^ ERROR struct takes 1 generic argument but 2 generic arguments were supplied -//~| ERROR `U` is not constrained fn main() { diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr index 3c2b726fcce..e7ceb7372bf 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr @@ -116,13 +116,7 @@ error[E0207]: the type parameter `S` is not constrained by the impl trait, self LL | impl<T, S> Trait<T, S> for () {} | ^ unconstrained type parameter -error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:40:35 - | -LL | impl<T: Trait<u32, Assoc=String>, U> YetAnotherTrait for Struct<T, U> {} - | ^ unconstrained type parameter - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0107, E0207. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs index e1e93f79920..01a98a30895 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.rs @@ -13,11 +13,8 @@ fn main() { } fn weird0() -> impl Sized + !Sized {} -//~^ ERROR mismatched types -//~| ERROR type mismatch resolving `() == impl !Sized + Sized` +//~^ ERROR type mismatch resolving `() == impl !Sized + Sized` fn weird1() -> impl !Sized + Sized {} -//~^ ERROR mismatched types -//~| ERROR type mismatch resolving `() == impl !Sized + Sized` +//~^ ERROR type mismatch resolving `() == impl !Sized + Sized` fn weird2() -> impl !Sized {} -//~^ ERROR mismatched types -//~| ERROR type mismatch resolving `() == impl !Sized` +//~^ ERROR type mismatch resolving `() == impl !Sized` diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr index 62792761870..d803e56e817 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-bound.stderr @@ -1,50 +1,17 @@ -error[E0308]: mismatched types - --> $DIR/opaque-type-unsatisfied-bound.rs:15:36 - | -LL | fn weird0() -> impl Sized + !Sized {} - | ------------------- ^^ types differ - | | - | the expected opaque type - | - = note: expected opaque type `impl !Sized + Sized` - found unit type `()` - error[E0271]: type mismatch resolving `() == impl !Sized + Sized` --> $DIR/opaque-type-unsatisfied-bound.rs:15:16 | LL | fn weird0() -> impl Sized + !Sized {} | ^^^^^^^^^^^^^^^^^^^ types differ -error[E0308]: mismatched types - --> $DIR/opaque-type-unsatisfied-bound.rs:18:36 - | -LL | fn weird1() -> impl !Sized + Sized {} - | ------------------- ^^ types differ - | | - | the expected opaque type - | - = note: expected opaque type `impl !Sized + Sized` - found unit type `()` - error[E0271]: type mismatch resolving `() == impl !Sized + Sized` - --> $DIR/opaque-type-unsatisfied-bound.rs:18:16 + --> $DIR/opaque-type-unsatisfied-bound.rs:17:16 | LL | fn weird1() -> impl !Sized + Sized {} | ^^^^^^^^^^^^^^^^^^^ types differ -error[E0308]: mismatched types - --> $DIR/opaque-type-unsatisfied-bound.rs:21:28 - | -LL | fn weird2() -> impl !Sized {} - | ----------- ^^ types differ - | | - | the expected opaque type - | - = note: expected opaque type `impl !Sized` - found unit type `()` - error[E0271]: type mismatch resolving `() == impl !Sized` - --> $DIR/opaque-type-unsatisfied-bound.rs:21:16 + --> $DIR/opaque-type-unsatisfied-bound.rs:19:16 | LL | fn weird2() -> impl !Sized {} | ^^^^^^^^^^^ types differ @@ -63,7 +30,7 @@ note: required by a bound in `consume` LL | fn consume(_: impl Trait) {} | ^^^^^ required by this bound in `consume` -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0271, E0277, E0308. +Some errors have detailed explanations: E0271, E0277. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs index 72bca1a8910..bb2e861a1a7 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.rs @@ -3,7 +3,6 @@ #![feature(negative_bounds, unboxed_closures)] fn produce() -> impl !Fn<(u32,)> {} -//~^ ERROR mismatched types -//~| ERROR type mismatch resolving `() == impl !Fn<(u32,)>` +//~^ ERROR type mismatch resolving `() == impl !Fn<(u32,)>` fn main() {} diff --git a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr index a4fb4b2b5c4..1fd30410b00 100644 --- a/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr +++ b/tests/ui/traits/negative-bounds/opaque-type-unsatisfied-fn-bound.stderr @@ -1,21 +1,9 @@ -error[E0308]: mismatched types - --> $DIR/opaque-type-unsatisfied-fn-bound.rs:5:34 - | -LL | fn produce() -> impl !Fn<(u32,)> {} - | ---------------- ^^ types differ - | | - | the expected opaque type - | - = note: expected opaque type `impl !Fn<(u32,)>` - found unit type `()` - error[E0271]: type mismatch resolving `() == impl !Fn<(u32,)>` --> $DIR/opaque-type-unsatisfied-fn-bound.rs:5:17 | LL | fn produce() -> impl !Fn<(u32,)> {} | ^^^^^^^^^^^^^^^^ types differ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0271, E0308. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs index 4e279a84a33..8fddbd7ecdc 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs @@ -23,10 +23,10 @@ fn main() { let x = String::from("hello, world"); drop(<() as Foo>::copy_me(&x)); //~^ ERROR overflow evaluating the requirement `<() as Foo>::Item: Sized` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed` //~| ERROR overflow evaluating the requirement `String <: <() as Foo>::Item` + //~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed` //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed` - //~| ERROR overflow evaluating the requirement `<() as Foo>::Item normalizes-to _` + //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` + //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _` println!("{x}"); } diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr index ac3f19b3fe6..874644317eb 100644 --- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr @@ -52,13 +52,14 @@ LL | drop(<() as Foo>::copy_me(&x)); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) -error[E0275]: overflow evaluating the requirement `<() as Foo>::Item normalizes-to _` +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _` --> $DIR/alias-bound-unsound.rs:24:10 | LL | drop(<() as Foo>::copy_me(&x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 7 previous errors diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.rs b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.rs new file mode 100644 index 00000000000..424508dd9d9 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.rs @@ -0,0 +1,39 @@ +// compile-flags: -Znext-solver +#![feature(rustc_attrs)] + +// A test intended to check how we handle provisional results +// for a goal computed with an inductive and a coinductive stack. +// +// Unfortunately this doesn't really detect whether we've done +// something wrong but instead only showcases that we thought of +// this. +// +// FIXME(-Znext-solver=coinductive): With the new coinduction approach +// the same goal stack can be both inductive and coinductive, depending +// on why we're proving a specific nested goal. Rewrite this test +// at that point instead of relying on `BInd`. + + +#[rustc_coinductive] +trait A {} + +#[rustc_coinductive] +trait B {} +trait BInd {} +impl<T: ?Sized + B> BInd for T {} + +#[rustc_coinductive] +trait C {} +trait CInd {} +impl<T: ?Sized + C> CInd for T {} + +impl<T: ?Sized + BInd + C> A for T {} +impl<T: ?Sized + CInd + C> B for T {} +impl<T: ?Sized + B + A> C for T {} + +fn impls_a<T: A>() {} + +fn main() { + impls_a::<()>(); + //~^ ERROR overflow evaluating the requirement `(): A` +} diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr new file mode 100644 index 00000000000..e828bdeb16b --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr @@ -0,0 +1,16 @@ +error[E0275]: overflow evaluating the requirement `(): A` + --> $DIR/mixed-cycles-1.rs:37:15 + | +LL | impls_a::<()>(); + | ^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mixed_cycles_1`) +note: required by a bound in `impls_a` + --> $DIR/mixed-cycles-1.rs:34:15 + | +LL | fn impls_a<T: A>() {} + | ^ required by this bound in `impls_a` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs new file mode 100644 index 00000000000..300f30ecad2 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs @@ -0,0 +1,32 @@ +// compile-flags: -Znext-solver +#![feature(rustc_attrs)] + +// A test showcasing that the solver may need to +// compute a goal which is already in the provisional +// cache. +// +// However, given that `(): BInd` and `(): B` are currently distinct +// goals, this is actually not possible right now. +// +// FIXME(-Znext-solver=coinductive): With the new coinduction approach +// the same goal stack can be both inductive and coinductive, depending +// on why we're proving a specific nested goal. Rewrite this test +// at that point. + +#[rustc_coinductive] +trait A {} + +#[rustc_coinductive] +trait B {} +trait BInd {} +impl<T: ?Sized + B> BInd for T {} + +impl<T: ?Sized + BInd + B> A for T {} +impl<T: ?Sized + BInd> B for T {} + +fn impls_a<T: A>() {} + +fn main() { + impls_a::<()>(); + //~^ ERROR overflow evaluating the requirement `(): A` +} diff --git a/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr new file mode 100644 index 00000000000..ec13093f707 --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr @@ -0,0 +1,16 @@ +error[E0275]: overflow evaluating the requirement `(): A` + --> $DIR/mixed-cycles-2.rs:30:15 + | +LL | impls_a::<()>(); + | ^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mixed_cycles_2`) +note: required by a bound in `impls_a` + --> $DIR/mixed-cycles-2.rs:27:15 + | +LL | fn impls_a<T: A>() {} + | ^ required by this bound in `impls_a` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs b/tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs new file mode 100644 index 00000000000..ab7c4c7601b --- /dev/null +++ b/tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs @@ -0,0 +1,70 @@ +// compile-flags: -Znext-solver +// check-pass +#![feature(rustc_attrs)] + +// A test showcasing that using a provisional cache can differ +// from only tracking stack entries. +// +// Without a provisional cache, we have the following proof tree: +// +// - (): A +// - (): B +// - (): A (coinductive cycle) +// - (): C +// - (): B (coinductive cycle) +// - (): C +// - (): B +// - (): A (coinductive cycle) +// - (): C (coinductive cycle) +// +// While with the current provisional cache implementation we get: +// +// - (): A +// - (): B +// - (): A (coinductive cycle) +// - (): C +// - (): B (coinductive cycle) +// - (): C +// - (): B (provisional cache hit) +// +// Note that if even if we were to expand the provisional cache hit, +// the proof tree would still be different: +// +// - (): A +// - (): B +// - (): A (coinductive cycle) +// - (): C +// - (): B (coinductive cycle) +// - (): C +// - (): B (provisional cache hit, expanded) +// - (): A (coinductive cycle) +// - (): C +// - (): B (coinductive cycle) +// +// Theoretically, this can result in observable behavior differences +// due to incompleteness. However, this would require a very convoluted +// example and would still be sound. The difference is determinstic +// and can not be observed outside of the cycle itself as we don't move +// non-root cycle participants into the global cache. +// +// For an example of how incompleteness could impact the observable behavior here, see +// +// tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs +#[rustc_coinductive] +trait A {} + +#[rustc_coinductive] +trait B {} + +#[rustc_coinductive] +trait C {} + +impl<T: ?Sized + B + C> A for T {} +impl<T: ?Sized + A + C> B for T {} +impl<T: ?Sized + B> C for T {} + +fn impls_a<T: A>() {} + +fn main() { + impls_a::<()>(); +} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs index 327ef865de9..71b1502d775 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.rs @@ -13,8 +13,10 @@ fn needs_bar<S: Bar>() {} fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() { needs_bar::<T::Assoc1>(); - //~^ ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar` - //~| ERROR overflow evaluating the requirement `<T as Foo2>::Assoc2` + //~^ ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _` + //~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _` + //~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1 == _` + //~| ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar` } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr index eda62b99c44..bad6820f738 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization-2.stderr @@ -11,7 +11,7 @@ note: required by a bound in `needs_bar` LL | fn needs_bar<S: Bar>() {} | ^^^ required by this bound in `needs_bar` -error[E0275]: overflow evaluating the requirement `<T as Foo2>::Assoc2` +error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _` --> $DIR/recursive-self-normalization-2.rs:15:5 | LL | needs_bar::<T::Assoc1>(); @@ -19,6 +19,23 @@ LL | needs_bar::<T::Assoc1>(); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`) -error: aborting due to 2 previous errors +error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _` + --> $DIR/recursive-self-normalization-2.rs:15:5 + | +LL | needs_bar::<T::Assoc1>(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1 == _` + --> $DIR/recursive-self-normalization-2.rs:15:17 + | +LL | needs_bar::<T::Assoc1>(); + | ^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`) + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs index f45d208e666..809a6a59ca6 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs @@ -9,8 +9,10 @@ fn needs_bar<S: Bar>() {} fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() { needs_bar::<T::Assoc>(); - //~^ ERROR overflow evaluating the requirement `<T as Foo>::Assoc: Bar` - //~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc` [E0275] + //~^ ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _` + //~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _` + //~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc == _` + //~| ERROR overflow evaluating the requirement `<T as Foo>::Assoc: Bar` } fn main() {} diff --git a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr index b0a0a69761a..80005d344ba 100644 --- a/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr +++ b/tests/ui/traits/next-solver/overflow/recursive-self-normalization.stderr @@ -11,7 +11,7 @@ note: required by a bound in `needs_bar` LL | fn needs_bar<S: Bar>() {} | ^^^ required by this bound in `needs_bar` -error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc` +error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _` --> $DIR/recursive-self-normalization.rs:11:5 | LL | needs_bar::<T::Assoc>(); @@ -19,6 +19,23 @@ LL | needs_bar::<T::Assoc>(); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`) -error: aborting due to 2 previous errors +error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _` + --> $DIR/recursive-self-normalization.rs:11:5 + | +LL | needs_bar::<T::Assoc>(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc == _` + --> $DIR/recursive-self-normalization.rs:11:17 + | +LL | needs_bar::<T::Assoc>(); + | ^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`) + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/treat-err-as-bug/eagerly-emit.rs b/tests/ui/treat-err-as-bug/eagerly-emit.rs new file mode 100644 index 00000000000..5f32f5a1d94 --- /dev/null +++ b/tests/ui/treat-err-as-bug/eagerly-emit.rs @@ -0,0 +1,11 @@ +// compile-flags: -Zeagerly-emit-delayed-bugs + +trait Foo {} + +fn main() {} + +fn f() -> impl Foo { + //~^ ERROR the trait bound `i32: Foo` is not satisfied + //~| ERROR `report_selection_error` did not emit an error + 1i32 +} diff --git a/tests/ui/treat-err-as-bug/eagerly-emit.stderr b/tests/ui/treat-err-as-bug/eagerly-emit.stderr new file mode 100644 index 00000000000..3d25741d52d --- /dev/null +++ b/tests/ui/treat-err-as-bug/eagerly-emit.stderr @@ -0,0 +1,28 @@ +error: `report_selection_error` did not emit an error + --> $DIR/eagerly-emit.rs:7:11 + | +LL | fn f() -> impl Foo { + | ^^^^^^^^ + +error: trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/eagerly-emit.rs:7:11 + | +LL | fn f() -> impl Foo { + | ^^^^^^^^ the trait `Foo` is not implemented for `i32` +... +LL | 1i32 + | ---- return type was inferred to be `i32` here + | +help: this trait has no implementations, consider adding one + --> $DIR/eagerly-emit.rs:3:1 + | +LL | trait Foo {} + | ^^^^^^^^^ + +error: expected fulfillment errors + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/treat-err-as-bug/err.stderr b/tests/ui/treat-err-as-bug/err.stderr index 3a56445a26b..4c5d0e5ae79 100644 --- a/tests/ui/treat-err-as-bug/err.stderr +++ b/tests/ui/treat-err-as-bug/err.stderr @@ -1,4 +1,4 @@ -error[E0080]: could not evaluate static initializer +error: internal compiler error[E0080]: could not evaluate static initializer --> $DIR/err.rs:11:21 | LL | pub static C: u32 = 0 - 1; |
